Soft Patch Panel
 help / color / mirror / Atom feed
* [spp] [PATCH] controller: add multi-node support
@ 2018-12-17  3:32 ogawa.yasufumi
  0 siblings, 0 replies; only message in thread
From: ogawa.yasufumi @ 2018-12-17  3:32 UTC (permalink / raw)
  To: ferruh.yigit, spp, ogawa.yasufumi

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

This update is to manage multiple nodes of SPP from SPP CLI.

To add several nodes, give several address of spp-ctl with `-b` option.
Default address is `127.0.0.1:7777` and it is able to be overwritten.
If port number is not given, default value `7777` is remained.

  # Use two spp-ctl running on "192.168.1.101:7777" and
  # "192.168.1.102:7777".
  $ python src/spp.py -b 192.168.1.101 -b 192.168.1.102

`server` command is provided for switching spp-ctl under the management
of SPP CLI.

  # Show all of spp-ctl nodes.
  spp > server  # or 'server list'
    1: 192.168.1.101:7777 *
    2: 192.168.1.102:7777

  # Switch to second node.
  spp > server 2
  Switch spp-ctl to "2: 192.168.1.102:7777".

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi@lab.ntt.co.jp>
---
 src/controller/commands/pri.py    |  2 +-
 src/controller/commands/server.py | 79 +++++++++++++++++++++++++++++++++++++++
 src/controller/shell.py           | 29 +++++++++++++-
 src/controller/spp.py             | 42 ++++++++++++++++-----
 src/controller/spp_ctl_client.py  |  5 ++-
 5 files changed, 142 insertions(+), 15 deletions(-)
 create mode 100644 src/controller/commands/server.py

diff --git a/src/controller/commands/pri.py b/src/controller/commands/pri.py
index 51da7ab..750d355 100644
--- a/src/controller/commands/pri.py
+++ b/src/controller/commands/pri.py
@@ -8,7 +8,7 @@ class SppPrimary(object):
     SppPrimary class is intended to be used in Shell class as a delegator
     for running 'pri' command.
 
-    'self.command()' is called from do_pri() and 'self.complete()' is called
+    'self.run()' is called from do_pri() and 'self.complete()' is called
     from complete_pri() of both of which is defined in Shell.
     """
 
diff --git a/src/controller/commands/server.py b/src/controller/commands/server.py
new file mode 100644
index 0000000..ca362ab
--- /dev/null
+++ b/src/controller/commands/server.py
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017-2018 Nippon Telegraph and Telephone Corporation
+
+import re
+from ..spp_common import logger
+
+
+class SppCtlServer(object):
+    """Execute server command for switching spp-ctl.
+
+    SppCtlServer class is intended to be used in Shell class as a delegator
+    for running 'server' command.
+
+    'self.run()' is called from do_pri() and 'self.complete()' is called
+    from complete_pri() of both of which is defined in Shell.
+    """
+
+    SERVER_CMDS = ['list']
+
+    def __init__(self, spp_cli_objs):
+        self.spp_cli_objs = spp_cli_objs
+        self.current_idx = 0
+
+    def run(self, commands):
+        args = re.sub(r'\s+', ' ', commands).split(' ')
+        if '' in args:
+            args.remove('')
+
+        if len(args) == 0 or args[0] == 'list':
+            self._show_list()
+        else:
+            idx = int(args[0]) - 1
+            self._switch_to(idx)
+
+    def complete(self, text, line, begidx, endidx):
+        """Completion for server command.
+
+        Called from complete_server() to complete server command.
+        """
+
+        candidates = []
+        for i in range(len(self.spp_cli_objs)):
+            candidates.append(str(i))
+        candidates = candidates + self.SERVER_CMDS[:]
+
+        if not text:
+            completions = candidates
+        else:
+            completions = [p for p in candidates
+                           if p.startswith(text)
+                           ]
+
+        return completions
+
+    def get_current_server(self):
+        return self.spp_cli_objs[self.current_idx]
+
+    def _show_list(self):
+        cnt = 1
+        for cli_obj in self.spp_cli_objs:
+            # Put a mark to current server.
+            if cnt == self.current_idx + 1:
+                current = '*'
+            else:
+                current = ''
+
+            print('  %d: %s:%s %s' % (
+                  cnt, cli_obj.ip_addr, cli_obj.port, current))
+            cnt += 1
+
+    def _switch_to(self, idx):
+        if len(self.spp_cli_objs) > idx:
+            self.current_idx = idx
+            cli_obj = self.spp_cli_objs[self.current_idx]
+            print('Switch spp-ctl to "%d: %s:%d"' %
+                    (idx+1, cli_obj.ip_addr, cli_obj.port))
+        else:
+            print('Index should be less than %d!' %
+                    (len(self.spp_cli_objs) + 1))
diff --git a/src/controller/shell.py b/src/controller/shell.py
index 615b9a1..6ce4dca 100644
--- a/src/controller/shell.py
+++ b/src/controller/shell.py
@@ -7,6 +7,7 @@ import cmd
 from .commands import bye
 from .commands import pri
 from .commands import nfv
+from .commands import server
 from .commands import topo
 from .commands import vf
 from .commands import mirror
@@ -38,9 +39,10 @@ class Shell(cmd.Cmd, object):
     else:
         readline.write_history_file(hist_file)
 
-    def __init__(self, spp_ctl_cli, use_cache=False):
+    def __init__(self, spp_cli_objs, use_cache=False):
         cmd.Cmd.__init__(self)
-        self.spp_ctl_cli = spp_ctl_cli
+        self.spp_ctl_server = server.SppCtlServer(spp_cli_objs)
+        self.spp_ctl_cli = spp_cli_objs[0]
         self.use_cache = use_cache
         self.init_spp_procs()
         self.spp_topo = topo.SppTopo(self.spp_ctl_cli, {}, self.topo_size)
@@ -128,6 +130,9 @@ class Shell(cmd.Cmd, object):
     def print_status(self):
         """Display information about connected clients."""
 
+        print('- spp-ctl:')
+        print('  - address: %s:%s' % (self.spp_ctl_cli.ip_addr,
+                                      self.spp_ctl_cli.port))
         res = self.spp_ctl_cli.get('processes')
         if res is not None:
             if res.status_code == 200:
@@ -252,6 +257,26 @@ class Shell(cmd.Cmd, object):
             self.recorded_file.close()
             self.recorded_file = None
 
+    def do_server(self, commands):
+        """Switch SPP REST API server.
+
+        spp > server  # or 'server list'
+          1: 192.168.1.101:7777 *
+          2: 192.168.1.102:7777
+
+        spp > server 2
+        Switch spp-ctl to "2: 192.168.1.102:7777".
+        """
+
+        self.spp_ctl_server.run(commands)
+        self.spp_ctl_cli = self.spp_ctl_server.get_current_server()
+
+    def complete_server(self, text, line, begidx, endidx):
+        """Completion for server command."""
+
+        res = self.spp_ctl_server.complete(text, line, begidx, endidx)
+        return res
+
     def do_status(self, _):
         """Display status info of SPP processes
 
