Soft Patch Panel
 help / color / mirror / Atom feed
* [spp] [PATCH 0/5] update SPP CLI
@ 2018-12-12  2:03 ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 1/5] controller: change sec command to nfv ogawa.yasufumi
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: ogawa.yasufumi @ 2018-12-12  2:03 UTC (permalink / raw)
  To: ferruh.yigit, spp, ogawa.yasufumi

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

Hi,

This series of patches is for three updates for SPP CLI. It also include
other misc upates for refactoring.

1. Change sec command to nfv

As suggested in [1], there are several commands in SPP CLI for managing
secondary processes, `sec`, `vf` and `mirror`, and `sec` should be
changed to `nfv`.

[1] https://mails.dpdk.org/archives/spp/2018-November/000990.html

2. Add checking for add and del commands

To avoid invalid operations for ports, add checking for the operations.
If you add a port which is already registered actually, it might cause
a problem.

3. Update bye command to support spp_vf and spp_mirror

Bye command is available ony for spp_nfv because spp_vf and spp_mirror
does not provide `exit` command for graceful termination. This update is
to add exiting spp_vf and spp_mirror with `bye` command for graceful
shutdown.

Thanks,
Yasufumi

Yasufumi Ogawa (5):
  controller: change sec command to nfv
  controller: change to exclude used port from del
  controller: add checking for add and del cmds
  controller: refactor SppNfv class
  controller: update SppBye class

 src/controller/commands/bye.py |  27 +--
 src/controller/commands/nfv.py | 435 +++++++++++++++++++++++++++++++++++++++++
 src/controller/commands/sec.py | 202 -------------------
 src/controller/shell.py        | 153 ++++++++++-----
 4 files changed, 551 insertions(+), 266 deletions(-)
 create mode 100644 src/controller/commands/nfv.py
 delete mode 100644 src/controller/commands/sec.py

-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [spp] [PATCH 1/5] controller: change sec command to nfv
  2018-12-12  2:03 [spp] [PATCH 0/5] update SPP CLI ogawa.yasufumi
