Soft Patch Panel
 help / color / mirror / Atom feed
From: Yasufumi Ogawa <yasufum.o@gmail.com>
To: spp@dpdk.org, ferruh.yigit@intel.com, yasufum.o@gmail.com
Subject: [spp] [PATCH 4/8] cli: add to get sec ID and procs to SppCtlClient
Date: Mon, 12 Aug 2019 16:12:38 +0900	[thread overview]
Message-ID: <20190812071242.18934-5-yasufum.o@gmail.com> (raw)
In-Reply-To: <20190812071242.18934-1-yasufum.o@gmail.com>

Shell class has get_sec_ids() for retrieving secondary IDs of given
type by requesting all processes information to spp-ctl. However, it is
better to request the information from SppCtlClient which a delegator
for the requesting.

This update is to remove get_sec_ids() from Shell and add the same
method to SppCtlClient.

In addition to above update,  add get_sec_procs() to SppCtlClient to be
able to retrieve other than spp_nfv in topo. By this change, all types
of secondary process can be referred from anywhere in SppTopo so that
there is no need to pass `sec_list` as an argument of member methods.

Supporting other than spp_nfv in topo command is added in the next
patches.

Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
---
 src/cli/commands/topo.py  | 111 ++++++++++++++------------------------
 src/cli/shell.py          |  48 ++++++-----------
 src/cli/spp_ctl_client.py |  42 +++++++++++++++
 3 files changed, 99 insertions(+), 102 deletions(-)

diff --git a/src/cli/commands/topo.py b/src/cli/commands/topo.py
index 86495c8..96284c7 100644
--- a/src/cli/commands/topo.py
+++ b/src/cli/commands/topo.py
@@ -28,7 +28,8 @@ class SppTopo(object):
         self.graph_size = None
 
         # Graphviz params
-        topo_file = '{dir}/../config/topo.yml'.format(dir=os.path.dirname(__file__))
+        topo_file = '{dir}/../config/topo.yml'.format(
+                dir=os.path.dirname(__file__))
         topo_conf = yaml.load(open(topo_file), Loader=yaml.FullLoader)
         self.SEC_COLORS = topo_conf['topo_sec_colors']['val']
         self.PORT_COLORS = topo_conf['topo_port_colors']['val']
@@ -40,18 +41,20 @@ class SppTopo(object):
             print('Config "topo_size" is invalid value.')
             exit()
 
-    def run(self, args, sec_ids):
+    def run(self, args):
         args_ary = args.split()
         if len(args_ary) == 0:
             print("Usage: topo dst [ftype]")
             return False
-        elif (args_ary[0] == "term") or (args_ary[0] == "http"):
-            self.show(args_ary[0], sec_ids, self.graph_size)
-        elif len(args_ary) == 1:
+        elif args_ary[0] == "term":
+            self.to_term(self.graph_size)
+        elif args_ary[0] == "http":
+            self.to_http()
+        elif len(args_ary) == 1:  # find ftype from filename
             ftype = args_ary[0].split(".")[-1]
-            self.output(args_ary[0], sec_ids, ftype)
-        elif len(args_ary) == 2:
-            self.output(args_ary[0], sec_ids, args_ary[1])
+            self.to_file(args_ary[0], ftype)
+        elif len(args_ary) == 2:  # ftype is given as args_ary[1]
+            self.to_file(args_ary[0], args_ary[1])
         else:
             print("Usage: topo dst [ftype]")
 
@@ -66,12 +69,12 @@ class SppTopo(object):
         matched = re.match(r'(\d+)%$', size)
         if matched:  # percentage
             i = int(matched.group(1))
-            if i > 0 and  i <= 100:
+            if i > 0 and i <= 100:
                 self.graph_size = size
                 return True
             else:
                 return False
-        elif re.match(r'0\.\d+$',size):  # ratio
+        elif re.match(r'0\.\d+$', size):  # ratio
             i = float(size) * 100
             self.graph_size = str(i) + '%'
             return True
@@ -81,58 +84,22 @@ class SppTopo(object):
         else:
             return False
 
