From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 4B1A5A0471 for ; Mon, 12 Aug 2019 09:12:55 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 43D794C80; Mon, 12 Aug 2019 09:12:55 +0200 (CEST) Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.171]) by dpdk.org (Postfix) with ESMTP id 096554C80 for ; Mon, 12 Aug 2019 09:12:54 +0200 (CEST) Received: by mail-pl1-f171.google.com with SMTP id z3so698913pln.6 for ; Mon, 12 Aug 2019 00:12:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id:in-reply-to:references; bh=IH7urQYeD1JqLdfrwdw43I5WX4sewvIG73LHl1Z10VE=; b=BdjEotj1SMFGyrPYcqXrvSbraCIGQl1iiuf5lEdQu4E4C0C30PLyFzqx3dKXSURywA eQ9BWAEt4SMZPdeDSw4KY11rnpjHgxEzVXghrsB+U+ib37EKpt9DXx10MHZlE1A9m3fS A/b1UblhtBpiVLfi/IDuoSnZYvpEZ8lajrh0iMeFQ9QlOizvia3VjYrrGFCDsWavqujp dQv/dZJLyB9qkpi725r9IAy7AUtlIBv98+cz/vULGC8ID+7ZdPkm3f8DxRFlTeusJZ4+ a8aAmvsb0j/94aTexfLmHGtPJ2R4npVZQ0VbbMADv+5dKsIrqdBUfjNVuqZaiS9hMkRu QWPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references; bh=IH7urQYeD1JqLdfrwdw43I5WX4sewvIG73LHl1Z10VE=; b=flIgwifjfG12lWpnghhYkWQ9vHTshROYSMt80sIE+KXc01yjc7Laq8aaHAEtxsNAux PUvuK3Z2RWSGghepBOr8wdPp8Vj1Co/lqkMVBNGyzDvYvnOhSKINwUidB3J/cn8F2P8F KA7z3rMZxvpFtatp5xkXOUOtMGz+seQdguKNZVDZY4dE/rnuYN41I098A5vDXCggobx6 X0bLaEKINVnNKQmspjCRgRMVObyJuquDmUmODhFi6P0EilyQvEO/yzHur2724uYE2+Km a8xhS72bSZg/RcH3LhFh8NPHEjFR0zmlZI5aoPHi8nQepAW5rPXyPMCpVAs8AywZYEwe 2Xog== X-Gm-Message-State: APjAAAUSld6F56ERtF8XhqON2l4WKtKYWbwozI3bEBEKxjjKUJszbfYc 6I/AO7iHI6iBJW4OKw38fvfI+WEW X-Google-Smtp-Source: APXvYqzutNC/RbmfEQnNZd/QwqfGcrIviDy+X4tXbjdHjJbj5+SjxhJJuYxXKN1HUSfKXgEJSWkvDQ== X-Received: by 2002:a17:902:524:: with SMTP id 33mr19263040plf.27.1565593973024; Mon, 12 Aug 2019 00:12:53 -0700 (PDT) Received: from localhost.localdomain ([2400:4050:c8c2:de00:8000:cb51:dfcb:76c]) by smtp.gmail.com with ESMTPSA id h9sm94675326pgh.51.2019.08.12.00.12.51 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Mon, 12 Aug 2019 00:12:52 -0700 (PDT) From: Yasufumi Ogawa To: spp@dpdk.org, ferruh.yigit@intel.com, yasufum.o@gmail.com Date: Mon, 12 Aug 2019 16:12:38 +0900 Message-Id: <20190812071242.18934-5-yasufum.o@gmail.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20190812071242.18934-1-yasufum.o@gmail.com> References: <20190812071242.18934-1-yasufum.o@gmail.com> Subject: [spp] [PATCH 4/8] cli: add to get sec ID and procs to SppCtlClient 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: , Errors-To: spp-bounces@dpdk.org Sender: "spp" Shell class has get_sec_ids() for retrieving secondary IDs of given type by requesting all processes information to spp-ctl. However, it is better to request the information from SppCtlClient which a delegator for the requesting. This update is to remove get_sec_ids() from Shell and add the same method to SppCtlClient. In addition to above update, add get_sec_procs() to SppCtlClient to be able to retrieve other than spp_nfv in topo. By this change, all types of secondary process can be referred from anywhere in SppTopo so that there is no need to pass `sec_list` as an argument of member methods. Supporting other than spp_nfv in topo command is added in the next patches. Signed-off-by: Yasufumi Ogawa --- src/cli/commands/topo.py | 111 ++++++++++++++------------------------ src/cli/shell.py | 48 ++++++----------- src/cli/spp_ctl_client.py | 42 +++++++++++++++ 3 files changed, 99 insertions(+), 102 deletions(-) diff --git a/src/cli/commands/topo.py b/src/cli/commands/topo.py index 86495c8..96284c7 100644 --- a/src/cli/commands/topo.py +++ b/src/cli/commands/topo.py @@ -28,7 +28,8 @@ class SppTopo(object): self.graph_size = None # Graphviz params - topo_file = '{dir}/../config/topo.yml'.format(dir=os.path.dirname(__file__)) + topo_file = '{dir}/../config/topo.yml'.format( + dir=os.path.dirname(__file__)) topo_conf = yaml.load(open(topo_file), Loader=yaml.FullLoader) self.SEC_COLORS = topo_conf['topo_sec_colors']['val'] self.PORT_COLORS = topo_conf['topo_port_colors']['val'] @@ -40,18 +41,20 @@ class SppTopo(object): print('Config "topo_size" is invalid value.') exit() - def run(self, args, sec_ids): + def run(self, args): args_ary = args.split() if len(args_ary) == 0: print("Usage: topo dst [ftype]") return False - elif (args_ary[0] == "term") or (args_ary[0] == "http"): - self.show(args_ary[0], sec_ids, self.graph_size) - elif len(args_ary) == 1: + elif args_ary[0] == "term": + self.to_term(self.graph_size) + elif args_ary[0] == "http": + self.to_http() + elif len(args_ary) == 1: # find ftype from filename ftype = args_ary[0].split(".")[-1] - self.output(args_ary[0], sec_ids, ftype) - elif len(args_ary) == 2: - self.output(args_ary[0], sec_ids, args_ary[1]) + self.to_file(args_ary[0], ftype) + elif len(args_ary) == 2: # ftype is given as args_ary[1] + self.to_file(args_ary[0], args_ary[1]) else: print("Usage: topo dst [ftype]") @@ -66,12 +69,12 @@ class SppTopo(object): matched = re.match(r'(\d+)%$', size) if matched: # percentage i = int(matched.group(1)) - if i > 0 and i <= 100: + if i > 0 and i <= 100: self.graph_size = size return True else: return False - elif re.match(r'0\.\d+$',size): # ratio + elif re.match(r'0\.\d+$', size): # ratio i = float(size) * 100 self.graph_size = str(i) + '%' return True @@ -81,58 +84,22 @@ class SppTopo(object): else: return False - def show(self, dtype, sec_ids, size): - res_ary = [] - error_codes = self.spp_ctl_cli.rest_common_error_codes - - for sec_id in sec_ids: - res = self.spp_ctl_cli.get('nfvs/{sid}'.format(sid=sec_id)) - if res.status_code == 200: - res_ary.append(res.json()) - elif res.status_code in error_codes: - # Print default error message - pass - else: - # Ignore unknown response because no problem for drawing graph - pass - - if dtype == "http": - self.to_http(res_ary) - elif dtype == "term": - self.to_term(res_ary, size) - else: - print("Invalid file type") - - def output(self, fname, sec_ids, ftype="dot"): - res_ary = [] - error_codes = self.spp_ctl_cli.rest_common_error_codes - - for sec_id in sec_ids: - res = self.spp_ctl_cli.get('nfvs/{sid}'.format(sid=sec_id)) - if res.status_code == 200: - res_ary.append(res.json()) - elif res.status_code in error_codes: - # Print default error message - pass - else: - # Ignore unknown response because no problem for drawing graph - pass - + def to_file(self, fname, ftype="dot"): if ftype == "dot": - self.to_dot(res_ary, fname) + self.to_dot(fname) elif ftype == "json" or ftype == "js": - self.to_json(res_ary, fname) + self.to_json(fname) elif ftype == "yaml" or ftype == "yml": - self.to_yaml(res_ary, fname) + self.to_yaml(fname) elif ftype == "jpg" or ftype == "png" or ftype == "bmp": - self.to_img(res_ary, fname) + self.to_img(fname) else: print("Invalid file type") - return res_ary + return False print("Create topology: '{fname}'".format(fname=fname)) - return res_ary + return True - def to_dot(self, sec_list, output_fname): + def to_dot(self, output_fname): """Output dot script.""" node_attrs = 'node[shape="rectangle", style="filled"];' @@ -145,7 +112,7 @@ class SppTopo(object): links = [] # parse status message from sec. - for sec in sec_list: + for sec in self.spp_ctl_cli.get_sec_procs('nfv'): if sec is None: continue for port in sec['ports']: @@ -186,7 +153,7 @@ class SppTopo(object): dst_type, dst_id = patch['dst'].split(':') tmp = link_style.format(src_type, src_id, self.LINK_TYPE, - dst_type, dst_id, attrs) + dst_type, dst_id, attrs) links.append(tmp) output = ["{} spp{{".format(self.GRAPH_TYPE)] @@ -202,8 +169,8 @@ class SppTopo(object): for node in phy_nodes: label = re.sub(r'{}'.format(self.delim_node), ':', node) output.append( - '{n}[label="{l}", fillcolor="{c}"];'.format( - n=node, l=label, c=self.PORT_COLORS["phy"])) + '{nd}[label="{lbl}", fillcolor="{col}"];'.format( + nd=node, lbl=label, col=self.PORT_COLORS["phy"])) ring_nodes = [] for node in rings: @@ -213,8 +180,8 @@ class SppTopo(object): for node in ring_nodes: label = re.sub(r'{}'.format(self.delim_node), ':', node) output.append( - '{n}[label="{l}", fillcolor="{c}"];'.format( - n=node, l=label, c=self.PORT_COLORS["ring"])) + '{nd}[label="{lbl}", fillcolor="{col}"];'.format( + nd=node, lbl=label, col=self.PORT_COLORS["ring"])) vhost_nodes = [] for node in vhosts: @@ -224,8 +191,8 @@ class SppTopo(object): for node in vhost_nodes: label = re.sub(r'{}'.format(self.delim_node), ':', node) output.append( - '{n}[label="{l}", fillcolor="{c}"];'.format( - n=node, l=label, c=self.PORT_COLORS["vhost"])) + '{nd}[label="{lbl}", fillcolor="{col}"];'.format( + nd=node, lbl=label, col=self.PORT_COLORS["vhost"])) # Align the same type of nodes with rank attribute output.append( @@ -283,31 +250,33 @@ class SppTopo(object): f.write("\n".join(output)) f.close() - def to_json(self, sec_list, output_fname): + def to_json(self, output_fname): import json f = open(output_fname, "w+") + sec_list = self.spp_ctl_cli.get_sec_procs('nfv') f.write(json.dumps(sec_list)) f.close() - def to_yaml(self, sec_list, output_fname): + def to_yaml(self, output_fname): import yaml f = open(output_fname, "w+") + sec_list = self.spp_ctl_cli.get_sec_procs('nfv') f.write(yaml.dump(sec_list)) f.close() - def to_img(self, sec_list, output_fname): + def to_img(self, output_fname): tmpfile = "{fn}.dot".format(fn=uuid.uuid4().hex) - self.to_dot(sec_list, tmpfile) + self.to_dot(tmpfile) fmt = output_fname.split(".")[-1] cmd = "dot -T{fmt} {dotf} -o {of}".format( fmt=fmt, dotf=tmpfile, of=output_fname) subprocess.call(cmd, shell=True) subprocess.call("rm -f {tmpf}".format(tmpf=tmpfile), shell=True) - def to_http(self, sec_list): + def to_http(self): import websocket tmpfile = "{fn}.dot".format(fn=uuid.uuid4().hex) - self.to_dot(sec_list, tmpfile) + self.to_dot(tmpfile) msg = open(tmpfile).read() subprocess.call("rm -f {tmpf}".format(tmpf=tmpfile), shell=True) # TODO(yasufum) change to be able to use other than `localhost`. @@ -319,9 +288,9 @@ class SppTopo(object): except socket.error: print('Error: Connection refused! Is running websocket server?') - def to_term(self, sec_list, size): + def to_term(self, size): tmpfile = "{fn}.jpg".format(fn=uuid.uuid4().hex) - self.to_img(sec_list, tmpfile) + self.to_img(tmpfile) from distutils import spawn # TODO(yasufum) add check for using only supported terminal @@ -342,7 +311,7 @@ class SppTopo(object): size=img_size, fn1=tmpfile, fn2=tmpfile) subprocess.call(cmd, shell=True) subprocess.call("{cmd} {fn}".format(cmd=img_cmd, fn=tmpfile), - shell=True) + shell=True) subprocess.call(["rm", "-f", tmpfile]) else: print("img2sixel (or imgcat.sh for MacOS) not found!") diff --git a/src/cli/shell.py b/src/cli/shell.py index 8eae982..ebea911 100644 --- a/src/cli/shell.py +++ b/src/cli/shell.py @@ -90,22 +90,22 @@ class Shell(cmd.Cmd, object): self.secondaries = {} self.secondaries['nfv'] = {} - for sec_id in self.get_sec_ids('nfv'): + for sec_id in self.spp_ctl_cli.get_sec_ids('nfv'): self.secondaries['nfv'][sec_id] = nfv.SppNfv( self.spp_ctl_cli, sec_id) self.secondaries['vf'] = {} - for sec_id in self.get_sec_ids('vf'): + for sec_id in self.spp_ctl_cli.get_sec_ids('vf'): self.secondaries['vf'][sec_id] = vf.SppVf( self.spp_ctl_cli, sec_id) self.secondaries['mirror'] = {} - for sec_id in self.get_sec_ids('mirror'): + for sec_id in self.spp_ctl_cli.get_sec_ids('mirror'): self.secondaries['mirror'][sec_id] = mirror.SppMirror( self.spp_ctl_cli, sec_id) self.secondaries['pcap'] = {} - for sec_id in self.get_sec_ids('pcap'): + for sec_id in self.spp_ctl_cli.get_sec_ids('pcap'): self.secondaries['pcap'][sec_id] = pcap.SppPcap( self.spp_ctl_cli, sec_id) @@ -142,24 +142,6 @@ class Shell(cmd.Cmd, object): """ pass - def get_sec_ids(self, ptype): - """Return a list of IDs of running secondary processes. - - 'ptype' is 'nfv', 'vf' or 'mirror' or 'pcap'. - """ - - ids = [] - res = self.spp_ctl_cli.get('processes') - if res is not None: - if res.status_code == 200: - try: - for ent in res.json(): - if ent['type'] == ptype: - ids.append(ent['client-id']) - except KeyError as e: - print('Error: {} is not defined!'.format(e)) - return ids - def print_status(self): """Display information about connected clients.""" @@ -347,7 +329,7 @@ class Shell(cmd.Cmd, object): tokens = line.split(';') if len(tokens) == 1: # Add SppNfv of sec_id if it is not exist - sec_ids = self.get_sec_ids('nfv') + sec_ids = self.spp_ctl_cli.get_sec_ids('nfv') for idx in sec_ids: if self.secondaries['nfv'][idx] is None: self.secondaries['nfv'][idx] = nfv.SppNfv( @@ -372,7 +354,8 @@ class Shell(cmd.Cmd, object): self.spp_ctl_cli, idx) res = self.secondaries['nfv'][idx].complete( - self.get_sec_ids('nfv'), text, line, begidx, endidx) + self.spp_ctl_cli.get_sec_ids('nfv'), + text, line, begidx, endidx) # logger.info(res) return res @@ -407,7 +390,7 @@ class Shell(cmd.Cmd, object): tokens = line.split(';') if len(tokens) == 1: # Add SppVf of sec_id if it is not exist - sec_ids = self.get_sec_ids('vf') + sec_ids = self.spp_ctl_cli.get_sec_ids('vf') for idx in sec_ids: if self.secondaries['vf'][idx] is None: self.secondaries['vf'][idx] = vf.SppVf( @@ -432,7 +415,8 @@ class Shell(cmd.Cmd, object): self.spp_ctl_cli, idx) return self.secondaries['vf'][idx].complete( - self.get_sec_ids('vf'), text, line, begidx, endidx) + self.spp_ctl_cli.get_sec_ids('vf'), + text, line, begidx, endidx) def do_mirror(self, cmd): """Send a command to spp_mirror.""" @@ -463,7 +447,7 @@ class Shell(cmd.Cmd, object): tokens = line.split(';') if len(tokens) == 1: # Add SppMirror of sec_id if it is not exist - sec_ids = self.get_sec_ids('mirror') + sec_ids = self.spp_ctl_cli.get_sec_ids('mirror') for idx in sec_ids: if self.secondaries['mirror'][idx] is None: self.secondaries['mirror'][idx] = mirror.SppMirror( @@ -489,7 +473,8 @@ class Shell(cmd.Cmd, object): self.spp_ctl_cli, idx) return self.secondaries['mirror'][idx].complete( - self.get_sec_ids('mirror'), text, line, begidx, endidx) + self.spp_ctl_cli.get_sec_ids('mirror'), + text, line, begidx, endidx) def do_pcap(self, cmd): """Send a command to spp_pcap.""" @@ -520,7 +505,7 @@ class Shell(cmd.Cmd, object): tokens = line.split(';') if len(tokens) == 1: # Add SppPcap of sec_id if it is not exist - sec_ids = self.get_sec_ids('pcap') + sec_ids = self.spp_ctl_cli.get_sec_ids('pcap') for idx in sec_ids: if self.secondaries['pcap'][idx] is None: self.secondaries['pcap'][idx] = pcap.SppPcap( @@ -546,7 +531,8 @@ class Shell(cmd.Cmd, object): self.spp_ctl_cli, idx) return self.secondaries['pcap'][idx].complete( - self.get_sec_ids('pcap'), text, line, begidx, endidx) + self.spp_ctl_cli.get_sec_ids('pcap'), + text, line, begidx, endidx) def do_record(self, fname): """Save commands as a recipe file.""" @@ -908,7 +894,7 @@ class Shell(cmd.Cmd, object): def do_topo(self, args): """Output network topology.""" - self.spp_topo.run(args, self.get_sec_ids('nfv')) + self.spp_topo.run(args) def help_topo(self): """Print help message of topo command.""" diff --git a/src/cli/spp_ctl_client.py b/src/cli/spp_ctl_client.py index 3cce628..ff95136 100644 --- a/src/cli/spp_ctl_client.py +++ b/src/cli/spp_ctl_client.py @@ -68,3 +68,45 @@ class SppCtlClient(object): return False else: return True + + def get_sec_ids(self, ptype): + """Return a list of IDs of running secondary processes. + + 'ptype' is 'nfv', 'vf' or 'mirror' or 'pcap'. + """ + + ids = [] + res = self.get('processes') + if res is not None: + if res.status_code == 200: + try: + for ent in res.json(): + if ent['type'] == ptype: + ids.append(ent['client-id']) + except KeyError as e: + print('Error: {} is not defined!'.format(e)) + return ids + + def get_sec_procs(self, ptype): + """Get secondary processes info of given type. + + Processes info from spp-ctl is retrieved as JSON, then loaded with + json() before returned. + """ + + sec_procs = [] + error_codes = self.rest_common_error_codes + + for sec_id in self.get_sec_ids(ptype): + # NOTE: take care API's proc type are defined as plural such as + # 'nfvs', 'vfs' or so. + res = self.get('{pt}s/{sid}'.format(pt=ptype, sid=sec_id)) + if res.status_code == 200: + sec_procs.append(res.json()) + elif res.status_code in error_codes: + # TODO(yasufum) Print default error message + pass + else: + # Ignore unknown response because no problem for drawing graph + pass + return sec_procs -- 2.17.1