From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from bcmv-tmail01.ecl.ntt.co.jp (bcmv-tmail01.ecl.ntt.co.jp [124.146.185.148]) by dpdk.org (Postfix) with ESMTP id D9A0058C4 for ; Wed, 12 Dec 2018 03:05:31 +0100 (CET) Received: from bcmv-ns01.ecl.ntt.co.jp (bcmv-ns01.ecl.ntt.co.jp [129.60.83.123]) by bcmv-tmail01.ecl.ntt.co.jp (8.14.4/8.14.4) with ESMTP id wBC25TKt014138; Wed, 12 Dec 2018 11:05:29 +0900 Received: from bcmv-ns01.ecl.ntt.co.jp (localhost [127.0.0.1]) by bcmv-ns01.ecl.ntt.co.jp (Postfix) with ESMTP id 7A04515E; Wed, 12 Dec 2018 11:05:29 +0900 (JST) Received: from localhost.localdomain (lobster.nslab.ecl.ntt.co.jp [129.60.13.95]) by bcmv-ns01.ecl.ntt.co.jp (Postfix) with ESMTP id 56D1410B; Wed, 12 Dec 2018 11:05:29 +0900 (JST) From: ogawa.yasufumi@lab.ntt.co.jp To: ferruh.yigit@intel.com, spp@dpdk.org, ogawa.yasufumi@lab.ntt.co.jp Date: Wed, 12 Dec 2018 11:03:11 +0900 Message-Id: <1544580195-9242-2-git-send-email-ogawa.yasufumi@lab.ntt.co.jp> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1544580195-9242-1-git-send-email-ogawa.yasufumi@lab.ntt.co.jp> References: <1544580195-9242-1-git-send-email-ogawa.yasufumi@lab.ntt.co.jp> X-TM-AS-MML: disable Subject: [spp] [PATCH 1/5] controller: change sec command to nfv X-BeenThere: spp@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Soft Patch Panel List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 12 Dec 2018 02:05:32 -0000 From: Yasufumi Ogawa 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 --- 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