From 304bb9d5a710d6570fc3bffb394c90e76eab4bc2 Mon Sep 17 00:00:00 2001 From: Andy Bavier Date: Wed, 19 Aug 2009 19:33:46 +0000 Subject: [PATCH] Generate Capacity in VINI Rspec --- sfa/rspecs/aggregates/rspec_manager_vini.py | 114 +++++++----------- sfa/rspecs/aggregates/vini/__init__.py | 0 sfa/rspecs/aggregates/vini/rspec.py | 68 +++++++++++ sfa/rspecs/aggregates/vini/topology.py | 38 ++++++ .../{vini_utils.py => vini/utils.py} | 66 +++++++--- sfa/rspecs/aggregates/{ => vini}/vini.xml | 0 6 files changed, 201 insertions(+), 85 deletions(-) create mode 100644 sfa/rspecs/aggregates/vini/__init__.py create mode 100644 sfa/rspecs/aggregates/vini/rspec.py create mode 100755 sfa/rspecs/aggregates/vini/topology.py rename sfa/rspecs/aggregates/{vini_utils.py => vini/utils.py} (86%) rename sfa/rspecs/aggregates/{ => vini}/vini.xml (100%) diff --git a/sfa/rspecs/aggregates/rspec_manager_vini.py b/sfa/rspecs/aggregates/rspec_manager_vini.py index d2d5e60d..f84f5c1b 100644 --- a/sfa/rspecs/aggregates/rspec_manager_vini.py +++ b/sfa/rspecs/aggregates/rspec_manager_vini.py @@ -3,10 +3,10 @@ from sfa.util.misc import * from sfa.util.rspec import Rspec from sfa.server.registry import Registries from sfa.plc.nodes import * -from sfa.rspecs.aggregates.vini_utils import * +from sfa.rspecs.aggregates.vini.utils import * +from sfa.rspecs.aggregates.vini.rspec import * import sys -SFA_VINI_DEFAULT_RSPEC = '/etc/sfa/vini.rspec' SFA_VINI_WHITELIST = '/etc/sfa/vini.whitelist' """ @@ -134,62 +134,54 @@ def create_slice_vini_aggregate(api, hrn, nodes): return 1 def get_rspec(api, hrn): - # Get default rspec - default = Rspec() - default.parseFile(SFA_VINI_DEFAULT_RSPEC) + rspec = ViniRspec() + (sites, nodes, tags) = get_topology(api) + + rspec.updateCapacity(sites, nodes) if (hrn): slicename = hrn_to_pl_slicename(hrn) - defaultrspec = default.toDict() - nodedict = get_nodedict(defaultrspec) - - # call the default sfa.plc.nodes.get_rspec() method - nodes = Nodes(api) - rspec = nodes.get_rspec(hrn) - - # Grab all the PLC info we'll need at once slice = get_slice(api, slicename) if slice: - nodes = get_nodes(api) - tags = get_slice_tags(api) - - # Add the node tags from the Capacity statement to Node objects - for (k, v) in nodedict.iteritems(): - for id in nodes: - if v == nodes[id].hostname: - nodes[id].tag = k - - 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) - - d = default.toDict() - d['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] = linkspecs - d['Rspec']['Request'][0]['NetSpec'][0]['name'] = hrn - new = Rspec() - new.parseDict(d) - rspec = new.toxml() - else: - # Return canned response for now... - rspec = default.toxml() + slice.hrn = hrn + rspec.updateRequest(slice, nodes, tags) + else: + # call the default sfa.plc.nodes.get_rspec() method + return Nodes(api).get_rspec(hrn) - return rspec + return rspec.toxml() + + +""" +Check the requested topology against the available topology and capacity +""" +def check_request(hrn, rspec, nodes, sites, sitelinks, maxbw): + linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] + if linkspecs: + for l in linkspecs: + n1 = Node.lookup(l['endpoint'][0]) + n2 = Node.lookup(l['endpoint'][1]) + bw = l['bw'][0] + reqbps = get_tc_rate(bw) + maxbps = get_tc_rate(maxbw) + if reqbps <= 0: + raise GeniInvalidArgument(bw, "BW") + if reqbps > maxbps: + raise PermissionError(" %s requested %s but max BW is %s" % + (hrn, bw, maxbw)) + + if adjacent_nodes(n1, n2, sites, sitelinks): + availbps = get_avail_bps(n1, n2, sites, sitelinks) + if availbps < reqbps: + raise PermissionError("%s: capacity exceeded" % hrn) + else: + raise PermissionError("%s: nodes %s and %s not adjacent" + % (hrn, n1.tag, n2.tag)) +""" +Hook called via 'sfi.py create' +""" def create_slice(api, hrn, xml): r = Rspec(xml) rspec = r.toDict() @@ -203,24 +195,15 @@ def create_slice(api, hrn, xml): whitelist[slice] = maxbw if hrn in whitelist: - maxbps = get_tc_rate(whitelist[hrn]) + maxbw = whitelist[hrn] else: raise PermissionError("%s not in VINI whitelist" % hrn) - ### Check to make sure that the slice isn't requesting more - ### than its maximum bandwidth. - linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] - if linkspecs: - for l in linkspecs: - bw = l['bw'][0] - bps = get_tc_rate(bw) - if bps <= 0: - raise GeniInvalidArgument(bw, "BW") - if bps > maxbps: - raise PermissionError(" %s requested %s but max BW is %s" % (hrn, bw, whitelist[hrn])) + # Construct picture of global topology + (sites, nodes, tags) = get_topology(api) # Check request against current allocations - # Request OK + #check_request(hrn, rspec, nodes, sites, sitelinks, maxbw) nodes = rspec_to_nodeset(rspec) create_slice_vini_aggregate(api, hrn, nodes) @@ -230,13 +213,8 @@ def create_slice(api, hrn, xml): linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] if linkspecs: slicename = hrn_to_pl_slicename(hrn) - - # Grab all the PLC info we'll need at once slice = get_slice(api, slicename) if slice: - nodes = get_nodes(api) - tags = get_slice_tags(api) - slice.update_tag('vini_topo', 'manual', tags) slice.assign_egre_key(tags) slice.turn_on_netns(tags) diff --git a/sfa/rspecs/aggregates/vini/__init__.py b/sfa/rspecs/aggregates/vini/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sfa/rspecs/aggregates/vini/rspec.py b/sfa/rspecs/aggregates/vini/rspec.py new file mode 100644 index 00000000..3076219c --- /dev/null +++ b/sfa/rspecs/aggregates/vini/rspec.py @@ -0,0 +1,68 @@ +from sfa.util.rspec import Rspec +from sfa.rspecs.aggregates.vini.utils import * +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 updateCapacity(self, sites, nodes): + d = self.toDict() + sitespecs = [] + sitelinkspecs = [] + for s in sites: + site = sites[s] + if not site.public: + continue + sdict = {} + nodespecs = [] + for node in site.get_sitenodes(nodes): + if not node.tag: + continue + ndict = {} + ndict['hostname'] = [node.hostname] + ndict['name'] = node.tag + ndict['bw'] = ['999Mbit'] + nodespecs.append(ndict) + sdict['NodeSpec'] = nodespecs + sdict['name'] = site.name + sitespecs.append(sdict) + + for sl in site.sitelinks: + if sl.site1 == site: + sldict = {} + sldict['endpoint'] = [sl.site1.name, sl.site2.name] + sldict['bw'] = [str(sl.availMbps) + "Mbit"] + sitelinkspecs.append(sldict) + + d['Rspec']['Capacity'][0]['NetSpec'][0]['SiteSpec'] = sitespecs + d['Rspec']['Capacity'][0]['NetSpec'][0]['SiteLinkSpec'] = sitelinkspecs + 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) + + 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/topology.py b/sfa/rspecs/aggregates/vini/topology.py new file mode 100755 index 00000000..cb65fb50 --- /dev/null +++ b/sfa/rspecs/aggregates/vini/topology.py @@ -0,0 +1,38 @@ +#!/usr/bin/python + +# $Id: topology.py 14181 2009-07-01 19:46:07Z acb $ +# $URL: https://svn.planet-lab.org/svn/NodeManager-topo/trunk/topology.py $ + +# +# Links in the physical topology, gleaned from looking at the Internet2 +# and NLR topology maps. Link (a, b) connects sites with IDs a and b. +# +PhysicalLinks = [(2, 12), # I2 Princeton - New York + (4, 5), # NLR Chicago - Houston + (4, 6), # NLR Chicago - Atlanta + (4, 7), # NLR Chicago - Seattle + (4, 9), # NLR Chicago - New York + (4, 10), # NLR Chicago - Wash DC + (5, 6), # NLR Houston - Atlanta + (5, 8), # NLR Houston - Los Angeles + (6, 10), # NLR Atlanta - Wash DC + (6, 14), # NLR Atlanta - Ga Tech + (7, 8), # NLR Seattle - Los Angeles + (9, 10), # NLR New York - Wash DC + (11, 13), # I2 Chicago - Wash DC + (11, 15), # I2 Chicago - Atlanta + (11, 16), # I2 Chicago - CESNET + (11, 17), # I2 Chicago - Kansas City + (12, 13), # I2 New York - Wash DC + (13, 15), # I2 Wash DC - Atlanta + (14, 15), # Ga Tech - I2 Atlanta + (15, 19), # I2 Atlanta - Houston + (17, 19), # I2 Kansas City - Houston + (17, 22), # I2 Kansas City - Salt Lake City + (17, 24), # I2 Kansas City - UMKC + (19, 20), # I2 Houston - Los Angeles + (20, 21), # I2 Los Angeles - Seattle + (20, 22), # I2 Los Angeles - Salt Lake City + (21, 22)] # I2 Seattle - Salt Lake City + + diff --git a/sfa/rspecs/aggregates/vini_utils.py b/sfa/rspecs/aggregates/vini/utils.py similarity index 86% rename from sfa/rspecs/aggregates/vini_utils.py rename to sfa/rspecs/aggregates/vini/utils.py index 82f86069..8250e840 100644 --- a/sfa/rspecs/aggregates/vini_utils.py +++ b/sfa/rspecs/aggregates/vini/utils.py @@ -1,5 +1,6 @@ import re import socket +from sfa.rspecs.aggregates.vini.topology import * # Taken from bwlimit.py # @@ -86,14 +87,6 @@ class Node: def get_site(self, sites): return sites[self.site_id] - - def adjacent_nodes(self, sites, nodes, node_ids): - mysite = self.get_site(sites) - adj_ids = mysite.adj_node_ids.intersection(node_ids) - adj_nodes = [] - for id in adj_ids: - adj_nodes.append(nodes[id]) - return adj_nodes def init_links(self): self.links = [] @@ -104,14 +97,36 @@ class Node: net = self.get_virt_net(remote) link = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net self.links.append(link) + + def add_tag(self, sites): + s = self.get_site(sites) + words = self.hostname.split(".") + index = words[0].replace("node", "") + if index.isdigit(): + self.tag = s.tag + index + else: + self.tag = None + +class SiteLink: + def __init__(self, site1, site2, mbps = 1000): + self.site1 = site1 + self.site2 = site2 + self.totalMbps = mbps + self.availMbps = mbps + + site1.add_sitelink(self) + site2.add_sitelink(self) + class Site: def __init__(self, site): self.id = site['site_id'] self.node_ids = site['node_ids'] - self.adj_site_ids = set() - self.adj_node_ids = set() + self.name = site['abbreviated_name'] + self.tag = site['login_base'] + self.public = site['is_public'] + self.sitelinks = [] def get_sitenodes(self, nodes): n = [] @@ -119,11 +134,9 @@ class Site: n.append(nodes[i]) return n - def add_adjacency(self, site): - self.adj_site_ids.add(site.id) - for n in site.node_ids: - self.adj_node_ids.add(n) - + def add_sitelink(self, link): + self.sitelinks.append(link) + class Slice: def __init__(self, slice): @@ -277,9 +290,9 @@ class Slicetag: """ Create a dictionary of site objects keyed by site ID """ -def get_sites(): +def get_sites(api): tmp = [] - for site in GetSites(): + for site in api.plshell.GetSites(api.plauth): t = site['site_id'], Site(site) tmp.append(t) return dict(tmp) @@ -334,4 +347,23 @@ 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) + + return (sites, nodes, tags) diff --git a/sfa/rspecs/aggregates/vini.xml b/sfa/rspecs/aggregates/vini/vini.xml similarity index 100% rename from sfa/rspecs/aggregates/vini.xml rename to sfa/rspecs/aggregates/vini/vini.xml -- 2.43.0