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