From 1530b34cabea906fef56f1537cd32aead6103f9d Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Wed, 14 Dec 2016 01:19:31 +0200 Subject: scapy launcher Change-Id: Iafe0365e82c5386e87dbf7abd0f0982c3ff95d80 Signed-off-by: Yaroslav Brustinov --- scripts/dpdk_setup_ports.py | 36 +++++---- scripts/external_libs/netstat.py | 97 +++++++++++++++++++++++ scripts/scapy_daemon_server | 163 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 scripts/external_libs/netstat.py create mode 100755 scripts/scapy_daemon_server diff --git a/scripts/dpdk_setup_ports.py b/scripts/dpdk_setup_ports.py index 59b113ba..fce099b5 100755 --- a/scripts/dpdk_setup_ports.py +++ b/scripts/dpdk_setup_ports.py @@ -242,9 +242,7 @@ class ConfigCreator(object): class map_driver(object): args=None; cfg_file='/etc/trex_cfg.yaml' - parent_cfg = None - dump_interfaces = None - no_ofed_check = None + parent_args = None class DpdkSetup(Exception): pass @@ -409,7 +407,7 @@ Other network devices self.raise_error ("Configuration file %s is old, should include version field\n" % fcfg ) if int(cfg_dict['version'])<2 : - self.raise_error ("Configuration file %s is old, should include version field with value greater than 2\n" % fcfg) + self.raise_error ("Configuration file %s is old, expected version 2, got: %s\n" % (fcfg, cfg_dict['version'])) if 'interfaces' not in self.m_cfg_dict[0]: self.raise_error ("Configuration file %s is old, should include interfaces field even number of elemets" % fcfg) @@ -461,11 +459,13 @@ Other network devices def do_run (self,only_check_all_mlx=False): self.run_dpdk_lspci () - if map_driver.dump_interfaces is None or (map_driver.dump_interfaces == [] and map_driver.parent_cfg): + if (map_driver.parent_args.dump_interfaces is None or + (map_driver.parent_args.dump_interfaces == [] and + map_driver.parent_args.cfg)): self.load_config_file() if_list=self.m_cfg_dict[0]['interfaces'] else: - if_list = map_driver.dump_interfaces + if_list = map_driver.parent_args.dump_interfaces if not if_list: for dev in self.m_devices.values(): if dev.get('Driver_str') in dpdk_nic_bind.dpdk_drivers: @@ -489,18 +489,18 @@ Other network devices Mellanox_cnt=Mellanox_cnt+1 - if not map_driver.dump_interfaces : + if not map_driver.parent_args.dump_interfaces: if ((Mellanox_cnt>0) and (Mellanox_cnt!= len(if_list))): err=" All driver should be from one vendor. you have at least one driver from Mellanox but not all "; raise DpdkSetup(err) - if not map_driver.dump_interfaces : + if not map_driver.parent_args.dump_interfaces: if Mellanox_cnt>0 : self.set_only_mellanox_nics() if self.get_only_mellanox_nics(): - if not map_driver.no_ofed_check: + if not map_driver.parent_args.no_ofed_check: self.verify_ofed_os() self.check_ofed_version() @@ -543,6 +543,11 @@ Other network devices sys.exit(1) else: print('WARNING: Some other program is using DPDK driver.\nIf it is TRex and you did not configure it for dual run, current command will fail.') + if map_driver.parent_args.stl: + ret = os.system('%s scapy_daemon_server restart' % sys.executable) + if ret: + sys.exit(1) + def do_return_to_linux(self): if not self.m_devices: @@ -850,10 +855,11 @@ def parse_parent_cfg (parent_cfg): parent_parser.add_argument('--dump-interfaces', nargs='*', default=None) parent_parser.add_argument('--no-ofed-check', action = 'store_true') parent_parser.add_argument('--no-watchdog', action = 'store_true') - args, _ = parent_parser.parse_known_args(shlex.split(parent_cfg)) - if args.help: + parent_parser.add_argument('-i', action = 'store_true', dest = 'stl', default = False) + map_driver.parent_args, _ = parent_parser.parse_known_args(shlex.split(parent_cfg)) + if map_driver.parent_args.help: sys.exit(0) - return (args.cfg, args.dump_interfaces, args.no_ofed_check) + def process_options (): parser = argparse.ArgumentParser(usage=""" @@ -970,9 +976,9 @@ To see more detailed info on interfaces (table): map_driver.args = parser.parse_args(); if map_driver.args.parent : - map_driver.parent_cfg, map_driver.dump_interfaces, map_driver.no_ofed_check = parse_parent_cfg (map_driver.args.parent) - if map_driver.parent_cfg != '': - map_driver.cfg_file = map_driver.parent_cfg; + parse_parent_cfg (map_driver.args.parent) + if map_driver.parent_args.cfg: + map_driver.cfg_file = map_driver.parent_args.cfg; if map_driver.args.cfg : map_driver.cfg_file = map_driver.args.cfg; diff --git a/scripts/external_libs/netstat.py b/scripts/external_libs/netstat.py new file mode 100644 index 00000000..03598573 --- /dev/null +++ b/scripts/external_libs/netstat.py @@ -0,0 +1,97 @@ +#!/usr/bin/python + +# based on http://voorloopnul.com/blog/a-python-netstat-in-less-than-100-lines-of-code/ +# added caching of glob for x10 speedup (additional x2 by omitting pid) + +import pwd +import os +import glob + +PROC_TCP = "/proc/net/tcp" +STATE = { + '01':'ESTABLISHED', + '02':'SYN_SENT', + '03':'SYN_RECV', + '04':'FIN_WAIT1', + '05':'FIN_WAIT2', + '06':'TIME_WAIT', + '07':'CLOSE', + '08':'CLOSE_WAIT', + '09':'LAST_ACK', + '0A':'LISTEN', + '0B':'CLOSING' + } + +def _load(): + ''' Read the table of tcp connections & remove header ''' + with open(PROC_TCP,'r') as f: + content = f.readlines() + content.pop(0) + return content + +def _hex2dec(s): + return str(int(s,16)) + +def _ip(s): + ip = [(_hex2dec(s[6:8])),(_hex2dec(s[4:6])),(_hex2dec(s[2:4])),(_hex2dec(s[0:2]))] + return '.'.join(ip) + +def _remove_empty(array): + return [x for x in array if x] + +def _convert_ip_port(array): + host,port = array.split(':') + return _ip(host),_hex2dec(port) + +def netstat(with_pid = True): + ''' + Function to return a list with status of tcp connections at linux systems + To get pid of all network process running on system, you must run this script + as superuser + ''' + + pid_cache.clear() + content=_load() + result = [] + for line in content: + line_array = _remove_empty(line.split(' ')) # Split lines and remove empty spaces. + l_host,l_port = _convert_ip_port(line_array[1]) # Convert ipaddress and port from hex to decimal. + r_host,r_port = _convert_ip_port(line_array[2]) + tcp_id = line_array[0] + state = STATE.get(line_array[3]) + uid = pwd.getpwuid(int(line_array[7]))[0] # Get user from UID. + inode = line_array[9] # Need the inode to get process pid. + if with_pid: + pid = _get_pid_of_inode(inode) # Get pid prom inode. + try: # try read the process name. + exe = os.readlink('/proc/' + pid + '/exe') + except: + exe = None + else: + pid = exe = None + result.append([tcp_id, uid, l_host, l_port, r_host, r_port, state, pid, exe]) + pid_cache.clear() + return result + +pid_cache = {} +def _get_pid_of_inode(inode): + ''' + To retrieve the process pid, check every running process and look for one using + the given inode. + ''' + if not pid_cache: + for fd in glob.glob('/proc/[0-9]*/fd/[0-9]*'): + try: + pid_cache[fd] = os.readlink(fd) + except: + pass + inode = '[' + inode + ']' + for key, val in pid_cache.items(): + if inode in val: + return key.split('/')[2] + return None + +if __name__ == '__main__': + for conn in netstat(): + print(conn) + diff --git a/scripts/scapy_daemon_server b/scripts/scapy_daemon_server new file mode 100755 index 00000000..490e8033 --- /dev/null +++ b/scripts/scapy_daemon_server @@ -0,0 +1,163 @@ +#!/usr/bin/python + +import os, sys, getpass +import tempfile +import time +import subprocess, shlex +from argparse import ArgumentParser, RawTextHelpFormatter +import errno + +def fail(msg): + print(msg) + sys.exit(-1) + +if getpass.getuser() != 'root': + fail('Please run this program as root/with sudo') + +pwd = os.path.abspath(os.path.dirname(__file__)) +ext_libs_path = os.path.join(pwd, 'external_libs') +if ext_libs_path not in sys.path: + sys.path.append(ext_libs_path) + +import netstat +try: + from termstyle import termstyle +except ImportError: + import termstyle + + +def inv(f): + return lambda *a, **k: not f(*a, **k) + + +def progress(success_check, start_msg, success_msg, fail_msg, timeout = 10, poll_rate = 0.5): + sys.stdout.write('%s...' % start_msg) + sys.stdout.flush() + for i in range(int(timeout/poll_rate)): + if success_check(): + print(termstyle.green(' ' + success_msg)) + return 0 + time.sleep(poll_rate) + sys.stdout.write('.') + sys.stdout.flush() + if success_check(): + print(termstyle.green(' ' + success_msg)) + return 0 + print(termstyle.red(' ' + fail_msg)) + return 1 + + +def run_command(command, timeout = 15, poll_rate = 0.1, cwd = None): + assert timeout > 0, 'Timeout should be positive' + assert poll_rate > 0, 'Poll rate should be positive' + try: + tempfile.TemporaryFile(bufsize=0) + temp_params = {'bufsize': 0} + except: + tempfile.TemporaryFile(buffering=0) + temp_params = {'buffering': 0} + with tempfile.TemporaryFile(**temp_params) as stdout_file: + proc = subprocess.Popen(shlex.split(command), stdout = stdout_file, stderr = subprocess.STDOUT, cwd = cwd, close_fds = True, universal_newlines = True) + for i in range(int(timeout/poll_rate)): + time.sleep(poll_rate) + if proc.poll() is not None: # process stopped + break + if proc.poll() is None: + proc.kill() # timeout + stdout_file.seek(0) + return (errno.ETIMEDOUT, '%s\n\n...Timeout of %s second(s) is reached!' % (stdout_file.read(), timeout)) + stdout_file.seek(0) + return (proc.returncode, stdout_file.read()) + + +def get_daemon_pid(): + pid = None + for conn in netstat.netstat(): + if conn[2] == '0.0.0.0' and int(conn[3]) == args.daemon_port and conn[6] == 'LISTEN': + pid = conn[7] + if pid is None: + raise Exception('Found the connection, but could not determine pid: %s' % conn) + break + return pid + + +# faster variant of get_daemon_pid +def is_running(): + for conn in netstat.netstat(with_pid = False): + if conn[2] == '0.0.0.0' and int(conn[3]) == args.daemon_port and conn[6] == 'LISTEN': + return True + return False + + +def show_daemon_status(): + if is_running(): + print(termstyle.green('Scapy server is running')) + else: + print(termstyle.red('Scapy server is NOT running')) + + +def start_daemon(): + if is_running(): + print(termstyle.red('Scapy server is already running')) + return + server_path = os.path.join(pwd, 'automation', 'trex_control_plane', 'stl', 'services', 'scapy_server') + with tempfile.TemporaryFile() as stdout_file: + subprocess.Popen(shlex.split('sudo -u nobody %s scapy_zmq_server.py -s %s' % (sys.executable, args.daemon_port)), stdout = stdout_file, + stderr = subprocess.STDOUT, cwd = server_path, close_fds = True, universal_newlines = True) + ret = progress(is_running, 'Starting Scapy server', 'Scapy server is started', 'Scapy server failed to run') + if ret: + stdout_file.seek(0) + print('Output: %s' % stdout_file.read()) + sys.exit(1) + + +def restart_daemon(): + if is_running(): + kill_daemon() + time.sleep(0.5) + start_daemon() + + +def kill_daemon(): + pid = get_daemon_pid() + if not pid: + print(termstyle.red('Scapy server is NOT running')) + return True + run_command('kill %s' % pid) # usual kill + ret = progress(inv(is_running), 'Killing Scapy server', 'Scapy server is killed', 'failed') + if not ret: + return + _, out = run_command('kill -9 %s' % pid) # unconditional kill + ret = progress(inv(is_running), 'Killing Scapy server with -9', 'Scapy server is killed', 'failed') + if ret: + fail('Failed to kill Scapy server, even with -9. Please review manually.\nOutput: %s' % out) + +### Main ### + +if __name__ == '__main__': + actions_help = '''Specify action command to be applied on server. + (*) start : start the application in as a daemon process. + (*) show : prompt an updated status of daemon process (running/ not running). + (*) stop : exit Scapy server daemon process. + (*) restart : stop, then start again the application as daemon process + ''' + action_funcs = {'start': start_daemon, + 'show': show_daemon_status, + 'stop': kill_daemon, + 'restart': restart_daemon, + } + + parser = ArgumentParser(description = 'Runs Scapy server application.', + formatter_class = RawTextHelpFormatter, + ) + + parser.add_argument('-p', '--port', type = int, default = 4507, metavar="PORT", dest="daemon_port", + help="Select tcp port on which Scapy server listens.\nDefault port is 4507.", action="store") + parser.add_argument('action', choices=action_funcs.keys(), + action='store', help=actions_help) + parser.usage = None + args = parser.parse_args() + + action_funcs[args.action]() + + -- cgit 1.2.3-korg