@ 2018-12-12  2:03 ` ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 2/5] controller: change to exclude used port from del ogawa.yasufumi
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: ogawa.yasufumi @ 2018-12-12  2:03 UTC (permalink / raw)
  To: ferruh.yigit, spp, ogawa.yasufumi

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

This update is to change `sec` command to `nfv` and correct completion
of patch sub command. Completion of `patch` command should not show
ports already used, and should not show dst port after `reset`.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
 src/controller/commands/bye.py |   8 +-
 src/controller/commands/nfv.py | 314 +++++++++++++++++++++++++++++++++++++++++
 src/controller/commands/sec.py | 202 --------------------------
 src/controller/shell.py        |  80 +++++++----
 4 files changed, 372 insertions(+), 232 deletions(-)
 create mode 100644 src/controller/commands/nfv.py
 delete mode 100644 src/controller/commands/sec.py

diff --git a/src/controller/commands/bye.py b/src/controller/commands/bye.py
index 3ffc259..dfbd048 100644
--- a/src/controller/commands/bye.py
+++ b/src/controller/commands/bye.py
@@ -14,10 +14,10 @@ class SppBye(object):
 
     BYE_CMDS = ['sec', 'all']
 
-    def __init__(self, spp_ctl_cli, spp_primary, spp_secondary):
+    def __init__(self, spp_ctl_cli, spp_primary, spp_nfvs):
         self.spp_ctl_cli = spp_ctl_cli
         self.spp_primary = spp_primary
-        self.spp_secondary = spp_secondary
+        self.spp_nfvs = spp_nfvs
 
     def run(self, args, sec_ids):
 
@@ -44,5 +44,5 @@ class SppBye(object):
     def close_all_secondary(self, sec_ids):
         """Terminate all secondary processes."""
 
-        for i in sec_ids:
-            self.spp_secondary.run(i, 'exit')
+        for i, nfv in self.spp_nfvs.items():
+            nfv.run(i, 'exit')
diff --git a/src/controller/commands/nfv.py b/src/controller/commands/nfv.py
new file mode 100644
index 0000000..9af4449
--- /dev/null
+++ b/src/controller/commands/nfv.py
@@ -0,0 +1,314 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
+
+from .. import spp_common
+
+
+class SppNfv(object):
+    """Exec spp_nfv command.
+
+    SppNfv lass is intended to be used in Shell class as a delegator for
+    running 'nfv' command.
+
+    'self.command()' is called from do_nfv() and 'self.complete()' is called
+    from complete_nfv() of both of which is defined in Shell.
+    """
+
+    # All of commands and sub-commands used for validation and completion.
+    NFV_CMDS = ['status', 'exit', 'forward', 'stop', 'add', 'patch', 'del']
+
+    def __init__(self, spp_ctl_cli, sec_id, use_cache=False):
+        self.spp_ctl_cli = spp_ctl_cli
+        self.sec_id = sec_id
+        self.ports = []  # registered ports
+        self.patchs = []
+
+        # Call REST API each time of completion if it is True.
+        self.use_cache = use_cache
+
+    def run(self, cmdline):
+        """Called from do_nfv() to Send command to secondary process."""
+
+        cmd = cmdline.split(' ')[0]
+        params = cmdline.split(' ')[1:]
+
+        if cmd == 'status':
+            res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
+            if res is not None:
+                error_codes = self.spp_ctl_cli.rest_common_error_codes
+                if res.status_code == 200:
+                    self.print_nfv_status(res.json())
+                elif res.status_code in error_codes:
+                    pass
+                else:
+                    print('Error: unknown response.')
+
+        elif cmd == 'add':
+            if self.use_cache is True:
+                self.ports.append(params[0])
+
+            req_params = {'action': 'add', 'port': params[0]}
+
+            res = self.spp_ctl_cli.put('nfvs/%d/ports' %
+                                       self.sec_id, req_params)
+            if res is not None:
+                error_codes = self.spp_ctl_cli.rest_common_error_codes
+                if res.status_code == 204:
+                    print('Add %s.' % params[0])
+                elif res.status_code in error_codes:
+                    pass
+                else:
+                    print('Error: unknown response.')
+
+        elif cmd == 'del':
+            if self.use_cache is True:
+                if params[0] in self.ports:
+                    self.ports.remove(params[0])
+
+            req_params = {'action': 'del', 'port': params[0]}
+            res = self.spp_ctl_cli.put('nfvs/%d/ports' %
+                                       self.sec_id, req_params)
+            if res is not None:
+                error_codes = self.spp_ctl_cli.rest_common_error_codes
+                if res.status_code == 204:
+                    print('Delete %s.' % params[0])
+                elif res.status_code in error_codes:
+                    pass
+                else:
+                    print('Error: unknown response.')
+
+        elif cmd == 'forward' or cmd == 'stop':
+            if cmd == 'forward':
+                req_params = {'action': 'start'}
+            elif cmd == 'stop':
+                req_params = {'action': 'stop'}
+            else:
+                print('Unknown command. "forward" or "stop"?')
+
+            res = self.spp_ctl_cli.put('nfvs/%d/forward' %
+                                       self.sec_id, req_params)
+            if res is not None:
+                error_codes = self.spp_ctl_cli.rest_common_error_codes
+                if res.status_code == 204:
+                    if cmd == 'forward':
+                        print('Start forwarding.')
+                    else:
+                        print('Stop forwarding.')
+                elif res.status_code in error_codes:
+                    pass
+                else:
+                    print('Error: unknown response.')
+
+        elif cmd == 'patch':
+            if params[0] == 'reset':
+                res = self.spp_ctl_cli.delete('nfvs/%d/patches' % self.sec_id)
+                if res is not None:
+                    error_codes = self.spp_ctl_cli.rest_common_error_codes
+                    if res.status_code == 204:
+                        print('Clear all of patches.')
+                    elif res.status_code in error_codes:
+                        pass
+                    else:
+                        print('Error: unknown response.')
+            else:
+                req_params = {'src': params[0], 'dst': params[1]}
+                res = self.spp_ctl_cli.put(
+                        'nfvs/%d/patches' % self.sec_id, req_params)
+                if res is not None:
+                    error_codes = self.spp_ctl_cli.rest_common_error_codes
+                    if res.status_code == 204:
+                        print('Patch ports (%s -> %s).' % (
+                            params[0], params[1]))
+                    elif res.status_code in error_codes:
+                        pass
+                    else:
+                        print('Error: unknown response.')
+
+        elif cmd == 'exit':
+            res = self.spp_ctl_cli.delete('nfvs/%d' % self.sec_id)
+            if res is not None:
+                error_codes = self.spp_ctl_cli.rest_common_error_codes
+                if res.status_code == 204:
+                    print('Exit nfv %d' % self.sec_id)
+                elif res.status_code in error_codes:
+                    pass
+                else:
+                    print('Error: unknown response.')
+
+        else:
+            print('Invalid command "%s".' % cmd)
+
+    def print_nfv_status(self, json_obj):
+        """Parse and print message from SPP secondary.
+
+        Print status received from secondary.
+
+          spp > nfv 1;status
+          - status: idling
+          - ports:
+            - phy:0 -> ring:0
+            - phy:1
+
+        The format of the received message is JSON and ended with
+        series of null character "\x00".
+
+          {"client-id":1,...,"patches":[{"src":"phy:0"...},...]}'\x00..
+        """
+
+        nfv_attr = json_obj
+        print('- status: %s' % nfv_attr['status'])
+        print('- ports:')
+        for port in nfv_attr['ports']:
+            dst = None
+            for patch in nfv_attr['patches']:
+                if patch['src'] == port:
+                    dst = patch['dst']
+
+            if dst is None:
+                print('  - %s' % port)
+            else:
+                print('  - %s -> %s' % (port, dst))
+
+    def get_registered_ports(self):
+        res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
+        if res is not None:
+            error_codes = self.spp_ctl_cli.rest_common_error_codes
+            if res.status_code == 200:
+                return res.json()['ports']
+            elif res.status_code in error_codes:
+                pass
+            else:
+                print('Error: unknown response.')
+
+    def get_registered_patches(self):
+        res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
+        if res is not None:
+            error_codes = self.spp_ctl_cli.rest_common_error_codes
+            if res.status_code == 200:
+                return res.json()['patches']
+            elif res.status_code in error_codes:
+                pass
+            else:
+                print('Error: unknown response.')
+
+    def complete(self, sec_ids, text, line, begidx, endidx):
+        """Completion for spp_nfv commands.
+
+        Called from complete_nfv() to complete secondary command.
+        """
+
+        try:
+            completions = []
+            tokens = line.split(';')
+
+            if len(tokens) == 2:
+                sub_tokens = tokens[1].split(' ')
+
+                if len(sub_tokens) == 1:
+                    if not (sub_tokens[0] in self.NFV_CMDS):
+                        completions = self._compl_first_tokens(sub_tokens[0])
+
+                else:
+                    if sub_tokens[0] in ['status', 'exit', 'forward', 'stop']:
+                        if len(sub_tokens) < 2:
+                            if sub_tokens[0].startswith(sub_tokens[1]):
+                                completions = [sub_tokens[0]]
+
+                    elif sub_tokens[0] == 'add':
+                        completions = self._compl_add(sub_tokens)
+
+                    elif sub_tokens[0] == 'del':
+                        completions = self._compl_del(sub_tokens)
+
+                    elif sub_tokens[0] == 'patch':
+                        completions = self._compl_patch(sub_tokens)
+
+            return completions
+
+        except Exception as e:
+            print(e)
+
+    def _compl_first_tokens(self, token):
+        res = []
+        for kw in self.NFV_CMDS:
+            if kw.startswith(token):
+                res.append(kw)
+        return res
+
+    def _compl_add(self, sub_tokens):
+        if len(sub_tokens) < 3:
+            res = []
+
+            port_types = spp_common.PORT_TYPES[:]
+            port_types.remove('phy')
+
+            for kw in port_types:
+                if kw.startswith(sub_tokens[1]):
+                    res.append(kw + ':')
+            return res
+
+    def _compl_del(self, sub_tokens):
+        if len(sub_tokens) < 3:
+            res = []
+
+            if self.use_cache is False:
+                self.ports = self.get_registered_ports()
+
+            for kw in self.ports:
+                if kw.startswith(sub_tokens[1]):
+                    if ':' in sub_tokens[1]:  # exp, 'ring:' or 'ring:0'
+                        res.append(kw.split(':')[1])
+                    else:
+                        res.append(kw)
+
+            for p in res:
+                if p.startswith('phy:'):
+                    res.remove(p)
+
+            return res
+
+    def _compl_patch(self, sub_tokens):
+        # Patch command consists of three tokens max, for instance,
+        # `nfv 1; patch phy:0 ring:1`.
+        if len(sub_tokens) < 4:
+            res = []
+
+            if self.use_cache is False:
+                self.ports = self.get_registered_ports()
+                self.patches = self.get_registered_patches()
+
+            # Get patched ports of src and dst to be used for completion.
+            src_ports = []
+            dst_ports = []
+            for pt in self.patches:
+                src_ports.append(pt['src'])
+                dst_ports.append(pt['dst'])
+
+            # Remove patched ports from candidates.
+            target_idx = len(sub_tokens) - 1  # target is src or dst
+            tmp_ports = self.ports[:]  # candidates
+            if target_idx == 1:  # find src port
+                # If some of ports are patched, `reset` should be included.
+                if self.patches != []:
+                    tmp_ports.append('reset')
+                for pt in src_ports:
+                    tmp_ports.remove(pt)  # remove patched ports
+            else:  # find dst port
+                # If `reset` is given, no need to show dst ports.
+                if sub_tokens[target_idx - 1] == 'reset':
+                    tmp_ports = []
+                else:
+                    for pt in dst_ports:
+                        tmp_ports.remove(pt)
+
+            # Return candidates.
+            for kw in tmp_ports:
+                if kw.startswith(sub_tokens[target_idx]):
+                    # Completion does not work correctly if `:` is included in
+                    # tokens. Required to create keyword only after `:`.
+                    if ':' in sub_tokens[target_idx]:  # 'ring:' or 'ring:0'
+                        res.append(kw.split(':')[1])  # add only after `:`
+                    else:
+                        res.append(kw)
+
+            return res
diff --git a/src/controller/commands/sec.py b/src/controller/commands/sec.py
deleted file mode 100644
index ec1da58..0000000
--- a/src/controller/commands/sec.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# SPDX-License-Identifier: BSD-3-Clause
-# Copyright(c) 2018 Nippon Telegraph and Telephone Corporation
-
-
-class SppSecondary(object):
-    """Exec SPP secondary command.
-
-    SppSecondaryclass is intended to be used in Shell class as a delegator
-    for running 'sec' command.
-
-    'self.command()' is called from do_sec() and 'self.complete()' is called
-    from complete_sec() of both of which is defined in Shell.
-    """
-
-    # All of commands and sub-commands used for validation and completion.
-    SEC_CMDS = ['status', 'exit', 'forward', 'stop', 'add', 'patch', 'del']
-    SEC_SUBCMDS = ['vhost', 'ring', 'pcap', 'nullpmd']
-
-    def __init__(self, spp_ctl_cli):
-        self.spp_ctl_cli = spp_ctl_cli
-
-    def run(self, sec_id, cmdline):
-        """Called from do_sec() to Send command to secondary process."""
-
-        cmd = cmdline.split(' ')[0]
-        params = cmdline.split(' ')[1:]
-
-        if cmd == 'status':
-            res = self.spp_ctl_cli.get('nfvs/%d' % sec_id)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 200:
-                    self.print_sec_status(res.json())
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
-
-        elif cmd == 'add':
-            req_params = {'action': 'add', 'port': params[0]}
-            res = self.spp_ctl_cli.put('nfvs/%d/ports' % sec_id, req_params)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    print('Add %s.' % params[0])
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
-
-        elif cmd == 'del':
-            req_params = {'action': 'del', 'port': params[0]}
-            res = self.spp_ctl_cli.put('nfvs/%d/ports' % sec_id, req_params)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    print('Delete %s.' % params[0])
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
-
-        elif cmd == 'forward' or cmd == 'stop':
-            if cmd == 'forward':
-                req_params = {'action': 'start'}
-            elif cmd == 'stop':
-                req_params = {'action': 'stop'}
-            else:
-                print('Unknown command. "forward" or "stop"?')
-
-            res = self.spp_ctl_cli.put('nfvs/%d/forward' % sec_id, req_params)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    if cmd == 'forward':
-                        print('Start forwarding.')
-                    else:
-                        print('Stop forwarding.')
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
-
-        elif cmd == 'patch':
-            if params[0] == 'reset':
-                res = self.spp_ctl_cli.delete('nfvs/%d/patches' % sec_id)
-                if res is not None:
-                    error_codes = self.spp_ctl_cli.rest_common_error_codes
-                    if res.status_code == 204:
-                        print('Clear all of patches.')
-                    elif res.status_code in error_codes:
-                        pass
-                    else:
-                        print('Error: unknown response.')
-            else:
-                req_params = {'src': params[0], 'dst': params[1]}
-                res = self.spp_ctl_cli.put(
-                        'nfvs/%d/patches' % sec_id, req_params)
-                if res is not None:
-                    error_codes = self.spp_ctl_cli.rest_common_error_codes
-                    if res.status_code == 204:
-                        print('Patch ports (%s -> %s).' % (
-                            params[0], params[1]))
-                    elif res.status_code in error_codes:
-                        pass
-                    else:
-                        print('Error: unknown response.')
-
-        elif cmd == 'exit':
-            res = self.spp_ctl_cli.delete('nfvs/%d' % sec_id)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    print('Exit sec %d' % sec_id)
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
-
-        else:
-            print('Invalid command "%s".' % cmd)
-
-    def print_sec_status(self, json_obj):
-        """Parse and print message from SPP secondary.
-
-        Print status received from secondary.
-
-          spp > sec 1;status
-          - status: idling
-          - ports:
-            - phy:0 -> ring:0
-            - phy:1
-
-        The format of the received message is JSON and ended with
-        series of null character "\x00".
-
-          {"client-id":1,...,"patches":[{"src":"phy:0"...},...]}'\x00..
-        """
-
-        sec_attr = json_obj
-        print('- status: %s' % sec_attr['status'])
-        print('- ports:')
-        for port in sec_attr['ports']:
-            dst = None
-            for patch in sec_attr['patches']:
-                if patch['src'] == port:
-                    dst = patch['dst']
-
-            if dst is None:
-                print('  - %s' % port)
-            else:
-                print('  - %s -> %s' % (port, dst))
-
-    def complete(self, sec_ids, text, line, begidx, endidx):
-        """Completion for secondary process commands.
-
-        Called from complete_sec() to complete secondary command.
-        """
-
-        try:
-            cleaned_line = line
-
-            if len(cleaned_line.split()) == 1:
-                completions = [str(i)+";" for i in sec_ids]
-            elif len(cleaned_line.split()) == 2:
-                if not (";" in cleaned_line):
-                    tmplist = [str(i) for i in sec_ids]
-                    completions = [p+";"
-                                   for p in tmplist
-                                   if p.startswith(text)
-                                   ]
-                elif cleaned_line[-1] == ";":
-                    completions = self.SEC_CMDS[:]
-                else:
-                    seccmd = cleaned_line.split(";")[1]
-                    if cleaned_line[-1] != " ":
-                        completions = [p
-                                       for p in self.SEC_CMDS
-                                       if p.startswith(seccmd)
-                                       ]
-                    elif ("add" in seccmd) or ("del" in seccmd):
-                        completions = self.SEC_SUBCMDS[:]
-                    else:
-                        completions = []
-            elif len(cleaned_line.split()) == 3:
-                subcmd = cleaned_line.split()[-1]
-                if ("add" == subcmd) or ("del" == subcmd):
-                    completions = self.SEC_SUBCMDS[:]
-                else:
-                    if cleaned_line[-1] == " ":
-                        completions = []
-                    else:
-                        completions = [p
-                                       for p in self.SEC_SUBCMDS
-                                       if p.startswith(subcmd)
-                                       ]
-            else:
-                completions = []
-            return completions
-        except Exception as e:
-            print(len(cleaned_line.split()))
-            print(e)
diff --git a/src/controller/shell.py b/src/controller/shell.py
index 84d4e2f..eafc5de 100644
--- a/src/controller/shell.py
+++ b/src/controller/shell.py
@@ -6,7 +6,7 @@ from __future__ import absolute_import
 import cmd
 from .commands import bye
 from .commands import pri
-from .commands import sec
+from .commands import nfv
 from .commands import topo
 from .commands import vf
 from .commands import mirror
@@ -42,7 +42,10 @@ class Shell(cmd.Cmd, object):
         cmd.Cmd.__init__(self)
         self.spp_ctl_cli = spp_ctl_cli
         self.spp_primary = pri.SppPrimary(self.spp_ctl_cli)
-        self.spp_secondary = sec.SppSecondary(self.spp_ctl_cli)
+
+        self.spp_nfvs = {}
+        for sec_id in self.get_sec_ids('nfv'):
+            self.spp_nfvs[sec_id] = nfv.SppNfv(self.spp_ctl_cli, sec_id)
 
         self.spp_vfs = {}
         for sec_id in self.get_sec_ids('vf'):
@@ -55,7 +58,7 @@ class Shell(cmd.Cmd, object):
 
         self.spp_topo = topo.SppTopo(self.spp_ctl_cli, {}, self.topo_size)
         self.spp_bye = bye.SppBye(self.spp_ctl_cli, self.spp_primary,
-                                  self.spp_secondary)
+                                  self.spp_nfvs)
 
     def default(self, line):
         """Define defualt behaviour.
@@ -141,7 +144,8 @@ class Shell(cmd.Cmd, object):
                 cnt = 1
                 for pt in ['nfv', 'vf', 'mirror']:
                     for obj in sec_obj[pt]:
-                        print('    %d: %s:%s' % (cnt, obj['type'], obj['client-id']))
+                        print('    %d: %s:%s' %
+                              (cnt, obj['type'], obj['client-id']))
                         cnt += 1
             elif res.status_code in self.spp_ctl_cli.rest_common_error_codes:
                 pass
@@ -264,20 +268,19 @@ class Shell(cmd.Cmd, object):
 
         return self.spp_primary.complete(text, line, begidx, endidx)
 
-    def do_sec(self, cmd):
-        """Send a command to secondary process specified with ID.
+    def do_nfv(self, cmd):
+        """Send a command to spp_nfv specified with ID.
 
-        SPP secondary process is specified with secondary ID and takes
-        sub commands.
+        Spp_nfv is specified with secondary ID and takes sub commands.
 
-        spp > sec 1; status
-        spp > sec 1; add ring:0
-        spp > sec 1; patch phy:0 ring:0
+        spp > nfv 1; status
+        spp > nfv 1; add ring:0
+        spp > nfv 1; patch phy:0 ring:0
 
         You can refer all of sub commands by pressing TAB after
-        'sec 1;'.
+        'nfv 1;'.
 
-        spp > sec 1;  # press TAB
+        spp > nfv 1;  # press TAB
         add     del     exit    forward patch   status  stop
         """
 
@@ -287,21 +290,44 @@ class Shell(cmd.Cmd, object):
         if len(cmds) < 2:
             print("Required an ID and ';' before the command.")
         elif str.isdigit(cmds[0]):
-            sec_id = int(cmds[0])
-            if self.check_sec_cmds(cmds[1]):
-                self.spp_secondary.run(sec_id, cmds[1])
-            else:
-                print("Invalid sec command")
+            self.spp_nfvs[int(cmds[0])].run(cmds[1])
         else:
-            print(cmds[0])
-            print("first %s" % cmds[1])
+            print('Invalid command: %s' % tmparg)
 
-    def complete_sec(self, text, line, begidx, endidx):
-        """Completion for secondary process commands"""
+    def complete_nfv(self, text, line, begidx, endidx):
+        """Completion for nfv command"""
 
         line = self.clean_cmd(line)
-        return self.spp_secondary.complete(
-                self.get_sec_ids('nfv'), text, line, begidx, endidx)
+
+        tokens = line.split(';')
+        if len(tokens) == 1:
+            # Add SppNfv of sec_id if it is not exist
+            sec_ids = self.get_sec_ids('nfv')
+            for idx in sec_ids:
+                if self.spp_nfvs[idx] is None:
+                    self.spp_nfvs[idx] = nfv.SppNfv(self.spp_ctl_cli, idx)
+
+            if len(line.split()) == 1:
+                res = [str(i)+';' for i in sec_ids]
+            else:
+                if not (';' in line):
+                    res = [str(i)+';'
+                           for i in sec_ids
+                           if (str(i)+';').startswith(text)]
+            return res
+        elif len(tokens) == 2:
+            first_tokens = tokens[0].split(' ')  # 'nfv 1' => ['nfv', '1']
+            if len(first_tokens) == 2:
+                idx = int(first_tokens[1])
+
+                # Add SppVf of sec_id if it is not exist
+                if self.spp_nfvs[idx] is None:
+                    self.spp_nfvs[idx] = nfv.SppNfv(self.spp_ctl_cli, idx)
+
+                res = self.spp_nfvs[idx].complete(self.get_sec_ids('nfv'),
+                                                  text, line, begidx, endidx)
+                # logger.info(res)
+                return res
 
     def do_vf(self, cmd):
         """Send a command to spp_vf.
@@ -451,7 +477,8 @@ class Shell(cmd.Cmd, object):
             sec_ids = self.get_sec_ids('mirror')
             for idx in sec_ids:
                 if self.spp_mirrors[idx] is None:
-                    self.spp_mirrors[idx] = mirror.SppMirror(self.spp_ctl_cli, idx)
+                    self.spp_mirrors[idx] = mirror.SppMirror(
+                            self.spp_ctl_cli, idx)
 
             if len(line.split()) == 1:
                 res = [str(i)+';' for i in sec_ids]
@@ -469,7 +496,8 @@ class Shell(cmd.Cmd, object):
 
                 # Add SppMirror of sec_id if it is not exist
                 if self.spp_mirrors[idx] is None:
-                    self.spp_mirrors[idx] = mirror.SppMirror(self.spp_ctl_cli, idx)
+                    self.spp_mirrors[idx] = mirror.SppMirror(
+                            self.spp_ctl_cli, idx)
 
                 return self.spp_mirrors[idx].complete(
                         self.get_sec_ids('mirror'), text, line, begidx, endidx)
-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [spp] [PATCH 2/5] controller: change to exclude used port from del
  2018-12-12  2:03 [spp] [PATCH 0/5] update SPP CLI ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 1/5] controller: change sec command to nfv ogawa.yasufumi
@ 2018-12-12  2:03 ` ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 3/5] controller: add checking for add and del cmds ogawa.yasufumi
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: ogawa.yasufumi @ 2018-12-12  2:03 UTC (permalink / raw)
  To: ferruh.yigit, spp, ogawa.yasufumi

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

