16fa3e8b29a1c9a23dfa332f54f9e4a2d9830bfd
[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 name.text == 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, e.text))
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         node = self.get_node_element(hostname, network)
98         sliver = node.find("sliver")
99         return self.attributes_list(sliver)
100
101     def get_slice_attributes(self, network=None):
102         slice_attributes = []
103         nodes_with_slivers = self.get_nodes_with_slivers(network)
104         for default_attribute in self.get_default_sliver_attributes(network):
105             attribute = {'name': str(default_attribute[0]), 'value': str(default_attribute[1]), 'node_id': None}
106             slice_attributes.append(attribute)
107         for node in nodes_with_slivers:
108             sliver_attributes = self.get_sliver_attributes(node, network)
109             for sliver_attribute in sliver_attributes:
110                 attribute = {'name': str(sliver_attribute[0]), 'value': str(sliver_attribute[1]), 'node_id': node}
111                 slice_attributes.append(attribute)    
112         return slice_attributes
113
114     def get_site_nodes(self, siteid, network=None):
115         if network:
116             nodes = self.xml.xpath('//network[@name="%s"]/site[@id="%s"]/node/hostname/text()' % \
117                                     (network, siteid))
118         else:
119             nodes = self.xml.xpath('//site[@id="%s"]/node/hostname/text()' % siteid)
120         return nodes
121         
122     def get_links(self, network=None):
123         if network: 
124             links = self.xml.xpath('//network[@name="%s"]/link' % network)
125         else:
126             links = self.xml.xpath('//link')    
127         linklist = []
128         for link in links:
129             (end1, end2) = link.get("endpoints").split()
130             name = link.find("description")
131             linklist.append((name.text,
132                              self.get_site_nodes(end1, network),
133                              self.get_site_nodes(end2, network)))
134         return linklist
135
136     def get_link(self, fromnode, tonode, network=None):
137         fromsite = fromnode.getparent()
138         tosite = tonode.getparent()
139         fromid = fromsite.get("id")
140         toid = tosite.get("id")
141         if network:
142             query = "//network[@name='%s']" % network + "/link[@endpoints = '%s %s']"
143         else:
144             query = "//link[@endpoints = '%s %s']"
145
146         results = self.rspec.xpath(query % (fromid, toid))
147         if not results:
148             results = self.rspec.xpath(query % (toid, fromid))
149         return results
150
151     def query_links(self, fromnode, tonode, network=None):
152         return get_link(fromnode, tonode, network)
153
154     def get_vlinks(self, network=None):
155         vlinklist = []
156         if network: 
157             vlinks = self.xml.xpath("//network[@name='%s']//vlink" % network)
158         else:
159             vlinks = self.xml.xpath("//vlink") 
160         for vlink in vlinks:
161             endpoints = vlink.get("endpoints")
162             (end1, end2) = endpoints.split()
163             if network: 
164                 node1 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
165                                        (network, end1))[0]
166                 node2 = self.xml.xpath('//network[@name="%s"]//node[@id="%s"]/hostname/text()' % \
167                                        (network, end2))[0]
168             else: 
169                 node1 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end1)[0]
170                 node2 = self.xml.xpath('//node[@id="%s"]/hostname/text()' % end2)[0]
171             desc = "%s <--> %s" % (node1, node2)
172             kbps = vlink.find("kbps")
173             vlinklist.append((endpoints, desc, kbps.text))
174         return vlinklist
175
176     def get_vlink(self, endponts, network=None):
177         if network:
178             query = "//network[@name='%s']//vlink[@endpoints = '%s']" % (network, endpoints)
179         else:
180             query = "//vlink[@endpoints = '%s']" % (network, endpoints)
181         results = self.rspec.xpath(query)
182         return results
183         
184     def query_vlinks(self, endpoints, network=None):
185         return get_vlink(endpoints,network)
186
187     ##################
188     # Builder
189     ##################
190
191     def add_network(self, network):
192         network_tag = etree.SubElement(self.xml, 'network', id=network)     
193
194     def add_nodes(self, nodes, network = None, no_dupes=False):
195         if not isinstance(nodes, list):
196             nodes = [nodes]
197         for node in nodes:
198             if no_dupes and \
199               self.get_node_element(node['hostname']):
200                 # node already exists
201                 continue
202
203             network_tag = self.xml
204             if 'network' in node:
205                 network = node['network']
206                 network_tags = self.xml.xpath('//network[@name="%s"]' % network)
207                 if not network_tags:
208                     network_tag = etree.SubElement(self.xml, 'network', name=network)
209                 else:
210                     network_tag = network_tags[0]
211                      
212             node_tag = etree.SubElement(network_tag, 'node')
213             if 'network' in node:
214                 node_tag.set('component_manager_id', hrn_to_urn(network, 'authority+sa'))
215             if 'urn' in node:
216                 node_tag.set('component_id', node['urn']) 
217             if 'site_urn' in node:
218                 node_tag.set('site_id', node['site_urn'])
219             if 'node_id' in node: 
220                 node_tag.set('node_id', 'n'+str(node['node_id']))
221             if 'hostname' in node:
222                 hostname_tag = etree.SubElement(node_tag, 'hostname').text = node['hostname']
223             if 'interfaces' in node:
224                 for interface in node['interfaces']:
225                     if 'bwlimit' in interface and interface['bwlimit']:
226                         bwlimit = etree.SubElement(node_tag, 'bw_limit', units='kbps').text = str(interface['bwlimit']/1000)
227             if 'tags' in node:
228                 for tag in node['tags']:
229                    # expose this hard wired list of tags, plus the ones that are marked 'sfa' in their category 
230                    if tag['tagname'] in ['fcdistro', 'arch'] or 'sfa' in tag['category'].split('/'):
231                         tag_element = etree.SubElement(node_tag, tag['tagname']).text=tag['value']
232
233             if 'site' in node:
234                 longitude = str(node['site']['longitude'])
235                 latitude = str(node['site']['latitude'])
236                 location = etree.SubElement(node_tag, 'location', country='unknown', \
237                                             longitude=longitude, latitude=latitude)                
238
239     def add_interfaces(self, interfaces):
240         pass     
241
242     def add_links(self, links):
243         pass
244     
245     def add_slivers(self, slivers, network=None, sliver_urn=None, no_dupes=False):
246         slivers = self._process_slivers(slivers)
247         nodes_with_slivers = self.get_nodes_with_slivers(network)
248         for sliver in slivers:
249             if sliver['hostname'] in nodes_with_slivers:
250                 continue
251             node_elem = self.get_node_element(sliver['hostname'], network)
252             sliver_elem = etree.SubElement(node_elem, 'sliver')
253             if 'tags' in sliver:
254                 for tag in sliver['tags']:
255                     etree.SubElement(sliver_elem, tag['tagname']).text = value=tag['value']
256
257     def remove_slivers(self, slivers, network=None, no_dupes=False):
258         slivers = self._process_slivers(slivers)
259         for sliver in slivers:
260             node_elem = self.get_node_element(sliver['hostname'], network)
261             sliver_elem = node_elem.find('sliver')
262             if sliver_elem != None:
263                 node_elem.remove(sliver_elem)
264     
265     def add_default_sliver_attribute(self, name, value, network=None):
266         if network:
267             defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
268         else:
269             defaults = self.xml.xpath("//sliver_defaults" % network)
270         if not defaults :
271             network_tag = self.xml.xpath("//network[@name='%s']" % network)
272             if isinstance(network_tag, list):
273                 network_tag = network_tag[0]
274             defaults = self.add_element('sliver_defaults', attrs={}, parent=network_tag)
275         elif isinstance(defaults, list):
276             defaults = defaults[0]
277         self.add_attribute(defaults, name, value)
278
279     def add_sliver_attribute(self, hostname, name, value, network=None):
280         node = self.get_node_element(hostname, network)
281         sliver = node.find("sliver")
282         self.add_attribute(sliver, name, value)
283
284     def remove_default_sliver_attribute(self, name, value, network=None):
285         if network:
286             defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
287         else:
288             defaults = self.xml.xpath("//sliver_defaults" % network)
289         self.remove_attribute(defaults, name, value)
290
291     def remove_sliver_attribute(self, hostname, name, value, network=None):
292         node = self.get_node_element(hostname, network)
293         sliver = node.find("sliver")
294         self.remove_attribute(sliver, name, value)
295
296     def add_vlink(self, fromhost, tohost, kbps, network=None):
297         fromnode = self.get_node_element(fromhost, network)
298         tonode = self.get_node_element(tohost, network)
299         links = self.get_link(fromnode, tonode, network)
300
301         for link in links:
302             vlink = etree.SubElement(link, "vlink")
303             fromid = fromnode.get("id")
304             toid = tonode.get("id")
305             vlink.set("endpoints", "%s %s" % (fromid, toid))
306             self.add_attribute(vlink, "kbps", kbps)
307
308
309     def remove_vlink(self, endpoints, network=None):
310         vlinks = self.query_vlinks(endpoints, network)
311         for vlink in vlinks:
312             vlink.getparent().remove(vlink)
313
314
315     def merge(self, in_rspec):
316         """
317         Merge contents for specified rspec with current rspec 
318         """
319
320         from sfa.rspecs.rspec_parser import parse_rspec
321         rspec = parse_rspec(in_rspec)
322         if rspec.type.lower() == 'protogeni':
323             from sfa.rspecs.rspec_converter import RSpecConverter
324             in_rspec = RSpecConverter.to_sfa_rspec(in_rspec)
325             
326         # just copy over all networks
327         current_networks = self.get_networks()
328         rspec = SfaRSpec(rspec=in_rspec)
329         networks = rspec.get_network_elements()
330         for network in networks:
331             current_network = network.get('name')
332             if not current_network in current_networks:
333                 self.xml.append(network)
334                 current_networks.append(current_network)
335             
336         
337          
338
339 if __name__ == '__main__':
340     rspec = SfaRSpec()
341     nodes = [
342     {'network': 'plc',
343      'hostname': 'node1.planet-lab.org',
344      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
345       'node_id': 1,
346     },
347     {'network': 'plc',
348      'hostname': 'node2.planet-lab.org',
349      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
350       'node_id': 1,
351     },
352     {'network': 'ple',
353      'hostname': 'node1.planet-lab.eu',
354      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
355       'node_id': 1,
356     },
357     ]
358     rspec.add_nodes(nodes)
359     print rspec