-    def show(self, dtype, sec_ids, size):
-        res_ary = []
-        error_codes = self.spp_ctl_cli.rest_common_error_codes
-
-        for sec_id in sec_ids:
-            res = self.spp_ctl_cli.get('nfvs/{sid}'.format(sid=sec_id))
-            if res.status_code == 200:
-                res_ary.append(res.json())
-            elif res.status_code in error_codes:
-                # Print default error message
-                pass
-            else:
-                # Ignore unknown response because no problem for drawing graph
-                pass
-
-        if dtype == "http":
-            self.to_http(res_ary)
-        elif dtype == "term":
-            self.to_term(res_ary, size)
-        else:
-            print("Invalid file type")
-
-    def output(self, fname, sec_ids, ftype="dot"):
-        res_ary = []
-        error_codes = self.spp_ctl_cli.rest_common_error_codes
-
-        for sec_id in sec_ids:
-            res = self.spp_ctl_cli.get('nfvs/{sid}'.format(sid=sec_id))
-            if res.status_code == 200:
-                res_ary.append(res.json())
-            elif res.status_code in error_codes:
-                # Print default error message
-                pass
-            else:
-                # Ignore unknown response because no problem for drawing graph
-                pass
-
+    def to_file(self, fname, ftype="dot"):
         if ftype == "dot":
-            self.to_dot(res_ary, fname)
+            self.to_dot(fname)
         elif ftype == "json" or ftype == "js":
-            self.to_json(res_ary, fname)
+            self.to_json(fname)
         elif ftype == "yaml" or ftype == "yml":
-            self.to_yaml(res_ary, fname)
+            self.to_yaml(fname)
         elif ftype == "jpg" or ftype == "png" or ftype == "bmp":
-            self.to_img(res_ary, fname)
+            self.to_img(fname)
         else:
             print("Invalid file type")
-            return res_ary
+            return False
         print("Create topology: '{fname}'".format(fname=fname))
-        return res_ary
+        return True
 
-    def to_dot(self, sec_list, output_fname):
+    def to_dot(self, output_fname):
         """Output dot script."""
 
         node_attrs = 'node[shape="rectangle", style="filled"];'
@@ -145,7 +112,7 @@ class SppTopo(object):
         links = []
 
         # parse status message from sec.
-        for sec in sec_list:
+        for sec in self.spp_ctl_cli.get_sec_procs('nfv'):
             if sec is None:
                 continue
             for port in sec['ports']:
@@ -186,7 +153,7 @@ class SppTopo(object):
                     dst_type, dst_id = patch['dst'].split(':')
 
                 tmp = link_style.format(src_type, src_id, self.LINK_TYPE,
-                                    dst_type, dst_id, attrs)
+                                        dst_type, dst_id, attrs)
                 links.append(tmp)
 
         output = ["{} spp{{".format(self.GRAPH_TYPE)]
@@ -202,8 +169,8 @@ class SppTopo(object):
         for node in phy_nodes:
             label = re.sub(r'{}'.format(self.delim_node), ':', node)
             output.append(
-                '{n}[label="{l}", fillcolor="{c}"];'.format(
-                    n=node, l=label, c=self.PORT_COLORS["phy"]))
+                '{nd}[label="{lbl}", fillcolor="{col}"];'.format(
+                    nd=node, lbl=label, col=self.PORT_COLORS["phy"]))
 
         ring_nodes = []
         for node in rings:
@@ -213,8 +180,8 @@ class SppTopo(object):
         for node in ring_nodes:
             label = re.sub(r'{}'.format(self.delim_node), ':', node)
             output.append(
-                '{n}[label="{l}", fillcolor="{c}"];'.format(
-                    n=node, l=label, c=self.PORT_COLORS["ring"]))
+                '{nd}[label="{lbl}", fillcolor="{col}"];'.format(
+                    nd=node, lbl=label, col=self.PORT_COLORS["ring"]))
 
         vhost_nodes = []
         for node in vhosts:
@@ -224,8 +191,8 @@ class SppTopo(object):
         for node in vhost_nodes:
             label = re.sub(r'{}'.format(self.delim_node), ':', node)
             output.append(
-                '{n}[label="{l}", fillcolor="{c}"];'.format(
-                    n=node, l=label, c=self.PORT_COLORS["vhost"]))
+                '{nd}[label="{lbl}", fillcolor="{col}"];'.format(
+                    nd=node, lbl=label, col=self.PORT_COLORS["vhost"]))
 
         # Align the same type of nodes with rank attribute
         output.append(
@@ -283,31 +250,33 @@ class SppTopo(object):
         f.write("\n".join(output))
         f.close()
 
-    def to_json(self, sec_list, output_fname):
+    def to_json(self, output_fname):
         import json
         f = open(output_fname, "w+")
+        sec_list = self.spp_ctl_cli.get_sec_procs('nfv')
         f.write(json.dumps(sec_list))
         f.close()
 
-    def to_yaml(self, sec_list, output_fname):
+    def to_yaml(self, output_fname):
         import yaml
         f = open(output_fname, "w+")
+        sec_list = self.spp_ctl_cli.get_sec_procs('nfv')
         f.write(yaml.dump(sec_list))
         f.close()
 
-    def to_img(self, sec_list, output_fname):
+    def to_img(self, output_fname):
         tmpfile = "{fn}.dot".format(fn=uuid.uuid4().hex)
-        self.to_dot(sec_list, tmpfile)
+        self.to_dot(tmpfile)
         fmt = output_fname.split(".")[-1]
         cmd = "dot -T{fmt} {dotf} -o {of}".format(
                 fmt=fmt, dotf=tmpfile, of=output_fname)
         subprocess.call(cmd, shell=True)
         subprocess.call("rm -f {tmpf}".format(tmpf=tmpfile), shell=True)
 
-    def to_http(self, sec_list):
+    def to_http(self):
         import websocket
         tmpfile = "{fn}.dot".format(fn=uuid.uuid4().hex)
-        self.to_dot(sec_list, tmpfile)
+        self.to_dot(tmpfile)
         msg = open(tmpfile).read()
         subprocess.call("rm -f {tmpf}".format(tmpf=tmpfile), shell=True)
         # TODO(yasufum) change to be able to use other than `localhost`.
@@ -319,9 +288,9 @@ class SppTopo(object):
         except socket.error:
             print('Error: Connection refused! Is running websocket server?')
 
-    def to_term(self, sec_list, size):
+    def to_term(self, size):
         tmpfile = "{fn}.jpg".format(fn=uuid.uuid4().hex)
-        self.to_img(sec_list, tmpfile)
+        self.to_img(tmpfile)
         from distutils import spawn
 
         # TODO(yasufum) add check for using only supported terminal
@@ -342,7 +311,7 @@ class SppTopo(object):
                     size=img_size, fn1=tmpfile, fn2=tmpfile)
             subprocess.call(cmd, shell=True)
             subprocess.call("{cmd} {fn}".format(cmd=img_cmd, fn=tmpfile),
-                    shell=True)
+                            shell=True)
             subprocess.call(["rm", "-f", tmpfile])
         else:
             print("img2sixel (or imgcat.sh for MacOS) not found!")
diff --git a/src/cli/shell.py b/src/cli/shell.py
index 8eae982..ebea911 100644
--- a/src/cli/shell.py
+++ b/src/cli/shell.py
@@ -90,22 +90,22 @@ class Shell(cmd.Cmd, object):
 
         self.secondaries = {}
         self.secondaries['nfv'] = {}
-        for sec_id in self.get_sec_ids('nfv'):
+        for sec_id in self.spp_ctl_cli.get_sec_ids('nfv'):
             self.secondaries['nfv'][sec_id] = nfv.SppNfv(
                     self.spp_ctl_cli, sec_id)
 
         self.secondaries['vf'] = {}
-        for sec_id in self.get_sec_ids('vf'):
+        for sec_id in self.spp_ctl_cli.get_sec_ids('vf'):
             self.secondaries['vf'][sec_id] = vf.SppVf(
                     self.spp_ctl_cli, sec_id)
 
         self.secondaries['mirror'] = {}
-        for sec_id in self.get_sec_ids('mirror'):
+        for sec_id in self.spp_ctl_cli.get_sec_ids('mirror'):
             self.secondaries['mirror'][sec_id] = mirror.SppMirror(
                     self.spp_ctl_cli, sec_id)
 
         self.secondaries['pcap'] = {}
