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