In completion of `del`, exclude ports already used.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
 src/controller/commands/nfv.py | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/src/controller/commands/nfv.py b/src/controller/commands/nfv.py
index 9af4449..7a8d073 100644
--- a/src/controller/commands/nfv.py
+++ b/src/controller/commands/nfv.py
@@ -248,19 +248,33 @@ class SppNfv(object):
             return res
 
     def _compl_del(self, sub_tokens):
+        # Del command consists of two tokens max, for instance,
+        # `nfv 1; del ring:1`.
         if len(sub_tokens) < 3:
             res = []
 
             if self.use_cache is False:
                 self.ports = self.get_registered_ports()
+                self.patches = self.get_registered_patches()
 
-            for kw in self.ports:
-                if kw.startswith(sub_tokens[1]):
-                    if ':' in sub_tokens[1]:  # exp, 'ring:' or 'ring:0'
-                        res.append(kw.split(':')[1])
-                    else:
-                        res.append(kw)
+            # Used ports should not be included in the candidate of del.
+            used_ports = []
+            for pt in self.ports:
+                for ppt in self.patches:
+                    if ((pt in ppt['src']) or (pt in ppt['dst'])):
+                            used_ports.append(pt)
+            used_ports = list(set(used_ports))
 
+            # Remove ports already used from candidate.
+            for kw in self.ports:
+                if not (kw in used_ports):
+                    if kw.startswith(sub_tokens[1]):
+                        if ':' in sub_tokens[1]:  # exp, 'ring:' or 'ring:0'
+                            res.append(kw.split(':')[1])
+                        else:
+                            res.append(kw)
+
+            # Physical port cannot be removed.
             for p in res:
                 if p.startswith('phy:'):
                     res.remove(p)
