cosmetic - this code is broken, moving the matter to Tony
[sfa.git] / sfa / planetlab / plaggregate.py
1 #!/usr/bin/python
2 from collections import defaultdict
3 from sfa.util.xrn import Xrn, hrn_to_urn, urn_to_hrn
4 from sfa.util.sfatime import utcparse, datetime_to_string
5 from sfa.util.sfalogging import logger
6
7 from sfa.rspecs.rspec import RSpec
8 from sfa.rspecs.elements.hardware_type import HardwareType
9 from sfa.rspecs.elements.node import Node
10 from sfa.rspecs.elements.link import Link
11 from sfa.rspecs.elements.sliver import Sliver
12 from sfa.rspecs.elements.login import Login
13 from sfa.rspecs.elements.location import Location
14 from sfa.rspecs.elements.interface import Interface
15 from sfa.rspecs.elements.services import Services
16 from sfa.rspecs.elements.pltag import PLTag
17 from sfa.rspecs.elements.lease import Lease
18 from sfa.rspecs.elements.granularity import Granularity
19 from sfa.rspecs.version_manager import VersionManager
20
21 from sfa.planetlab.plxrn import PlXrn, hostname_to_urn, hrn_to_pl_slicename, slicename_to_hrn
22 from sfa.planetlab.vlink import get_tc_rate
23 from sfa.planetlab.topology import Topology
24
25 import time
26
27 class PlAggregate:
28
29     def __init__(self, driver):
30         self.driver = driver
31
32     def get_nodes(self, options={}):
33         filter = {'peer_id': None}
34         geni_available = options.get('geni_available')    
35         if geni_available == True:
36             filter['boot_state'] = 'boot'
37         nodes = self.driver.shell.GetNodes(filter)
38        
39         return nodes  
40  
41     def get_sites(self, filter={}):
42         sites = {}
43         for site in self.driver.shell.GetSites(filter):
44             sites[site['site_id']] = site
45         return sites
46
47     def get_interfaces(self, filter={}):
48         interfaces = {}
49         for interface in self.driver.shell.GetInterfaces(filter):
50             iface = Interface()
51             if interface['bwlimit']:
52                 interface['bwlimit'] = str(int(interface['bwlimit'])/1000)
53             interfaces[interface['interface_id']] = interface
54         return interfaces
55
56     def get_links(self, sites, nodes, interfaces):
57         
58         topology = Topology() 
59         links = []
60         for (site_id1, site_id2) in topology:
61             site_id1 = int(site_id1)
62             site_id2 = int(site_id2)
63             link = Link()
64             if not site_id1 in sites or site_id2 not in sites:
65                 continue
66             site1 = sites[site_id1]
67             site2 = sites[site_id2]
68             # get hrns
69             site1_hrn = self.driver.hrn + '.' + site1['login_base']
70             site2_hrn = self.driver.hrn + '.' + site2['login_base']
71
72             for s1_node_id in site1['node_ids']:
73                 for s2_node_id in site2['node_ids']:
74                     if s1_node_id not in nodes or s2_node_id not in nodes:
75                         continue
76                     node1 = nodes[s1_node_id]
77                     node2 = nodes[s2_node_id]
78                     # set interfaces
79                     # just get first interface of the first node
80                     if1_xrn = PlXrn(auth=self.driver.hrn, interface='node%s:eth0' % (node1['node_id']))
81                     if1_ipv4 = interfaces[node1['interface_ids'][0]]['ip']
82                     if2_xrn = PlXrn(auth=self.driver.hrn, interface='node%s:eth0' % (node2['node_id']))
83                     if2_ipv4 = interfaces[node2['interface_ids'][0]]['ip']
84
85                     if1 = Interface({'component_id': if1_xrn.urn, 'ipv4': if1_ipv4} )
86                     if2 = Interface({'component_id': if2_xrn.urn, 'ipv4': if2_ipv4} )
87
88                     # set link
89                     link = Link({'capacity': '1000000', 'latency': '0', 'packet_loss': '0', 'type': 'ipv4'})
90                     link['interface1'] = if1
91                     link['interface2'] = if2
92                     link['component_name'] = "%s:%s" % (site1['login_base'], site2['login_base'])
93                     link['component_id'] = PlXrn(auth=self.driver.hrn, interface=link['component_name']).get_urn()
94                     link['component_manager_id'] =  hrn_to_urn(self.driver.hrn, 'authority+am')
95                     links.append(link)
96
97         return links
98
99     def get_node_tags(self, filter={}):
100         node_tags = {}
101         for node_tag in self.driver.shell.GetNodeTags(filter):
102             node_tags[node_tag['node_tag_id']] = node_tag
103         return node_tags
104
105     def get_pl_initscripts(self, filter={}):
106         pl_initscripts = {}
107         filter.update({'enabled': True})
108         for initscript in self.driver.shell.GetInitScripts(filter):
109             pl_initscripts[initscript['initscript_id']] = initscript
110         return pl_initscripts
111
112     def get_slivers(self, urns, options):
113         names = set()
114         ids = set()
115         for urn in urns:
116             xrn = PlXrn(xrn=urn)
117             names.add(xrn.get_slice_name())
118             if xrn.id:
119                 ids.add(xrn.id)
120
121         slices = self.driver.shell.GetSlices(names)
122         # filter on id
123         if ids:
124             slices = [slice for slice in slices if slice['slice_id'] in ids]
125
126         tags_dict = self.get_slice_tags(slices)
127         nodes_dict = self.get_slice_nodes(slices, options)
128         slivers = []
129         for node in nodes_dict.values():
130             sliver = node.update(slices[0]) 
131             sliver['tags'] = tags_dict[node['node_id']]
132         return slivers
133
134     def node_to_rspec_node(self, sites, interfaces, tags, pl_initscripts=[], grain=None, options={}):
135         rspec_node = Node()
136         # xxx how to retrieve site['login_base']
137         site=sites_dict[node['site_id']]
138         rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], node['hostname'])
139         rspec_node['component_name'] = node['hostname']
140         rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
141         rspec_node['authority_id'] = hrn_to_urn(PlXrn.site_hrn(self.driver.hrn, site['login_base']), 'authority+sa')
142         # do not include boot state (<available> element) in the manifest rspec
143         rspec_node['boot_state'] = node['boot_state']
144         rspec_node['exclusive'] = 'false'
145         rspec_node['hardware_types'] = [HardwareType({'name': 'plab-pc'}),
146                                         HardwareType({'name': 'pc'})]
147         # only doing this because protogeni rspec needs
148         # to advertise available initscripts
149         rspec_node['pl_initscripts'] = pl_initscripts.values()
150         # add site/interface info to nodes.
151         # assumes that sites, interfaces and tags have already been prepared.
152         if site['longitude'] and site['latitude']:
153             location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'})
154             rspec_node['location'] = location
155         # Granularity
156         granularity = Granularity({'grain': grain})
157         rspec_node['granularity'] = granularity
158         rspec_node['interfaces'] = []
159         if_count=0
160         for if_id in node['interface_ids']:
161             interface = Interface(interfaces[if_id])
162             interface['ipv4'] = interface['ip']
163             interface['component_id'] = PlXrn(auth=self.driver.hrn,
164                                               interface='node%s:eth%s' % (node['node_id'], if_count)).get_urn()
165             # interfaces in the manifest need a client id
166             if slice:
167                 interface['client_id'] = "%s:%s" % (node['node_id'], if_id)
168             rspec_node['interfaces'].append(interface)
169             if_count+=1
170
171         tags = [PLTag(node_tags[tag_id]) for tag_id in node['node_tag_ids']]
172         rspec_node['tags'] = tags
173         return rspec_node
174
175     def sliver_to_rspec_node(self, sliver):
176         # get the granularity in second for the reservation system
177         grain = self.driver.shell.GetLeaseGranularity()
178         if sliver['slice_ids_whitelist'] and sliver['slice_id'] not in sliver['slice_ids_whitelist']:
179             return # (was: continue)
180         rspec_node = self.get_rspec_node(node, sites_dict, interfaces, node_tags, pl_initscripts, grain)
181         # xxx how to retrieve site['login_base']
182         rspec_node['expires'] = datetime_to_string(utcparse(slice[0]['expires']))
183         # remove interfaces from manifest
184         rspec_node['interfaces'] = []
185         # add sliver info
186         id = ":".join(map(str, [slices[0]['slice_id'], node['node_id']]))
187         sliver_xrn = Xrn(slice_urn, id=id).get_urn()
188         sliver_xrn.set_authority(self.driver.hrn)
189         sliver = Sliver({'sliver_id': sliver_xrn.get_urn(),
190                          'name': slice[0]['name'],
191                          'type': 'plab-vserver',
192                          'tags': []})
193         rspec_node['sliver_id'] = sliver['sliver_id']
194         rspec_node['client_id'] = node['hostname']
195         rspec_node['slivers'] = [sliver]
196
197         # slivers always provide the ssh service
198         login = Login({'authentication': 'ssh-keys', 'hostname': node['hostname'], 'port':'22', 'username': sliver['name']})
199         service = Services({'login': login})
200         rspec_node['services'] = [service]    
201         rspec_nodes.append(rspec_node)
202         return rspec_node      
203
204     def get_slice_tags(self, slices):
205         slice_tag_ids = []
206         for slice in slices:
207             slice_tag_ids.extend(slice['slice_tag_ids'])
208         tags = self.driver.shell.GetSliceTags({'slice_tag_id': slice_tag_ids})
209         # sorted by node_id
210         tags_dict = defaultdict([])
211         for tag in tags:
212             tags_dict[tag['node_id']] = tag
213         return tags_dict
214
215     def get_slice_nodes(self, slices, options={}):
216         filter = {'peer_id': None}
217         tags_filter = {}
218         if slice and 'node_ids' in slice and slice['node_ids']:
219             filter['node_id'] = slice['node_ids']
220             tags_filter=filter.copy()
221
222         geni_available = options.get('geni_available')
223         if geni_available == True:
224             filter['boot_state'] = 'boot'
225         nodes = self.driver.shell.GetNodes(filter)
226         nodes_dict = {}
227         for node in nodes:
228             nodes_dict[node['node_id']] = node
229         return nodes_dict
230
231     def rspec_node_to_geni_sliver(self, rspec_node):
232         op_status = "geni_unknown"
233         state = sliver['boot_stat'].lower()
234         if state == 'boot':
235             op_status = 'geni_ready'
236         else:
237             op_status =' geni_failed'
238
239         # required fields
240         geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'],
241                        'geni_expires': rspec_node['expires'],
242                        'geni_allocation_status': 'geni_provisioned',
243                        'geni_operational_status': op_status,
244                        'geni_error': None,
245                        }
246         return geni_sliver        
247
248     def get_leases(self, slice=None, options={}):
249         
250         now = int(time.time())
251         filter={}
252         filter.update({'clip':now})
253         if slice:
254            filter.update({'name':slice['name']})
255         return_fields = ['lease_id', 'hostname', 'site_id', 'name', 't_from', 't_until']
256         leases = self.driver.shell.GetLeases(filter)
257         grain = self.driver.shell.GetLeaseGranularity()
258
259         site_ids = []
260         for lease in leases:
261             site_ids.append(lease['site_id'])
262
263         # get sites
264         sites_dict  = self.get_sites({'site_id': site_ids}) 
265   
266         rspec_leases = []
267         for lease in leases:
268
269             rspec_lease = Lease()
270             
271             # xxx how to retrieve site['login_base']
272             site_id=lease['site_id']
273             site=sites_dict[site_id]
274
275             rspec_lease['lease_id'] = lease['lease_id']
276             rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], lease['hostname'])
277             slice_hrn = slicename_to_hrn(self.driver.hrn, lease['name'])
278             slice_urn = hrn_to_urn(slice_hrn, 'slice')
279             rspec_lease['slice_id'] = slice_urn
280             rspec_lease['start_time'] = lease['t_from']
281             rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) / grain
282             rspec_leases.append(rspec_lease)
283         return rspec_leases
284
285     
286     def list_resources(self, version = None, options={}):
287
288         version_manager = VersionManager()
289         version = version_manager.get_version(version)
290         rspec_version = version_manager._get_version(version.type, version.version, 'ad')
291         rspec = RSpec(version=rspec_version, user_options=options)
292        
293         if not options.get('list_leases') or options['list_leases'] != 'leases':
294             # get nodes
295             nodes  = self.get_nodes(options)
296             site_ids = []
297             interface_ids = []
298             tag_ids = []
299             nodes_dict = {}
300             for sliver in slivers:
301                 site_ids.append(sliver['site_id'])
302                 interface_ids.extend(sliver['interface_ids'])
303                 tag_ids.extend(sliver['node_tag_ids'])
304                 nodes_dict[sliver['node_id']] = sliver
305             sites = self.get_sites({'site_id': site_ids})
306             interfaces = self.get_interfaces({'interface_id':interface_ids})
307             node_tags = self.get_node_tags(tags_filter)
308             pl_initscripts = self.get_pl_initscripts()
309             # convert nodes to rspec nodes
310             rspec_nodes = []
311             for node in nodes:
312                 rspec_node = self.node_to_rspec_node(node, sites, interfaces, node_tags, pl_initscripts)
313                 rspec_nodes.append(rspec_node)
314             rspec.version.add_nodes(rspec_nodes)
315
316             # add links
317             links = self.get_links(sites_dict, nodes_dict, interfaces)        
318             rspec.version.add_links(links)
319         return rspec.toxml()
320
321     def describe(self, urns, version=None, options={}):
322         # update nova connection
323         tenant_name = OSXrn(xrn=urns[0], type='slice').get_tenant_name()
324         self.driver.shell.nova_manager.connect(tenant=tenant_name)
325
326         version_manager = VersionManager()
327         version = version_manager.get_version(version)
328         rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
329         rspec = RSpec(version=version, user_options=options)
330
331         # get slivers
332         geni_slivers = []
333         slivers = self.get_slivers(urns, options) 
334         if len(slivers) == 0:
335             raise SliverDoesNotExist("You have not allocated any slivers here")
336         rspec.xml.set('expires',  datetime_to_string(utcparse(slivers[0]['expires'])))
337       
338         if not options.get('list_leases') or options['list_leases'] != 'leases':
339             # add slivers
340             site_ids = []
341             interface_ids = []
342             tag_ids = []
343             nodes_dict = {}
344             for sliver in slivers:
345                 site_ids.append(sliver['site_id'])
346                 interface_ids.extend(sliver['interface_ids'])
347                 tag_ids.extend(sliver['node_tag_ids'])
348                 nodes_dict[sliver['node_id']] = sliver
349             sites = self.get_sites({'site_id': site_ids})
350             interfaces = self.get_interfaces({'interface_id':interface_ids})
351             node_tags = self.get_node_tags(tags_filter)
352             pl_initscripts = self.get_pl_initscripts()
353             rspec_nodes = []
354             for sliver in slivers:
355                 if sliver['slice_ids_whitelist'] and sliver['slice_id'] not in sliver['slice_ids_whitelist']:
356                     continue
357                 rspec_node = self.sliver_to_rspec_node(sites, interfaces, node_tags)
358                 geni_sliver = self.rspec_node_to_geni_sliver(rspec_node)
359                 rspec_nodes.append(rspec_node) 
360                 geni_slivers.append(geni_sliver)
361             rspec.version.add_nodes(rspec_nodes)
362
363             # add sliver defaults
364             default_sliver = slivers.get(None, [])
365             if default_sliver:
366                 default_sliver_attribs = default_sliver.get('tags', [])
367                 for attrib in default_sliver_attribs:
368                     rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value'])
369
370             # add links 
371             links = self.get_links(sites_dict, nodes_dict, interfaces)        
372             rspec.version.add_links(links)
373
374         if not options.get('list_leases') or options['list_leases'] != 'resources':
375             leases = self.get_leases(slivers[0])
376             rspec.version.add_leases(leases)
377
378                
379         return  {'geni_urn': urns[0],
380                  'geni_rspec': rspec.toxml(),
381                  'geni_slivers': geni_slivers}
382