5c0606ce4309af4db690d0c849ff8657e357040a
[sfa.git] / sfa / plc / 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 epochparse
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.plc.vlink import get_tc_rate
20 from sfa.plc.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),
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': 'plab-vserver',
131                                  'tags': []})
132                 slivers[tag['node_id']] = sliver
133             slivers[tag['node_id']]['tags'].append(tag)
134         
135         return (slice, slivers)
136
137     def get_nodes_and_links(self, slice=None,slivers=[], options={}):
138         filter = {}
139         tags_filter = {}
140         if slice and 'node_ids' in slice and slice['node_ids']:
141             filter['node_id'] = slice['node_ids']
142             tags_filter=filter.copy()
143
144         geni_available = options.get('geni_available')    
145         if geni_available:
146             filter['boot_state'] = 'boot'     
147         
148         filter.update({'peer_id': None})
149         nodes = self.driver.shell.GetNodes(filter)
150        
151         site_ids = []
152         interface_ids = []
153         tag_ids = []
154         nodes_dict = {}
155         for node in nodes:
156             site_ids.append(node['site_id'])
157             interface_ids.extend(node['interface_ids'])
158             tag_ids.extend(node['node_tag_ids'])
159             nodes_dict[node['node_id']] = node
160  
161         # get sites
162         sites_dict  = self.get_sites({'site_id': site_ids}) 
163         # get interfaces
164         interfaces = self.get_interfaces({'interface_id':interface_ids}) 
165         # get tags
166         node_tags = self.get_node_tags(tags_filter)
167         # get initscripts
168         pl_initscripts = self.get_pl_initscripts()
169         
170         links = self.get_links(sites_dict, nodes_dict, interfaces)
171
172         rspec_nodes = []
173         for node in nodes:
174             # skip whitelisted nodes
175             if node['slice_ids_whitelist']:
176                 if not slice or slice['slice_id'] not in node['slice_ids_whitelist']:
177                     continue
178             rspec_node = Node()
179             # xxx how to retrieve site['login_base']
180             site_id=node['site_id']
181             site=sites_dict[site_id]
182             rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], node['hostname'])
183             rspec_node['component_name'] = node['hostname']
184             rspec_node['component_manager_id'] = Xrn(self.driver.hrn, 'authority+cm').get_urn()
185             rspec_node['authority_id'] = hrn_to_urn(PlXrn.site_hrn(self.driver.hrn, site['login_base']), 'authority+sa')
186             # do not include boot state (<available> element) in the manifest rspec
187             if not slice:     
188                 rspec_node['boot_state'] = node['boot_state']
189             rspec_node['exclusive'] = 'false'
190             rspec_node['hardware_types']= [HardwareType({'name': 'plab-pc'}),
191                                            HardwareType({'name': 'pc'})]
192             # only doing this because protogeni rspec needs
193             # to advertise available initscripts 
194             rspec_node['pl_initscripts'] = pl_initscripts.values()
195              # add site/interface info to nodes.
196             # assumes that sites, interfaces and tags have already been prepared.
197             site = sites_dict[node['site_id']]
198             if site['longitude'] and site['latitude']:  
199                 location = Location({'longitude': site['longitude'], 'latitude': site['latitude'], 'country': 'unknown'})
200                 rspec_node['location'] = location
201             rspec_node['interfaces'] = []
202             if_count=0
203             for if_id in node['interface_ids']:
204                 interface = Interface(interfaces[if_id]) 
205                 interface['ipv4'] = interface['ip']
206                 interface['component_id'] = PlXrn(auth=self.driver.hrn, 
207                                                   interface='node%s:eth%s' % (node['node_id'], if_count)).get_urn()
208                 # interfaces in the manifest need a client id
209                 if slice:
210                     interface['client_id'] = "%s:%s" % (node['node_id'], if_id)            
211                 rspec_node['interfaces'].append(interface)
212                 if_count+=1
213
214             tags = [PLTag(node_tags[tag_id]) for tag_id in node['node_tag_ids']]
215             rspec_node['tags'] = tags
216             if node['node_id'] in slivers:
217                 # add sliver info
218                 sliver = slivers[node['node_id']]
219                 rspec_node['sliver_id'] = sliver['sliver_id']
220                 rspec_node['client_id'] = node['hostname']
221                 rspec_node['slivers'] = [sliver]
222                 
223                 # slivers always provide the ssh service
224                 login = Login({'authentication': 'ssh-keys', 'hostname': node['hostname'], 'port':'22'})
225                 service = Services({'login': login})
226                 rspec_node['services'] = [service]
227             rspec_nodes.append(rspec_node)
228         return (rspec_nodes, links)
229              
230         
231     def get_rspec(self, slice_xrn=None, version = None, options={}):
232
233         version_manager = VersionManager()
234         version = version_manager.get_version(version)
235         if not slice_xrn:
236             rspec_version = version_manager._get_version(version.type, version.version, 'ad')
237         else:
238             rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
239
240         slice, slivers = self.get_slice_and_slivers(slice_xrn)
241         rspec = RSpec(version=rspec_version, user_options=options)
242         if slice and 'expires' in slice:
243             rspec.xml.set('expires',  epochparse(slice['expires']))
244
245         nodes, links = self.get_nodes_and_links(slice, slivers)
246         rspec.version.add_nodes(nodes)
247         rspec.version.add_links(links)
248         
249         # add sliver defaults
250         default_sliver = slivers.get(None, [])
251         if default_sliver:
252             default_sliver_attribs = default_sliver.get('tags', [])
253             for attrib in default_sliver_attribs:
254                 logger.info(attrib)
255                 rspec.version.add_default_sliver_attribute(attrib['tagname'], attrib['value'])
256         return rspec.toxml()
257
258