-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [spp] [PATCH 3/5] controller: add checking for add and del cmds
  2018-12-12  2:03 [spp] [PATCH 0/5] update SPP CLI ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 1/5] controller: change sec command to nfv ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 2/5] controller: change to exclude used port from del ogawa.yasufumi
@ 2018-12-12  2:03 ` ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 4/5] controller: refactor SppNfv class ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 5/5] controller: update SppBye class ogawa.yasufumi
  4 siblings, 0 replies; 6+ messages in thread
From: ogawa.yasufumi @ 2018-12-12  2:03 UTC (permalink / raw)
  To: ferruh.yigit, spp, ogawa.yasufumi

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

To check invalid port, add checking for `add` and `del` commands.
`add` should not accept if assigned port is already added, and `patch`
should not accept if assigned is patched.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
 src/controller/commands/nfv.py | 88 +++++++++++++++++++++++++-----------------
 1 file changed, 53 insertions(+), 35 deletions(-)

diff --git a/src/controller/commands/nfv.py b/src/controller/commands/nfv.py
index 7a8d073..646cdc0 100644
--- a/src/controller/commands/nfv.py
+++ b/src/controller/commands/nfv.py
@@ -44,38 +44,57 @@ class SppNfv(object):
                     print('Error: unknown response.')
 
         elif cmd == 'add':
-            if self.use_cache is True:
-                self.ports.append(params[0])
+            if self.use_cache is False:
+                self.ports = self.get_registered_ports()
 
-            req_params = {'action': 'add', 'port': params[0]}
+            if params[0] in self.ports:
+                print("'%s' is already added." % params[0])
+            else:
+                if self.use_cache is True:
+                    self.ports.append(params[0])
 
-            res = self.spp_ctl_cli.put('nfvs/%d/ports' %
-                                       self.sec_id, req_params)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    print('Add %s.' % params[0])
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
+                req_params = {'action': 'add', 'port': params[0]}
+
+                res = self.spp_ctl_cli.put('nfvs/%d/ports' %
+                                           self.sec_id, req_params)
+                if res is not None:
+                    error_codes = self.spp_ctl_cli.rest_common_error_codes
+                    if res.status_code == 204:
+                        print('Add %s.' % params[0])
+                    elif res.status_code in error_codes:
+                        pass
+                    else:
+                        print('Error: unknown response.')
 
         elif cmd == 'del':
-            if self.use_cache is True:
-                if params[0] in self.ports:
-                    self.ports.remove(params[0])
+            if self.use_cache is False:
+                self.patches = self.get_registered_patches()
 
-            req_params = {'action': 'del', 'port': params[0]}
-            res = self.spp_ctl_cli.put('nfvs/%d/ports' %
-                                       self.sec_id, req_params)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    print('Delete %s.' % params[0])
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
+            # Patched ports should not be deleted.
+            patched_ports = []
+            for pport in self.patches:
+                patched_ports.append(pport['src'])
+                patched_ports.append(pport['dst'])
+            patched_ports = list(set(patched_ports))
+
+            if params[0] in patched_ports:
+                print("Cannot delete patched port '%s'." % params[0])
+            else:
+                if self.use_cache is True:
+                    if params[0] in self.ports:
+                        self.ports.remove(params[0])
+
+                req_params = {'action': 'del', 'port': params[0]}
+                res = self.spp_ctl_cli.put('nfvs/%d/ports' %
+                                           self.sec_id, req_params)
+                if res is not None:
+                    error_codes = self.spp_ctl_cli.rest_common_error_codes
+                    if res.status_code == 204:
+                        print('Delete %s.' % params[0])
+                    elif res.status_code in error_codes:
+                        pass
+                    else:
+                        print('Error: unknown response.')
 
         elif cmd == 'forward' or cmd == 'stop':
             if cmd == 'forward':
@@ -257,17 +276,16 @@ class SppNfv(object):
                 self.ports = self.get_registered_ports()
                 self.patches = self.get_registered_patches()
 
-            # Used ports should not be included in the candidate of del.
-            used_ports = []
-            for pt in self.ports:
-                for ppt in self.patches:
-                    if ((pt in ppt['src']) or (pt in ppt['dst'])):
-                            used_ports.append(pt)
-            used_ports = list(set(used_ports))
+            # Patched ports should not be included in the candidate of del.
+            patched_ports = []
+            for pport in self.patches:
+                patched_ports.append(pport['src'])
+                patched_ports.append(pport['dst'])
+            patched_ports = list(set(patched_ports))
 
             # Remove ports already used from candidate.
             for kw in self.ports:
-                if not (kw in used_ports):
+                if not (kw in patched_ports):
                     if kw.startswith(sub_tokens[1]):
                         if ':' in sub_tokens[1]:  # exp, 'ring:' or 'ring:0'
                             res.append(kw.split(':')[1])
-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [spp] [PATCH 4/5] controller: refactor SppNfv class
  2018-12-12  2:03 [spp] [PATCH 0/5] update SPP CLI ogawa.yasufumi
                   ` (2 preceding siblings ...)
  2018-12-12  2:03 ` [spp] [PATCH 3/5] controller: add checking for add and del cmds ogawa.yasufumi
@ 2018-12-12  2:03 ` ogawa.yasufumi
  2018-12-12  2:03 ` [spp] [PATCH 5/5] controller: update SppBye class ogawa.yasufumi
  4 siblings, 0 replies; 6+ messages in thread
From: ogawa.yasufumi @ 2018-12-12  2:03 UTC (permalink / raw)
  To: ferruh.yigit, spp, ogawa.yasufumi

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

* To become maintainance easier, define methods for each of sub command
  which starts from `_run`.

* Add `get_ports_and_patches()` for getting both of attributes at once
  to reduce the number of requests to spp-ctl.

* Add descriptions for all of methods.

* Revise names of variables and methods.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
 src/controller/commands/nfv.py | 349 ++++++++++++++++++++++++++---------------
 1 file changed, 219 insertions(+), 130 deletions(-)

diff --git a/src/controller/commands/nfv.py b/src/controller/commands/nfv.py
index 646cdc0..e6f95d0 100644
--- a/src/controller/commands/nfv.py
+++ b/src/controller/commands/nfv.py
@@ -10,14 +10,21 @@ class SppNfv(object):
     SppNfv lass is intended to be used in Shell class as a delegator for
     running 'nfv' command.
 
-    'self.command()' is called from do_nfv() and 'self.complete()' is called
-    from complete_nfv() of both of which is defined in Shell.
+    'self.command()' is called from do_nfv() and 'self.complete()' is
+    called from complete_nfv() of both of which is defined in Shell.
     """
 
-    # All of commands and sub-commands used for validation and completion.
-    NFV_CMDS = ['status', 'exit', 'forward', 'stop', 'add', 'patch', 'del']
+    # All of spp_nfv commands used for validation and completion.
+    NFV_CMDS = ['status', 'exit', 'forward', 'stop', 'add', 'patch',
+                'del']
 
     def __init__(self, spp_ctl_cli, sec_id, use_cache=False):
+        """Initialize SppNfv.
+
+        Turn use_cache `True` if you do not request to spp-ctl each
+        time.
+        """
+
         self.spp_ctl_cli = spp_ctl_cli
         self.sec_id = sec_id
         self.ports = []  # registered ports
@@ -33,126 +40,22 @@ class SppNfv(object):
         params = cmdline.split(' ')[1:]
 
         if cmd == 'status':
-            res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 200:
-                    self.print_nfv_status(res.json())
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
+            self._run_status()
 
         elif cmd == 'add':
-            if self.use_cache is False:
-                self.ports = self.get_registered_ports()
-
-            if params[0] in self.ports:
-                print("'%s' is already added." % params[0])
-            else:
-                if self.use_cache is True:
-                    self.ports.append(params[0])
-
-                req_params = {'action': 'add', 'port': params[0]}
-
-                res = self.spp_ctl_cli.put('nfvs/%d/ports' %
-                                           self.sec_id, req_params)
-                if res is not None:
-                    error_codes = self.spp_ctl_cli.rest_common_error_codes
-                    if res.status_code == 204:
-                        print('Add %s.' % params[0])
-                    elif res.status_code in error_codes:
-                        pass
-                    else:
-                        print('Error: unknown response.')
+            self._run_add(params)
 
         elif cmd == 'del':
-            if self.use_cache is False:
-                self.patches = self.get_registered_patches()
-
-            # Patched ports should not be deleted.
-            patched_ports = []
-            for pport in self.patches:
-                patched_ports.append(pport['src'])
-                patched_ports.append(pport['dst'])
-            patched_ports = list(set(patched_ports))
-
-            if params[0] in patched_ports:
-                print("Cannot delete patched port '%s'." % params[0])
-            else:
-                if self.use_cache is True:
-                    if params[0] in self.ports:
-                        self.ports.remove(params[0])
-
-                req_params = {'action': 'del', 'port': params[0]}
-                res = self.spp_ctl_cli.put('nfvs/%d/ports' %
-                                           self.sec_id, req_params)
-                if res is not None:
-                    error_codes = self.spp_ctl_cli.rest_common_error_codes
-                    if res.status_code == 204:
-                        print('Delete %s.' % params[0])
-                    elif res.status_code in error_codes:
-                        pass
-                    else:
-                        print('Error: unknown response.')
+            self._run_del(params)
 
         elif cmd == 'forward' or cmd == 'stop':
-            if cmd == 'forward':
-                req_params = {'action': 'start'}
-            elif cmd == 'stop':
-                req_params = {'action': 'stop'}
-            else:
-                print('Unknown command. "forward" or "stop"?')
-
-            res = self.spp_ctl_cli.put('nfvs/%d/forward' %
-                                       self.sec_id, req_params)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    if cmd == 'forward':
-                        print('Start forwarding.')
-                    else:
-                        print('Stop forwarding.')
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
+            self._run_forward_or_stop(cmd)
 
         elif cmd == 'patch':
-            if params[0] == 'reset':
-                res = self.spp_ctl_cli.delete('nfvs/%d/patches' % self.sec_id)
-                if res is not None:
-                    error_codes = self.spp_ctl_cli.rest_common_error_codes
-                    if res.status_code == 204:
-                        print('Clear all of patches.')
-                    elif res.status_code in error_codes:
-                        pass
-                    else:
-                        print('Error: unknown response.')
-            else:
-                req_params = {'src': params[0], 'dst': params[1]}
-                res = self.spp_ctl_cli.put(
-                        'nfvs/%d/patches' % self.sec_id, req_params)
-                if res is not None:
-                    error_codes = self.spp_ctl_cli.rest_common_error_codes
-                    if res.status_code == 204:
-                        print('Patch ports (%s -> %s).' % (
-                            params[0], params[1]))
-                    elif res.status_code in error_codes:
-                        pass
-                    else:
-                        print('Error: unknown response.')
+            self._run_patch(params)
 
         elif cmd == 'exit':
-            res = self.spp_ctl_cli.delete('nfvs/%d' % self.sec_id)
-            if res is not None:
-                error_codes = self.spp_ctl_cli.rest_common_error_codes
-                if res.status_code == 204:
-                    print('Exit nfv %d' % self.sec_id)
-                elif res.status_code in error_codes:
-                    pass
-                else:
-                    print('Error: unknown response.')
+            self._run_exit()
 
         else:
             print('Invalid command "%s".' % cmd)
@@ -167,11 +70,6 @@ class SppNfv(object):
           - ports:
             - phy:0 -> ring:0
             - phy:1
-
-        The format of the received message is JSON and ended with
-        series of null character "\x00".
-
-          {"client-id":1,...,"patches":[{"src":"phy:0"...},...]}'\x00..
         """
 
         nfv_attr = json_obj
@@ -188,7 +86,9 @@ class SppNfv(object):
             else:
                 print('  - %s -> %s' % (port, dst))
 
-    def get_registered_ports(self):
+    def get_ports(self):
+        """Get all of ports as a list."""
+
         res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
         if res is not None:
             error_codes = self.spp_ctl_cli.rest_common_error_codes
@@ -199,7 +99,14 @@ class SppNfv(object):
             else:
                 print('Error: unknown response.')
 
-    def get_registered_patches(self):
+    def get_patches(self):
+        """Get all of patched ports as a list of dicts.
+
+        Returned value is like as
+          [{'src': 'phy:0', 'dst': 'ring:0'},
+           {'src': 'ring:1', 'dst':'vhost:1'}, ...]
+        """
+
         res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
         if res is not None:
             error_codes = self.spp_ctl_cli.rest_common_error_codes
@@ -210,6 +117,41 @@ class SppNfv(object):
             else:
                 print('Error: unknown response.')
 
+    def get_ports_and_patches(self):
+        """Get all of ports and patchs at once.
+
+        This method is to execute `get_ports()` and `get_patches()` at
+        once to reduce request to spp-ctl. Returned value is a set of
+        lists. You use this method as following.
+          ports, patches = get_ports_and_patches()
+        """
+
+        res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
+        if res is not None:
+            error_codes = self.spp_ctl_cli.rest_common_error_codes
+            if res.status_code == 200:
+                ports = res.json()['ports']
+                patches = res.json()['patches']
+                return ports, patches
+            elif res.status_code in error_codes:
+                pass
+            else:
+                print('Error: unknown response.')
+
+    def get_patched_ports(self):
+        """Get all of patched ports as a list.
+
+        This method is to get a list of patched ports instead of a dict.
+        You use this method if you simply get patches without `src` and
+        `dst`.
+        """
+
+        patched_ports = []
+        for pport in self.patches:
+            patched_ports.append(pport['src'])
+            patched_ports.append(pport['dst'])
+        return list(set(patched_ports))
+
     def complete(self, sec_ids, text, line, begidx, endidx):
         """Completion for spp_nfv commands.
 
@@ -248,6 +190,8 @@ class SppNfv(object):
             print(e)
 
     def _compl_first_tokens(self, token):
+        """Complete spp_nfv command."""
+
         res = []
         for kw in self.NFV_CMDS:
             if kw.startswith(token):
@@ -255,6 +199,8 @@ class SppNfv(object):
         return res
 
     def _compl_add(self, sub_tokens):
+        """Complete `add` command."""
+
         if len(sub_tokens) < 3:
             res = []
 
@@ -267,21 +213,18 @@ class SppNfv(object):
             return res
 
     def _compl_del(self, sub_tokens):
+        """Complete `del` command."""
+
         # Del command consists of two tokens max, for instance,
         # `nfv 1; del ring:1`.
         if len(sub_tokens) < 3:
             res = []
 
             if self.use_cache is False:
-                self.ports = self.get_registered_ports()
-                self.patches = self.get_registered_patches()
+                self.ports, self.patches = self.get_ports_and_patches()
 
             # Patched ports should not be included in the candidate of del.
-            patched_ports = []
-            for pport in self.patches:
-                patched_ports.append(pport['src'])
-                patched_ports.append(pport['dst'])
-            patched_ports = list(set(patched_ports))
+            patched_ports = self.get_patched_ports()
 
             # Remove ports already used from candidate.
             for kw in self.ports:
@@ -300,14 +243,15 @@ class SppNfv(object):
             return res
 
     def _compl_patch(self, sub_tokens):
+        """Complete `patch` command."""
+
         # Patch command consists of three tokens max, for instance,
         # `nfv 1; patch phy:0 ring:1`.
         if len(sub_tokens) < 4:
             res = []
 
             if self.use_cache is False:
-                self.ports = self.get_registered_ports()
-                self.patches = self.get_registered_patches()
+                self.ports, self.patches = self.get_ports_and_patches()
 
             # Get patched ports of src and dst to be used for completion.
             src_ports = []
@@ -344,3 +288,148 @@ class SppNfv(object):
                         res.append(kw)
 
             return res
+
+    def _run_status(self):
+        """Run `status` command."""
+
+        res = self.spp_ctl_cli.get('nfvs/%d' % self.sec_id)
+        if res is not None:
+            error_codes = self.spp_ctl_cli.rest_common_error_codes
+            if res.status_code == 200:
+                self.print_nfv_status(res.json())
+            elif res.status_code in error_codes:
+                pass
+            else:
+                print('Error: unknown response.')
+
+    def _run_add(self, params):
+        """Run `add` command."""
+
+        if len(params) == 0:
+            print('Port is required!')
+        elif params[0] in self.ports:
+            print("'%s' is already added." % params[0])
+        else:
+            if self.use_cache is False:
+                self.ports = self.get_ports()
+
+            req_params = {'action': 'add', 'port': params[0]}
+
+            res = self.spp_ctl_cli.put('nfvs/%d/ports' %
+                                       self.sec_id, req_params)
+            if res is not None:
+                error_codes = self.spp_ctl_cli.rest_common_error_codes
+                if res.status_code == 204:
+                    if self.use_cache is True:
+                        if not (params[0] in self.ports):
+                            self.ports.append(params[0])
+                    print('Add %s.' % params[0])
+                elif res.status_code in error_codes:
+                    pass
+                else:
+                    print('Error: unknown response.')
+
+    def _run_del(self, params):
+        """Run `del` command."""
+
+        if len(params) == 0:
+            print('Port is required!')
+        elif 'phy:' in params[0]:
+            print("Cannot delete phy port '%s'." % params[0])
+        else:
+            if self.use_cache is False:
+                self.patches = self.get_patches()
+
+            # Patched ports should not be deleted.
+            patched_ports = self.get_patched_ports()
+
+            if params[0] in patched_ports:
+                print("Cannot delete patched port '%s'." % params[0])
+            else:
+                req_params = {'action': 'del', 'port': params[0]}
+                res = self.spp_ctl_cli.put('nfvs/%d/ports' %
+                                           self.sec_id, req_params)
+                if res is not None:
+                    error_codes = self.spp_ctl_cli.rest_common_error_codes
+                    if res.status_code == 204:
+                        if self.use_cache is True:
+                            if params[0] in self.ports:
+                                self.ports.remove(params[0])
+                        print('Delete %s.' % params[0])
+                    elif res.status_code in error_codes:
+                        pass
+                    else:
+                        print('Error: unknown response.')
+
+    def _run_forward_or_stop(self, cmd):
+        """Run `forward` or `stop` command.
+
+        Spp-ctl accepts this two commands via single API, so this method
+        runs one of which commands by referring `cmd` param.
+        """
+
+        if cmd == 'forward':
+            req_params = {'action': 'start'}
+        elif cmd == 'stop':
+            req_params = {'action': 'stop'}
+        else:
+            print('Unknown command. "forward" or "stop"?')
+
+        res = self.spp_ctl_cli.put('nfvs/%d/forward' %
+                                   self.sec_id, req_params)
+        if res is not None:
+            error_codes = self.spp_ctl_cli.rest_common_error_codes
+            if res.status_code == 204:
+                if cmd == 'forward':
+                    print('Start forwarding.')
+                else:
+                    print('Stop forwarding.')
+            elif res.status_code in error_codes:
+                pass
+            else:
+                print('Error: unknown response.')
+
+    def _run_patch(self, params):
+        """Run `patch` command."""
+
+        if len(params) == 0:
+            print('Params are required!')
+        elif params[0] == 'reset':
+            res = self.spp_ctl_cli.delete('nfvs/%d/patches' % self.sec_id)
+            if res is not None:
+                error_codes = self.spp_ctl_cli.rest_common_error_codes
+                if res.status_code == 204:
+                    print('Clear all of patches.')
+                elif res.status_code in error_codes:
+                    pass
+                else:
+                    print('Error: unknown response.')
+        else:
+            if len(params) < 2:
+                print('Dst port is required!')
+            else:
+                req_params = {'src': params[0], 'dst': params[1]}
+                res = self.spp_ctl_cli.put(
+                        'nfvs/%d/patches' % self.sec_id, req_params)
+                if res is not None:
+                    error_codes = self.spp_ctl_cli.rest_common_error_codes
+                    if res.status_code == 204:
+                        print('Patch ports (%s -> %s).' % (
+                            params[0], params[1]))
+                    elif res.status_code in error_codes:
+                        pass
+                    else:
+                        print('Error: unknown response.')
+
+    def _run_exit(self):
+        """Run `exit` command."""
+
+        res = self.spp_ctl_cli.delete('nfvs/%d' % self.sec_id)
+        if res is not None:
+            error_codes = self.spp_ctl_cli.rest_common_error_codes
+            if res.status_code == 204:
+                print('Exit nfv %d' % self.sec_id)
+            elif res.status_code in error_codes:
+                pass
+            else:
+                print('Error: unknown response.')
-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [spp] [PATCH 5/5] controller: update SppBye class
  2018-12-12  2:03 [spp] [PATCH 0/5] update SPP CLI ogawa.yasufumi
                   ` (3 preceding siblings ...)
  2018-12-12  2:03 ` [spp] [PATCH 4/5] controller: refactor SppNfv class ogawa.yasufumi
@ 2018-12-12  2:03 ` ogawa.yasufumi
  4 siblings, 0 replies; 6+ messages in thread
