def get_rspec(api, hrn):
rspec = ViniRspec()
- (sites, nodes, tags) = get_topology(api)
+ topo = Topology(api)
- rspec.updateCapacity(sites, nodes)
+ rspec.updateCapacity(topo)
if (hrn):
slicename = hrn_to_pl_slicename(hrn)
slice = get_slice(api, slicename)
if slice:
slice.hrn = hrn
- rspec.updateRequest(slice, nodes, tags)
+ topo.nodeTopoFromSliceTags(slice)
+ rspec.updateRequest(slice, topo)
else:
# call the default sfa.plc.nodes.get_rspec() method
return Nodes(api).get_rspec(hrn)
Hook called via 'sfi.py create'
"""
def create_slice(api, hrn, xml):
- r = Rspec(xml)
- rspec = r.toDict()
-
### Check the whitelist
### It consists of lines of the form: <slice hrn> <bw>
whitelist = {}
else:
raise PermissionError("%s not in VINI whitelist" % hrn)
- # Construct picture of global topology
- (sites, nodes, tags) = get_topology(api)
+ rspec = ViniRspec(xml)
+ topo = Topology(api)
+
+ topo.nodeTopoFromRspec(rspec)
# Check request against current allocations
#check_request(hrn, rspec, nodes, sites, sitelinks, maxbw)
-
- nodes = rspec_to_nodeset(rspec)
- create_slice_vini_aggregate(api, hrn, nodes)
-
- # Add VINI-specific topology attributes to slice here
- try:
- linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
- if linkspecs:
- slicename = hrn_to_pl_slicename(hrn)
- slice = get_slice(api, slicename)
- if slice:
- slice.update_tag('vini_topo', 'manual', tags)
- slice.assign_egre_key(tags)
- slice.turn_on_netns(tags)
- slice.add_cap_net_admin(tags)
-
- nodedict = {}
- for (k, v) in get_nodedict(rspec).iteritems():
- for id in nodes:
- if v == nodes[id].hostname:
- nodedict[k] = nodes[id]
-
- for l in linkspecs:
- n1 = nodedict[l['endpoint'][0]]
- n2 = nodedict[l['endpoint'][1]]
- bw = l['bw'][0]
- n1.add_link(n2, bw)
- n2.add_link(n1, bw)
-
- for node in slice.get_nodes(nodes):
- if node.links:
- topo_str = "%s" % node.links
- slice.update_tag('topo_rspec', topo_str, tags, node)
-
- # Update slice tags in database
- for i in tags:
- tag = tags[i]
- if tag.slice_id == slice.id:
- if tag.tagname == 'topo_rspec' and not tag.updated:
- tag.delete()
- tag.write(api)
- except KeyError:
- # Bad Rspec
- pass
+ nodes = topo.nodesInTopo()
+ hostnames = []
+ for node in nodes:
+ hostnames.append(node.hostname)
+ create_slice_vini_aggregate(api, hrn, hostnames)
- return True
-
-def get_nodedict(rspec):
- nodedict = {}
- try:
- sitespecs = rspec['Rspec']['Capacity'][0]['NetSpec'][0]['SiteSpec']
- for s in sitespecs:
- for node in s['NodeSpec']:
- nodedict[node['name']] = node['hostname'][0]
- except KeyError:
- pass
+ slicename = hrn_to_pl_slicename(hrn)
+ slice = get_slice(api, slicename)
+ if slice:
+ topo.updateSliceTags(slice)
- return nodedict
+ return True
-
-def rspec_to_nodeset(rspec):
- nodes = set()
- try:
- nodedict = get_nodedict(rspec)
- linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
- for l in linkspecs:
- for e in l['endpoint']:
- nodes.add(nodedict[e])
- except KeyError:
- # Bad Rspec
- pass
-
- return nodes
def main():
r = Rspec()
SFA_VINI_DEFAULT_RSPEC = '/etc/sfa/vini.rspec'
class ViniRspec(Rspec):
- def __init__(self):
- Rspec.__init__(self)
- self.parseFile(SFA_VINI_DEFAULT_RSPEC)
+ def __init__(self, xml = None, xsd = None, NSURL = None):
+ Rspec.__init__(self, xml, xsd, NSURL)
+ if not xml:
+ self.parseFile(SFA_VINI_DEFAULT_RSPEC)
- def updateCapacity(self, sites, nodes):
+ def updateCapacity(self, topo):
d = self.toDict()
sitespecs = []
sitelinkspecs = []
- for s in sites:
- site = sites[s]
+ for site in topo.getSites():
if not site.public:
continue
sdict = {}
nodespecs = []
- for node in site.get_sitenodes(nodes):
+ for node in site.get_sitenodes(topo.nodes):
if not node.tag:
continue
ndict = {}
sdict['name'] = site.name
sitespecs.append(sdict)
- for sl in site.sitelinks:
- if sl.site1 == site:
+ for sl in site.links:
+ if sl.end1 == site:
sldict = {}
- sldict['endpoint'] = [sl.site1.name, sl.site2.name]
+ sldict['endpoint'] = [sl.end1.name, sl.end2.name]
sldict['bw'] = [format_tc_rate(sl.bps)]
sitelinkspecs.append(sldict)
self.parseDict(d)
- def updateRequest(self, slice, nodes, tags):
- endpoints = []
- for node in slice.get_nodes(nodes):
- linktag = slice.get_tag('topo_rspec', tags, node)
- if linktag:
- l = eval(linktag.value)
- for (id, realip, bw, lvip, rvip, vnet) in l:
- endpoints.append((node.id, id, bw))
-
- if endpoints:
- linkspecs = []
- for (l, r, bw) in endpoints:
- if (r, l, bw) in endpoints:
- if l < r:
- edict = {}
- edict['endpoint'] = [nodes[l].tag, nodes[r].tag]
- edict['bw'] = [bw]
- linkspecs.append(edict)
+ def updateRequest(self, slice, topo):
+ linkspecs = []
+ for link in topo.nodelinks:
+ edict = {}
+ edict['endpoint'] = [link.end1.tag, link.end2.tag]
+ edict['bw'] = [format_tc_rate(link.bps)]
+ linkspecs.append(edict)
- d = self.toDict()
- d['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] = linkspecs
- d['Rspec']['Request'][0]['NetSpec'][0]['name'] = slice.hrn
- self.parseDict(d)
\ No newline at end of file
+ d = self.toDict()
+ d['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] = linkspecs
+ d['Rspec']['Request'][0]['NetSpec'][0]['name'] = slice.hrn
+ self.parseDict(d)
\ No newline at end of file
class Node:
- def __init__(self, node, mbps = 1000):
+ def __init__(self, node, bps = 1000 * 1000000):
self.id = node['node_id']
self.hostname = node['hostname']
self.shortname = self.hostname.replace('.vini-veritas.net', '')
self.site_id = node['site_id']
self.ipaddr = socket.gethostbyname(self.hostname)
- self.bps = mbps * 1000000
- self.links = []
+ self.bps = bps
+ self.links = set()
def get_link_id(self, remote):
if self.id < remote.id:
def get_site(self, sites):
return sites[self.site_id]
- def init_links(self):
- self.links = []
-
- def add_link(self, remote, bw):
+ def get_topo_rspec(self, link):
+ if link.end1 == self:
+ remote = link.end2
+ elif link.end2 == self:
+ remote = link.end1
+ else:
+ raise Error("Link does not connect to Node")
+
my_ip = self.get_virt_ip(remote)
remote_ip = remote.get_virt_ip(self)
net = self.get_virt_net(remote)
- link = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net
- self.links.append(link)
+ bw = format_tc_rate(link.bps)
+ return (remote.id, remote.ipaddr, bw, my_ip, remote_ip, net)
+
+ def add_link(self, link):
+ self.links.add(link)
def add_tag(self, sites):
s = self.get_site(sites)
else:
self.tag = None
- # Assumes there is at most one SiteLink between two sites
+ # Assumes there is at most one Link between two sites
def get_sitelink(self, node, sites):
site1 = sites[self.site_id]
site2 = sites[node.site_id]
- sl = site1.sitelinks.intersection(site2.sitelinks)
+ sl = site1.links.intersection(site2.links)
if len(sl):
return sl.pop()
return None
-class SiteLink:
- def __init__(self, site1, site2, mbps = 1000):
- self.site1 = site1
- self.site2 = site2
- self.bps = mbps * 1000000
+class Link:
+ def __init__(self, end1, end2, bps = 1000 * 1000000):
+ self.end1 = end1
+ self.end2 = end2
+ self.bps = bps
- site1.add_sitelink(self)
- site2.add_sitelink(self)
+ end1.add_link(self)
+ end2.add_link(self)
class Site:
self.name = site['abbreviated_name']
self.tag = site['login_base']
self.public = site['is_public']
- self.sitelinks = set()
+ self.links = set()
def get_sitenodes(self, nodes):
n = []
n.append(nodes[i])
return n
- def add_sitelink(self, link):
- self.sitelinks.add(link)
+ def add_link(self, link):
+ self.links.add(link)
class Slice:
api.plshell.DeleteSliceTag(api.plauth, self.id)
+"""
+A topology is a compound object consisting of:
+* a dictionary mapping site IDs to Site objects
+* a dictionary mapping node IDs to Node objects
+* the Site objects are connected via SiteLink objects representing
+ the physical topology and available bandwidth
+* the Node objects are connected via Link objects representing
+ the requested or assigned virtual topology of a slice
+"""
+class Topology:
+ def __init__(self, api):
+ self.api = api
+ self.sites = get_sites(api)
+ self.nodes = get_nodes(api)
+ self.tags = get_slice_tags(api)
+ self.sitelinks = []
+ self.nodelinks = []
+
+ for (s1, s2) in PhysicalLinks:
+ self.sitelinks.append(Link(self.sites[s1], self.sites[s2]))
+
+ for id in self.nodes:
+ self.nodes[id].add_tag(self.sites)
+
+ for t in self.tags:
+ tag = self.tags[t]
+ if tag.tagname == 'topo_rspec':
+ node1 = self.nodes[tag.node_id]
+ l = eval(tag.value)
+ for (id, realip, bw, lvip, rvip, vnet) in l:
+ allocbps = get_tc_rate(bw)
+ node1.bps -= allocbps
+ try:
+ node2 = self.nodes[id]
+ if node1.id < node2.id:
+ sl = node1.get_sitelink(node2, self.sites)
+ sl.bps -= allocbps
+ except:
+ pass
+
+
+ def lookupSite(self, id):
+ val = None
+ try:
+ val = self.sites[id]
+ except:
+ raise KeyError("site ID %s not found" % id)
+ return val
+
+ def getSites(self):
+ sites = []
+ for s in self.sites:
+ sites.append(self.sites[s])
+ return sites
+
+ def lookupNode(self, id):
+ val = None
+ try:
+ val = self.nodes[id]
+ except:
+ raise KeyError("node ID %s not found" % id)
+ return val
+
+ def getNodes(self):
+ nodes = []
+ for n in self.nodes:
+ nodes.append(self.nodes[n])
+ return nodes
+
+ def nodesInTopo(self):
+ nodes = []
+ for n in self.nodes:
+ if self.nodes[n].links:
+ nodes.append(self.nodes[n])
+ return nodes
+
+ def lookupSliceTag(self, id):
+ val = None
+ try:
+ val = self.tags[id]
+ except:
+ raise KeyError("slicetag ID %s not found" % id)
+ return val
+
+ def getSliceTags(self):
+ tags = []
+ for t in self.tags:
+ tags.append(self.tags[t])
+ return tags
+
+ 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
+
+ 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))
+
+ def nodeTopoFromSliceTags(self, slice):
+ if self.nodelinks:
+ raise Error("virtual topology already present")
+
+ for node in slice.get_nodes(self.nodes):
+ linktag = slice.get_tag('topo_rspec', self.tags, node)
+ if linktag:
+ l = eval(linktag.value)
+ for (id, realip, bw, lvip, rvip, vnet) in l:
+ if node.id < id:
+ bps = get_tc_rate(bw)
+ remote = self.lookupNode(id)
+ self.nodelinks.append(Link(node, remote, bps))
+
+ def updateSliceTags(self, slice):
+ if not self.nodelinks:
+ return
+
+ slice.update_tag('vini_topo', 'manual', self.tags)
+ slice.assign_egre_key(self.tags)
+ slice.turn_on_netns(self.tags)
+ slice.add_cap_net_admin(self.tags)
+
+ for node in slice.get_nodes(self.nodes):
+ linkdesc = []
+ for link in node.links:
+ linkdesc.append(node.get_topo_rspec(link))
+ if linkdesc:
+ topo_str = "%s" % linkdesc
+ slice.update_tag('topo_rspec', topo_str, self.tags, node)
+
+ # Update slice tags in database
+ for tag in self.getSliceTags():
+ if tag.slice_id == slice.id:
+ if tag.tagname == 'topo_rspec' and not tag.updated:
+ tag.delete()
+ tag.write(self.api)
+
+
"""
Create a dictionary of site objects keyed by site ID
"""
return "%s" % key
-"""
-Return the network topology.
-The topology consists of:
-* a dictionary mapping site IDs to Site objects
-* a dictionary mapping node IDs to Node objects
-* the Site objects are connected via SiteLink objects representing
- the physical topology and available bandwidth
-"""
-def get_topology(api):
- sites = get_sites(api)
- nodes = get_nodes(api)
- tags = get_slice_tags(api)
-
- for (s1, s2) in PhysicalLinks:
- SiteLink(sites[s1], sites[s2])
-
- for id in nodes:
- nodes[id].add_tag(sites)
-
- for t in tags:
- tag = tags[t]
- if tag.tagname == 'topo_rspec':
- node1 = nodes[tag.node_id]
- l = eval(tag.value)
- for (id, realip, bw, lvip, rvip, vnet) in l:
- allocbps = get_tc_rate(bw)
- node1.bps -= allocbps
- try:
- node2 = nodes[id]
- if node1.id < node2.id:
- sl = node1.get_sitelink(node2, sites)
- sl.bps -= allocbps
- except:
- pass
-
- return (sites, nodes, tags)