+from __future__ import with_statement
import re
import socket
from sfa.util.faults import *
from sfa.rspecs.aggregates.vini.topology import *
+from xmlbuilder import XMLBuilder
+import sys
-default_topo_xml = """
- <LinkSpec>
- <endpoint>i2atla1</endpoint>
- <endpoint>i2chic1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2atla1</endpoint>
- <endpoint>i2hous1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2atla1</endpoint>
- <endpoint>i2wash1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2chic1</endpoint>
- <endpoint>i2kans1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2chic1</endpoint>
- <endpoint>i2wash1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2hous1</endpoint>
- <endpoint>i2kans1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2hous1</endpoint>
- <endpoint>i2losa1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2kans1</endpoint>
- <endpoint>i2salt1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2losa1</endpoint>
- <endpoint>i2salt1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2losa1</endpoint>
- <endpoint>i2seat1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2newy1</endpoint>
- <endpoint>i2wash1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>
- <LinkSpec>
- <endpoint>i2salt1</endpoint>
- <endpoint>i2seat1</endpoint>
- <bw>1Mbit</bw>
- </LinkSpec>"""
-
# Taken from bwlimit.py
#
# See tc_util.c and http://physics.nist.gov/cuu/Units/binary.html. Be
class Node:
def __init__(self, node, bps = 1000 * 1000000):
self.id = node['node_id']
+ self.idtag = "n%s" % self.id
self.hostname = node['hostname']
- self.shortname = self.hostname.replace('.vini-veritas.net', '')
+ self.name = self.shortname = self.hostname.replace('.vini-veritas.net', '')
self.site_id = node['site_id']
self.ipaddr = socket.gethostbyname(self.hostname)
self.bps = bps
self.links = set()
+ self.sliver = False
def get_link_id(self, remote):
if self.id < remote.id:
if len(sl):
return sl.pop()
return None
+
+ def add_sliver(self):
+ self.sliver = True
+
+ def toxml(self, xml, hrn):
+ if not self.tag:
+ return
+ with xml.node(id = self.idtag):
+ with xml.hostname:
+ xml << self.hostname
+ with xml.kbps:
+ xml << str(int(self.bps/1000))
+ if self.sliver:
+ with xml.sliver:
+ pass
class Link:
- def __init__(self, end1, end2, bps = 1000 * 1000000):
+ def __init__(self, end1, end2, bps = 1000 * 1000000, parent = None):
self.end1 = end1
self.end2 = end2
self.bps = bps
-
+ self.parent = parent
+ self.children = []
+
end1.add_link(self)
end2.add_link(self)
+ if self.parent:
+ self.parent.children.append(self)
+
+ def toxml(self, xml):
+ end_ids = "%s %s" % (self.end1.idtag, self.end2.idtag)
+
+ if self.parent:
+ element = xml.vlink(endpoints=end_ids)
+ else:
+ element = xml.link(endpoints=end_ids)
+
+ with element:
+ with xml.description:
+ xml << "%s -- %s" % (self.end1.name, self.end2.name)
+ with xml.kbps:
+ xml << str(int(self.bps/1000))
+ for child in self.children:
+ child.toxml(xml)
+
class Site:
def __init__(self, site):
self.id = site['site_id']
+ self.idtag = "s%s" % self.id
self.node_ids = site['node_ids']
self.name = site['abbreviated_name'].replace(" ", "_")
self.tag = site['login_base']
def add_link(self, link):
self.links.add(link)
-
+
+ def toxml(self, xml, hrn, nodes):
+ if not (self.public and self.enabled and self.node_ids):
+ return
+ with xml.site(id = self.idtag):
+ with xml.name:
+ xml << self.name
+
+ for node in self.get_sitenodes(nodes):
+ node.toxml(xml, hrn)
+
class Slice:
def __init__(self, slice):
pass
+ """ Lookup site based on id or idtag value """
def lookupSite(self, id):
val = None
+ if isinstance(id, basestring):
+ id = int(id.lstrip('s'))
try:
val = self.sites[id]
except:
sites.append(self.sites[s])
return sites
+ """ Lookup node based on id or idtag value """
def lookupNode(self, id):
val = None
+ if isinstance(id, basestring):
+ id = int(id.lstrip('n'))
try:
val = self.nodes[id]
except:
def nodesInTopo(self):
nodes = []
for n in self.nodes:
- if self.nodes[n].links:
- nodes.append(self.nodes[n])
+ node = self.nodes[n]
+ if node.sliver:
+ nodes.append(node)
return nodes
def lookupSliceTag(self, id):
return link
return None
- def nodeTopoFromRspec(self, rspec):
+
+ def __add_vlink(self, vlink, slicenodes, parent = None):
+ n1 = n2 = None
+ if 'endpoints' in vlink:
+ end = vlink['endpoints'].split()
+ n1 = self.lookupNode(end[0])
+ n2 = self.lookupNode(end[1])
+ elif parent:
+ """ Try to infer the endpoints """
+ (n1, n2) = self.__infer_endpoints(parent['endpoints'],slicenodes)
+ else:
+ raise Error("no endpoints given")
+
+ #print "Added virtual link: %s -- %s" % (n1.tag, n2.tag)
+ bps = int(vlink['kbps'][0]) * 1000
+ sitelink = self.lookupSiteLink(n1, n2)
+ if not sitelink:
+ raise PermissionError("nodes %s and %s not adjacent" %
+ (n1.idtag, n2.idtag))
+ self.nodelinks.append(Link(n1, n2, bps, sitelink))
+ return
+
+ """
+ Infer the endpoints of the virtual link. If the slice exists on
+ only a single node at each end of the physical link, we'll assume that
+ the user wants the virtual link to terminate at these nodes.
+ """
+ def __infer_endpoints(self, endpoints, slicenodes):
+ n = []
+ ends = endpoints.split()
+ for end in ends:
+ found = 0
+ site = self.lookupSite(end)
+ for id in site.node_ids:
+ if id in slicenodes:
+ n.append(slicenodes[id])
+ found += 1
+ if found != 1:
+ raise Error("could not infer endpoint for site %s" % site.id)
+ #print "Inferred endpoints: %s %s" % (n[0].idtag, n[1].idtag)
+ return n
+
+ def nodeTopoFromRSpec(self, rspec):
if self.nodelinks:
raise Error("virtual topology already present")
rspecdict = rspec.toDict()
nodedict = {}
for node in self.getNodes():
- nodedict[node.tag] = node
+ nodedict[node.idtag] = node
- linkspecs = rspecdict['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
- for l in linkspecs:
- n1 = nodedict[l['endpoint'][0]]
- n2 = nodedict[l['endpoint'][1]]
- bps = get_tc_rate(l['bw'][0])
- self.nodelinks.append(Link(n1, n2, bps))
-
+ slicenodes = {}
+
+ top = rspecdict['rspec']
+ if ('network' in top):
+ sites = top['network'][0]['site']
+ for site in sites:
+ for node in site['node']:
+ if 'sliver' in node:
+ n = nodedict[node['id']]
+ slicenodes[n.id] = n
+ n.add_sliver()
+ links = top['network'][0]['link']
+ for link in links:
+ if 'vlink' in link:
+ for vlink in link['vlink']:
+ self.__add_vlink(vlink, slicenodes, link)
+ elif ('request' in top):
+ for sliver in top['request'][0]['sliver']:
+ n = nodedict[sliver['nodeid']]
+ slicenodes[n.id] = n
+ n.add_sliver()
+ for vlink in top['request'][0]['vlink']:
+ self.__add_vlink(vlink, slicenodes)
+ return
+
def nodeTopoFromSliceTags(self, slice):
if self.nodelinks:
raise Error("virtual topology already present")
for node in slice.get_nodes(self.nodes):
+ node.sliver = True
linktag = slice.get_tag('topo_rspec', self.tags, node)
if linktag:
l = eval(linktag.value)
if node.id < id:
bps = get_tc_rate(bw)
remote = self.lookupNode(id)
- self.nodelinks.append(Link(node, remote, bps))
+ sitelink = self.lookupSiteLink(node, remote)
+ self.nodelinks.append(Link(node,remote,bps,sitelink))
def updateSliceTags(self, slice):
if not self.nodelinks:
"""
Check the requested topology against the available topology and capacity
"""
- def verifyNodeTopo(self, hrn, topo, maxbw):
- maxbps = get_tc_rate(maxbw)
+ def verifyNodeTopo(self, hrn, topo):
for link in self.nodelinks:
if link.bps <= 0:
raise GeniInvalidArgument(bw, "BW")
- if link.bps > maxbps:
- raise PermissionError(" %s requested %s but max BW is %s" %
- (hrn, format_tc_rate(link.bps), maxbw))
n1 = link.end1
n2 = link.end2
Produce XML directly from the topology specification.
"""
def toxml(self, hrn = None):
- xml = """<?xml version="1.0"?>
-<Rspec xmlns="http://www.planet-lab.org/sfa/rspec/" name="vini">
- <Capacity>
- <NetSpec name="physical_topology">"""
-
- for site in self.getSites():
- if not (site.public and site.enabled):
- continue
-
- xml += """
- <SiteSpec name="%s"> """ % site.name
-
- for node in site.get_sitenodes(self.nodes):
- if not node.tag:
- continue
+ xml = XMLBuilder()
+ with xml.rspec(type="VINI"):
+ if hrn:
+ element = xml.network(name="Public VINI", slice=hrn)
+ else:
+ element = xml.network(name="Public VINI")
- xml += """
- <NodeSpec name="%s">
- <hostname>%s</hostname>
- <bw>%s</bw>
- </NodeSpec>""" % (node.tag, node.hostname, format_tc_rate(node.bps))
- xml += """
- </SiteSpec>"""
-
- for link in self.sitelinks:
- xml += """
- <SiteLinkSpec>
- <endpoint>%s</endpoint>
- <endpoint>%s</endpoint>
- <bw>%s</bw>
- </SiteLinkSpec>""" % (link.end1.name, link.end2.name, format_tc_rate(link.bps))
-
-
- if hrn:
- name = hrn
- else:
- name = 'default_topology'
- xml += """
- </NetSpec>
- </Capacity>
- <Request>
- <NetSpec name="%s">""" % name
-
- if hrn:
- for link in self.nodelinks:
- xml += """
- <LinkSpec>
- <endpoint>%s</endpoint>
- <endpoint>%s</endpoint>
- <bw>%s</bw>
- </LinkSpec>""" % (link.end1.tag, link.end2.tag, format_tc_rate(link.bps))
- else:
- xml += default_topo_xml
-
- xml += """
- </NetSpec>
- </Request>
-</Rspec>"""
-
- # Remove all leading whitespace and newlines
- lines = xml.split("\n")
- noblanks = ""
- for line in lines:
- noblanks += line.strip()
- return noblanks
+ with element:
+ for site in self.getSites():
+ site.toxml(xml, hrn, self.nodes)
+ for link in self.sitelinks:
+ link.toxml(xml)
+ return str(xml)
"""
Create a dictionary of site objects keyed by site ID