From: ogawa.yasufumi @ 2018-12-12  2:03 UTC (permalink / raw)
  To: ferruh.yigit, spp, ogawa.yasufumi

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

This update is to support `bye` command other than spp_nfv. Although
spp_vf and spp_mirror do not support this command currently, SPP CLI
is updadted first. SPP CLI does not send `exit` command to spp_vf and
spp_mirror because it is not supported currently, so still need to be
upadated after implementation of the command.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
 src/controller/commands/bye.py | 27 ++++++------
 src/controller/shell.py        | 97 +++++++++++++++++++++++++-----------------
 2 files changed, 74 insertions(+), 50 deletions(-)

diff --git a/src/controller/commands/bye.py b/src/controller/commands/bye.py
index dfbd048..af77b08 100644
--- a/src/controller/commands/bye.py
+++ b/src/controller/commands/bye.py
@@ -3,7 +3,7 @@
 
 
 class SppBye(object):
-    """Exec SPP bye command.
+    """Run SPP bye command.
 
     SppBye class is intended to be used in Shell class as a delegator
     for running 'bye' command.
@@ -14,21 +14,21 @@ class SppBye(object):
 
     BYE_CMDS = ['sec', 'all']
 
-    def __init__(self, spp_ctl_cli, spp_primary, spp_nfvs):
-        self.spp_ctl_cli = spp_ctl_cli
-        self.spp_primary = spp_primary
-        self.spp_nfvs = spp_nfvs
+    def __init__(self):
+        pass
 
