From c2c3a2a188ec988abbf64b0f701fea56a5bf213d Mon Sep 17 00:00:00 2001 From: Andy Bavier Date: Thu, 20 Aug 2009 18:16:50 +0000 Subject: [PATCH] Convert Rspecs and SliceTags to data structure with Node objects connected by Link objects --- sfa/rspecs/aggregates/rspec_manager_vini.py | 96 ++------ sfa/rspecs/aggregates/vini/rspec.py | 53 ++--- sfa/rspecs/aggregates/vini/utils.py | 230 +++++++++++++++----- 3 files changed, 212 insertions(+), 167 deletions(-) diff --git a/sfa/rspecs/aggregates/rspec_manager_vini.py b/sfa/rspecs/aggregates/rspec_manager_vini.py index f84f5c1b..ab82c136 100644 --- a/sfa/rspecs/aggregates/rspec_manager_vini.py +++ b/sfa/rspecs/aggregates/rspec_manager_vini.py @@ -135,16 +135,17 @@ def create_slice_vini_aggregate(api, hrn, nodes): 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) @@ -183,9 +184,6 @@ def check_request(hrn, rspec, nodes, sites, sitelinks, maxbw): 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: whitelist = {} @@ -199,85 +197,27 @@ def create_slice(api, hrn, xml): 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() diff --git a/sfa/rspecs/aggregates/vini/rspec.py b/sfa/rspecs/aggregates/vini/rspec.py index aae96b3f..9ee36028 100644 --- a/sfa/rspecs/aggregates/vini/rspec.py +++ b/sfa/rspecs/aggregates/vini/rspec.py @@ -5,21 +5,21 @@ import sys 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 = {} @@ -31,10 +31,10 @@ class ViniRspec(Rspec): 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) @@ -43,26 +43,15 @@ class ViniRspec(Rspec): 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 diff --git a/sfa/rspecs/aggregates/vini/utils.py b/sfa/rspecs/aggregates/vini/utils.py index c3a057f4..57a010fd 100644 --- a/sfa/rspecs/aggregates/vini/utils.py +++ b/sfa/rspecs/aggregates/vini/utils.py @@ -64,14 +64,14 @@ def format_tc_rate(rate): 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: @@ -103,15 +103,22 @@ class Node: 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) @@ -122,24 +129,24 @@ class Node: 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: @@ -149,7 +156,7 @@ 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 = [] @@ -157,8 +164,8 @@ class Site: 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: @@ -310,6 +317,151 @@ class Slicetag: 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 """ @@ -370,39 +522,3 @@ def free_egre_key(slicetags): 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) -- 2.43.0