diff --git a/src/controller/spp.py b/src/controller/spp.py
index 2e1c173..7b7316c 100644
--- a/src/controller/spp.py
+++ b/src/controller/spp.py
@@ -5,6 +5,7 @@
 from __future__ import absolute_import
 
 import argparse
+import re
 from .shell import Shell
 from . import spp_ctl_client
 import sys
@@ -12,19 +13,40 @@ import sys
 
 def main(argv):
 
+    # Default
+    api_ipaddr = '127.0.0.1'
+    api_port = 7777
+
     parser = argparse.ArgumentParser(description="SPP Controller")
-    parser.add_argument('-b', '--bind-addr', type=str, default='127.0.0.1',
-                        help='bind address, default=127.0.0.1')
-    parser.add_argument('-a', '--api-port', type=int, default=7777,
-                        help='bind address, default=7777')
+    parser.add_argument('-b', '--bind-addr', action='append',
+                        default=['%s:%s' % (api_ipaddr, api_port)],
+                        help='bind address, default=127.0.0.1:7777')
     args = parser.parse_args()
 
-    spp_ctl_cli = spp_ctl_client.SppCtlClient(args.bind_addr,
-                                              args.api_port)
-    if spp_ctl_cli.is_server_running() is False:
-        print('Is not spp-ctl running, nor correct IP address?')
-        exit()
-    shell = Shell(spp_ctl_cli)
+    if len(args.bind_addr) > 1:
+        args.bind_addr.pop(0)
+
+    spp_cli_objs = []
+    for addr in args.bind_addr:
+        if ':' in addr:
+            api_ipaddr, api_port = addr.split(':')
+        else:
+            api_ipaddr = addr
+
+        if not re.match(r'\d*\.\d*\.\d*\.\d*', addr):
+            print('Invalid address "%s"' % args.bind_addr)
+            exit()
+
+        spp_ctl_cli = spp_ctl_client.SppCtlClient(api_ipaddr, int(api_port))
+
+        if spp_ctl_cli.is_server_running() is False:
+            print('Is not spp-ctl running on %s, nor correct IP address?' %
+                  api_ipaddr)
+            exit()
+
+        spp_cli_objs.append(spp_ctl_cli)
+
+    shell = Shell(spp_cli_objs)
     shell.cmdloop()
     shell = None
 
diff --git a/src/controller/spp_ctl_client.py b/src/controller/spp_ctl_client.py
index 3713326..f04173b 100644
--- a/src/controller/spp_ctl_client.py
+++ b/src/controller/spp_ctl_client.py
@@ -9,9 +9,10 @@ class SppCtlClient(object):
 
     rest_common_error_codes = [400, 404, 500]
 
-    def __init__(self, ip_addr='localhost', port=7777):
-        api_ver = 'v1'
+    def __init__(self, ip_addr='localhost', port=7777, api_ver='v1'):
         self.base_url = 'http://%s:%d/%s' % (ip_addr, port, api_ver)
+        self.ip_addr = ip_addr
+        self.port = port
 
     def request_handler(func):
         """Request handler for spp-ctl.
-- 
2.7.4

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2018-12-17  3:34 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-17  3:32 [spp] [PATCH] controller: add multi-node support 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).