1 from __future__ import with_statement
2 from sfa.util.faults import *
3 from xmlbuilder import XMLBuilder
6 from sfa.plc.network import *
7 from sfa.managers.vini.topology import PhysicalLinks
9 # Taken from bwlimit.py
11 # See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
12 # warned that older versions of tc interpret "kbps", "mbps", "mbit",
13 # and "kbit" to mean (in this system) "kibps", "mibps", "mibit", and
14 # "kibit" and that if an older version is installed, all rates will
15 # be off by a small fraction.
23 "gibit": 1024*1024*1024,
25 "tibit": 1024*1024*1024*1024,
26 "tbit": 1000000000000,
32 "gibps": 8*1024*1024*1024,
34 "tibps": 8*1024*1024*1024*1024,
41 Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
46 m = re.match(r"([0-9.]+)(\D*)", s)
49 suffix = m.group(2).lower()
50 if suffixes.has_key(suffix):
51 return int(float(m.group(1)) * suffixes[suffix])
55 def format_tc_rate(rate):
57 Formats a bits/second rate into a tc rate string
60 if rate >= 1000000000 and (rate % 1000000000) == 0:
61 return "%.0fgbit" % (rate / 1000000000.)
62 elif rate >= 1000000 and (rate % 1000000) == 0:
63 return "%.0fmbit" % (rate / 1000000.)
65 return "%.0fkbit" % (rate / 1000.)
67 return "%.0fbit" % rate
71 def __init__(self, network, node, bps = 1000 * 1000000):
72 Node.__init__(self, network, node)
75 self.name = self.hostname.replace('.vini-veritas.net', '')
77 def get_link_id(self, remote):
78 if self.id < remote.id:
79 link = (self.id<<7) + remote.id
81 link = (remote.id<<7) + self.id
84 def get_iface_id(self, remote):
85 if self.id < remote.id:
91 def get_virt_ip(self, remote):
92 link = self.get_link_id(remote)
93 iface = self.get_iface_id(remote)
95 second = ((link & 0x3f)<<2) + iface
96 return "192.168.%d.%d" % (first, second)
98 def get_virt_net(self, remote):
99 link = self.get_link_id(remote)
101 second = (link & 0x3f)<<2
102 return "192.168.%d.%d/30" % (first, second)
104 def get_topo_rspec(self, link):
105 if link.end1 == self:
107 elif link.end2 == self:
110 raise Error("Link does not connect to Node")
112 my_ip = self.get_virt_ip(remote)
113 remote_ip = remote.get_virt_ip(self)
114 net = self.get_virt_net(remote)
115 bw = format_tc_rate(link.bps)
116 ipaddr = remote.get_primary_iface().ipv4
117 return (remote.id, ipaddr, bw, my_ip, remote_ip, net)
119 def add_link(self, link):
122 # Assumes there is at most one Link between two sites
123 def get_sitelink(self, node):
124 site1 = self.network.sites[self.site_id]
125 site2 = self.network.sites[node.site_id]
126 sl = site1.links.intersection(site2.links)
131 def toxml(self, xml):
132 slice = self.network.slice
133 if self.whitelist and not self.sliver:
134 if not slice or slice.id not in self.whitelist:
137 with xml.node(id = self.idtag):
140 with xml.bw_unallocated(units="kbps"):
141 xml << str(int(self.bps/1000))
142 self.get_primary_iface().toxml(xml)
144 self.sliver.toxml(xml)
147 class ViniSite(Site):
148 def __init__(self, network, site):
149 Site.__init__(self, network, site)
152 def add_link(self, link):
155 class ViniSlice(Slice):
156 def assign_egre_key(self):
157 tag = self.get_tag('egre_key')
160 key = self.network.free_egre_key()
162 raise InvalidRSpec("ran out of EGRE keys!")
163 tag = self.update_tag('egre_key', key, None, 'admin')
166 def turn_on_netns(self):
167 tag = self.get_tag('netns')
168 if (not tag) or (tag.value != '1'):
169 tag = self.update_tag('netns', '1', None, 'admin')
172 def turn_off_netns(self):
173 tag = self.get_tag('netns')
174 if tag and (tag.value != '0'):
178 def add_cap_net_admin(self):
179 tag = self.get_tag('capabilities')
181 caps = tag.value.split(',')
183 if cap == "CAP_NET_ADMIN":
187 newcaps = "CAP_NET_ADMIN," + tag.value
188 self.update_tag('capabilities', newcaps, None, 'admin')
190 tag = self.add_tag('capabilities', 'CAP_NET_ADMIN', None, 'admin')
193 def remove_cap_net_admin(self):
194 tag = self.get_tag('capabilities')
196 caps = tag.value.split(',')
199 if cap != "CAP_NET_ADMIN":
202 value = ','.join(newcaps)
203 self.update_tag('capabilities', value, None, 'admin')
209 def __init__(self, end1, end2, bps = 1000 * 1000000, parent = None):
220 self.parent.children.append(self)
222 def toxml(self, xml):
223 end_ids = "%s %s" % (self.end1.idtag, self.end2.idtag)
226 with xml.vlink(endpoints=end_ids):
227 with xml.description:
228 xml << "%s -- %s" % (self.end1.name, self.end2.name)
230 xml << str(int(self.bps/1000))
232 with xml.link(endpoints=end_ids):
233 with xml.description:
234 xml << "%s -- %s" % (self.end1.name, self.end2.name)
235 with xml.bw_unallocated(units="kbps"):
236 xml << str(int(self.bps/1000))
237 for child in self.children:
242 class ViniNetwork(Network):
243 def __init__(self, api, type = "SFA"):
244 Network.__init__(self, api, type)
248 for (s1, s2) in PhysicalLinks:
249 self.sitelinks.append(Link(self.sites[s1], self.sites[s2]))
253 if tag.tagname == 'topo_rspec':
254 node1 = self.nodes[tag.node_id]
256 for (id, realip, bw, lvip, rvip, vnet) in l:
257 allocbps = get_tc_rate(bw)
258 node1.bps -= allocbps
260 node2 = self.nodes[id]
261 if node1.id < node2.id:
262 sl = node1.get_sitelink(node2)
267 def lookupSiteLink(self, node1, node2):
268 site1 = self.sites[node1.site_id]
269 site2 = self.sites[node2.site_id]
270 for link in self.sitelinks:
271 if site1 == link.end1 and site2 == link.end2:
273 if site2 == link.end1 and site1 == link.end2:
279 Check the requested topology against the available topology and capacity
281 def verifyTopology(self):
282 for link in self.nodelinks:
284 raise InvalidRSpec("must request positive bandwidth")
288 sitelink = self.lookupSiteLink(n1, n2)
290 raise InvalidRSpec("nodes %s and %s are not adjacent" %
291 (n1.idtag, n2.idtag))
292 if sitelink.bps < link.bps:
293 raise InvalidRSpec("not enough capacity between %s and %s" %
294 (n1.idtag, n2.idtag))
296 def __add_vlink(self, vlink, parent = None):
298 endpoints = vlink.get("endpoints")
300 (end1, end2) = endpoints.split()
301 n1 = self.lookupNode(end1)
302 n2 = self.lookupNode(end2)
304 """ Try to infer the endpoints for the virtual link """
305 site_endpoints = parent.get("endpoints")
306 (n1, n2) = self.__infer_endpoints(site_endpoints)
308 raise InvalidRSpec("no endpoints given")
310 #print "Added virtual link: %s -- %s" % (n1.tag, n2.tag)
311 bps = int(vlink.findtext("kbps")) * 1000
312 sitelink = self.lookupSiteLink(n1, n2)
314 raise InvalidRSpec("nodes %s and %s are not adjacent" %
315 (n1.idtag, n2.idtag))
316 self.nodelinks.append(Link(n1, n2, bps, sitelink))
320 Infer the endpoints of the virtual link. If the slice exists on
321 only a single node at each end of the physical link, we'll assume that
322 the user wants the virtual link to terminate at these nodes.
324 def __infer_endpoints(self, endpoints):
326 ends = endpoints.split()
329 site = self.lookupSite(end)
330 for id in site.node_ids:
331 if id in self.nodedict:
332 n.append(self.nodedict[id])
335 raise InvalidRSpec("could not infer endpoint for site %s" %
337 #print "Inferred endpoints: %s %s" % (n[0].idtag, n[1].idtag)
340 def addRSpec(self, xml, schema = None):
341 Network.addRSpec(self, xml, schema)
343 for node in self.nodesWithSlivers():
344 self.nodedict[node.id] = node
346 # Find vlinks under link elements
347 for vlink in self.rspec.iterfind("./network/link/vlink"):
348 link = vlink.getparent()
349 self.__add_vlink(vlink, link)
351 # Find vlinks that specify endpoints
352 for vlink in self.rspec.iterfind("./request/vlink[@endpoints]"):
353 self.__add_vlink(vlink)
357 Network.addSlice(self)
359 for node in self.slice.get_nodes():
360 linktag = self.slice.get_tag('topo_rspec', node)
362 l = eval(linktag.value)
363 for (id, realip, bw, lvip, rvip, vnet) in l:
365 bps = get_tc_rate(bw)
366 remote = self.lookupNode(id)
367 sitelink = self.lookupSiteLink(node, remote)
368 self.nodelinks.append(Link(node,remote,bps,sitelink))
371 def updateSliceTags(self):
374 tag = slice.update_tag('vini_topo', 'manual', None, 'admin')
375 slice.assign_egre_key()
376 slice.turn_on_netns()
377 slice.add_cap_net_admin()
379 for node in self.nodesWithSlivers():
381 for link in node.links:
382 linkdesc.append(node.get_topo_rspec(link))
384 topo_str = "%s" % linkdesc
385 tag = slice.update_tag('topo_rspec', topo_str, node, 'admin')
387 # Expire the un-updated topo_rspec tags
388 for tag in self.getSliceTags():
389 if tag.tagname in ['topo_rspec']:
390 if not tag.was_updated():
393 Network.updateSliceTags(self)
398 def free_egre_key(self):
400 for tag in self.getSliceTags():
401 if tag.tagname == 'egre_key':
402 used.add(int(tag.value))
404 for i in range(1, 256):
409 raise KeyError("No more EGRE keys available")
414 Produce XML directly from the topology specification.
417 xml = XMLBuilder(format = True, tab_step = " ")
418 with xml.RSpec(type=self.type):
420 element = xml.network(name=self.api.hrn, slice=self.slice.hrn)
422 element = xml.network(name=self.api.hrn)
426 self.slice.toxml(xml)
427 for site in self.getSites():
429 for link in self.sitelinks:
432 header = '<?xml version="1.0"?>\n'
433 return header + str(xml)
436 Create a dictionary of ViniSite objects keyed by site ID
438 def get_sites(self, api):
440 for site in api.driver.GetSites({'peer_id': None}):
441 t = site['site_id'], ViniSite(self, site)
447 Create a dictionary of ViniNode objects keyed by node ID
449 def get_nodes(self, api):
451 for node in api.driver.GetNodes({'peer_id': None}):
452 t = node['node_id'], ViniNode(self, node)
457 Return a ViniSlice object for a single slice
459 def get_slice(self, api, hrn):
460 slicename = hrn_to_pl_slicename(hrn)
461 slice = api.driver.GetSlices([slicename])
463 self.slice = ViniSlice(self, slicename, slice[0])