review management of relationships - allow to update them with sfaadmin
[sfa.git] / sfa / planetlab / plaggregate.py
1 #!/usr/bin/python
2 from sfa.util.xrn import Xrn, hrn_to_urn, urn_to_hrn, urn_to_sliver_id
3 from sfa.util.sfatime import utcparse, datetime_to_string
4 from sfa.util.sfalogging import logger
5
6 from sfa.rspecs.rspec import RSpec
7 from sfa.rspecs.elements.hardware_type import HardwareType
8 from sfa.rspecs.elements.node import Node
9 from sfa.rspecs.elements.link import Link
10 from sfa.rspecs.elements.sliver import Sliver
11 from sfa.rspecs.elements.login import Login
12 from sfa.rspecs.elements.location import Location
13 from sfa.rspecs.elements.interface import Interface
14 from sfa.rspecs.elements.services import Services
15 from sfa.rspecs.elements.pltag import PLTag
16 from sfa.rspecs.version_manager import VersionManager
17
18 from sfa.util.plxrn import PlXrn, hostname_to_urn, hrn_to_pl_slicename
19 from sfa.planetlab.vlink import get_tc_rate
20 from sfa.planetlab.topology import Topology
21
22
23 class PlAggregate:
24
25     def __init__(self, driver):
26         self.driver = driver
27  
28     def get_sites(self, filter={}):
29         sites = {}
30         for site in self.driver.shell.GetSites(filter):
31             sites[site['site_id']] = site
32         return sites
33
34     def get_interfaces(self, filter={}):
35         interfaces = {}
36         for interface in self.driver.shell.GetInterfaces(filter):
37             iface = Interface()
38             if interface['bwlimit']:
39                 interface['bwlimit'] = str(int(interface['bwlimit'])/1000)
40             interfaces[interface['interface_id']] = interface
41         return interfaces
42
43     def get_links(self, sites, nodes, interfaces):
44         
45         topology = Topology() 
46         links = []
47         for (site_id1, site_id2) in topology:
48             site_id1 = int(site_id1)
49             site_id2 = int(site_id2)
50             link = Link()
51             if not site_id1 in sites or site_id2 not in sites:
52                 continue
53             site1 = sites[site_id1]
54             site2 = sites[site_id2]
55             # get hrns
56             site1_hrn = self.driver.hrn + '.' + site1['login_base']
57             site2_hrn = self.driver.hrn + '.' + site2['login_base']
58
59             for s1_node_id in site1['node_ids']:
60                 for s2_node_id in site2['node_ids']:
61                     if s1_node_id not in nodes or s2_node_id not in nodes:
62                         continue
63                     node1 = nodes[s1_node_id]
64                     node2 = nodes[s2_node_id]
65                     # set interfaces
66                     # just get first interface of the first node
67                     if1_xrn = PlXrn(auth=self.driver.hrn, interface='node%s:eth0' % (node1['node_id']))
68                     if1_ipv4 = interfaces[node1['interface_ids'][0]]['ip']
69                     if2_xrn = PlXrn(auth=self.driver.hrn, interface='node%s:eth0' % (node2['node_id']))
70                     if2_ipv4 = interfaces[node2['interface_ids'][0]]['ip']
71
72                     if1 = Interface({'component_id': if1_xrn.urn, 'ipv4': if1_ipv4} )
73                     if2 = Interface({'component_id': if2_xrn.urn, 'ipv4': if2_ipv4} )
74
75                     # set link
76                     link = Link({'capacity': '1000000', 'latency': '0', 'packet_loss': '0', 'type': 'ipv4'})
77                     link['interface1'] = if1
78                     link['interface2'] = if2
79                     link['component_name'] = "%s:%s" % (site1['login_base'], site2['login_base'])
80                     link['component_id'] = PlXrn(auth=self.driver.hrn, interface=link['component_name']).get_urn()
81                     link['component_manager_id'] =  hrn_to_urn(self.driver.hrn, 'authority+am')
82                     links.append(link)
83
84         return links
85
86     def get_node_tags(self, filter={}):
87         node_tags = {}
88         for node_tag in self.driver.shell.GetNodeTags(filter):
89             node_tags[node_tag['node_tag_id']] = node_tag
90         return node_tags
91
92     def get_pl_initscripts(self, filter={}):
93         pl_initscripts = {}
94         filter.update({'enabled': True})
95         for initscript in self.driver.shell.GetInitScripts(filter):
96             pl_initscripts[initscript['initscript_id']] = initscript
97         return pl_initscripts
98
99
100     def get_slice_and_slivers(self, slice_xrn):
101         """
102         Returns a dict of slivers keyed on the sliver's node_id
103         """
104         slivers = {}
105         slice = None
106         if not slice_xrn:
107             return (slice, slivers)
108         slice_urn = hrn_to_urn(slice_xrn, 'slice')
109         slice_hrn, _ = urn_to_hrn(slice_xrn)
110         slice_name = hrn_to_pl_slicename(slice_hrn)
111         slices = self.driver.shell.GetSlices(slice_name)
112         if not slices:
113             return (slice, slivers)
114         slice = slices[0]
115
116         # sort slivers by node id    
117         for node_id in slice['node_ids']:
118             sliver = Sliver({'sliver_id': urn_to_sliver_id(slice_urn, slice['slice_id'], node_id, authority=self.driver.hrn),
119                              'name': slice['name'],
120                              'type': 'plab-vserver', 
121                              'tags': []})
122             slivers[node_id]= sliver
123
124         # sort sliver attributes by node id    
125         tags = self.driver.shell.GetSliceTags({'slice_tag_id': slice['slice_tag_ids']})
126         for tag in tags:
127             # most likely a default/global sliver attribute (node_id == None)
128             if tag['node_id'] not in slivers:
129                 sliver = Sliver({'sliver_id': urn_to_sliver_id(slice_urn, slice['slice_id'], ""),
130                                  'name': slice['name'],
131                                  'type': 'plab-vserver',
132                                  'tags': []})
133                 slivers[tag['node_id']] = sliver
134             slivers[tag['node_id']]['tags'].append(tag)
135         
136         return (slice, slivers)
137
138     def get_nodes_and_links(self, slice_xrn, slice=None,slivers=[], options={}):
139         # if we are dealing with a slice that has no node just return 
140         # and empty list    
141         if slice_xrn:
142             if not slice or not slice['node_ids']:
143                 return ([],[])
144
145         filter = {}
146         tags_filter = {}
147         if slice and 'node_ids' in slice and slice['node_ids']:
148             filter['node_id'] = slice['node_ids']
149             tags_filter=filter.copy()
150
151         geni_available = options.get('geni_available')    
152         if geni_available:
153             filter['boot_state'] = 'boot'     
154         
155         filter.update({'peer_id': None})
156         nodes = self.driver.shell.GetNodes(filter)
157        
158         site_ids = []
159         interface_ids = []
160         tag_ids = []
161         nodes_dict = {}
162         for node in nodes:
163             site_ids.append(node['site_id'])
164             interface_ids.extend(node['interface_ids'])
165             tag_ids.extend(node['node_tag_ids'])
166             nodes_dict[node['node_id']] = node
167  
168         # get sites
169         sites_dict  = self.get_sites({'site_id': site_ids}) 
170         # get interfaces
171         interfaces = self.get_interfaces({'interface_id':interface_ids}) 
172         # get tags
173         node_tags = self.get_node_tags(tags_filter)
174         # get initscripts
175         pl_initscripts = self.get_pl_initscripts()
176         
177         links = self.get_links(sites_dict, nodes_dict, interfaces)
178
179         rspec_nodes = []
180         for node in nodes:
181             # skip whitelisted nodes
182             if node['slice_ids_whitelist']:
183                 if not slice or slice['slice_id'] not in node['slice_ids_whitelist']:
184                     continue
185             rspec_node = Node()
186             # xxx how to retrieve site['login_base']
187             site_id=node['site_id']
188             site=sites_dict[site_id]
189             rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], node['hostname'])
190             rspec_node['component_name'] = node['hostname']
191             rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
192             rspec_node['authority_id'] = hrn_to_urn(PlXrn.site_hrn(self.driver.hrn, site['login_base']), 'authority+sa')
193             # do not include boot state (<available> element) in the manifest rspec
194             if not slice:     
195                 rspec_node['boot_state'] = node['boot_state']
196             rspec_node['exclusive'] = 'false'
197             rspec_node['hardware_types'] = [HardwareType({'name': 'plab-pc'}),
198                                             HardwareType({'name': 'pc'})]
199             # only doing this because protogeni rspec needs
200             # to advertise available initscripts 
201             rspec_node['pl_initscripts'] = pl_initscripts.values()
202              # add site/interface info to nodes.
203             # assumes that sites, interfaces and tags have already been prepared.
204             site = sites_dict[node['site_id']]
205             if site['longitude'] and site['latitude']:  
206                 location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'})
207                 rspec_node['location'] = location
208             rspec_node['interfaces'] = []
209             if_count=0
210             for if_id in node['interface_ids']:
211                 interface = Interface(interfaces[if_id]) 
212                 interface['ipv4'] = interface['ip']
213                 interface['component_id'] = PlXrn(auth=self.driver.hrn, 
214                                                   interface='node%s:eth%s' % (node['node_id'], if_count)).get_urn()
215                 # interfaces in the manifest need a client id
216                 if slice:
217                     interface['client_id'] = "%s:%s" % (node['node_id'], if_id)            
218                 rspec_node['interfaces'].append(interface)
219                 if_count+=1
220
221             tags = [PLTag(node_tags[tag_id]) for tag_id in node['node_tag_ids']]
222             rspec_node['tags'] = tags
223             if node['node_id'] in slivers:
224                 # add sliver info
225                 sliver = slivers[node['node_id']]
226                 rspec_node['sliver_id'] = sliver['sliver_id']
227                 rspec_node['client_id'] = node['hostname']
228                 rspec_node['slivers'] = [sliver]
229                 
230                 # slivers always provide the ssh service
231                 login = Login({'authentication': 'ssh-keys', 'hostname': node['hostname'], 'port':'22', 'username': sliver['name']})
232                 service = Services({'login': login})
233                 rspec_node['services'] = [service]
234             rspec_nodes.append(rspec_node)
235         return (rspec_nodes, links)
236              
237         
238     def get_rspec(self, slice_xrn=None, version = None, options={}):
239
240         version_manager = VersionManager()
241         version = version_manager.get_version(version)
242         if not slice_xrn:
243             rspec_version = version_manager._get_version(version.type, version.version, 'ad')
244         else:
245             rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
246
247         slice, slivers = self.get_slice_and_slivers(slice_xrn)
248         rspec = RSpec(version=rspec_version, user_options=options)
249         if slice and 'expires' in slice:
250             rspec.xml.set('expires',  datetime_to_string(utcparse(slice['expires'])))
251
252         nodes, links = self.get_nodes_and_links(slice_xrn, slice, slivers)
253         rspec.version.add_nodes(nodes)
254         rspec.version.add_links(links)
255         
256         # add sliver defaults
257         default_sliver = slivers.get(None, [])
258         if default_sliver:
259             default_sliver_attribs = default_sliver.get('tags', [])
260             for attrib in default_sliver_attribs:
261                 logger.info(attrib)
262                 rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value'])
263         return rspec.toxml()
264
265