[spp] [PATCH 11/13] controller: add topo_subgraph command

ogawa.yasufumi at lab.ntt.co.jp ogawa.yasufumi at lab.ntt.co.jp
Tue Mar 6 11:50:53 CET 2018


From: Yasufumi Ogawa <ogawa.yasufumi at lab.ntt.co.jp>

'topo_subgraph' is for defining subgraph of resources in a network
topology generated by 'topo'. Typically it is used for grouping vhost
interfaces of each of VMs or containers to be more understandable the
generated network topology.

Usage: topo_subgraph [VERB] [LABEL] [SUBGRAPH]
       VERB: 'add' or 'del'

If you assign vhost1 and vhost2 to VM1, define subgraph as following.

  spp > topo_subgraph add VM1 vhost1,vhost2

Or delete defined subgraph.

  spp > topo_subgraph del VM1

To show all of defined subgraphs, simply run without arguments.

  spp > topo_subgraph

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi at lab.ntt.co.jp>
---
 src/controller/shell.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/controller/topo.py  | 43 +++++++++++++++++++++----
 2 files changed, 120 insertions(+), 8 deletions(-)

diff --git a/src/controller/shell.py b/src/controller/shell.py
index 8a0580a..8aae86d 100644
--- a/src/controller/shell.py
+++ b/src/controller/shell.py
@@ -30,6 +30,7 @@ class Shell(cmd.Cmd, object):
     BYE_CMDS = ['sec', 'all']
 
     PLUGIN_DIR = 'command'
+    subgraphs = {}
 
     def default(self, line):
         """Define defualt behaviour
@@ -483,6 +484,87 @@ class Shell(cmd.Cmd, object):
         if args == '':
             pprint(vars(self))
 
+    def terms_topo_subgraph(self):
+        """Define terms of topo_subgraph command"""
+
+        return ['add', 'del']
+
+    def do_topo_subgraph(self, args):
+        """Subgarph manager for topo
+
+        Subgraph is a group of object defined in dot language.
+        For topo command, it is used for grouping resources of each
+        of VM or container to topology be more understandable.
+
+        Add subgraph labeled 'vm1'. Resource name is capitalized and
+        both of them is OK.
+        spp > topo_subgraph add vm1 VHOST1;VHOST2  # upper case
+        spp > topo_subgraph add vm1 vhost1;vhost2  # lower case
+
+        Delete subgraph 'vm1'.
+        spp > topo_subgraph del vm1
+
+        To show subgraphs, run topo_subgraph without args.
+        spp > topo_subgraph
+        {'vm1', 'VHOST1;VHOST2'}
+        """
+
+        args_cleaned = re.sub(r"\s+", ' ', args).strip()
+        # Show subgraphs if given no argments
+        if (args_cleaned == ''):
+            if len(self.subgraphs) == 0:
+                print("No subgraph.")
+            else:
+                for label, subg in self.subgraphs.items():
+                    print('label: %s\tsubgraph: "%s"' % (label, subg))
+        else:  # add or del
+            tokens = args_cleaned.split(' ')
+            # Add subgraph
+            if tokens[0] == 'add':
+                if len(tokens) == 3:
+                    label = tokens[1]
+                    subg = tokens[2].upper()
+                    if ',' in subg:
+                        subg = re.sub(r",", ";", subg)
+
+                    # TODO(yasufum) add validation for subgraph
+                    self.subgraphs[label] = subg
+                    print("Add subgraph '%s'" % label)
+                else:
+                    print("Invalid syntax '%s'!" % args_cleaned)
+            # Delete subgraph
+            elif ((tokens[0] == 'del') or
+                    (tokens[0] == 'delete') or
+                    (tokens[0] == 'remove')):
+                del(self.subgraphs[tokens[1]])
+                print("Delete subgraph '%s'" % tokens[1])
+
+            else:
+                print("Ivalid subcommand '%s'!" % tokens[0])
+
+    def complete_topo_subgraph(self, text, line, begidx, endidx):
+        terms = self.terms_topo_subgraph()
+
+        tokens = re.sub(r"\s+", ' ', line).strip().split(' ')
+        if text == '':
+            if len(tokens) == 1:
+                return terms
+            elif len(tokens) == 2 and tokens[1] == 'del':
+                return self.subgraphs.keys()
+        elif text != '':
+            completions = []
+            if len(tokens) == 3 and tokens[1] == 'del':
+                for t in self.subgraphs.keys():
+                    if t.startswith(tokens[2]):
+                        completions.append(t)
+            elif len(tokens) == 2:
+                for t in terms:
+                    if t.startswith(text):
+                        completions.append(t)
+            return completions
+        else:
+            pass
+
     def do_topo(self, args):
         """Output network topology
 
