older clients sill use the rspec_version option so lets support that as well for now
[sfa.git] / sfa / plc / plaggregate.py
1 #!/usr/bin/python
2 from sfa.util.xrn import hrn_to_urn, urn_to_hrn, urn_to_sliver_id
3 from sfa.util.sfatime import epochparse
4
5 from sfa.rspecs.rspec import RSpec
6 from sfa.rspecs.elements.hardware_type import HardwareType
7 from sfa.rspecs.elements.node import Node
8 from sfa.rspecs.elements.link import Link
9 from sfa.rspecs.elements.sliver import Sliver
10 from sfa.rspecs.elements.login import Login
11 from sfa.rspecs.elements.location import Location
12 from sfa.rspecs.elements.interface import Interface
13 from sfa.rspecs.elements.services import Services
14 from sfa.rspecs.elements.pltag import PLTag
15 from sfa.rspecs.version_manager import VersionManager
16
17 from sfa.util.plxrn import PlXrn, hostname_to_urn, hrn_to_pl_slicename
18 from sfa.plc.vlink import get_tc_rate
19 from sfa.plc.topology import Topology
20
21
22 class PlAggregate:
23
24     api = None
25     #panos new user options variable
26     user_options = {}
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.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.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.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.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.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),
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.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': 'plab-vserver',
134                                  'tags': []})
135                 slivers[tag['node_id']] = sliver
136             slivers[tag['node_id']]['tags'].append(tag)
137         
138         return (slice, slivers)
139
140     def get_nodes_and_links(self, slice=None,slivers=[], options={}):
141         filter = {}
142         tags_filter = {}
143         if slice and 'node_ids' in slice and slice['node_ids']:
144             filter['node_id'] = slice['node_ids']
145             tags_filter=filter.copy()
146
147         geni_available = options.get('geni_available')    
148         if geni_available:
149             filter['boot_state'] = 'boot'     
150         
151         filter.update({'peer_id': None})
152         nodes = self.driver.GetNodes(filter)
153        
154         site_ids = []
155         interface_ids = []
156         tag_ids = []
157         nodes_dict = {}
158         for node in nodes:
159             site_ids.append(node['site_id'])
160             interface_ids.extend(node['interface_ids'])
161             tag_ids.extend(node['node_tag_ids'])
162             nodes_dict[node['node_id']] = node
163  
164         # get sites
165         sites_dict  = self.get_sites({'site_id': site_ids}) 
166         # get interfaces
167         interfaces = self.get_interfaces({'interface_id':interface_ids}) 
168         # get tags
169         node_tags = self.get_node_tags(tags_filter)
170         # get initscripts
171         pl_initscripts = self.get_pl_initscripts()
172         
173         links = self.get_links(sites_dict, nodes_dict, interfaces)
174
175         rspec_nodes = []
176         for node in nodes:
177             # skip whitelisted nodes
178             if node['slice_ids_whitelist']:
179                 if not slice or slice['slice_id'] not in node['slice_ids_whitelist']:
180                     continue
181             rspec_node = Node()
182             # xxx how to retrieve site['login_base']
183             site_id=node['site_id']
184             site=sites_dict[site_id]
185             rspec_node['component_id'] = hostname_to_urn(self.driver.hrn, site['login_base'], node['hostname'])
186             rspec_node['component_name'] = node['hostname']
187             rspec_node['component_manager_id'] = self.driver.hrn
188             rspec_node['authority_id'] = hrn_to_urn(PlXrn.site_hrn(self.driver.hrn, site['login_base']), 'authority+sa')
189             rspec_node['boot_state'] = node['boot_state']
190             rspec_node['exclusive'] = 'False'
191             rspec_node['hardware_types']= [HardwareType({'name': 'plab-pc'}),
192                                            HardwareType({'name': 'pc'})]
193             # only doing this because protogeni rspec needs
194             # to advertise available initscripts 
195             rspec_node['pl_initscripts'] = pl_initscripts.values()
196              # add site/interface info to nodes.
197             # assumes that sites, interfaces and tags have already been prepared.
198             site = sites_dict[node['site_id']]
199             if site['longitude'] and site['latitude']:  
200                 location = Location({'longitude': site['longitude'], 'latitude': site['latitude']})
201                 rspec_node['location'] = location
202             rspec_node['interfaces'] = []
203             if_count=0
204             for if_id in node['interface_ids']:
205                 interface = Interface(interfaces[if_id]) 
206                 interface['ipv4'] = interface['ip']
207                 interface['component_id'] = PlXrn(auth=self.driver.hrn, 
208                                                   interface='node%s:eth%s' % (node['node_id'], if_count)).get_urn()
209                 rspec_node['interfaces'].append(interface)
210                 if_count+=1
211
212             tags = [PLTag(node_tags[tag_id]) for tag_id in node['node_tag_ids']]
213             rspec_node['tags'] = tags
214             if node['node_id'] in slivers:
215                 # add sliver info
216                 sliver = slivers[node['node_id']]
217                 rspec_node['sliver_id'] = sliver['sliver_id']
218                 rspec_node['client_id'] = node['hostname']
219                 rspec_node['slivers'] = [sliver]
220                 
221                 # slivers always provide the ssh service
222                 login = Login({'authentication': 'ssh-keys', 'hostname': node['hostname'], 'port':'22'})
223                 service = Services({'login': login})
224                 rspec_node['services'] = [service]
225             rspec_nodes.append(rspec_node)
226         return (rspec_nodes, links)
227              
228         
229     def get_rspec(self, slice_xrn=None, version = None, options={}):
230
231         version_manager = VersionManager()
232         version = version_manager.get_version(version)
233         if not slice_xrn:
234             rspec_version = version_manager._get_version(version.type, version.version, 'ad')
235         else:
236             rspec_version = version_manager._get_version(version.type, version.version, 'manifest')
237
238         slice, slivers = self.get_slice_and_slivers(slice_xrn)
239         rspec = RSpec(version=rspec_version, user_options=options)
240         if slice and 'expires' in slice:
241             rspec.xml.set('expires',  epochparse(slice['expires']))
242
243         nodes, links = self.get_nodes_and_links(slice, slivers)
244         rspec.version.add_nodes(nodes)
245         rspec.version.add_links(links)
246         
247         # add sliver defaults
248         default_sliver_attribs = slivers.get(None, [])
249         for sliver_attrib in default_sliver_attribs:
250             rspec.version.add_default_sliver_attribute(sliver_attrib['name'], sliver_attrib['value'])  
251         
252         return rspec.toxml()
253
254