-    def run(self, args, sec_ids):
+    def run(self, args, spp_primary, spp_secondaries):
 
         cmds = args.split(' ')
         if cmds[0] == 'sec':
-            self.close_all_secondary(sec_ids)
+            print('Closing secondary ...')
+            self.close_all_secondary(spp_secondaries)
+
         elif cmds[0] == 'all':
             print('Closing secondary ...')
-            self.close_all_secondary(sec_ids)
+            self.close_all_secondary(spp_secondaries)
             print('Closing primary ...')
-            self.spp_primary.do_exit()
+            spp_primary.do_exit()
 
     def complete(self, text, line, begidx, endidx):
 
@@ -41,8 +41,11 @@ class SppBye(object):
                            ]
         return completions
 
-    def close_all_secondary(self, sec_ids):
+    def close_all_secondary(self, spp_secondaries):
         """Terminate all secondary processes."""
 
-        for i, nfv in self.spp_nfvs.items():
-            nfv.run(i, 'exit')
+        for sec_type, spp_procs in spp_secondaries.items():
+            # TODO(yasufum) Remove if they support exit command.
+            if not (sec_type in ['vf', 'mirror']):
+                for sec in spp_procs.values():
+                    sec.run('exit')
diff --git a/src/controller/shell.py b/src/controller/shell.py
index eafc5de..615b9a1 100644
--- a/src/controller/shell.py
+++ b/src/controller/shell.py
@@ -38,28 +38,39 @@ class Shell(cmd.Cmd, object):
     else:
         readline.write_history_file(hist_file)
 
