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