@@ -506,7 +588,8 @@ class Shell(cmd.Cmd, object):
             tp = topo.Topo(
                 spp_common.SECONDARY_LIST,
                 spp_common.MAIN2SEC,
-                spp_common.SEC2MAIN)
+                spp_common.SEC2MAIN,
+                self.subgraphs)
             args_ary = args.split()
             if len(args_ary) == 0:
                 print("Usage: topo dst [ftype]")
diff --git a/src/controller/topo.py b/src/controller/topo.py
index 30a9c1a..92d1634 100644
--- a/src/controller/topo.py
+++ b/src/controller/topo.py
@@ -20,11 +20,12 @@ class Topo(object):
     * text (dot, json, yaml)
     """
 
-    def __init__(self, sec_ids, m2s_queues, s2m_queues):
+    def __init__(self, sec_ids, m2s_queues, s2m_queues, sub_graphs):
         logger.info("Topo initialized with sec IDs %s" % sec_ids)
         self.sec_ids = sec_ids
         self.m2s_queues = m2s_queues
         self.s2m_queues = s2m_queues
+        self.sub_graphs = sub_graphs
 
     def show(self, dtype):
         res_ary = []
@@ -153,26 +154,54 @@ class Topo(object):
         # rank
         output.append(
             '{rank=same; %s}' % ("; ".join(ring_labels)))
+        output.append(
+            '{rank=same; %s}' % ("; ".join(vhost_labels)))
+
         if len(phys) > 0:
             output.append(
                 '{rank=max; %s}' % (
                     phys[0]["iface"]["type"] + phys[0]["iface"]["id"]))
-        output.append(
-            '{rank=same; %s}' % ("; ".join(phy_labels)))
+        elif len(vhosts) > 0:
+            output.append(
+                '{rank=max; %s}' % (
+                    vhosts[0]["iface"]["type"] + vhosts[0]["iface"]["id"]))
+
+        if len(phy_labels) > 0:
+            output.append(
+                '{rank=same; %s}' % ("; ".join(phy_labels)))
+
+        # Add subgraph
+        ssgs = []
+        if len(self.sub_graphs) > 0:
+            cnt = 1
+            for label, val in self.sub_graphs.items():
+                cluster_id = "cluster%d" % cnt
+                ssg_label = label
+                ssg_ports = val
+                ssg = 'subgraph %s {label="%s" %s}' % (
+                        cluster_id, ssg_label, ssg_ports)
+                ssgs.append(ssg)
+                cnt += 1
 
-        # subgraph
         cluster_id = "cluster0"
         sg_label = "Host"
         sg_ports = "; ".join(phy_labels + ring_labels)
-        output.append(
-            'subgraph %s {label="%s" %s}' % (cluster_id, sg_label, sg_ports))
+        if len(ssgs) == 0:
+            output.append(
+                    'subgraph %s {label="%s" %s}' % (
+                        cluster_id, sg_label, sg_ports))
+        else:
+            tmp = 'label="%s" %s' % (sg_label, sg_ports)
+            contents = [tmp] + ssgs
+            output.append(
+                    'subgraph %s {%s}' % (cluster_id, '; '.join(contents)))
 
+        # Add links
         for link in links:
             output.append(link)
 
         output.append("}")
 
-        # remove duplicated entries
         f = open(output_fname, "w+")
         f.write("\n".join(output))
         f.close()
-- 
2.13.1



More information about the spp mailing list