fix Describe
[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         slice_ids = set()
115         node_ids = []
116         for urn in urns:
117             xrn = PlXrn(xrn=urn)
118             if xrn.type == 'sliver':
119                  # id: slice_id-node_id
120                 id_parts = xrn.leaf.split('-')
121                 slice_ids.add(id_parts[0]) 
122                 node_ids.append(id_parts[1])
123             else:  
124                 names.add(xrn.pl_slicename())
125             if xrn.id:
126                 ids.add(xrn.id)
127
128         filter = {}
129         if names:
130             filter['name'] = list(names)
131         if slice_ids:
132             filter['slice_id'] = list(slice_ids) 
133         slices = self.driver.shell.GetSlices(filter)
134         slice = slices[0]
135         if node_ids:
136             slice['node_ids'] = node_ids
137         tags_dict = self.get_slice_tags(slice)
138         nodes_dict = self.get_slice_nodes(slice, options)
139         slivers = []
140         for node in nodes_dict.values():
141             node.update(slices[0]) 
142             node['tags'] = tags_dict[node['node_id']]
143             sliver_hrn = '%s.%s-%s' % (self.driver.hrn, slice['slice_id'], node['node_id'])
144             node['urn'] = PlXrn(xrn=sliver_hrn, type='sliver').get_urn() 
145             slivers.append(node)
146         return slivers
147
148     def node_to_rspec_node(self, node, sites, interfaces, node_tags, pl_initscripts=[], grain=None, options={}):
149         rspec_node = Node()
150         # xxx how to retrieve site['login_base']
151         site=sites[node['site_id']]
152         rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], node['hostname'])
153         rspec_node['component_name'] = node['hostname']
154         rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
155         rspec_node['authority_id'] = hrn_to_urn(PlXrn.site_hrn(self.driver.hrn, site['login_base']), 'authority+sa')
156         # do not include boot state (<available> element) in the manifest rspec
157         rspec_node['boot_state'] = node['boot_state']
158         rspec_node['exclusive'] = 'false'
159         rspec_node['hardware_types'] = [HardwareType({'name': 'plab-pc'}),
160                                         HardwareType({'name': 'pc'})]
161         # only doing this because protogeni rspec needs
162         # to advertise available initscripts
163         rspec_node['pl_initscripts'] = pl_initscripts.values()
164         # add site/interface info to nodes.
165         # assumes that sites, interfaces and tags have already been prepared.
166         if site['longitude'] and site['latitude']:
167             location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'})
168             rspec_node['location'] = location
169         # Granularity
170         granularity = Granularity({'grain': grain})
171         rspec_node['granularity'] = granularity
172         rspec_node['interfaces'] = []
173         if_count=0
174         for if_id in node['interface_ids']:
175             interface = Interface(interfaces[if_id])
176             interface['ipv4'] = interface['ip']
177             interface['component_id'] = PlXrn(auth=self.driver.hrn,
178                                               interface='node%s:eth%s' % (node['node_id'], if_count)).get_urn()
179             # interfaces in the manifest need a client id
180             if slice:
181                 interface['client_id'] = "%s:%s" % (node['node_id'], if_id)
182             rspec_node['interfaces'].append(interface)
183             if_count+=1
184         tags = [PLTag(node_tags[tag_id]) for tag_id in node['node_tag_ids']]
185         rspec_node['tags'] = tags
186         return rspec_node
187
188     def sliver_to_rspec_node(self, sliver, sites, interfaces, node_tags, pl_initscripts):
189         # get the granularity in second for the reservation system
190         grain = self.driver.shell.GetLeaseGranularity()
191         rspec_node = self.node_to_rspec_node(sliver, sites, interfaces, node_tags, pl_initscripts, grain)
192         # xxx how to retrieve site['login_base']
193         rspec_node['expires'] = datetime_to_string(utcparse(sliver['expires']))
194         # remove interfaces from manifest
195         rspec_node['interfaces'] = []
196         # add sliver info
197         rspec_sliver = Sliver({'sliver_id': sliver['urn'],
198                          'name': sliver['name'],
199                          'type': 'plab-vserver',
200                          'tags': []})
201         rspec_node['sliver_id'] = rspec_sliver['sliver_id']
202         rspec_node['client_id'] = sliver['hostname']
203         rspec_node['slivers'] = [rspec_sliver]
204
205         # slivers always provide the ssh service
206         login = Login({'authentication': 'ssh-keys', 'hostname': sliver['hostname'], 'port':'22', 'username': sliver['name']})
207         service = Services({'login': login})
208         rspec_node['services'] = [service]    
209         return rspec_node      
210
211     def get_slice_tags(self, slice):
212         slice_tag_ids = []
213         slice_tag_ids.extend(slice['slice_tag_ids'])
214         tags = self.driver.shell.GetSliceTags({'slice_tag_id': slice_tag_ids})
215         # sorted by node_id
216         tags_dict = defaultdict(list)
217         for tag in tags:
218             tags_dict[tag['node_id']] = tag
219         return tags_dict
220
221     def get_slice_nodes(self, slice, options={}):
222         nodes_dict = {}
223         filter = {'peer_id': None}
224         tags_filter = {}
225         if slice and slice.get('node_ids'):
226             filter['node_id'] = slice['node_ids']
227         else:
228             # there are no nodes to look up
229             return nodes_dict
230         tags_filter=filter.copy()
231         geni_available = options.get('geni_available')
232         if geni_available == True:
233             filter['boot_state'] = 'boot'
234         nodes = self.driver.shell.GetNodes(filter)
235         for node in nodes:
236             nodes_dict[node['node_id']] = node
237         return nodes_dict
238
239     def rspec_node_to_geni_sliver(self, rspec_node):
240         op_status = "geni_unknown"
241         state = rspec_node['boot_state'].lower()
242         if state == 'boot':
243             op_status = 'geni_ready'
244         else:
245             op_status =' geni_failed'
246
247         # required fields
248         geni_sliver = {'geni_sliver_urn': rspec_node['sliver_id'],
249                        'geni_expires': rspec_node['expires'],
250                        'geni_allocation_status': 'geni_provisioned',
251                        'geni_operational_status': op_status,
252                        'geni_error': None,
253                        }
254         return geni_sliver        
255
256     def get_leases(self, slice=None, options={}):
257         
258         now = int(time.time())
259         filter={}
260         filter.update({'clip':now})
261         if slice:
262            filter.update({'name':slice['name']})
263         return_fields = ['lease_id', 'hostname', 'site_id', 'name', 't_from', 't_until']
264         leases = self.driver.shell.GetLeases(filter)
265         grain = self.driver.shell.GetLeaseGranularity()
266
267         site_ids = []
268         for lease in leases:
269             site_ids.append(lease['site_id'])
270
271         # get sites
272         sites_dict  = self.get_sites({'site_id': site_ids}) 
273   
274         rspec_leases = []
275         for lease in leases:
276
277             rspec_lease = Lease()
278             
279             # xxx how to retrieve site['login_base']
280             site_id=lease['site_id']
281             site=sites_dict[site_id]
282
283             rspec_lease['lease_id'] = lease['lease_id']
284             rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], lease['hostname'])
285             slice_hrn = slicename_to_hrn(self.driver.hrn, lease['name'])
286             slice_urn = hrn_to_urn(slice_hrn, 'slice')
287             rspec_lease['slice_id'] = slice_urn
288             rspec_lease['start_time'] = lease['t_from']
289             rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) / grain
290             rspec_leases.append(rspec_lease)
291         return rspec_leases
292
293     
294     def list_resources(self, version = None, options={}):
295
296         version_manager = VersionManager()
297         version = version_manager.get_version(version)
298         rspec_version = version_manager._get_version(version.type, version.version, 'ad')
299         rspec = RSpec(version=rspec_version, user_options=options)
300        
301         if not options.get('list_leases') or options['list_leases'] != 'leases':
302             # get nodes
303             nodes  = self.get_nodes(options)
304             site_ids = []
305             interface_ids = []
306             tag_ids = []
307             nodes_dict = {}
308             for node in nodes:
309                 site_ids.append(node['site_id'])
310                 interface_ids.extend(node['interface_ids'])
311                 tag_ids.extend(node['node_tag_ids'])
312                 nodes_dict[node['node_id']] = node
313             sites = self.get_sites({'site_id': site_ids})
314             interfaces = self.get_interfaces({'interface_id':interface_ids})
315             node_tags = self.get_node_tags({'node_tag_id': tag_ids})
316             pl_initscripts = self.get_pl_initscripts()
317             # convert nodes to rspec nodes
318             rspec_nodes = []
319             for node in nodes:
320                 rspec_node = self.node_to_rspec_node(node, sites, interfaces, node_tags, pl_initscripts)
321                 rspec_nodes.append(rspec_node)
322             rspec.version.add_nodes(rspec_nodes)
323
324             # add links
325             links = self.get_links(sites, nodes_dict, interfaces)        
326             rspec.version.add_links(links)
327         return rspec.toxml()
328
329     def describe(self, urns, version=None, options={}):
330         version_manager = VersionManager()
331         version = version_manager.get_version(version)
332         rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
333         rspec = RSpec(version=version, user_options=options)
334
335         # get slivers
336         geni_slivers = []
337         slivers = self.get_slivers(urns, options) 
338         if len(slivers) == 0:
339             raise SliverDoesNotExist("You have not allocated any slivers here")
340         rspec.xml.set('expires',  datetime_to_string(utcparse(slivers[0]['expires'])))
341       
342         if not options.get('list_leases') or options['list_leases'] != 'leases':
343             # add slivers
344             site_ids = []
345             interface_ids = []
346             tag_ids = []
347             nodes_dict = {}
348             for sliver in slivers:
349                 site_ids.append(sliver['site_id'])
350                 interface_ids.extend(sliver['interface_ids'])
351                 tag_ids.extend(sliver['node_tag_ids'])
352                 nodes_dict[sliver['node_id']] = sliver
353             sites = self.get_sites({'site_id': site_ids})
354             interfaces = self.get_interfaces({'interface_id':interface_ids})
355             node_tags = self.get_node_tags({'node_tag_id': tag_ids})
356             pl_initscripts = self.get_pl_initscripts()
357             rspec_nodes = []
358             for sliver in slivers:
359                 if sliver['slice_ids_whitelist'] and sliver['slice_id'] not in sliver['slice_ids_whitelist']:
360                     continue
361                 rspec_node = self.sliver_to_rspec_node(sliver, sites, interfaces, node_tags, pl_initscripts)
362                 geni_sliver = self.rspec_node_to_geni_sliver(rspec_node)
363                 rspec_nodes.append(rspec_node) 
364                 geni_slivers.append(geni_sliver)
365             rspec.version.add_nodes(rspec_nodes)
366
367             # add sliver defaults
368             #default_sliver = slivers.get(None, [])
369             #if default_sliver:
370             #    default_sliver_attribs = default_sliver.get('tags', [])
371             #    for attrib in default_sliver_attribs:
372             #        rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value'])
373
374             # add links 
375             links = self.get_links(sites, nodes_dict, interfaces)        
376             rspec.version.add_links(links)
377
378         if not options.get('list_leases') or options['list_leases'] != 'resources':
379             leases = self.get_leases(slivers[0])
380             rspec.version.add_leases(leases)
381
382                
383         return {'geni_urn': urns[0], 
384                 'geni_rspec': rspec.toxml(),
385                 'geni_slivers': geni_slivers}