Added the possibility for users to define a set of information fields
[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     ###################
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                     #panos expose tags only if requested by the user using the -i option
210                     if self.user_options:
211                         #print "[sfa rspec] found tag ",tag," and have info ",self.user_options.get('info')
212                         if self.user_options.get('info') and tag['tagname'] in self.user_options.get('info'):
213                             tag_element = etree.SubElement(node_tag, tag['tagname'], value=tag['value'])
214
215                     if 'sfa' in tag['category'].split('/'):
216                         tag_element = etree.SubElement(node_tag, tag['tagname'], value=tag['value'])
217
218             if 'site' in node:
219                 longitude = str(node['site']['longitude'])
220                 latitude = str(node['site']['latitude'])
221                 location = etree.SubElement(node_tag, 'location', country='unknown', \
222                                             longitude=longitude, latitude=latitude)                
223
224     def add_interfaces(self, interfaces):
225         pass     
226
227     def add_links(self, links):
228         pass
229     
230     def add_slivers(self, slivers, network=None, no_dupes=False):
231         if not isinstance(slivers, list):
232             slivers = [slivers]
233
234         nodes_with_slivers = self.get_nodes_with_slivers(network)
235         for sliver in slivers:
236             if sliver['hostname'] in nodes_with_slivers:
237                 continue
238             node_elem = self.get_node_element(sliver['hostname'], network)
239             sliver_elem = etree.SubElement(node_elem, 'sliver')
240             if 'tags' in sliver:
241                 for tag in sliver['tags']:
242                     etree.SubElement(sliver_elem, tag['tagname'], value=tag['value'])
243
244     def remove_slivers(self, slivers, network=None, no_dupes=False):
245         if not isinstance(slivers, list):
246             slivers = [slivers]
247         for sliver in slivers:
248             node_elem = self.get_node_element(sliver['hostname'], network)
249             sliver_elem = node.find('sliver')
250             if sliver_elem != None:
251                 node_elem.remove(sliver)                 
252     
253     def add_default_sliver_attribute(self, name, value, network=None):
254         if network:
255             defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
256         else:
257             defaults = self.xml.xpath("//sliver_defaults" % network)
258         if defaults is None:
259             defaults = etree.Element("sliver_defaults")
260             network = self.xml.xpath("//network[@name='%s']" % network)
261             network.insert(0, defaults)
262         self.add_attribute(defaults, name, value)
263
264     def add_sliver_attribute(self, hostname, name, value, network=None):
265         node = self.get_node_element(hostname, network)
266         sliver = node.find("sliver")
267         self.add_attribute(sliver, name, value)
268
269     def remove_default_sliver_attribute(self, name, value, network=None):
270         if network:
271             defaults = self.xml.xpath("//network[@name='%s']/sliver_defaults" % network)
272         else:
273             defaults = self.xml.xpath("//sliver_defaults" % network)
274         self.remove_attribute(defaults, name, value)
275
276     def remove_sliver_attribute(self, hostname, name, value, network=None):
277         node = self.get_node_element(hostname, network)
278         sliver = node.find("sliver")
279         self.remove_attribute(sliver, name, value)
280
281     def add_vlink(self, fromhost, tohost, kbps, network=None):
282         fromnode = self.get_node_element(fromhost, network)
283         tonode = self.get_node_element(tohost, network)
284         links = self.get_link(fromnode, tonode, network)
285
286         for link in links:
287             vlink = etree.SubElement(link, "vlink")
288             fromid = fromnode.get("id")
289             toid = tonode.get("id")
290             vlink.set("endpoints", "%s %s" % (fromid, toid))
291             self.add_attribute(vlink, "kbps", kbps)
292
293
294     def remove_vlink(self, endpoints, network=None):
295         vlinks = self.query_vlinks(endpoints, network)
296         for vlink in vlinks:
297             vlink.getparent().remove(vlink)
298
299
300     def merge(self, in_rspec):
301         """
302         Merge contents for specified rspec with current rspec 
303         """
304
305         # just copy over all networks
306         current_networks = self.get_networks()
307         rspec = SfaRSpec(rspec=in_rspec)
308         networks = rspec.get_network_elements()
309         for network in networks:
310             current_network = network.get('name')
311             if not current_network in current_networks:
312                 self.xml.append(network)
313                 current_networks.append(current_network)
314         
315          
316
317 if __name__ == '__main__':
318     rspec = SfaRSpec()
319     nodes = [
320     {'network': 'plc',
321      'hostname': 'node1.planet-lab.org',
322      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
323       'node_id': 1,
324     },
325     {'network': 'plc',
326      'hostname': 'node2.planet-lab.org',
327      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
328       'node_id': 1,
329     },
330     {'network': 'ple',
331      'hostname': 'node1.planet-lab.eu',
332      'site_urn': 'urn:publicid:IDN+plc+authority+cm',
333       'node_id': 1,
334     },
335     ]
336     rspec.add_nodes(nodes)
337     print rspec