3 from sfa.rspecs.aggregates.vini.topology import *
5 # Taken from bwlimit.py
7 # See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
8 # warned that older versions of tc interpret "kbps", "mbps", "mbit",
9 # and "kbit" to mean (in this system) "kibps", "mibps", "mibit", and
10 # "kibit" and that if an older version is installed, all rates will
11 # be off by a small fraction.
19 "gibit": 1024*1024*1024,
21 "tibit": 1024*1024*1024*1024,
22 "tbit": 1000000000000,
28 "gibps": 8*1024*1024*1024,
30 "tibps": 8*1024*1024*1024*1024,
37 Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
42 m = re.match(r"([0-9.]+)(\D*)", s)
45 suffix = m.group(2).lower()
46 if suffixes.has_key(suffix):
47 return int(float(m.group(1)) * suffixes[suffix])
51 def format_tc_rate(rate):
53 Formats a bits/second rate into a tc rate string
56 if rate >= 1000000000 and (rate % 1000000000) == 0:
57 return "%.0fgbit" % (rate / 1000000000.)
58 elif rate >= 1000000 and (rate % 1000000) == 0:
59 return "%.0fmbit" % (rate / 1000000.)
61 return "%.0fkbit" % (rate / 1000.)
63 return "%.0fbit" % rate
67 def __init__(self, node, bps = 1000 * 1000000):
68 self.id = node['node_id']
69 self.hostname = node['hostname']
70 self.shortname = self.hostname.replace('.vini-veritas.net', '')
71 self.site_id = node['site_id']
72 self.ipaddr = socket.gethostbyname(self.hostname)
76 def get_link_id(self, remote):
77 if self.id < remote.id:
78 link = (self.id<<7) + remote.id
80 link = (remote.id<<7) + self.id
83 def get_iface_id(self, remote):
84 if self.id < remote.id:
90 def get_virt_ip(self, remote):
91 link = self.get_link_id(remote)
92 iface = self.get_iface_id(remote)
94 second = ((link & 0x3f)<<2) + iface
95 return "192.168.%d.%d" % (first, second)
97 def get_virt_net(self, remote):
98 link = self.get_link_id(remote)
100 second = (link & 0x3f)<<2
101 return "192.168.%d.%d/30" % (first, second)
103 def get_site(self, sites):
104 return sites[self.site_id]
106 def get_topo_rspec(self, link):
107 if link.end1 == self:
109 elif link.end2 == self:
112 raise Error("Link does not connect to Node")
114 my_ip = self.get_virt_ip(remote)
115 remote_ip = remote.get_virt_ip(self)
116 net = self.get_virt_net(remote)
117 bw = format_tc_rate(link.bps)
118 return (remote.id, remote.ipaddr, bw, my_ip, remote_ip, net)
120 def add_link(self, link):
123 def add_tag(self, sites):
124 s = self.get_site(sites)
125 words = self.hostname.split(".")
126 index = words[0].replace("node", "")
128 self.tag = s.tag + index
132 # Assumes there is at most one Link between two sites
133 def get_sitelink(self, node, sites):
134 site1 = sites[self.site_id]
135 site2 = sites[node.site_id]
136 sl = site1.links.intersection(site2.links)
143 def __init__(self, end1, end2, bps = 1000 * 1000000):
153 def __init__(self, site):
154 self.id = site['site_id']
155 self.node_ids = site['node_ids']
156 self.name = site['abbreviated_name']
157 self.tag = site['login_base']
158 self.public = site['is_public']
161 def get_sitenodes(self, nodes):
163 for i in self.node_ids:
167 def add_link(self, link):
172 def __init__(self, slice):
173 self.id = slice['slice_id']
174 self.name = slice['name']
175 self.node_ids = set(slice['node_ids'])
176 self.slice_tag_ids = slice['slice_tag_ids']
178 def get_tag(self, tagname, slicetags, node = None):
179 for i in self.slice_tag_ids:
181 if tag.tagname == tagname:
182 if (not node) or (node.id == tag.node_id):
187 def get_nodes(self, nodes):
189 for id in self.node_ids:
194 # Add a new slice tag
195 def add_tag(self, tagname, value, slicetags, node = None):
196 record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
198 record['node_id'] = node.id
200 record['node_id'] = None
201 tag = Slicetag(record)
202 slicetags[tag.id] = tag
203 self.slice_tag_ids.append(tag.id)
208 # Update a slice tag if it exists, else add it
209 def update_tag(self, tagname, value, slicetags, node = None):
210 tag = self.get_tag(tagname, slicetags, node)
211 if tag and tag.value == value:
217 tag = self.add_tag(tagname, value, slicetags, node)
220 def assign_egre_key(self, slicetags):
221 if not self.get_tag('egre_key', slicetags):
223 key = free_egre_key(slicetags)
224 self.update_tag('egre_key', key, slicetags)
226 # Should handle this case...
230 def turn_on_netns(self, slicetags):
231 tag = self.get_tag('netns', slicetags)
232 if (not tag) or (tag.value != '1'):
233 self.update_tag('netns', '1', slicetags)
236 def turn_off_netns(self, slicetags):
237 tag = self.get_tag('netns', slicetags)
238 if tag and (tag.value != '0'):
242 def add_cap_net_admin(self, slicetags):
243 tag = self.get_tag('capabilities', slicetags)
245 caps = tag.value.split(',')
247 if cap == "CAP_NET_ADMIN":
250 newcaps = "CAP_NET_ADMIN," + tag.value
251 self.update_tag('capabilities', newcaps, slicetags)
253 self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
256 def remove_cap_net_admin(self, slicetags):
257 tag = self.get_tag('capabilities', slicetags)
259 caps = tag.value.split(',')
262 if cap != "CAP_NET_ADMIN":
265 value = ','.join(newcaps)
266 self.update_tag('capabilities', value, slicetags)
271 # Update the vsys/setup-link and vsys/setup-nat slice tags.
272 def add_vsys_tags(self, slicetags):
274 for i in self.slice_tag_ids:
276 if tag.tagname == 'vsys':
277 if tag.value == 'setup-link':
279 elif tag.value == 'setup-nat':
282 self.add_tag('vsys', 'setup-link', slicetags)
284 self.add_tag('vsys', 'setup-nat', slicetags)
290 def __init__(self, tag):
291 self.id = tag['slice_tag_id']
293 # Make one up for the time being...
294 self.id = Slicetag.newid
296 self.slice_id = tag['slice_id']
297 self.tagname = tag['tagname']
298 self.value = tag['value']
299 self.node_id = tag['node_id']
304 # Mark a tag as deleted
309 def write(self, api):
312 api.plshell.UpdateSliceTag(api.plauth, self.id, self.value)
314 api.plshell.AddSliceTag(api.plauth, self.slice_id,
315 self.tagname, self.value, self.node_id)
316 elif self.deleted and int(self.id) > 0:
317 api.plshell.DeleteSliceTag(api.plauth, self.id)
321 A topology is a compound object consisting of:
322 * a dictionary mapping site IDs to Site objects
323 * a dictionary mapping node IDs to Node objects
324 * the Site objects are connected via SiteLink objects representing
325 the physical topology and available bandwidth
326 * the Node objects are connected via Link objects representing
327 the requested or assigned virtual topology of a slice
330 def __init__(self, api):
332 self.sites = get_sites(api)
333 self.nodes = get_nodes(api)
334 self.tags = get_slice_tags(api)
338 for (s1, s2) in PhysicalLinks:
339 self.sitelinks.append(Link(self.sites[s1], self.sites[s2]))
341 for id in self.nodes:
342 self.nodes[id].add_tag(self.sites)
346 if tag.tagname == 'topo_rspec':
347 node1 = self.nodes[tag.node_id]
349 for (id, realip, bw, lvip, rvip, vnet) in l:
350 allocbps = get_tc_rate(bw)
351 node1.bps -= allocbps
353 node2 = self.nodes[id]
354 if node1.id < node2.id:
355 sl = node1.get_sitelink(node2, self.sites)
361 def lookupSite(self, id):
366 raise KeyError("site ID %s not found" % id)
372 sites.append(self.sites[s])
375 def lookupNode(self, id):
380 raise KeyError("node ID %s not found" % id)
386 nodes.append(self.nodes[n])
389 def nodesInTopo(self):
392 if self.nodes[n].links:
393 nodes.append(self.nodes[n])
396 def lookupSliceTag(self, id):
401 raise KeyError("slicetag ID %s not found" % id)
404 def getSliceTags(self):
407 tags.append(self.tags[t])
410 def nodeTopoFromRspec(self, rspec):
412 raise Error("virtual topology already present")
414 rspecdict = rspec.toDict()
416 for node in self.getNodes():
417 nodedict[node.tag] = node
419 linkspecs = rspecdict['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
421 n1 = nodedict[l['endpoint'][0]]
422 n2 = nodedict[l['endpoint'][1]]
423 bps = get_tc_rate(l['bw'][0])
424 self.nodelinks.append(Link(n1, n2, bps))
426 def nodeTopoFromSliceTags(self, slice):
428 raise Error("virtual topology already present")
430 for node in slice.get_nodes(self.nodes):
431 linktag = slice.get_tag('topo_rspec', self.tags, node)
433 l = eval(linktag.value)
434 for (id, realip, bw, lvip, rvip, vnet) in l:
436 bps = get_tc_rate(bw)
437 remote = self.lookupNode(id)
438 self.nodelinks.append(Link(node, remote, bps))
440 def updateSliceTags(self, slice):
441 if not self.nodelinks:
444 slice.update_tag('vini_topo', 'manual', self.tags)
445 slice.assign_egre_key(self.tags)
446 slice.turn_on_netns(self.tags)
447 slice.add_cap_net_admin(self.tags)
449 for node in slice.get_nodes(self.nodes):
451 for link in node.links:
452 linkdesc.append(node.get_topo_rspec(link))
454 topo_str = "%s" % linkdesc
455 slice.update_tag('topo_rspec', topo_str, self.tags, node)
457 # Update slice tags in database
458 for tag in self.getSliceTags():
459 if tag.slice_id == slice.id:
460 if tag.tagname == 'topo_rspec' and not tag.updated:
466 Create a dictionary of site objects keyed by site ID
470 for site in api.plshell.GetSites(api.plauth):
471 t = site['site_id'], Site(site)
477 Create a dictionary of node objects keyed by node ID
481 for node in api.plshell.GetNodes(api.plauth):
482 t = node['node_id'], Node(node)
487 Create a dictionary of slice objects keyed by slice ID
489 def get_slice(api, slicename):
490 slice = api.plshell.GetSlices(api.plauth, [slicename])
492 return Slice(slice[0])
497 Create a dictionary of slicetag objects keyed by slice tag ID
499 def get_slice_tags(api):
501 for tag in api.plshell.GetSliceTags(api.plauth):
502 t = tag['slice_tag_id'], Slicetag(tag)
509 def free_egre_key(slicetags):
513 if tag.tagname == 'egre_key':
514 used.add(int(tag.value))
516 for i in range(1, 256):
521 raise KeyError("No more EGRE keys available")