diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/functions.py | 43 | ||||
-rwxr-xr-x | tools/pkt_gen/xena/xena.py | 14 | ||||
-rw-r--r-- | tools/pkt_gen/xena/xena_json.py | 68 | ||||
-rw-r--r-- | tools/systeminfo.py | 8 | ||||
-rw-r--r-- | tools/tasks.py | 63 |
5 files changed, 184 insertions, 12 deletions
diff --git a/tools/functions.py b/tools/functions.py new file mode 100644 index 00000000..5079a9f0 --- /dev/null +++ b/tools/functions.py @@ -0,0 +1,43 @@ +# Copyright 2016 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Various helper functions +""" + +from conf import settings + +# +# Support functions +# + +def settings_update_paths(): + """ Configure paths to OVS and DPDK based on VSWITCH and VNF values + """ + # set dpdk and ovs paths accorfing to VNF and VSWITCH + if settings.getValue('VSWITCH').endswith('Vanilla'): + # settings paths for Vanilla + settings.setValue('OVS_DIR', (settings.getValue('OVS_DIR_VANILLA'))) + elif settings.getValue('VSWITCH').endswith('Vhost'): + if settings.getValue('VNF').endswith('Cuse'): + # settings paths for Cuse + settings.setValue('RTE_SDK', (settings.getValue('RTE_SDK_CUSE'))) + settings.setValue('OVS_DIR', (settings.getValue('OVS_DIR_CUSE'))) + else: + # settings paths for VhostUser + settings.setValue('RTE_SDK', (settings.getValue('RTE_SDK_USER'))) + settings.setValue('OVS_DIR', (settings.getValue('OVS_DIR_USER'))) + else: + # default - set to VHOST USER but can be changed during enhancement + settings.setValue('RTE_SDK', (settings.getValue('RTE_SDK_USER'))) + settings.setValue('OVS_DIR', (settings.getValue('OVS_DIR_USER'))) diff --git a/tools/pkt_gen/xena/xena.py b/tools/pkt_gen/xena/xena.py index dd23d0e5..67ac5652 100755 --- a/tools/pkt_gen/xena/xena.py +++ b/tools/pkt_gen/xena/xena.py @@ -142,11 +142,17 @@ class Xena(ITrafficGenerator): settings.getValue('TRAFFICGEN_XENA_PASSWORD') ) j_file.set_port(0, settings.getValue('TRAFFICGEN_XENA_MODULE1'), - settings.getValue('TRAFFICGEN_XENA_PORT1') - ) + settings.getValue('TRAFFICGEN_XENA_PORT1')) j_file.set_port(1, settings.getValue('TRAFFICGEN_XENA_MODULE2'), - settings.getValue('TRAFFICGEN_XENA_PORT2') - ) + settings.getValue('TRAFFICGEN_XENA_PORT2')) + j_file.set_port_ip_v4( + 0, settings.getValue("TRAFFICGEN_XENA_PORT0_IP"), + settings.getValue("TRAFFICGEN_XENA_PORT0_CIDR"), + settings.getValue("TRAFFICGEN_XENA_PORT0_GATEWAY")) + j_file.set_port_ip_v4( + 1, settings.getValue("TRAFFICGEN_XENA_PORT1_IP"), + settings.getValue("TRAFFICGEN_XENA_PORT1_CIDR"), + settings.getValue("TRAFFICGEN_XENA_PORT1_GATEWAY")) j_file.set_test_options( packet_sizes=self._params['traffic']['l2']['framesize'], iterations=trials, loss_rate=loss_rate, diff --git a/tools/pkt_gen/xena/xena_json.py b/tools/pkt_gen/xena/xena_json.py index 39cc56c8..971426cf 100644 --- a/tools/pkt_gen/xena/xena_json.py +++ b/tools/pkt_gen/xena/xena_json.py @@ -308,6 +308,52 @@ class XenaJSON(object): self.json_data['PortHandler']['EntityList'][index]['PortRef'][ 'PortIndex'] = port + def set_port_ip_v4(self, port, ip_addr, netmask, gateway): + """ + Set the port IP info + :param port: port number as int of port to set ip info + :param ip_addr: ip address in dot notation format as string + :param netmask: cidr number for netmask (ie 24/16/8) as int + :param gateway: gateway address in dot notation format + :return: None + """ + available_ports = range(len( + self.json_data['PortHandler']['EntityList'])) + if port not in available_ports: + raise ValueError("{}{}{}".format( + 'Port assignment must be an available port ', + 'number in baseconfig file. Port=', port)) + self.json_data['PortHandler']['EntityList'][ + port]["IpV4Address"] = ip_addr + self.json_data['PortHandler']['EntityList'][ + port]["IpV4Gateway"] = gateway + self.json_data['PortHandler']['EntityList'][ + port]["IpV4RoutingPrefix"] = int(netmask) + + def set_port_ip_v6(self, port, ip_addr, netmask, gateway): + """ + Set the port IP info + :param port: port number as int of port to set ip info + :param ip_addr: ip address as 8 groups of 4 hexadecimal groups separated + by a colon. + :param netmask: cidr number for netmask (ie 24/16/8) as int + :param gateway: gateway address as string in 8 group of 4 hexadecimal + groups separated by a colon. + :return: None + """ + available_ports = range(len( + self.json_data['PortHandler']['EntityList'])) + if port not in available_ports: + raise ValueError("{}{}{}".format( + 'Port assignment must be an available port ', + 'number in baseconfig file. Port=', port)) + self.json_data['PortHandler']['EntityList'][ + port]["IpV6Address"] = ip_addr + self.json_data['PortHandler']['EntityList'][ + port]["IpV6Gateway"] = gateway + self.json_data['PortHandler']['EntityList'][ + port]["IpV6RoutingPrefix"] = int(netmask) + def set_test_options(self, packet_sizes, duration, iterations, loss_rate, micro_tpld=False): """ @@ -418,6 +464,22 @@ def print_json_report(json_data): print("Chassis Password: {}".format(json_data['ChassisManager'][ 'ChassisList'][0]['Password'])) print("### Port Configuration ###") + print("Port 1 IPv4:{}/{} gateway:{}".format( + json_data['PortHandler']['EntityList'][0]["IpV4Address"], + json_data['PortHandler']['EntityList'][0]["IpV4RoutingPrefix"], + json_data['PortHandler']['EntityList'][0]["IpV4Gateway"])) + print("Port 1 IPv6:{}/{} gateway:{}".format( + json_data['PortHandler']['EntityList'][0]["IpV6Address"], + json_data['PortHandler']['EntityList'][0]["IpV6RoutingPrefix"], + json_data['PortHandler']['EntityList'][0]["IpV6Gateway"])) + print("Port 2 IPv4:{}/{} gateway:{}".format( + json_data['PortHandler']['EntityList'][1]["IpV4Address"], + json_data['PortHandler']['EntityList'][1]["IpV4RoutingPrefix"], + json_data['PortHandler']['EntityList'][1]["IpV4Gateway"])) + print("Port 2 IPv6:{}/{} gateway:{}".format( + json_data['PortHandler']['EntityList'][1]["IpV6Address"], + json_data['PortHandler']['EntityList'][1]["IpV6RoutingPrefix"], + json_data['PortHandler']['EntityList'][1]["IpV6Gateway"])) print("Port 1: {}/{} group: {}".format( json_data['PortHandler']['EntityList'][0]['PortRef']['ModuleIndex'], json_data['PortHandler']['EntityList'][0]['PortRef']['PortIndex'], @@ -512,6 +574,12 @@ if __name__ == "__main__": JSON.set_chassis_info('192.168.0.5', 'vsperf') JSON.set_port(0, 1, 0) JSON.set_port(1, 1, 1) + JSON.set_port_ip_v4(0, '192.168.240.10', 32, '192.168.240.1') + JSON.set_port_ip_v4(1, '192.168.240.11', 32, '192.168.240.1') + JSON.set_port_ip_v6(0, 'a1a1:a2a2:a3a3:a4a4:a5a5:a6a6:a7a7:a8a8', 128, + 'a1a1:a2a2:a3a3:a4a4:a5a5:a6a6:a7a7:1111') + JSON.set_port_ip_v6(1, 'b1b1:b2b2:b3b3:b4b4:b5b5:b6b6:b7b7:b8b8', 128, + 'b1b1:b2b2:b3b3:b4b4:b5b5:b6b6:b7b7:1111') JSON.set_header_layer2(dst_mac='dd:dd:dd:dd:dd:dd', src_mac='ee:ee:ee:ee:ee:ee') JSON.set_header_vlan(vlan_id=5) diff --git a/tools/systeminfo.py b/tools/systeminfo.py index ba490946..9d8eb5cb 100644 --- a/tools/systeminfo.py +++ b/tools/systeminfo.py @@ -168,6 +168,14 @@ def get_pid(proc_name_str): """ return get_pids([proc_name_str]) +def pid_isalive(pid): + """ Checks if given PID is alive + + :param pid: PID of the process + :returns: True if given process is running, False otherwise + """ + return os.path.isdir('/proc/' + str(pid)) + # This function uses long switch per purpose, so let us suppress pylint warning too-many-branches # pylint: disable=R0912 def get_version(app_name): diff --git a/tools/tasks.py b/tools/tasks.py index 90b7e553..dda5217d 100644 --- a/tools/tasks.py +++ b/tools/tasks.py @@ -26,6 +26,7 @@ import locale import time from conf import settings +from tools import systeminfo CMD_PREFIX = 'cmd : ' @@ -150,6 +151,55 @@ def run_interactive_task(cmd, logger, msg): return child +def terminate_task_subtree(pid, signal='-15', sleep=10, logger=None): + """Terminate given process and all its children + + Function will sent given signal to the process. In case + that process will not terminate within given sleep interval + and signal was not SIGKILL, then process will be killed by SIGKILL. + After that function will check if all children of the process + are terminated and if not the same terminating procedure is applied + on any living child (only one level of children is considered). + + :param pid: Process ID to terminate + :param signal: Signal to be sent to the process + :param sleep: Maximum delay in seconds after signal is sent + :param logger: Logger to write details to + """ + try: + output = subprocess.check_output("pgrep -P " + str(pid), shell=True).decode().rstrip('\n') + except subprocess.CalledProcessError: + output = "" + + terminate_task(pid, signal, sleep, logger) + + # just for case children were kept alive + children = output.split('\n') + for child in children: + terminate_task(child, signal, sleep, logger) + +def terminate_task(pid, signal='-15', sleep=10, logger=None): + """Terminate process with given pid + + Function will sent given signal to the process. In case + that process will not terminate within given sleep interval + and signal was not SIGKILL, then process will be killed by SIGKILL. + + :param pid: Process ID to terminate + :param signal: Signal to be sent to the process + :param sleep: Maximum delay in seconds after signal is sent + :param logger: Logger to write details to + """ + if systeminfo.pid_isalive(pid): + run_task(['sudo', 'kill', signal, str(pid)], logger) + logger.debug('Wait for process %s to terminate after signal %s', pid, signal) + for dummy in range(sleep): + time.sleep(1) + if not systeminfo.pid_isalive(pid): + break + + if signal.lstrip('-').upper() not in ('9', 'KILL', 'SIGKILL') and systeminfo.pid_isalive(pid): + terminate_task(pid, '-9', sleep, logger) class Process(object): """Control an instance of a long-running process. @@ -242,17 +292,14 @@ class Process(object): self.kill() raise exc - def kill(self, signal='-15', sleep=2): + def kill(self, signal='-15', sleep=10): """Kill process instance if it is alive. :param signal: signal to be sent to the process :param sleep: delay in seconds after signal is sent """ - if self._child and self._child.isalive(): - run_task(['sudo', 'kill', signal, str(self._child.pid)], - self._logger) - self._logger.debug('Wait for process to terminate') - time.sleep(sleep) + if self.is_running(): + terminate_task_subtree(self._child.pid, signal, sleep, self._logger) if self.is_relinquished(): self._relinquish_thread.join() @@ -275,7 +322,7 @@ class Process(object): :returns: True if process is running, else False """ - return self._child is not None + return self._child and self._child.isalive() def _affinitize_pid(self, core, pid): """Affinitize a process with ``pid`` to ``core``. @@ -298,7 +345,7 @@ class Process(object): """ self._logger.info('Affinitizing process') - if self._child and self._child.isalive(): + if self.is_running(): self._affinitize_pid(core, self._child.pid) class ContinueReadPrintLoop(threading.Thread): |