-    def __init__(self, spp_ctl_cli):
+    def __init__(self, spp_ctl_cli, use_cache=False):
         cmd.Cmd.__init__(self)
         self.spp_ctl_cli = spp_ctl_cli
-        self.spp_primary = pri.SppPrimary(self.spp_ctl_cli)
+        self.use_cache = use_cache
+        self.init_spp_procs()
+        self.spp_topo = topo.SppTopo(self.spp_ctl_cli, {}, self.topo_size)
+
+    def init_spp_procs(self):
+        """Initialize delegators of SPP processes.
+
+        Delegators accept a command and sent it to SPP proesses. This method
+        is also called from `precmd()` method to update it to the latest
+        status.
+        """
 
-        self.spp_nfvs = {}
+        self.primary = pri.SppPrimary(self.spp_ctl_cli)
+
+        self.secondaries = {}
+        self.secondaries['nfv'] = {}
         for sec_id in self.get_sec_ids('nfv'):
-            self.spp_nfvs[sec_id] = nfv.SppNfv(self.spp_ctl_cli, sec_id)
+            self.secondaries['nfv'][sec_id] = nfv.SppNfv(
+                    self.spp_ctl_cli, sec_id)
 
-        self.spp_vfs = {}
+        self.secondaries['vf'] = {}
         for sec_id in self.get_sec_ids('vf'):
-            self.spp_vfs[sec_id] = vf.SppVf(self.spp_ctl_cli, sec_id)
+            self.secondaries['vf'][sec_id] = vf.SppVf(
+                    self.spp_ctl_cli, sec_id)
 
-        self.spp_mirrors = {}
+        self.secondaries['mirror'] = {}
         for sec_id in self.get_sec_ids('mirror'):