-        for sec_id in self.get_sec_ids('pcap'):
+        for sec_id in self.spp_ctl_cli.get_sec_ids('pcap'):
             self.secondaries['pcap'][sec_id] = pcap.SppPcap(
                     self.spp_ctl_cli, sec_id)
 
@@ -142,24 +142,6 @@ class Shell(cmd.Cmd, object):
         """
         pass
 
-    def get_sec_ids(self, ptype):
-        """Return a list of IDs of running secondary processes.
-
-        'ptype' is 'nfv', 'vf' or 'mirror' or 'pcap'.
-        """
-
-        ids = []
-        res = self.spp_ctl_cli.get('processes')
-        if res is not None:
-            if res.status_code == 200:
-                try:
-                    for ent in res.json():
-                        if ent['type'] == ptype:
-                            ids.append(ent['client-id'])
-                except KeyError as e:
-                    print('Error: {} is not defined!'.format(e))
-        return ids
-
     def print_status(self):
         """Display information about connected clients."""
 
@@ -347,7 +329,7 @@ class Shell(cmd.Cmd, object):
         tokens = line.split(';')
         if len(tokens) == 1:
             # Add SppNfv of sec_id if it is not exist
-            sec_ids = self.get_sec_ids('nfv')
+            sec_ids = self.spp_ctl_cli.get_sec_ids('nfv')
             for idx in sec_ids:
                 if self.secondaries['nfv'][idx] is None:
                     self.secondaries['nfv'][idx] = nfv.SppNfv(
@@ -372,7 +354,8 @@ class Shell(cmd.Cmd, object):
                             self.spp_ctl_cli, idx)
 
                 res = self.secondaries['nfv'][idx].complete(
-                        self.get_sec_ids('nfv'), text, line, begidx, endidx)
+                        self.spp_ctl_cli.get_sec_ids('nfv'),
+                        text, line, begidx, endidx)
 
                 # logger.info(res)
                 return res
@@ -407,7 +390,7 @@ class Shell(cmd.Cmd, object):
         tokens = line.split(';')
         if len(tokens) == 1:
             # Add SppVf of sec_id if it is not exist
-            sec_ids = self.get_sec_ids('vf')
+            sec_ids = self.spp_ctl_cli.get_sec_ids('vf')
             for idx in sec_ids:
                 if self.secondaries['vf'][idx] is None:
                     self.secondaries['vf'][idx] = vf.SppVf(
@@ -432,7 +415,8 @@ class Shell(cmd.Cmd, object):
                             self.spp_ctl_cli, idx)
 
                 return self.secondaries['vf'][idx].complete(
-                        self.get_sec_ids('vf'), text, line, begidx, endidx)
+                        self.spp_ctl_cli.get_sec_ids('vf'),
+                        text, line, begidx, endidx)
 
     def do_mirror(self, cmd):
         """Send a command to spp_mirror."""
@@ -463,7 +447,7 @@ class Shell(cmd.Cmd, object):
         tokens = line.split(';')
         if len(tokens) == 1:
             # Add SppMirror of sec_id if it is not exist
-            sec_ids = self.get_sec_ids('mirror')
+            sec_ids = self.spp_ctl_cli.get_sec_ids('mirror')
             for idx in sec_ids:
                 if self.secondaries['mirror'][idx] is None:
                     self.secondaries['mirror'][idx] = mirror.SppMirror(
@@ -489,7 +473,8 @@ class Shell(cmd.Cmd, object):
                             self.spp_ctl_cli, idx)
 
                 return self.secondaries['mirror'][idx].complete(
-                        self.get_sec_ids('mirror'), text, line, begidx, endidx)
+                        self.spp_ctl_cli.get_sec_ids('mirror'),
+                        text, line, begidx, endidx)
 
     def do_pcap(self, cmd):
         """Send a command to spp_pcap."""
@@ -520,7 +505,7 @@ class Shell(cmd.Cmd, object):
         tokens = line.split(';')
         if len(tokens) == 1:
             # Add SppPcap of sec_id if it is not exist
-            sec_ids = self.get_sec_ids('pcap')
+            sec_ids = self.spp_ctl_cli.get_sec_ids('pcap')
             for idx in sec_ids:
                 if self.secondaries['pcap'][idx] is None:
                     self.secondaries['pcap'][idx] = pcap.SppPcap(
@@ -546,7 +531,8 @@ class Shell(cmd.Cmd, object):
                             self.spp_ctl_cli, idx)
 
                 return self.secondaries['pcap'][idx].complete(
-                        self.get_sec_ids('pcap'), text, line, begidx, endidx)
+                        self.spp_ctl_cli.get_sec_ids('pcap'),
+                        text, line, begidx, endidx)
 
     def do_record(self, fname):
         """Save commands as a recipe file."""
@@ -908,7 +894,7 @@ class Shell(cmd.Cmd, object):
 
     def do_topo(self, args):
         """Output network topology."""
-        self.spp_topo.run(args, self.get_sec_ids('nfv'))
+        self.spp_topo.run(args)
 
     def help_topo(self):
         """Print help message of topo command."""
diff --git a/src/cli/spp_ctl_client.py b/src/cli/spp_ctl_client.py
index 3cce628..ff95136 100644
--- a/src/cli/spp_ctl_client.py
+++ b/src/cli/spp_ctl_client.py
@@ -68,3 +68,45 @@ class SppCtlClient(object):
             return False
         else:
             return True
+
+    def get_sec_ids(self, ptype):
+        """Return a list of IDs of running secondary processes.
+
+        'ptype' is 'nfv', 'vf' or 'mirror' or 'pcap'.
+        """
+
+        ids = []
+        res = self.get('processes')
+        if res is not None:
+            if res.status_code == 200:
+                try:
+                    for ent in res.json():
+                        if ent['type'] == ptype:
+                            ids.append(ent['client-id'])
+                except KeyError as e:
+                    print('Error: {} is not defined!'.format(e))
+        return ids
+
+    def get_sec_procs(self, ptype):
+        """Get secondary processes info of given type.
+
+        Processes info from spp-ctl is retrieved as JSON, then loaded with
+        json() before returned.
+        """
+
+        sec_procs = []
+        error_codes = self.rest_common_error_codes
+
+        for sec_id in self.get_sec_ids(ptype):
+            # NOTE: take care API's proc type are defined as plural such as
+            # 'nfvs', 'vfs' or so.
+            res = self.get('{pt}s/{sid}'.format(pt=ptype, sid=sec_id))
+            if res.status_code == 200:
+                sec_procs.append(res.json())
+            elif res.status_code in error_codes:
+                # TODO(yasufum) Print default error message
+                pass
+            else:
+                # Ignore unknown response because no problem for drawing graph
+                pass
+        return sec_procs
-- 
2.17.1


  parent reply	other threads:[~2019-08-12  7:12 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-08-12  7:12 [spp] [PATCH 0/8] Update topo to support other than spp_nfv Yasufumi Ogawa
2019-08-12  7:12 ` [spp] [PATCH 1/8] cli: remove topo_resize command Yasufumi Ogawa
2019-08-12  7:12 ` [spp] [PATCH 2/8] cli: revise composing dot script Yasufumi Ogawa
2019-08-12  7:12 ` [spp] [PATCH 3/8] cli: move style params for dot to config Yasufumi Ogawa
2019-08-12  7:12 ` Yasufumi Ogawa [this message]
2019-08-12  7:12 ` [spp] [PATCH 5/8] cli: support other than spp_nfv in topo command Yasufumi Ogawa
2019-08-12  7:12 ` [spp] [PATCH 6/8] cli: add port types for " Yasufumi Ogawa
2019-08-12  7:12 ` [spp] [PATCH 7/8] cli: add checking JSON objs in topo Yasufumi Ogawa
2019-08-12  7:12 ` [spp] [PATCH 8/8] cli: revise description for imgcat Yasufumi Ogawa

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190812071242.18934-5-yasufum.o@gmail.com \
    --to=yasufum.o@gmail.com \
    --cc=ferruh.yigit@intel.com \
    --cc=spp@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).