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