3 from sfa.rspecs.aggregates.vini.topology import *
7 <endpoint>i2atla1</endpoint>
8 <endpoint>i2chic1</endpoint>
12 <endpoint>i2atla1</endpoint>
13 <endpoint>i2hous1</endpoint>
17 <endpoint>i2atla1</endpoint>
18 <endpoint>i2wash1</endpoint>
22 <endpoint>i2chic1</endpoint>
23 <endpoint>i2kans1</endpoint>
27 <endpoint>i2chic1</endpoint>
28 <endpoint>i2wash1</endpoint>
32 <endpoint>i2hous1</endpoint>
33 <endpoint>i2kans1</endpoint>
37 <endpoint>i2hous1</endpoint>
38 <endpoint>i2losa1</endpoint>
42 <endpoint>i2kans1</endpoint>
43 <endpoint>i2salt1</endpoint>
47 <endpoint>i2losa1</endpoint>
48 <endpoint>i2salt1</endpoint>
52 <endpoint>i2losa1</endpoint>
53 <endpoint>i2seat1</endpoint>
57 <endpoint>i2newy1</endpoint>
58 <endpoint>i2wash1</endpoint>
62 <endpoint>i2salt1</endpoint>
63 <endpoint>i2seat1</endpoint>
67 # Taken from bwlimit.py
69 # See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
70 # warned that older versions of tc interpret "kbps", "mbps", "mbit",
71 # and "kbit" to mean (in this system) "kibps", "mibps", "mibit", and
72 # "kibit" and that if an older version is installed, all rates will
73 # be off by a small fraction.
81 "gibit": 1024*1024*1024,
83 "tibit": 1024*1024*1024*1024,
84 "tbit": 1000000000000,
90 "gibps": 8*1024*1024*1024,
92 "tibps": 8*1024*1024*1024*1024,
99 Parses an integer or a tc rate string (e.g., 1.5mbit) into bits/second
104 m = re.match(r"([0-9.]+)(\D*)", s)
107 suffix = m.group(2).lower()
108 if suffixes.has_key(suffix):
109 return int(float(m.group(1)) * suffixes[suffix])
113 def format_tc_rate(rate):
115 Formats a bits/second rate into a tc rate string
118 if rate >= 1000000000 and (rate % 1000000000) == 0:
119 return "%.0fgbit" % (rate / 1000000000.)
120 elif rate >= 1000000 and (rate % 1000000) == 0:
121 return "%.0fmbit" % (rate / 1000000.)
123 return "%.0fkbit" % (rate / 1000.)
125 return "%.0fbit" % rate
129 def __init__(self, node, bps = 1000 * 1000000):
130 self.id = node['node_id']
131 self.hostname = node['hostname']
132 self.shortname = self.hostname.replace('.vini-veritas.net', '')
133 self.site_id = node['site_id']
134 self.ipaddr = socket.gethostbyname(self.hostname)
138 def get_link_id(self, remote):
139 if self.id < remote.id:
140 link = (self.id<<7) + remote.id
142 link = (remote.id<<7) + self.id
145 def get_iface_id(self, remote):
146 if self.id < remote.id:
152 def get_virt_ip(self, remote):
153 link = self.get_link_id(remote)
154 iface = self.get_iface_id(remote)
156 second = ((link & 0x3f)<<2) + iface
157 return "192.168.%d.%d" % (first, second)
159 def get_virt_net(self, remote):
160 link = self.get_link_id(remote)
162 second = (link & 0x3f)<<2
163 return "192.168.%d.%d/30" % (first, second)
165 def get_site(self, sites):
166 return sites[self.site_id]
168 def get_topo_rspec(self, link):
169 if link.end1 == self:
171 elif link.end2 == self:
174 raise Error("Link does not connect to Node")
176 my_ip = self.get_virt_ip(remote)
177 remote_ip = remote.get_virt_ip(self)
178 net = self.get_virt_net(remote)
179 bw = format_tc_rate(link.bps)
180 return (remote.id, remote.ipaddr, bw, my_ip, remote_ip, net)
182 def add_link(self, link):
185 def add_tag(self, sites):
186 s = self.get_site(sites)
187 words = self.hostname.split(".")
188 index = words[0].replace("node", "")
190 self.tag = s.tag + index
194 # Assumes there is at most one Link between two sites
195 def get_sitelink(self, node, sites):
196 site1 = sites[self.site_id]
197 site2 = sites[node.site_id]
198 sl = site1.links.intersection(site2.links)
205 def __init__(self, end1, end2, bps = 1000 * 1000000):
215 def __init__(self, site):
216 self.id = site['site_id']
217 self.node_ids = site['node_ids']
218 self.name = site['abbreviated_name'].replace(" ", "_")
219 self.tag = site['login_base']
220 self.public = site['is_public']
223 def get_sitenodes(self, nodes):
225 for i in self.node_ids:
229 def add_link(self, link):
234 def __init__(self, slice):
235 self.id = slice['slice_id']
236 self.name = slice['name']
237 self.node_ids = set(slice['node_ids'])
238 self.slice_tag_ids = slice['slice_tag_ids']
240 def get_tag(self, tagname, slicetags, node = None):
241 for i in self.slice_tag_ids:
243 if tag.tagname == tagname:
244 if (not node) or (node.id == tag.node_id):
249 def get_nodes(self, nodes):
251 for id in self.node_ids:
256 # Add a new slice tag
257 def add_tag(self, tagname, value, slicetags, node = None):
258 record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
260 record['node_id'] = node.id
262 record['node_id'] = None
263 tag = Slicetag(record)
264 slicetags[tag.id] = tag
265 self.slice_tag_ids.append(tag.id)
270 # Update a slice tag if it exists, else add it
271 def update_tag(self, tagname, value, slicetags, node = None):
272 tag = self.get_tag(tagname, slicetags, node)
273 if tag and tag.value == value:
279 tag = self.add_tag(tagname, value, slicetags, node)
282 def assign_egre_key(self, slicetags):
283 if not self.get_tag('egre_key', slicetags):
285 key = free_egre_key(slicetags)
286 self.update_tag('egre_key', key, slicetags)
288 # Should handle this case...
292 def turn_on_netns(self, slicetags):
293 tag = self.get_tag('netns', slicetags)
294 if (not tag) or (tag.value != '1'):
295 self.update_tag('netns', '1', slicetags)
298 def turn_off_netns(self, slicetags):
299 tag = self.get_tag('netns', slicetags)
300 if tag and (tag.value != '0'):
304 def add_cap_net_admin(self, slicetags):
305 tag = self.get_tag('capabilities', slicetags)
307 caps = tag.value.split(',')
309 if cap == "CAP_NET_ADMIN":
312 newcaps = "CAP_NET_ADMIN," + tag.value
313 self.update_tag('capabilities', newcaps, slicetags)
315 self.add_tag('capabilities', 'CAP_NET_ADMIN', slicetags)
318 def remove_cap_net_admin(self, slicetags):
319 tag = self.get_tag('capabilities', slicetags)
321 caps = tag.value.split(',')
324 if cap != "CAP_NET_ADMIN":
327 value = ','.join(newcaps)
328 self.update_tag('capabilities', value, slicetags)
333 # Update the vsys/setup-link and vsys/setup-nat slice tags.
334 def add_vsys_tags(self, slicetags):
336 for i in self.slice_tag_ids:
338 if tag.tagname == 'vsys':
339 if tag.value == 'setup-link':
341 elif tag.value == 'setup-nat':
344 self.add_tag('vsys', 'setup-link', slicetags)
346 self.add_tag('vsys', 'setup-nat', slicetags)
352 def __init__(self, tag):
353 self.id = tag['slice_tag_id']
355 # Make one up for the time being...
356 self.id = Slicetag.newid
358 self.slice_id = tag['slice_id']
359 self.tagname = tag['tagname']
360 self.value = tag['value']
361 self.node_id = tag['node_id']
366 # Mark a tag as deleted
371 def write(self, api):
374 api.plshell.UpdateSliceTag(api.plauth, self.id, self.value)
376 api.plshell.AddSliceTag(api.plauth, self.slice_id,
377 self.tagname, self.value, self.node_id)
378 elif self.deleted and int(self.id) > 0:
379 api.plshell.DeleteSliceTag(api.plauth, self.id)
383 A topology is a compound object consisting of:
384 * a dictionary mapping site IDs to Site objects
385 * a dictionary mapping node IDs to Node objects
386 * the Site objects are connected via SiteLink objects representing
387 the physical topology and available bandwidth
388 * the Node objects are connected via Link objects representing
389 the requested or assigned virtual topology of a slice
392 def __init__(self, api):
394 self.sites = get_sites(api)
395 self.nodes = get_nodes(api)
396 self.tags = get_slice_tags(api)
400 for (s1, s2) in PhysicalLinks:
401 self.sitelinks.append(Link(self.sites[s1], self.sites[s2]))
403 for id in self.nodes:
404 self.nodes[id].add_tag(self.sites)
408 if tag.tagname == 'topo_rspec':
409 node1 = self.nodes[tag.node_id]
411 for (id, realip, bw, lvip, rvip, vnet) in l:
412 allocbps = get_tc_rate(bw)
413 node1.bps -= allocbps
415 node2 = self.nodes[id]
416 if node1.id < node2.id:
417 sl = node1.get_sitelink(node2, self.sites)
423 def lookupSite(self, id):
428 raise KeyError("site ID %s not found" % id)
434 sites.append(self.sites[s])
437 def lookupNode(self, id):
442 raise KeyError("node ID %s not found" % id)
448 nodes.append(self.nodes[n])
451 def nodesInTopo(self):
454 if self.nodes[n].links:
455 nodes.append(self.nodes[n])
458 def lookupSliceTag(self, id):
463 raise KeyError("slicetag ID %s not found" % id)
466 def getSliceTags(self):
469 tags.append(self.tags[t])
472 def nodeTopoFromRspec(self, rspec):
474 raise Error("virtual topology already present")
476 rspecdict = rspec.toDict()
478 for node in self.getNodes():
479 nodedict[node.tag] = node
481 linkspecs = rspecdict['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
483 n1 = nodedict[l['endpoint'][0]]
484 n2 = nodedict[l['endpoint'][1]]
485 bps = get_tc_rate(l['bw'][0])
486 self.nodelinks.append(Link(n1, n2, bps))
488 def nodeTopoFromSliceTags(self, slice):
490 raise Error("virtual topology already present")
492 for node in slice.get_nodes(self.nodes):
493 linktag = slice.get_tag('topo_rspec', self.tags, node)
495 l = eval(linktag.value)
496 for (id, realip, bw, lvip, rvip, vnet) in l:
498 bps = get_tc_rate(bw)
499 remote = self.lookupNode(id)
500 self.nodelinks.append(Link(node, remote, bps))
502 def updateSliceTags(self, slice):
503 if not self.nodelinks:
506 slice.update_tag('vini_topo', 'manual', self.tags)
507 slice.assign_egre_key(self.tags)
508 slice.turn_on_netns(self.tags)
509 slice.add_cap_net_admin(self.tags)
511 for node in slice.get_nodes(self.nodes):
513 for link in node.links:
514 linkdesc.append(node.get_topo_rspec(link))
516 topo_str = "%s" % linkdesc
517 slice.update_tag('topo_rspec', topo_str, self.tags, node)
519 # Update slice tags in database
520 for tag in self.getSliceTags():
521 if tag.slice_id == slice.id:
522 if tag.tagname == 'topo_rspec' and not tag.updated:
526 def toxml(self, hrn = None):
527 xml = """<?xml version="1.0"?>
528 <Rspec xmlns="http://www.planet-lab.org/sfa/rspec/" name="vini">
530 <NetSpec name="physical_topology">"""
532 for site in self.getSites():
537 <SiteSpec name="%s"> """ % site.name
539 for node in site.get_sitenodes(self.nodes):
545 <hostname>%s</hostname>
547 </NodeSpec>""" % (node.tag, node.hostname, format_tc_rate(node.bps))
551 for link in self.sitelinks:
554 <endpoint>%s</endpoint>
555 <endpoint>%s</endpoint>
557 </SiteLinkSpec>""" % (link.end1.name, link.end2.name, format_tc_rate(link.bps))
563 name = 'default_topology'
568 <NetSpec name="%s">""" % name
571 for link in self.nodelinks:
574 <endpoint>%s</endpoint>
575 <endpoint>%s</endpoint>
577 </LinkSpec>""" % (link.end1.tag, link.end2.tag, format_tc_rate(link.bps))
579 xml += default_topo_xml
586 # Remove all leading whitespace and newlines
587 lines = xml.split("\n")
590 noblanks += line.strip()
595 Create a dictionary of site objects keyed by site ID
599 for site in api.plshell.GetSites(api.plauth):
600 t = site['site_id'], Site(site)
606 Create a dictionary of node objects keyed by node ID
610 for node in api.plshell.GetNodes(api.plauth):
611 t = node['node_id'], Node(node)
616 Create a dictionary of slice objects keyed by slice ID
618 def get_slice(api, slicename):
619 slice = api.plshell.GetSlices(api.plauth, [slicename])
621 return Slice(slice[0])
626 Create a dictionary of slicetag objects keyed by slice tag ID
628 def get_slice_tags(api):
630 for tag in api.plshell.GetSliceTags(api.plauth):
631 t = tag['slice_tag_id'], Slicetag(tag)
638 def free_egre_key(slicetags):
642 if tag.tagname == 'egre_key':
643 used.add(int(tag.value))
645 for i in range(1, 256):
650 raise KeyError("No more EGRE keys available")