summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYasufumi Ogawa <yasufum.o@gmail.com>2019-08-12 16:12:38 +0900
committerYasufumi Ogawa <yasufum.o@gmail.com>2019-08-13 17:08:08 +0900
commit3a3ad4cf70facf0b52d1ffb0b468ec82d89296b3 (patch)
treec068c44129f2bdbcb087675da5dd38b978958c8f
parent63d7df1178988dc5c68928be9fc512ceb368a2d3 (diff)
downloadspp-3a3ad4cf70facf0b52d1ffb0b468ec82d89296b3.zip
spp-3a3ad4cf70facf0b52d1ffb0b468ec82d89296b3.tar.gz
spp-3a3ad4cf70facf0b52d1ffb0b468ec82d89296b3.tar.xz
cli: add to get sec ID and procs to SppCtlClient
Shell class has get_sec_ids() for retrieving secondary IDs of given type by requesting all processes information to spp-ctl. However, it is better to request the information from SppCtlClient which a delegator for the requesting. This update is to remove get_sec_ids() from Shell and add the same method to SppCtlClient. In addition to above update, add get_sec_procs() to SppCtlClient to be able to retrieve other than spp_nfv in topo. By this change, all types of secondary process can be referred from anywhere in SppTopo so that there is no need to pass `sec_list` as an argument of member methods. Supporting other than spp_nfv in topo command is added in the next patches. Signed-off-by: Yasufumi Ogawa <yasufum.o@gmail.com>
-rw-r--r--src/cli/commands/topo.py111
-rw-r--r--src/cli/shell.py48
-rw-r--r--src/cli/spp_ctl_client.py42
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