Merge branch 'upstreammaster'
[sfa.git] / sfa / planetlab / plaggregate.py
1 #!/usr/bin/python
2 from sfa.util.xrn import Xrn, hrn_to_urn, urn_to_hrn
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_xrn = Xrn(slice_urn, type='sliver', id=node_id)
122             sliver_xrn.set_authority(self.driver.hrn)
123             sliver = Sliver({'sliver_id': sliver_xrn.urn,
124                              'name': slice['name'],
125                              'type': 'plab-vserver', 
126                              'tags': []})
127             slivers[node_id]= sliver
128
129         # sort sliver attributes by node id    
130         tags = self.driver.shell.GetSliceTags({'slice_tag_id': slice['slice_tag_ids']})
131         for tag in tags:
132             # most likely a default/global sliver attribute (node_id == None)
133             if tag['node_id'] not in slivers:
134                 sliver_xrn = Xrn(slice_urn, type='sliver', id=tag['node_id'])
135                 sliver_xrn.set_authority(self.driver.hrn)
136                 sliver = Sliver({'sliver_id': sliver_xrn.urn,
137                                  'name': slice['name'],
138                                  'type': 'plab-vserver',
139                                  'tags': []})
140                 slivers[tag['node_id']] = sliver
141             slivers[tag['node_id']]['tags'].append(tag)
142         
143         return (slice, slivers)
144
145     def get_nodes_and_links(self, slice_xrn, slice=None,slivers=[], options={}, requested_slivers={}):
146         # if we are dealing with a slice that has no node just return 
147         # and empty list    
148         if slice_xrn:
149             if not slice or not slice['node_ids']:
150                 return ([],[])
151
152         filter = {}
153         tags_filter = {}
154         if slice and 'node_ids' in slice and slice['node_ids']:
155             filter['node_id'] = slice['node_ids']
156             tags_filter=filter.copy()
157
158         geni_available = options.get('geni_available')    
159         if geni_available == True:
160             filter['boot_state'] = 'boot'     
161         
162         filter.update({'peer_id': None})
163         nodes = self.driver.shell.GetNodes(filter)
164         
165         # get the granularity in second for the reservation system
166         grain = self.driver.shell.GetLeaseGranularity()
167        
168         site_ids = []
169         interface_ids = []
170         tag_ids = []
171         nodes_dict = {}
172         for node in nodes:
173             site_ids.append(node['site_id'])
174             interface_ids.extend(node['interface_ids'])
175             tag_ids.extend(node['node_tag_ids'])
176             nodes_dict[node['node_id']] = node
177  
178         # get sites
179         sites_dict  = self.get_sites({'site_id': site_ids}) 
180         # get interfaces
181         interfaces = self.get_interfaces({'interface_id':interface_ids}) 
182         # get tags
183         node_tags = self.get_node_tags(tags_filter)
184         # get initscripts
185         pl_initscripts = self.get_pl_initscripts()
186         
187         links = self.get_links(sites_dict, nodes_dict, interfaces)
188
189         rspec_nodes = []
190         for node in nodes:
191             # skip whitelisted nodes
192             if node['slice_ids_whitelist']:
193                 if not slice or slice['slice_id'] not in node['slice_ids_whitelist']:
194                     continue
195             rspec_node = Node()
196             # xxx how to retrieve site['login_base']
197             site_id=node['site_id']
198             site=sites_dict[site_id]
199             rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], node['hostname'])
200             rspec_node['component_name'] = node['hostname']
201             rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
202             rspec_node['authority_id'] = hrn_to_urn(PlXrn.site_hrn(self.driver.hrn, site['login_base']), 'authority+sa')
203             if requested_slivers and node['hostname'] in requested_slivers:
204                 requested_sliver = requested_slivers[node['hostname']]
205                 if requested_sliver.get('client_id'):
206                     rspec_node['client_id'] = requested_sliver['client_id']
207             # do not include boot state (<available> element) in the manifest rspec
208             if not slice:     
209                 rspec_node['boot_state'] = node['boot_state']
210             rspec_node['exclusive'] = 'false'
211             rspec_node['hardware_types'] = [HardwareType({'name': 'plab-pc'}),
212                                             HardwareType({'name': 'pc'})]
213             # only doing this because protogeni rspec needs
214             # to advertise available initscripts 
215             rspec_node['pl_initscripts'] = pl_initscripts.values()
216              # add site/interface info to nodes.
217             # assumes that sites, interfaces and tags have already been prepared.
218             site = sites_dict[node['site_id']]
219             if site['longitude'] and site['latitude']:  
220                 location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'})
221                 rspec_node['location'] = location
222             # Granularity
223             granularity = Granularity({'grain': grain})
224             rspec_node['granularity'] = granularity
225
226             rspec_node['interfaces'] = []
227             if_count=0
228             for if_id in node['interface_ids']:
229                 interface = Interface(interfaces[if_id]) 
230                 interface['ipv4'] = interface['ip']
231                 interface['component_id'] = PlXrn(auth=self.driver.hrn, 
232                                                   interface='node%s:eth%s' % (node['node_id'], if_count)).get_urn()
233                 # interfaces in the manifest need a client id
234                 if slice:
235                     interface['client_id'] = "%s:%s" % (node['node_id'], if_id)            
236                 rspec_node['interfaces'].append(interface)
237                 if_count+=1
238
239             tags = [PLTag(node_tags[tag_id]) for tag_id in node['node_tag_ids']]
240             rspec_node['tags'] = tags
241             if node['node_id'] in slivers:
242                 # add sliver info
243                 sliver = slivers[node['node_id']]
244                 rspec_node['sliver_id'] = sliver['sliver_id']
245                 rspec_node['slivers'] = [sliver]
246                 
247                 # slivers always provide the ssh service
248                 login = Login({'authentication': 'ssh-keys', 'hostname': node['hostname'], 'port':'22', 'username': sliver['name']})
249                 service = Services({'login': login})
250                 rspec_node['services'] = [service]
251             rspec_nodes.append(rspec_node)
252         return (rspec_nodes, links)
253              
254
255     def get_leases(self, slice=None, options={}):
256         
257         now = int(time.time())
258         filter={}
259         filter.update({'clip':now})
260         if slice:
261            filter.update({'name':slice['name']})
262         return_fields = ['lease_id', 'hostname', 'site_id', 'name', 't_from', 't_until']
263         leases = self.driver.shell.GetLeases(filter)
264         grain = self.driver.shell.GetLeaseGranularity()
265
266         site_ids = []
267         for lease in leases:
268             site_ids.append(lease['site_id'])
269
270         # get sites
271         sites_dict  = self.get_sites({'site_id': site_ids}) 
272   
273         rspec_leases = []
274         for lease in leases:
275
276             rspec_lease = Lease()
277             
278             # xxx how to retrieve site['login_base']
279             site_id=lease['site_id']
280             site=sites_dict[site_id]
281
282             rspec_lease['lease_id'] = lease['lease_id']
283             rspec_lease['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], lease['hostname'])
284             slice_hrn = slicename_to_hrn(self.driver.hrn, lease['name'])
285             slice_urn = hrn_to_urn(slice_hrn, 'slice')
286             rspec_lease['slice_id'] = slice_urn
287             rspec_lease['start_time'] = lease['t_from']
288             rspec_lease['duration'] = (lease['t_until'] - lease['t_from']) / grain
289             rspec_leases.append(rspec_lease)
290         return rspec_leases
291
292     
293     def get_rspec(self, slice_xrn=None, version = None, options={}, requested_slivers={}):
294
295         version_manager = VersionManager()
296         version = version_manager.get_version(version)
297         if not slice_xrn:
298             rspec_version = version_manager._get_version(version.type, version.version, 'ad')
299         else:
300             rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
301
302         slice, slivers = self.get_slice_and_slivers(slice_xrn)
303         rspec = RSpec(version=rspec_version, user_options=options)
304         if slice and 'expires' in slice:
305             rspec.xml.set('expires',  datetime_to_string(utcparse(slice['expires'])))
306
307         if not options.get('list_leases') or options.get('list_leases') and options['list_leases'] != 'leases':
308             nodes, links = self.get_nodes_and_links(slice_xrn, slice, slivers, options, 
309                                                     requested_slivers=requested_slivers)
310             rspec.version.add_nodes(nodes)
311             rspec.version.add_links(links)
312             # add sliver defaults
313             default_sliver = slivers.get(None, [])
314             if default_sliver:
315                 default_sliver_attribs = default_sliver.get('tags', [])
316                 for attrib in default_sliver_attribs:
317                     logger.info(attrib)
318                     rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value'])
319         
320         if not options.get('list_leases') or options.get('list_leases') and options['list_leases'] != 'resources':
321            leases = self.get_leases(slice)
322            rspec.version.add_leases(leases)
323
324         return rspec.toxml()
325
326