5c62404b55a7dc37a9f7e9cdc5648b47483b375e
[sfa.git] / sfa / rspecs / sfa_rspec.py
1 #!/usr/bin/python 
2 from lxml import etree
3 from StringIO import StringIO
4 from sfa.rspecs.rspec import RSpec 
5 from sfa.util.xrn import *
6 from sfa.util.plxrn import hostname_to_urn
7 from sfa.util.config import Config
8 from sfa.rspecs.rspec_version import RSpecVersion  
9
10
11 _version = { 'type': 'SFA', 
12              'version': '1' 
13 }
14
15 sfa_rspec_version = RSpecVersion(_version)
16
17 class SfaRSpec(RSpec):
18     xml = None
19     header = '<?xml version="1.0"?>\n'
20     version = sfa_rspec_version
21
22     def create(self):
23         RSpec.create(self)
24         self.xml.set('type', 'SFA')
25
26     ###################
27     # Parser
28     ###################
29     def get_network_elements(self):
30         return self.xml.xpath('//network')
31
32     def get_networks(self):
33         return self.xml.xpath('//network[@name]/@name')
34
35     def get_node_element(self, hostname, network=None):
36         if network:
37             names = self.xml.xpath('//network[@name="%s"]//node/hostname' % network)
38         else:
39             names = self.xml.xpath('//node/hostname')
40         for name in names:
41             if str(name.text).strip() == hostname:
42                 return name.getparent()
43         return None
44  
45     def get_node_elements(self, network=None):
46         if network:
47             return self.xml.xpath('//network[@name="%s"]//node' % network)
48         else:
49             return self.xml.xpath('//node')
50
51     def get_nodes(self, network=None):
52         if network == None:
53             nodes = self.xml.xpath('//node/hostname/text()')
54         else:
55             nodes = self.xml.xpath('//network[@name="%s"]//node/hostname/text()' % network)
56
57         nodes = [node.strip() for node in nodes]
58         return nodes
59
60     def get_nodes_with_slivers(self, network = None):
61         if network:
62             nodes =  self.xml.xpath('//network[@name="%s"]//node[sliver]/hostname/text()' % network)   
63         else:
64             nodes = self.xml.xpath('//node[sliver]/hostname/text()')
65
66         nodes = [node.strip() for node in nodes]
67         return nodes     
68
69     def get_nodes_without_slivers(self, network=None): 
70         xpath_nodes_without_slivers = '//node[not(sliver)]/hostname/text()'
71         xpath_nodes_without_slivers_in_network = '//network[@name="%s"]//node[not(sliver)]/hostname/text()' 
72         if network:
73             return self.xml.xpath('//network[@name="%s"]//node[not(sliver)]/hostname/text()' % network)
74         else:
75             return self.xml.xpath('//node[not(sliver)]/hostname/text()')      
76
77
78     def attributes_list(self, elem):
79         # convert a list of attribute tags into list of tuples
80         # (tagnme, text_value) 
81         opts = []
82         if elem is not None:
83             for e in elem:
84                 opts.append((e.tag, str(e.text).strip()))
85         return opts
86
87     def get_default_sliver_attributes(self, network=None):
88         if network:
89             defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)        
90         else:
91             defaults = self.xml.xpath("//sliver_defaults")
92         if isinstance(defaults, list) and defaults:
93             defaults = defaults[0]
94         return self.attributes_list(defaults)
95
96     def get_sliver_attributes(self, hostname, network=None):
97         attributes = [] 
98         node = self.get_node_element(hostname, network)
99         #sliver = node.find("sliver")
100         slivers = node.xpath('./sliver')
101         if isinstance(slivers, list) and slivers:
102             attributes = self.attributes_list(slivers[0])
103         return attributes
104
105     def get_slice_attributes(self, network=None):
106         slice_attributes = []
107         nodes_with_slivers = self.get_nodes_with_slivers(network)
108         for default_attribute in self.get_default_sliver_attributes(network):
109             attribute = {'name': str(default_attribute[0]), 'value': str(default_attribute[1]), 'node_id': None}
110             slice_attributes.append(attribute)
111         for node in nodes_with_slivers:
112             sliver_attributes = self.get_sliver_attributes(node, network)
113             for sliver_attribute in sliver_attributes:
114                 attribute = {'name': str(sliver_attribute[0]), 'value': str(sliver_attribute[1]), 'node_id': node}
115                 slice_attributes.append(attribute)    
116         return slice_attributes
117
118     def get_site_nodes(self, siteid, network=None):
119         if network:
120             nodes = self.xml.xpath('//network[@name="%s"]/site[@id="%s"]/node/hostname/text()' % \
121                                     (network, siteid))
122         else:
123             nodes = self.xml.xpath('//site[@id="%s"]/node/hostname/text()' % siteid)
124         return nodes
125         
126     def get_links(self, network=None):
127         if network: 
128             links = self.xml.xpath('//network[@name="%s"]/link' % network)
129         else:
130             links = self.xml.xpath('//link')    
131         linklist = []
132         for link in links:
133             (end1, end2) = link.get("endpoints").split()
134             name = link.find("description")
135             linklist.append((name.text,
136                              self.get_site_nodes(end1, network),
137                              self.get_site_nodes(end2, network)))
138         return linklist
139
140     def get_link(self, fromnode, tonode, network=None):
141         fromsite = fromnode.getparent()
142         tosite = tonode.getparent()
143         fromid = fromsite.get("id")
144         toid = tosite.get("id")
145         if network:
146             query = "//network[@name='%s']" % network + "/link[@endpoints = '%s %s']"
147         else:
148             query = "//link[@endpoints = '%s %s']"
149
150         results = self.rspec.xpath(query % (fromid, toid))
151         if not results:
152             results = self.rspec.xpath(query % (toid, fromid))
153         return results
154
155     def query_links(self, fromnode, tonode, network=None):
156         return get_link(fromnode, tonode, network)
157
158     def get_vlinks(self, network=None):
159         vlinklist = []
160         if network: 
161             vlinks = self.xml.xpath("//network[@name='%s']//vlink" % network)
162         else:
163             vlinks = self.xml.xpath("//vlink") 
164         for vlink in vlinks:
165             endpoints = vlink.get("endpoints")
166             (end1, end2) = endpoints.split()
167             if network: 
168                 node1 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
169                                        (network, end1))[0]
170                 node2 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
171                                        (network, end2))[0]
172             else: 
173                 node1 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end1)[0]
174                 node2 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end2)[0]
175             desc = "%s <--> %s" % (node1, node2)
176             kbps = vlink.find("kbps")
177             vlinklist.append((endpoints, desc, kbps.text))
178         return vlinklist
179
180     def get_vlink(self, endponts, network=None):
181         if network:
182             query = "//network[@name='%s']//vlink[@endpoints = '%s']" % (network, endpoints)
183         else:
184             query = "//vlink[@endpoints = '%s']" % (network, endpoints)
185         results = self.rspec.xpath(query)
186         return results
187         
188     def query_vlinks(self, endpoints, network=None):
189         return get_vlink(endpoints,network)
190
191     ##################
192     # Builder
193     ##################
194
195     def add_network(self, network):
196         network_tags = self.xml.xpath('//network[@name="%s"]' % network)
197         if not network_tags:            
198             network_tag = etree.SubElement(self.xml, 'network', name=network)
199         else:
200             network_tag = network_tags[0]
201         return network_tag     
202
203     def add_nodes(self, nodes, network = None, no_dupes=False):
204         if not isinstance(nodes, list):
205             nodes = [nodes]
206         for node in nodes:
207             if no_dupes and \
208               self.get_node_element(node['hostname']):
209                 # node already exists
210                 continue
211
212             network_tag = self.xml
213             if 'network' in node:
214                 network = node['network']
215                 network_tag = self.add_network(network)
216      
217             node_tag = etree.SubElement(network_tag, 'node')
218             if 'network' in node:
219                 node_tag.set('component_manager_id', hrn_to_urn(network, 'authority+sa'))
220             if 'urn' in node:
221                 node_tag.set('component_id', node['urn']) 
222             if 'site_urn' in node:
223                 node_tag.set('site_id', node['site_urn'])
224             if 'node_id' in node: 
225                 node_tag.set('node_id', 'n'+str(node['node_id']))
226             if 'hostname' in node:
227                 hostname_tag = etree.SubElement(node_tag, 'hostname').text = node['hostname']
228             if 'interfaces' in node:
229                 for interface in node['interfaces']:
230                     if 'bwlimit' in interface and interface['bwlimit']:
231                         bwlimit = etree.SubElement(node_tag, 'bw_limit', units='kbps').text = str(interface['bwlimit']/1000)
232             if 'tags' in node:
233                 for tag in node['tags']:
234                    # expose this hard wired list of tags, plus the ones that are marked 'sfa' in their category 
235                    if tag['tagname'] in ['fcdistro', 'arch'] or 'sfa' in tag['category'].split('/'):
236                         tag_element = etree.SubElement(node_tag, tag['tagname']).text=tag['value']
237
238             if 'site' in node:
239                 longitude = str(node['site']['longitude'])
240                 latitude = str(node['site']['latitude'])
241                 location = etree.SubElement(node_tag, 'location', country='unknown', \
242                                             longitude=longitude, latitude=latitude)                
243
244     def add_interfaces(self, interfaces):
245         pass     
246
247     def add_links(self, links):
248         pass
249     
250     def add_slivers(self, slivers, network=None, sliver_urn=None, no_dupes=False):
251         # add slice name to network tag
252         network_tags = self.xml.xpath('//network')
253         if network_tags:
254             network_tag = network_tags[0]
255             network_tag.set('slice', urn_to_hrn(sliver_urn)[0])
256         slivers = self._process_slivers(slivers)
257         nodes_with_slivers = self.get_nodes_with_slivers(network)
258         for sliver in slivers:
259             if sliver['hostname'] in nodes_with_slivers:
260                 continue
261             node_elem = self.get_node_element(sliver['hostname'], network)
262             sliver_elem = etree.SubElement(node_elem, 'sliver')
263             if 'tags' in sliver:
264                 for tag in sliver['tags']:
265                     etree.SubElement(sliver_elem, tag['tagname']).text = value=tag['value']
266
267     def remove_slivers(self, slivers, network=None, no_dupes=False):
268         slivers = self._process_slivers(slivers)
269         for sliver in slivers:
270             node_elem = self.get_node_element(sliver['hostname'], network)
271             sliver_elem = node_elem.find('sliver')
272             if sliver_elem != None:
273                 node_elem.remove(sliver_elem)
274     
275     def add_default_sliver_attribute(self, name, value, network=None):
276         if network:
277             defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
278         else:
279             defaults = self.xml.xpath("//sliver_defaults" % network)
280         if not defaults :
281             network_tag = self.xml.xpath("//network[@name='%s']" % network)
282             if isinstance(network_tag, list):
283                 network_tag = network_tag[0]
284             defaults = self.add_element('sliver_defaults', attrs={}, parent=network_tag)
285         elif isinstance(defaults, list):
286             defaults = defaults[0]
287         self.add_attribute(defaults, name, value)
288
289     def add_sliver_attribute(self, hostname, name, value, network=None):
290         node = self.get_node_element(hostname, network)
291         sliver = node.find("sliver")
292         self.add_attribute(sliver, name, value)
293
294     def remove_default_sliver_attribute(self, name, value, network=None):
295         if network:
296             defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
297         else:
298             defaults = self.xml.xpath("//sliver_defaults" % network)
299         self.remove_attribute(defaults, name, value)
300
301     def remove_sliver_attribute(self, hostname, name, value, network=None):
302         node = self.get_node_element(hostname, network)
303         sliver = node.find("sliver")
304         self.remove_attribute(sliver, name, value)
305
306     def add_vlink(self, fromhost, tohost, kbps, network=None):
307         fromnode = self.get_node_element(fromhost, network)
308         tonode = self.get_node_element(tohost, network)
309         links = self.get_link(fromnode, tonode, network)
310
311         for link in links:
312             vlink = etree.SubElement(link, "vlink")
313             fromid = fromnode.get("id")
314             toid = tonode.get("id")
315             vlink.set("endpoints", "%s %s" % (fromid, toid))
316             self.add_attribute(vlink, "kbps", kbps)
317
318
319     def remove_vlink(self, endpoints, network=None):
320         vlinks = self.query_vlinks(endpoints, network)
321         for vlink in vlinks:
322             vlink.getparent().remove(vlink)
323
324
325     def merge(self, in_rspec):
326         """
327         Merge contents for specified rspec with current rspec 
328         """
329
330         from sfa.rspecs.rspec_parser import parse_rspec
331         rspec = parse_rspec(in_rspec)
332         if rspec.type.lower() == 'protogeni':
333             from sfa.rspecs.rspec_converter import RSpecConverter
334             in_rspec = RSpecConverter.to_sfa_rspec(in_rspec)
335             
336         # just copy over all networks
337         current_networks = self.get_networks()
338         rspec = SfaRSpec(rspec=in_rspec)
339         networks = rspec.get_network_elements()
340         for network in networks:
341             current_network = network.get('name')
342             if current_network and current_network not in current_networks:
343                 self.xml.append(network)
344                 current_networks.append(current_network)
345             
346         
347          
348
349 if __name__ == '__main__':
350     rspec = SfaRSpec()
351     nodes = [
352     {'network': 'plc',
353      'hostname': 'node1.planet-lab.org',
354      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
355       'node_id': 1,
356     },
357     {'network': 'plc',
358      'hostname': 'node2.planet-lab.org',
359      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
360       'node_id': 1,
361     },
362     {'network': 'ple',
363      'hostname': 'node1.planet-lab.eu',
364      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
365       'node_id': 1,
366     },
367     ]
368     rspec.add_nodes(nodes)
369     print rspec