-            self.spp_mirrors[sec_id] = mirror.SppMirror(
+            self.secondaries['mirror'][sec_id] = mirror.SppMirror(
                     self.spp_ctl_cli, sec_id)
 
-        self.spp_topo = topo.SppTopo(self.spp_ctl_cli, {}, self.topo_size)
-        self.spp_bye = bye.SppBye(self.spp_ctl_cli, self.spp_primary,
-                                  self.spp_nfvs)
-
     def default(self, line):
         """Define defualt behaviour.
 
@@ -222,6 +233,9 @@ class Shell(cmd.Cmd, object):
         It is called for checking a contents of command line.
         """
 
+        if self.use_cache is False:
+            self.init_spp_procs()
+
         if self.recorded_file:
             if not (
                     ('playback' in line) or
@@ -261,12 +275,12 @@ class Shell(cmd.Cmd, object):
         if logger is not None:
             logger.info("Receive pri command: '%s'" % command)
 
-        self.spp_primary.run(command)
+        self.primary.run(command)
 
     def complete_pri(self, text, line, begidx, endidx):
         """Completion for primary process commands."""
 
-        return self.spp_primary.complete(text, line, begidx, endidx)
+        return self.primary.complete(text, line, begidx, endidx)
 
     def do_nfv(self, cmd):
         """Send a command to spp_nfv specified with ID.
@@ -290,7 +304,7 @@ class Shell(cmd.Cmd, object):
         if len(cmds) < 2:
             print("Required an ID and ';' before the command.")
         elif str.isdigit(cmds[0]):
-            self.spp_nfvs[int(cmds[0])].run(cmds[1])
+            self.secondaries['nfv'][int(cmds[0])].run(cmds[1])
         else:
             print('Invalid command: %s' % tmparg)
 
@@ -304,8 +318,9 @@ class Shell(cmd.Cmd, object):
             # Add SppNfv of sec_id if it is not exist
             sec_ids = self.get_sec_ids('nfv')
             for idx in sec_ids:
-                if self.spp_nfvs[idx] is None:
-                    self.spp_nfvs[idx] = nfv.SppNfv(self.spp_ctl_cli, idx)
+                if self.secondaries['nfv'][idx] is None:
+                    self.secondaries['nfv'][idx] = nfv.SppNfv(
+                            self.spp_ctl_cli, idx)
 
             if len(line.split()) == 1:
                 res = [str(i)+';' for i in sec_ids]
@@ -321,11 +336,13 @@ class Shell(cmd.Cmd, object):
                 idx = int(first_tokens[1])
 
                 # Add SppVf of sec_id if it is not exist
-                if self.spp_nfvs[idx] is None:
-                    self.spp_nfvs[idx] = nfv.SppNfv(self.spp_ctl_cli, idx)
+                if self.secondaries['nfv'][idx] is None:
+                    self.secondaries['nfv'][idx] = nfv.SppNfv(
+                            self.spp_ctl_cli, idx)
+
+                res = self.secondaries['nfv'][idx].complete(
+                        self.get_sec_ids('nfv'), text, line, begidx, endidx)
 
-                res = self.spp_nfvs[idx].complete(self.get_sec_ids('nfv'),
-                                                  text, line, begidx, endidx)
                 # logger.info(res)
                 return res
 
@@ -386,7 +403,7 @@ class Shell(cmd.Cmd, object):
         if len(cmds) < 2:
             print("Required an ID and ';' before the command.")
         elif str.isdigit(cmds[0]):
-            self.spp_vfs[int(cmds[0])].run(cmds[1])
+            self.secondaries['vf'][int(cmds[0])].run(cmds[1])
         else:
             print('Invalid command: %s' % tmparg)
 
@@ -400,8 +417,9 @@ class Shell(cmd.Cmd, object):
             # Add SppVf of sec_id if it is not exist
             sec_ids = self.get_sec_ids('vf')
             for idx in sec_ids:
-                if self.spp_vfs[idx] is None:
-                    self.spp_vfs[idx] = vf.SppVf(self.spp_ctl_cli, idx)
+                if self.secondaries['vf'][idx] is None:
+                    self.secondaries['vf'][idx] = vf.SppVf(
+                            self.spp_ctl_cli, idx)
 
             if len(line.split()) == 1:
                 res = [str(i)+';' for i in sec_ids]
@@ -417,11 +435,12 @@ class Shell(cmd.Cmd, object):
                 idx = int(first_tokens[1])
 
                 # Add SppVf of sec_id if it is not exist
-                if self.spp_vfs[idx] is None:
-                    self.spp_vfs[idx] = vf.SppVf(self.spp_ctl_cli, idx)
+                if self.secondaries['vf'][idx] is None:
+                    self.secondaries['vf'][idx] = vf.SppVf(
+                            self.spp_ctl_cli, idx)
 
-                return self.spp_vfs[idx].complete(self.get_sec_ids('vf'),
-                                                  text, line, begidx, endidx)
+                return self.secondaries['vf'][idx].complete(
+                        self.get_sec_ids('vf'), text, line, begidx, endidx)
 
     def do_mirror(self, cmd):
         """Send a command to spp_mirror.
@@ -462,7 +481,7 @@ class Shell(cmd.Cmd, object):
         if len(cmds) < 2:
             print("Required an ID and ';' before the command.")
         elif str.isdigit(cmds[0]):
-            self.spp_mirrors[int(cmds[0])].run(cmds[1])
+            self.secondaries['mirror'][int(cmds[0])].run(cmds[1])
         else:
             print('Invalid command: %s' % tmparg)
 
@@ -476,8 +495,8 @@ class Shell(cmd.Cmd, object):
             # Add SppMirror of sec_id if it is not exist
             sec_ids = self.get_sec_ids('mirror')
             for idx in sec_ids:
-                if self.spp_mirrors[idx] is None:
-                    self.spp_mirrors[idx] = mirror.SppMirror(
+                if self.secondaries['mirror'][idx] is None:
+                    self.secondaries['mirror'][idx] = mirror.SppMirror(
                             self.spp_ctl_cli, idx)
 
             if len(line.split()) == 1:
@@ -495,11 +514,11 @@ class Shell(cmd.Cmd, object):
                 idx = int(first_tokens[1])
 
                 # Add SppMirror of sec_id if it is not exist
-                if self.spp_mirrors[idx] is None:
-                    self.spp_mirrors[idx] = mirror.SppMirror(
+                if self.secondaries['mirror'][idx] is None:
+                    self.secondaries['mirror'][idx] = mirror.SppMirror(
                             self.spp_ctl_cli, idx)
 
-                return self.spp_mirrors[idx].complete(
+                return self.secondaries['mirror'][idx].complete(
                         self.get_sec_ids('mirror'), text, line, begidx, endidx)
 
     def do_record(self, fname):
@@ -622,16 +641,18 @@ class Shell(cmd.Cmd, object):
         """
 
         cmds = args.split(' ')
-        if cmds[0] == '':
+        if cmds[0] == '':  # terminate SPP CLI itself
             self.do_exit('')
             return True
-        else:  # 'all' or 'sec'
-            self.spp_bye.run(args, self.get_sec_ids('nfv'))
+        else:  # terminate other SPP processes
+            spp_bye = bye.SppBye()
+            spp_bye.run(args, self.primary, self.secondaries)
 
     def complete_bye(self, text, line, begidx, endidx):
         """Completion for bye commands"""
 
-        return self.spp_bye.complete(text, line, begidx, endidx)
+        spp_bye = bye.SppBye()
+        return spp_bye.complete(text, line, begidx, endidx)
 
     def do_cat(self, arg):
         """View contents of a file.
-- 
2.7.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-12-12  2:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-12  2:03 [spp] [PATCH 0/5] update SPP CLI ogawa.yasufumi
2018-12-12  2:03 ` [spp] [PATCH 1/5] controller: change sec command to nfv ogawa.yasufumi
2018-12-12  2:03 ` [spp] [PATCH 2/5] controller: change to exclude used port from del ogawa.yasufumi
2018-12-12  2:03 ` [spp] [PATCH 3/5] controller: add checking for add and del cmds ogawa.yasufumi
2018-12-12  2:03 ` [spp] [PATCH 4/5] controller: refactor SppNfv class ogawa.yasufumi
2018-12-12  2:03 ` [spp] [PATCH 5/5] controller: update SppBye class ogawa.yasufumi

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).