diff options
Diffstat (limited to 'testsuites/vstf/vstf_scripts/vstf/agent')
52 files changed, 4677 insertions, 0 deletions
diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/__init__.py new file mode 100644 index 00000000..83b8d15d --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/agent.py b/testsuites/vstf/vstf_scripts/vstf/agent/agent.py new file mode 100644 index 00000000..b5745995 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/agent.py @@ -0,0 +1,142 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# !/usr/bin/env python +# coding=utf-8 + +import logging +import argparse +import signal + +from oslo.config import cfg + +from vstf.rpc_frame_work import rpc_consumer +from vstf.common.log import setup_logging +from vstf.common import daemon +from vstf.common.cfgparser import CfgParser + +LOG = logging.getLogger(__name__) + +server_opts = [ + cfg.StrOpt('user', default='guest', help="the rabbit's user, default guest"), + cfg.StrOpt('passwd', default='guest', help="the rabbit's passwd, default guest"), + cfg.StrOpt('host', default='localhost', help="tell the process wich interface to listen"), + cfg.StrOpt('port', default=5672, help=""), + cfg.StrOpt('id', default='agent', help="") + +] + +stc_opts = [ + cfg.StrOpt('package', default='', help="the STC python path") +] + + +class Client(daemon.Daemon): + """This is a consumer of vstf-agent which will create two channel to the + rabbitmq-server, one for direct call, one for fan call. + + agent start with a config file which record rabbitmq's ip, port and user passwd + also each agent has its own id. + + """ + + def __init__(self, agent, config_file): + """Record the config file, init the daemon. + + :param str config_file: the config of a VSTF agent. + + """ + super(Client, self).__init__('/tmp/esp_rpc_client.pid') + self.config_file = config_file + self.agent = agent + self.config = None + self.proxy = None + self.run_flag = True + + def init_config(self): + """Use olso.config to analyse the config file + + """ + parser = CfgParser(self.config_file) + parser.register_my_opts(server_opts, "rabbit") + parser.register_my_opts(stc_opts, "spirent") + self.config = parser.parse() + + def loop_thread(self): + LOG.info("Try to create direct proxy...") + self.proxy = rpc_consumer.VstfConsumer(self.agent, + self.config.rabbit.user, + self.config.rabbit.passwd, + self.config.rabbit.host, + self.config.rabbit.port, + self.config.rabbit.id) + self.proxy.run() + + def run(self): + """Run the rabbitmq consumers as a daemon. + + """ + signal.signal(signal.SIGTERM, self.process_exit) + self.loop_thread() + LOG.info("agent start ok!") + + def process_exit(self, signum, frame): + """This function try to stop the agent after running agent stop. + When we call vstf-agent stop which will send a signal SIGTERM to agent + When the agent catch the SIGTERM signal will call this function. + + """ + LOG.info("daemon catch the signalterm, start to stop the process.") + self.run_flag = False + if self.proxy: + self.proxy.stop() + + def start_agent(self): + self.init_config() + self.start() + + def stop_agent(self): + """Notice that: this function just kill the agent by pid file, it has + none vars of the agent. + + """ + LOG.info("call daemon stop.") + # kill the main thread + self.stop() + + +def main(): + setup_logging(level=logging.INFO, log_file="/var/log/vstf/vstf-agent.log") + parser = argparse.ArgumentParser(description='agent option') + parser.add_argument('action', choices=('start', 'stop', "restart"), + default="start", help="start or stop agent") + parser.add_argument('--agent_type', action='store', + default="soft", + choices=["soft", "spirent"], + help="the agent type, as now, just soft and spirent") + parser.add_argument('--config_file', action='store', + default="/etc/vstf/amqp/amqp.ini", + help="some env_build params recorded in the config file") + + args = parser.parse_args() + + client = Client(args.agent_type, args.config_file) + if "start" == args.action: + client.start_agent() + elif "stop" == args.action: + client.stop_agent() + elif "restart" == args.action: + client.stop_agent() + client.start_agent() + else: + raise Exception("only support actions: start/stop/restart") + + +if __name__ == '__main__': + main() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/__init__.py new file mode 100644 index 00000000..df7d24d0 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/__init__.py @@ -0,0 +1,9 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/__init__.py new file mode 100644 index 00000000..df7d24d0 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/__init__.py @@ -0,0 +1,9 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/collect.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/collect.py new file mode 100644 index 00000000..126a7d55 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/collect.py @@ -0,0 +1,110 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import platform +import logging +from collections import OrderedDict + +from vstf.agent.env.basic.commandline import CommandLine +from vstf.common import constants as const + +log = logging.getLogger(__name__) +CMD = CommandLine() + + +class Collect(object): + """collect host information such as _cpu, memory and so on""" + + def __init__(self): + super(Collect, self).__init__() + self._system = self._system() + self._cpu = self._cpu() + + def _system(self): + """the base _system info + {'os info':{'_system':'ubuntu', 'kernel': '3.13.3'}}""" + return {const.OS_INFO: + { + '_system': open('/etc/issue.net').readline().strip(), + 'kernel': platform.uname()[2] + } + } + + def _memery(self): + """ Return the information in /proc/meminfo + as a dictionary """ + meminfo = OrderedDict() + with open('/proc/meminfo') as f: + for line in f: + meminfo[line.split(':')[0]] = line.split(':')[1].strip() + + return {const.MEMORY_INFO: + { + "Mem Total": meminfo['MemTotal'], + "Mem Swap": meminfo['SwapTotal'] + } + } + + def _lscpu(self): + ret = {} + with os.popen("lscpu") as f: + for line in f: + ret[line.split(':')[0].strip()] = line.split(':')[1].strip() + return ret + + def _cpu(self): + ret = [] + with open('/proc/cpuinfo') as f: + cpuinfo = OrderedDict() + for line in f: + if not line.strip(): + ret.append(cpuinfo) + cpuinfo = OrderedDict() + elif len(line.split(':')) == 2: + cpuinfo[line.split(':')[0].strip()] = line.split(':')[1].strip() + else: + log.error("_cpu info unknow format <%(c)s>", {'c': line}) + return {const.CPU_INFO: + dict( + { + "Model Name": ret[0]['model name'], + "Address sizes": ret[0]['address sizes'] + }, + **(self._lscpu()) + ) + } + + def _hw_sysinfo(self): + cmdline = "dmidecode | grep -A 2 'System Information' | grep -v 'System Information'" + ret, output = CMD.execute(cmdline, shell=True) + if ret: + result = {} + # del the stderr + for tmp in output.strip().split('\n'): + if tmp is None or tmp is "": + continue + # split the items + tmp = tmp.split(":") + if len(tmp) >= 2: + # first item as key, and the other as value + result[tmp[0].strip("\t")] = ";".join(tmp[1:]) + return {const.HW_INFO: result} + else: + return {const.HW_INFO: "get hw info failed. check the host by cmd: dmidecode"} + + def collect_host_info(self): + return [self._system, self._cpu, self._memery(), self._hw_sysinfo()] + + +if __name__ == "__main__": + c = Collect() + import json + + print json.dumps(c.collect_host_info(), indent=4) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/commandline.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/commandline.py new file mode 100644 index 00000000..e4df9b27 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/commandline.py @@ -0,0 +1,55 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess +import threading +import logging +from vstf.common import constants + +LOG = logging.getLogger(__name__) + + +class CommandLine(object): + def __init__(self): + super(CommandLine, self).__init__() + self.proc = None + self.is_timeout = False + + def __kill_proc(self): + self.is_timeout = True + self.proc.kill() + + def execute(self, cmd, timeout=constants.TIMEOUT, shell=False): + """this func call subprocess.Popen(), + here setup a timer to deal with timeout. + :param cmd: cmd list like ['ls', 'home'] + :param timeout: for timer count for timeout + :return: (ret, output) the output (stdout+'\n'+stderr) + """ + # reset the timeout flag + self.is_timeout = False + self.proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=shell) + + timer = threading.Timer(timeout, self.__kill_proc, []) + timer.start() + stdout, stderr = self.proc.communicate() + timer.cancel() + + if self.proc.returncode or self.is_timeout: + if self.is_timeout: + LOG.error("run cmd<%(cmd)s> timeout", {"cmd": cmd}) + ret = False + output = "".join([stderr, stdout]) + else: + ret = True + output = stdout + return ret, output diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/device_manager.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/device_manager.py new file mode 100644 index 00000000..8b5387fe --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/device_manager.py @@ -0,0 +1,147 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import re +import logging +from vstf.agent.perf import netns +from vstf.common.utils import check_output, get_device_name, my_sleep, check_call, call, IPCommandHelper + +LOG = logging.getLogger(__name__) + +default_drivers = { + '82599': 'ixgbe', + '82576': 'igb', +} + + +class LspciHelper(object): + def __init__(self): + self.bdf_desc_map = {} + self.bdf_device_map = {} + self.device_bdf_map = {} + self.bdf_ip_map = {} + self.bdf_driver_map = {} + self.mac_bdf_map = {} + self.bdf_mac_map = {} + self._get_bdfs() + self._get_devices() + self._get_drivers() + self._get_ip_macs() + + def _get_bdfs(self): + self.bdf_desc_map = {} + out = check_output('lspci |grep Eth', shell=True) + for line in out.splitlines(): + bdf, desc = line.split(' ', 1) + self.bdf_desc_map[bdf] = desc + + def _get_devices(self): + for bdf, desc in self.bdf_desc_map.items(): + device = get_device_name(bdf) + if device is None: + LOG.info("cann't find device name for bdf:%s, no driver is available.", bdf) + try: + self._load_driver(desc) + except: + LOG.warn("!!!unable to load_driver for device:%s", bdf) + my_sleep(0.2) + device = get_device_name(bdf) + self.bdf_device_map[bdf] = device + if device: + self.device_bdf_map[device] = bdf + check_call("ip link set dev %s up" % device, shell=True) + + def _get_drivers(self): + for device, bdf in self.device_bdf_map.items(): + buf = check_output('ethtool -i %s | head -n1' % device, shell=True) + driver = buf.split()[1] + self.bdf_driver_map[bdf] = driver + + def _get_ip_macs(self): + for device, bdf in self.device_bdf_map.items(): + buf = check_output("ip addr show dev %s" % device, shell=True) + macs = re.compile("[A-F0-9]{2}(?::[A-F0-9]{2}){5}", re.IGNORECASE | re.MULTILINE) + for mac in macs.findall(buf): + if mac.lower() in ('00:00:00:00:00:00', 'ff:ff:ff:ff:ff:ff'): + continue + else: + break + ips = re.compile(r"inet (\d{1,3}\.\d{1,3}\.\d{1,3}.\d{1,3}/\d{1,2})", re.MULTILINE) + ip = ips.findall(buf) + if ip: + self.bdf_ip_map[bdf] = ip[0] + else: + self.bdf_ip_map[bdf] = None + self.bdf_mac_map[bdf] = mac + self.mac_bdf_map[mac] = bdf + + def _load_driver(self, desc): + for key in default_drivers: + if key in desc: + driver = default_drivers[key] + LOG.info("try to load default driver [%s]", driver) + check_call('modprobe %s' % driver, shell=True) + break + else: + LOG.warn("unsupported nic type:%s", desc) + + +class DeviceManager(object): + def __init__(self): + super(DeviceManager, self).__init__() + mgr = netns.NetnsManager() + mgr.clean_all_namespace() + self.lspci_helper = LspciHelper() + + def _get_device_detail(self, bdf): + device = self.lspci_helper.bdf_device_map[bdf] + mac = self.lspci_helper.bdf_mac_map[bdf] + ip = self.lspci_helper.bdf_ip_map[bdf] + desc = self.lspci_helper.bdf_desc_map[bdf] + driver = self.lspci_helper.bdf_driver_map[bdf] + detail = { + 'bdf': bdf, + 'device': device, + 'mac': mac, + 'ip': ip, + 'desc': desc, + 'driver': driver + } + return detail + + def get_device_detail(self, identity): + """ + Gets the detail of a network card. + + :param identity: be it the mac address, bdf, device name of a network card. + :return: device detail of a network card. + """ + if identity in self.lspci_helper.bdf_device_map: + bdf = identity + elif identity in self.lspci_helper.device_bdf_map: + bdf = self.lspci_helper.device_bdf_map[identity] + elif identity in self.lspci_helper.mac_bdf_map: + bdf = self.lspci_helper.mac_bdf_map[identity] + else: + raise Exception("cann't find the device by identity:%s" % identity) + return self._get_device_detail(bdf) + + def get_device_verbose(self, identity): + return IPCommandHelper().get_device_verbose(identity) + + def list_nic_devices(self): + """ + Get all the details of network devices in the host. + :return: a list of network card detail. + """ + device_list = [] + for bdf in self.lspci_helper.bdf_device_map.keys(): + detail = self._get_device_detail(bdf) + device_list.append(detail) + return device_list diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/image_manager.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/image_manager.py new file mode 100644 index 00000000..c3b5c6b3 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/image_manager.py @@ -0,0 +1,128 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from vstf.common.utils import check_call +import os +import logging + +LOG = logging.getLogger(__name__) + + +class _ImageManager(object): + """ + A qemu-img wrapper to create qcow2 child image from a parent image. + + """ + def __init__(self, parent_image_path, child_image_dir): + """ + :param parent_image_path str: the parent image path. + :param child_image_dir str: the destination path to put child images. + """ + self._create_child_str = 'qemu-img create -f %(image_type)s %(child_path)s -o backing_file=%(parent_path)s' + self._convert_str = "qemu-img convert -O %(image_type)s %(parent_path)s %(child_path)s" + self.child_image_dir = child_image_dir + self.parent_image_path = parent_image_path + assert os.path.isfile(self.parent_image_path) + assert os.path.isdir(self.child_image_dir) + + def create_child_image(self, child_name, full_clone=False, image_type='qcow2'): + """ + create a child image and put it in self.child_image_dir. + + :param child_name: the image name to be created.. + :return: return the path of child image. + """ + + image_path = os.path.join(self.child_image_dir, child_name) + '.' + image_type + if full_clone: + cmd = self._convert_str % {'image_type': image_type, 'child_path': image_path, 'parent_path': self.parent_image_path} + else: + cmd = self._create_child_str % {'child_path': image_path, 'parent_path': self.parent_image_path, 'image_type':image_type} + check_call(cmd.split()) + return image_path + + +class ImageManager(object): + def __init__(self, cfg): + """ + ImageManager creates images from configuration context. + + :param cfg: dict, example: + { + 'parent_image': "/mnt/sdb/ubuntu_salt_master.img", + 'dst_location': "/mnt/sdb", + 'full_clone':False, + 'type': "qcow2", + 'names': ['vm1','vm2','vm3','vm4'] + } + :return: + """ + super(ImageManager, self).__init__() + cfg = self._check_cfg(cfg) + self.parent_image = cfg['parent_image'] + self.image_dir = cfg['dst_location'] + self.full_clone = cfg['full_clone'] + self.image_type = cfg['type'] + self.names = cfg['names'] + self.mgr = _ImageManager(self.parent_image, self.image_dir) + + @staticmethod + def _check_cfg(cfg): + for key in ('parent_image', 'dst_location', 'full_clone', 'type', 'names'): + if key not in cfg: + raise Exception("does't find %s config" % key) + if cfg['type'] not in ('raw', 'qcow2'): + raise Exception("type:%s not supported, only support 'raw' and 'qcow2'" % cfg['type']) + if not cfg['full_clone'] and cfg['type'] == 'raw': + raise Exception("only support 'qcow2' for not full_clone image creation" % cfg['type']) + return cfg + + def create_all(self): + """ + create images by configuration context. + + :return: True for success, False for failure. + """ + for name in self.names: + image = self.mgr.create_child_image(name, self.full_clone, self.image_type) + LOG.info("image: %s created", image) + return True + + def clean_all(self): + """ + remove all the images created in one go. + + :return: True for success. Raise exception otherwise. + """ + for name in self.names: + image_path = os.path.join(self.image_dir, name + '.' + self.image_type) + try: + os.unlink(image_path) + LOG.info("remove:%s successfully", image_path) + except Exception: + LOG.info("cann't find path:%s", image_path) + return True + + +if __name__ == '__main__': + import argparse + import json + parser = argparse.ArgumentParser() + parser.add_argument('action', choices = ('create','clean'), help='action:create|clean') + parser.add_argument('--config', help='config file to parse') + args = parser.parse_args() + logging.basicConfig(level=logging.INFO) + image_cfg = json.load(open(args.config)) + mgr = ImageManager(image_cfg) + if args.action == 'create': + mgr.create_all() + if args.action == 'clean': + mgr.clean_all() + + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/source_manager.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/source_manager.py new file mode 100644 index 00000000..6edd14ca --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/source_manager.py @@ -0,0 +1,78 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import logging +import contextlib +from subprocess import CalledProcessError +from vstf.common.utils import check_call + +LOG = logging.getLogger(__name__) + + +@contextlib.contextmanager +def my_chdir(file_path): + old_cwd = os.path.realpath(os.curdir) + os.chdir(file_path) + LOG.info("cd %s", file_path) + yield + os.chdir(old_cwd) + LOG.info("cd %s", old_cwd) + + +class SourceCodeManager(object): + def __init__(self): + super(SourceCodeManager, self).__init__() + self.base_path = '/opt/vstf/' + + @staticmethod + def _git_pull(url, dest): + if not os.path.isdir(dest): + check_call("git clone %s %s" % (url, dest), shell=True) + else: + with my_chdir(dest): + check_call("git pull", shell=True) + + @staticmethod + def _install(dest): + with my_chdir(dest): + try: + check_call("make && make install", shell=True) + except CalledProcessError: + LOG.info("retry make again") + check_call("make clean; make && make install", shell=True) + + def src_install(self, cfg): + for key, item in cfg.items(): + repo_type = item['repo_type'] + url = item['url'] + install = item['install'] + if install is True: + LOG.info("installing src repo:%s", key) + if repo_type == "git": + target = self.base_path + key + self._git_pull(url, target) + self._install(target) + else: + raise Exception("unsupported repo type:%s" % repo_type) + else: + LOG.info("skip src repo:%s", key) + return True + + +if __name__ == '__main__': + import argparse + import json + parser = argparse.ArgumentParser() + parser.add_argument('--config', help='config file to parse') + args = parser.parse_args() + logging.basicConfig(level=logging.INFO) + cfg = json.load(open(args.config)) + mgr = SourceCodeManager() + mgr.src_install(cfg) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm9pfs.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm9pfs.py new file mode 100644 index 00000000..7364f8b2 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm9pfs.py @@ -0,0 +1,158 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import logging +import textwrap +from vstf.common.utils import my_sleep +from vstf.agent.env.fsmonitor import constant + +LOG = logging.getLogger(__name__) + + +class VMConfigBy9pfs(object): + """ + host side implemetation of a self-defined communication protocol using libvirt 9pfs to give commands to the Virtual Machine. + + """ + + def __init__(self, vm_9p_path): + """ + :param vm_9p_path: The host path of libvirt 9pfs for a vm. + :return: + """ + self.vm_9p_path = vm_9p_path + + def clean(self): + self._unlink(self._path(constant.VM_CMD_RETURN_CODE_FILE)) + self._unlink(self._path(constant.VM_CMD_DONE_FLAG_FILE)) + + def _path(self, relative_path): + return os.path.join(self.vm_9p_path, relative_path) + + def _unlink(self, file_path): + os.unlink(file_path) + LOG.info("os.unlink(%s)", file_path) + + def _read(self, filename): + filepath = self._path(filename) + with open(filepath, 'r') as f: + ret = f.read() + LOG.info("read(%s) -> %s", filepath, ret) + return ret + + def _write(self, filename, cmd): + filepath = self._path(filename) + with open(filepath, 'w') as f: + f.write("%s" % cmd) + LOG.info("write(%s) <- %s", filepath, cmd) + + def _wait_flag_file_to_exist(self, filename, timeout): + filepath = self._path(filename) + while timeout > 0: + if os.path.exists(filepath): + LOG.info("wait and find file:%s", filepath) + return True + my_sleep(1) + timeout -= 1 + LOG.info("waiting file to exist:%s", filepath) + return False + + def _get_cmd_return_code(self): + ret = self._read(constant.VM_CMD_RETURN_CODE_FILE) + return ret == constant.VM_CMD_EXCUTE_SUCCES_FLAG_CONTENT + + def _wait_command_done(self): + done = self._wait_flag_file_to_exist(constant.VM_CMD_DONE_FLAG_FILE, constant.VM_COMMON_CMD_EXCUTE_TIME_OUT) + if done: + return self._get_cmd_return_code() + else: + return 'timeout' + + def _set_cmd(self, cmd): + self._write(constant.VM_CMD_CONTENT_FILE, cmd) + self._write(constant.VM_CMD_SET_FLAG_FILE, '') + ret = self._wait_command_done() + if ret: + self.clean() + return ret + else: + raise Exception("9pfs command failure: timeout.") + + def wait_up(self): + return self._wait_flag_file_to_exist(constant.VM_UP_Flag_FILE, constant.VM_UP_TIME_OUT) + + def config_ip(self, mac, ip): + cmd = 'config_ip %s %s' % (mac, ip) + return self._set_cmd(cmd) + + def config_gw(self, ip): + cmd = 'config_gw %s' % ip + return self._set_cmd(cmd) + + def set_pktloop_dpdk(self, macs): + """ + To connect two network devices together in the vm and loop the packets received to another. + Use dpdk testpmd to loop the packets. See FSMonitor. + + :param macs: the mac address list of network cards of the vm. + :return: True for success, Exception for Failure. + """ + mac_str = ' '.join(macs) + cmd = 'set_pktloop_dpdk ' + mac_str + return self._set_cmd(cmd) + + def recover_nic_binding(self, macs): + """ + in contrast to set_pktloop_dpdk, disconnect the looping. + :param macs: the mac address list of network cards of the vm. + :return: True for success, Exception for Failure. + """ + mac_str = ' '.join(macs) + cmd = 'recover_nic_binding ' + mac_str + return self._set_cmd(cmd) + + def config_amqp(self, identity, server, port=5672, user="guest", passwd="guest"): + data = { + 'server': server, + 'port': port, + 'id': identity, + 'user': user, + 'passwd': passwd + } + header = "[rabbit]" + content = ''' + user=%(user)s + passwd=%(passwd)s + host=%(server)s + port=%(port)s + id=%(id)s''' % data + file_name = "amqp.ini" + dedented_text = textwrap.dedent(content) + self._write(file_name, header+dedented_text) + cmd = 'config_amqp %s' % file_name + return self._set_cmd(cmd) + + def stop_vstf(self): + cmd = "stop_vstf" + return self._set_cmd(cmd) + + def __repr__(self): + return self.__class__.__name__ + ':' + self.vm_9p_path + + +if __name__ == '__main__': + fs = VMConfigBy9pfs('/tmp/tmp4T6p7L') + print os.listdir(os.curdir) + print fs.config_ip('56:6f:44:a5:3f:a4', '192.168.188.200/23') + print fs.config_gw('192.168.188.1') + print fs.set_pktloop_dpdk(['56:6f:44:a5:3f:a2', '56:6f:44:a5:3f:a3']) + print fs.recover_nic_binding(['56:6f:44:a5:3f:a2', '56:6f:44:a5:3f:a3']) + print fs.config_amqp('192.168.188.200', '192.168.188.10') + print os.listdir(os.curdir) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_manager.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_manager.py new file mode 100644 index 00000000..60a3b37b --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_manager.py @@ -0,0 +1,222 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import shutil +import logging +from vstf.common.utils import check_and_kill, randomMAC, my_mkdir, check_call, check_output, my_sleep +from vstf.agent.env.basic.vm9pfs import VMConfigBy9pfs + +LOG = logging.getLogger(__name__) + + +class VMControlOperation(object): + """ + a libivrt virsh wrapper for creating virtual machine. + """ + + def __init__(self): + """ + all tmp files will be created under '/tmp/atf_vm_manager' + + """ + work_dir = '/tmp/atf_vm_manager' + shutil.rmtree(work_dir, ignore_errors=True) + my_mkdir(work_dir) + self.work_dir = work_dir + self.vnc_index = 0 + self.pci_index = 3 + self.net_index = 0 + self.vm_9p_controllers = {} + self.vm_configs = {} + self.image_mgr = None + + @staticmethod + def composite_xml(context): + """ + composit a libvirt xml configuration for creating vm from context. + + :param context: a dict containing all necessary options for creating a vm. + :return: libvirt xml configuration string + """ + from vm_xml_help import xml_head, xml_disk, xml_ovs, xml_pci, xml_9p, xml_tail, xml_ctrl_br, xml_br + xml = '' + tmp = xml_head.replace('VM_NAME', context['vm_name']) + tmp = tmp.replace('VM_MEMORY', str(context['vm_memory'])) + tmp = tmp.replace('CPU_NUM', str(context['vm_cpu'])) + xml += tmp + tmp = xml_disk.replace('IMAGE_TYPE', context['image_type']) + tmp = tmp.replace('IMAGE_PATH', context['image_path']) + xml += tmp + + if context['9p_path']: + tmp = xml_9p.replace('9P_PATH', context['9p_path']) + xml += tmp + + if context['eth_pci']: + for pci in context['eth_pci']: + bus = pci[:2] + slot = pci[3:5] + func = pci[6:7] + tmp = xml_pci.replace('BUS', bus) + tmp = tmp.replace('SLOT', slot) + tmp = tmp.replace('FUNCTION', func) + xml += tmp + + if context['ctrl_br']: + tmp = xml_ctrl_br.replace('CTRL_BR', context['ctrl_br']) + tmp = tmp.replace('CTRL_MAC', context['ctrl_mac']) + tmp = tmp.replace('CTRL_MODEL', context['ctrl_model']) + xml += tmp + + for tap_cfg in context['taps']: + if tap_cfg['br_type'] == "ovs": + br_type = "openvswitch" + else: + br_type = tap_cfg['br_type'] + if br_type == 'bridge': + xml_ovs = xml_br + tmp = xml_ovs.replace('BR_TYPE', br_type) + tmp = tmp.replace('TAP_MAC', tap_cfg['tap_mac']) + tmp = tmp.replace('TAP_NAME', tap_cfg['tap_name']) + tmp = tmp.replace('BR_NAME', tap_cfg['br_name']) + xml += tmp + + xml += xml_tail + return xml + + @staticmethod + def check_required_options(context): + for key in ('vm_name', 'vm_memory', 'vm_cpu', 'image_path', 'image_type', 'taps'): + if not context.has_key(key): + raise Exception("vm config error, must set %s option" % key) + + def set_vm_defaults(self, context): + vm_9p_path = '%s/%s' % (self.work_dir, context['vm_name']) + shutil.rmtree(vm_9p_path, ignore_errors=True) + my_mkdir(vm_9p_path) + default = {'vm_memory': 4194304, + 'vm_cpu': 4, + 'image_type': 'qcow2', + 'br_type': 'ovs', + '9p_path': vm_9p_path, + 'eth_pci': None, + 'ctrl_br': 'br0', + 'ctrl_mac': randomMAC(), + 'ctrl_model': 'virtio', + 'ctrl_ip_setting': '192.168.100.100/24', + 'ctrl_gw': '192.168.100.1' + } + for k, v in default.items(): + context.setdefault(k, v) + + def _shutdown_vm(self): + out = check_output("virsh list | sed 1,2d | awk '{print $2}'", shell=True) + vm_set = set(out.split()) + for vm in vm_set: + check_call("virsh shutdown %s" % vm, shell=True) + timeout = 60 + # wait for gracefully shutdown + while timeout > 0: + out = check_output("virsh list | sed 1,2d | awk '{print $2}'", shell=True) + vm_set = set(out.split()) + if len(vm_set) == 0: + break + timeout -= 2 + my_sleep(2) + LOG.info("waiting for vms:%s to shutdown gracefully", vm_set) + # destroy by force + for vm in vm_set: + check_call("virsh destroy %s" % vm, shell=True) + # undefine all + out = check_output("virsh list --all | sed 1,2d | awk '{print $2}'", shell=True) + vm_set = set(out.split()) + for vm in vm_set: + check_call("virsh undefine %s" % vm, shell=True) + # kill all qemu + check_and_kill('qemu-system-x86_64') + + def clean_all_vms(self): + self._shutdown_vm() + for _, ctrl in self.vm_9p_controllers.items(): + LOG.debug("remove vm9pfs dir:%s", ctrl.vm_9p_path) + shutil.rmtree(ctrl.vm_9p_path, ignore_errors=True) + self.vm_9p_controllers = {} + self.vm_configs = {} + # shutil.rmtree(self.work_dir, ignore_errors=True) + self.vnc_index = 0 + self.pci_index = 3 + self.net_index = 0 + self.vms = [] + return True + + def create_vm(self, context): + self.set_vm_defaults(context) + self.check_required_options(context) + xml = self.composite_xml(context) + vm_name = context['vm_name'] + file_name = os.path.join(self.work_dir, vm_name + '.xml') + with open(file_name, 'w') as f: + f.write(xml) + check_call('virsh define %s' % file_name, shell=True) + check_call('virsh start %s' % vm_name, shell=True) + vm_name = context['vm_name'] + vm_9pfs = context['9p_path'] + self.vm_9p_controllers[vm_name] = VMConfigBy9pfs(vm_9pfs) + self.vm_configs[vm_name] = context + LOG.debug("%s's vm_9pfs path:%s", vm_name, vm_9pfs) + return True + + def wait_vm(self, vm_name): + vm9pctrl = self.vm_9p_controllers[vm_name] + ret = vm9pctrl.wait_up() + if ret not in (True,): + raise Exception('vm running but stuck in boot process, please manully check.') + LOG.debug('waitVM %s up ok, ret:%s', vm_name, ret) + return True + + def init_config_vm(self, vm_name): + """ + using libvirt 9pfs to config boot up options like network ip/gw. + + :param vm_name: the vm to be config with. + :return: True if succeed, Exception if fail. + """ + vm_cfg = self.vm_configs[vm_name] + vm9pctrl = self.vm_9p_controllers[vm_name] + # print self.vm_9p_controllers + init_cfg = vm_cfg['init_config'] + if "ctrl_ip_setting" in init_cfg: + ret = vm9pctrl.config_ip(vm_cfg['ctrl_mac'], init_cfg['ctrl_ip_setting']) + assert ret == True + LOG.info('initConfigVM config ip ok') + if 'ctrl_gw' in init_cfg: + ret = vm9pctrl.config_gw(init_cfg['ctrl_gw']) + assert ret == True + LOG.info('initConfigVM ctrl_gw ok') + if "ctrl_ip_setting" in init_cfg and "amqp_server" in init_cfg: + identity = init_cfg['ctrl_ip_setting'].split('/')[0] + if init_cfg['amqp_id'].strip(): + identity = init_cfg['amqp_id'].strip() + server = init_cfg['amqp_server'] + port = init_cfg['amqp_port'] + user = init_cfg['amqp_user'] + passwd = init_cfg['amqp_passwd'] + ret = vm9pctrl.config_amqp(identity, server, port, user, passwd) + assert ret == True + LOG.info('initConfigVM config_amqp ok') + if 'tap_pktloop_config' in init_cfg: + taps = vm_cfg['taps'] + macs = [] + for tap in taps: + macs.append(tap['tap_mac']) + ret = vm9pctrl.set_pktloop_dpdk(macs) + assert ret == True + LOG.info('initConfigVM set_pktloop_dpdk ok') + return True diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_xml_help.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_xml_help.py new file mode 100644 index 00000000..6f9131e7 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_xml_help.py @@ -0,0 +1,85 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +xml_head = ''' +<domain type='kvm'> + <name>VM_NAME</name> + <memory unit='KiB'>VM_MEMORY</memory> + <currentMemory unit='KiB'>VM_MEMORY</currentMemory> + <!--numatune> + <memory mode='strict' nodeset='0'/> + </numatune--> + <vcpu placement='static'>CPU_NUM</vcpu> + <cpu mode='host-passthrough'> + </cpu> + <os> + <type arch='x86_64' >hvm</type> + <boot dev='hd'/> + </os> + <features> + <acpi/> + <apic/> + <pae/> + </features> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>restart</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator>''' +xml_disk = ''' + <disk type='file' device='disk'> + <driver name='qemu' type='IMAGE_TYPE' cache='none' io='native'/> + <source file='IMAGE_PATH'/> + <target dev='vda' bus='virtio'/> + </disk>''' + +xml_ctrl_br = ''' +<interface type='bridge'> + <mac address='CTRL_MAC'/> + <source bridge='CTRL_BR'/> + <model type='CTRL_MODEL'/> +</interface> +''' +xml_ovs = ''' + <interface type='bridge'> + <mac address='TAP_MAC'/> + <source bridge='BR_NAME'/> + <virtualport type='BR_TYPE'> + </virtualport> + <model type='virtio'/> + <driver name='vhost' queues='4'/> + <target dev='TAP_NAME'/> + </interface>''' +xml_br = ''' + <interface type='bridge'> + <mac address='TAP_MAC'/> + <source bridge='BR_NAME'/> + <model type='virtio'/> + <target dev='TAP_NAME'/> + </interface>''' + +xml_pci = ''' + <hostdev mode='subsystem' type='pci' managed='yes'> + <driver name='kvm'/> + <source> + <address domain='0x0000' bus='0xBUS' slot='0xSLOT' function='0xFUNCTION' /> + </source> + </hostdev>''' +xml_9p = ''' + <filesystem type='mount' accessmode='passthrough'> + <source dir='9P_PATH'/> + <target dir='9pfs'/> + </filesystem>''' +xml_tail = ''' + <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'> + <listen type='address' address='0.0.0.0'/> + </graphics> + </devices> +</domain>''' + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/builder.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/builder.py new file mode 100644 index 00000000..a66a8873 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/builder.py @@ -0,0 +1,55 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import logging + +import stevedore + +LOG = logging.getLogger(__name__) + + +class PluginManager(object): + def __init__(self): + self.instance = None + self.saved = {} + + def build(self, cfg): + scheme = cfg["scheme"] + if scheme in self.saved: + # reuse old instance + self.instance = self.saved[scheme] + else: + mgr = stevedore.driver.DriverManager(namespace="env_build.plugins", + name=scheme, + invoke_on_load=False) + self.instance = mgr.driver() + self.saved[scheme] = self.instance + + self.instance.clean() + return self.instance.build(cfg) + + def clean(self): + if self.instance: + self.instance.clean() + self.instance = None + + +if __name__ == "__main__": + import argparse + from vstf.controller.env_build.env_build import IntentParser + + parser = argparse.ArgumentParser() + parser.add_argument('--config', help='config file to parse') + args = parser.parse_args() + logging.basicConfig(level=logging.INFO) + parser = IntentParser(args.config) + cfg_intent = parser.parse_cfg_file() + for host_cfg in cfg_intent['env-build']: + tn = PluginManager() + tn.build(host_cfg) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/__init__.py new file mode 100644 index 00000000..fc9802be --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +##############################################################################
\ No newline at end of file diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/manager.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/manager.py new file mode 100644 index 00000000..6f895656 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/manager.py @@ -0,0 +1,44 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import stevedore + + +class DriverPluginManager(object): + def __init__(self): + self.plugins = {} + self.mgr = stevedore.extension.ExtensionManager(namespace="drivers.plugins", invoke_on_load=True) + + def load(self, drivers): + plugin = self.determine_driver_type(drivers) + ext = self.mgr[plugin] + ext.obj.load(drivers) + return True + + def clean(self): + self.mgr.map(self._clean) + return True + + def _clean(self, ext, *args, **kwargs): + ext.obj.clean() + + def get_all_supported_drivers(self): + if not self.plugins: + for ext_name in self.mgr.names(): + ext = self.mgr[ext_name] + self.plugins[ext_name] = ext.obj.get_supported_drivers() + return self.plugins + + def determine_driver_type(self, drivers): + s = set(drivers) + for plugin, supported in self.get_all_supported_drivers().items(): + if not (s - set(supported)): + return plugin + else: + raise Exception('unspported drivers: %s' % drivers) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/model.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/model.py new file mode 100644 index 00000000..ddc07449 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/model.py @@ -0,0 +1,42 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from abc import ABCMeta +from abc import abstractmethod + + +class DriverPlugin: + __metaclass__ = ABCMeta + + @abstractmethod + def __init__(self): + """don't pass in any args for __init__. + """ + + @abstractmethod + def clean(self): + """implement this clean function to clean environment before and after calling any other functions. + + """ + pass + + @abstractmethod + def load(self, drivers): + """load driver modules. + + :param list drivers:list of modules to be inserted. for example:[ixgbe,vhost_net] + + """ + pass + + @abstractmethod + def get_supported_drivers(self): + """return a list of supported driver modules. + """ + pass diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/origin_driver.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/origin_driver.py new file mode 100644 index 00000000..bf3c15c8 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/origin_driver.py @@ -0,0 +1,50 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from vstf.agent.env.driver_plugins import model +from vstf.common.utils import check_and_rmmod, check_call + + +class OriginDriverPlugin(model.DriverPlugin): + """ + implement for operating linux origin driver modules. + """ + + def __init__(self): + """ + list all origin drivers in self.origin_drivers + """ + self.origin_drivers = ['ixgbe', 'bnx2x', 'i40e', 'be2net', 'vhost_net'] + + def clean(self): + """clean drivers list in self.origin_drivers. + + """ + for mod in self.origin_drivers: + check_and_rmmod(mod) + + check_and_rmmod('tun') + return True + + def load(self, drivers): + """insmod drivers + + :param list drivers:list of drivers link ['ixgbe','vhost_net'] + """ + # load implicit 'tun' module dependency for vhost_net + if 'vhost_net' in drivers: + check_call("modprobe tun", shell=True) + + for drv in drivers: + check_call("modprobe %s" % drv, shell=True) + + return True + + def get_supported_drivers(self): + return self.origin_drivers diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/FSMonitor.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/FSMonitor.py new file mode 100644 index 00000000..e6559362 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/FSMonitor.py @@ -0,0 +1,220 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +import os +import time +import logging +import subprocess +import sys + +import constant +from utils import IPCommandHelper, umount, check_and_rmmod, check_output, check_call, call + +LOG_FILE = '/tmp/fsmonitor.log' +PID_FILE = '/tmp/fsmonitor.pid' +LOG = logging.getLogger('__name__') + + +class VMOperation(object): + def __init__(self): + self.RTE_SDK = '/home/dpdk-2.0.0' + self.RTE_TARGET = 'x86_64-native-linuxapp-gcc' + self.nr_hugepages = '512' + self.pid = 0 + self.ip_helper = IPCommandHelper() + + def config_ip(self, mac, ip): + device = self.ip_helper.mac_device_map[mac] + check_call("ifconfig %s %s up" % (device, ip), shell=True) + + def config_gw(self, ip): + call("route del default", shell=True) + check_call("route add default gw %s" % ip, shell=True) + + def recover_nic_binding(self, *tap_macs): + if self.pid: + os.kill(self.pid, 9) + self.pid = None + bdf_str = '' + for mac in tap_macs: + bdf = self.ip_helper.mac_bdf_map[mac] + bdf_str = bdf_str + ' ' + bdf + cmd = 'python %s/tools/dpdk_nic_bind.py --bind=virtio-pci %s' % (self.RTE_SDK, bdf_str) + LOG.debug("recover_nic_binding runs cmd = %s", cmd) + check_call(cmd, shell=True) + + def set_pktloop_dpdk(self, *tap_macs): + RTE_SDK = self.RTE_SDK + RTE_TARGET = self.RTE_TARGET + umount("/mnt/huge") + with open('/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages', 'w') as f: + f.write(self.nr_hugepages) + check_call("mkdir -p /mnt/huge", shell=True) + check_call("mount -t hugetlbfs nodev /mnt/huge", shell=True) + check_call("modprobe uio", shell=True) + check_and_rmmod('igb_uio') + check_call("insmod %s/%s/kmod/igb_uio.ko" % (RTE_SDK, RTE_TARGET), shell=True) + + bdf_str = '' + for mac in tap_macs: + bdf = self.ip_helper.mac_bdf_map[mac] + bdf_str = bdf_str + ' ' + bdf + + check_call('python %s/tools/dpdk_nic_bind.py --bind=igb_uio %s' % (RTE_SDK, bdf_str), shell=True) + cpu_num = int(check_output('cat /proc/cpuinfo | grep processor | wc -l', shell=True)) + cpu_bit_mask = 0 + i = cpu_num + while i: + cpu_bit_mask = (cpu_bit_mask << 1) + 1 + i -= 1 + cpu_bit_mask = hex(cpu_bit_mask) + cmd = "%s/%s/app/testpmd -c %s -n %d -- --disable-hw-vlan --disable-rss --nb-cores=%d --rxq=%d --txq=%d --rxd=4096 --txd=4096" % ( + RTE_SDK, + RTE_TARGET, + cpu_bit_mask, + cpu_num / 2, + cpu_num - 1, + (cpu_num - 1) / 2, + (cpu_num - 1) / 2 + ) + LOG.info("set_pktloop_dpdk runs cmd = %s", cmd) + p = subprocess.Popen(cmd.split()) + if not p.poll(): + self.pid = p.pid + return True + else: + raise Exception("start testpmd failed") + + def config_amqp(self, file_name): + if not os.path.isfile(file_name): + raise Exception("file: %s not exists." % file_name) + check_call("cp %s /etc/vstf/amqp/amqp.ini" % file_name, shell=True) + check_call("vstf-agent restart", shell=True) + return True + + def stop_vstf(self): + check_call("vstf-agent stop", shell=True) + return True + + +class FSMonitor(object): + def __init__(self, pidfile=None, interval=1): + if pidfile: + self.pidfile = pidfile + else: + self.pidfile = PID_FILE + self.interval = interval + self.handlers = [] + self.kill_old() + umount(constant.FS_MOUNT_POINT) + check_call("mkdir -p %s" % constant.FS_MOUNT_POINT, shell=True) + check_call("mount -t 9p 9pfs %s" % constant.FS_MOUNT_POINT, shell=True) + os.chdir(constant.FS_MOUNT_POINT) + with open(constant.VM_UP_Flag_FILE, 'w'): + pass + + def kill_old(self): + out = check_output("ps -ef | grep -v grep | egrep 'python.*%s' | awk '{print $2}'" % sys.argv[0], + shell=True) + if out: + for pid in out.split(): + if int(pid) != os.getpid(): + os.kill(int(pid), 9) + LOG.debug("found daemon:pid=%s and kill.", pid) + + def set_fail(self, failed_reason): + with open(constant.VM_CMD_RETURN_CODE_FILE, 'w') as f: + f.writelines([constant.VM_CMD_EXCUTE_FAILED_FLAG_CONTENT, '\n', failed_reason]) + with open(constant.VM_CMD_DONE_FLAG_FILE, 'w') as f: + pass + + def set_success(self): + with open(constant.VM_CMD_RETURN_CODE_FILE, 'w') as f: + f.write(constant.VM_CMD_EXCUTE_SUCCES_FLAG_CONTENT) + with open(constant.VM_CMD_DONE_FLAG_FILE, 'w') as f: + pass + + def register_handler(self, obj): + self.handlers.append(obj) + + def daemonize(self): + try: + pid = os.fork() + if pid > 0: + sys.exit(0) + except OSError, e: + sys.stderr.write('fork #1 failed:%d,(%s)\n' % (e.errno, e.strerror)) + sys.exit(1) + os.setsid() + os.umask(0) + try: + pid = os.fork() + if pid > 0: + sys.exit(0) + except OSError, e: + sys.stderr.write('fork #2 failed:%d,(%s)\n' % (e.errno, e.strerror)) + sys.exit(1) + LOG.debug("pid:%d,ppid:%d,sid:%d", os.getpid(), os.getppid(), os.getsid(os.getpid())) + old = open('/dev/null', 'r') + os.dup2(old.fileno(), sys.stdin.fileno()) + old = open('/dev/null', 'a+') + os.dup2(old.fileno(), sys.stdout.fileno()) + old = open('/dev/null', 'a+', 0) + os.dup2(old.fileno(), sys.stderr.fileno()) + pid = str(os.getpid()) + file(self.pidfile, 'w+').write('%s\n' % pid) + + def run_forever(self): + # todo:resolve handlers name space conflict + self.daemonize() + while True: + time.sleep(self.interval) + files = os.listdir(constant.FS_MOUNT_POINT) + if constant.VM_CMD_SET_FLAG_FILE in files and constant.VM_CMD_CONTENT_FILE in files: + with open(constant.VM_CMD_CONTENT_FILE, 'r') as f: + out = f.read().strip() + LOG.debug("new command arrived:%s", out) + cmd_param = out.split() + cmd = cmd_param[0] + param = cmd_param[1:] + for obj in self.handlers: + if hasattr(obj, cmd) and callable(getattr(obj, cmd)): + LOG.debug("method:%s found!", cmd) + method = getattr(obj, cmd) + try: + method(*param) + self.set_success() + LOG.debug("cmd sucessfully done") + except Exception, e: + LOG.debug('failed to run:%s %s,reason:%s', cmd, param, str(e)) + self.set_fail(str(e)) + break + else: + LOG.debug("method:%s not found!", cmd) + self.set_fail(constant.VM_CMD_NOT_FOUND) + os.remove(constant.VM_CMD_SET_FLAG_FILE) + os.remove(constant.VM_CMD_CONTENT_FILE) + + +if __name__ == '__main__': + # echo "set_pktloop_dpdk" > command;touch command_set + # echo "recover_nic_binding" > command;touch command_set + # echo "config_ip 56:6f:44:a5:3f:a2 192.168.188.200/23" > command;touch command_set + # echo "config_gw 192.168.188.1" > command;touch command_set + # echo set_pktloop_dpdk 56:6f:44:a5:3f:a2 56:6f:44:a5:3f:a3 > command;touch command_set + # echo recover_nic_binding 56:6f:44:a5:3f:a2 56:6f:44:a5:3f:a3 > command;touch command_set + import os + logging.basicConfig(level=logging.DEBUG, filename=LOG_FILE, filemode='w') + os.environ['PATH'] = os.environ["PATH"] + ":/usr/local/bin" + LOG.info(os.environ['PATH']) + vm_op = VMOperation() + agent = FSMonitor() + agent.register_handler(vm_op) + agent.run_forever() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/__init__.py new file mode 100644 index 00000000..83b8d15d --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/constant.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/constant.py new file mode 100644 index 00000000..33b37eb4 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/constant.py @@ -0,0 +1,21 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +VM_UP_Flag_FILE = 'up' +VM_CMD_DONE_FLAG_FILE = 'command_done' +VM_CMD_RESULT_FILE = 'command_result_data' +VM_CMD_SET_FLAG_FILE = 'command_set' +VM_CMD_CONTENT_FILE = 'command' +VM_CMD_RETURN_CODE_FILE = 'command_result' +VM_CMD_EXCUTE_SUCCES_FLAG_CONTENT = 'success' +VM_CMD_EXCUTE_FAILED_FLAG_CONTENT = 'fail' +VM_CMD_NOT_FOUND = 'comamnd_not_found' +VM_UP_TIME_OUT = 120 +VM_COMMON_CMD_EXCUTE_TIME_OUT = 10 +FS_MOUNT_POINT = '/mnt/9pfs'
\ No newline at end of file diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/utils.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/utils.py new file mode 100644 index 00000000..5bdb4159 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/utils.py @@ -0,0 +1,114 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess +from StringIO import StringIO +import re +import logging + +LOG = logging.getLogger(__name__) + + +def call(cmd, shell=False): + if shell: + LOG.info(cmd) + else: + LOG.info(' '.join(cmd)) + return subprocess.call(cmd, shell=shell) + + +def check_call(cmd, shell=False): + if shell: + LOG.info(cmd) + else: + LOG.info(' '.join(cmd)) + subprocess.check_call(cmd, shell=shell) + + +def check_output(cmd, shell=False): + if shell: + LOG.info(cmd) + else: + LOG.info(' '.join(cmd)) + return subprocess.check_output(cmd, shell=shell) + + +def check_and_kill(process): + cmd = "ps -ef | grep -v grep | awk '{print $8}' | grep -w %s | wc -l" % process + out = check_output(cmd, shell=True) + if int(out): + check_call(['killall', process]) + + +def check_and_rmmod(mod): + cmd = "lsmod | awk '{print $1}' | grep -w %s | wc -l" % mod + out = check_output(cmd, shell=True) + if int(out): + check_call(['rmmod', mod]) + + +def umount(path): + mount_path_set = set() + out = check_output("cat /proc/mounts", shell=True) + f = StringIO(out) + line = f.readline() + while line: + line = f.readline() + if line: + mpath = line.split()[1] + mount_path_set.add(mpath) + if path in mount_path_set: + ret = call("umount %s" % path, shell=True) + return ret == 0 + return True + + +class IPCommandHelper(object): + def __init__(self): + self.devices = [] + self.macs = [] + self.device_mac_map = {} + self.mac_device_map = {} + self.bdf_device_map = {} + self.device_bdf_map = {} + self.mac_bdf_map = {} + self.bdf_mac_map = {} + buf = check_output("ip link", shell=True) + macs = re.compile("[A-F0-9]{2}(?::[A-F0-9]{2}){5}", re.IGNORECASE | re.MULTILINE) + for mac in macs.findall(buf): + if mac.lower() in ('00:00:00:00:00:00', 'ff:ff:ff:ff:ff:ff'): + continue + self.macs.append(mac) + sio = StringIO(buf) + for line in sio: + m = re.match(r'^\d+:(.*):.*', line) + if m and m.group(1).strip() != 'lo': + self.devices.append(m.group(1).strip()) + for device, mac in zip(self.devices, self.macs): + self.device_mac_map[device] = mac + self.mac_device_map[mac] = device + for device in self.devices: + buf = check_output("ethtool -i %s" % device, shell=True) + bdfs = re.findall(r'^bus-info: \d{4}:(\d{2}:\d{2}\.\d*)$', buf, re.MULTILINE) + if bdfs: + self.bdf_device_map[bdfs[0]] = device + self.device_bdf_map[device] = bdfs[0] + mac = self.device_mac_map[device] + self.mac_bdf_map[mac] = bdfs[0] + self.bdf_mac_map[bdfs[0]] = mac + + +if __name__ == '__main__': + ip_helper = IPCommandHelper() + print ip_helper.device_mac_map + print ip_helper.mac_device_map + print ip_helper.bdf_device_map + print ip_helper.device_bdf_map + print ip_helper.mac_bdf_map + print ip_helper.bdf_mac_map diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/__init__.py new file mode 100644 index 00000000..83b8d15d --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/libvirt_plugin.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/libvirt_plugin.py new file mode 100644 index 00000000..27af8063 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/libvirt_plugin.py @@ -0,0 +1,70 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import logging + +from vstf.common.utils import ping, my_sleep +from vstf.agent.env.plugins.model import EnvBuilderPlugin +from vstf.agent.env.basic.source_manager import SourceCodeManager +from vstf.agent.env.basic.vm_manager import VMControlOperation +from vstf.agent.env.vswitch_plugins.manager import VswitchPluginManager +from vstf.agent.env.driver_plugins.manager import DriverPluginManager + +LOG = logging.getLogger(__name__) + + +class Plugin(EnvBuilderPlugin): + def __init__(self): + super(Plugin, self).__init__() + self.vm_mgr = VMControlOperation() + self.vs_mgr = VswitchPluginManager() + self.dr_mgr = DriverPluginManager() + + def clean(self): + self.vm_mgr.clean_all_vms() + self.vs_mgr.clean() + self.dr_mgr.clean() + + def load_drivers(self): + drivers = self.host_cfg['drivers'] + self.dr_mgr.load(drivers) + + def create_brs(self): + for br_cfg in self.host_cfg['bridges']: + plugin = self.vs_mgr.get_vs_plugin(br_cfg['type']) + plugin.create_br(br_cfg) + + def config_br_ports(self): + for vm_cfg in self.host_cfg['vms']: + for tap_cfg in vm_cfg['taps']: + plugin = self.vs_mgr.get_vs_plugin(tap_cfg['br_type']) + plugin.set_tap_vid(tap_cfg) + for br_cfg in self.host_cfg['bridges']: + plugin = self.vs_mgr.get_vs_plugin(br_cfg['type']) + plugin.set_fastlink(br_cfg) + + def create_vms(self): + for vm_cfg in self.host_cfg['vms']: + self.vm_mgr.create_vm(vm_cfg) + + def wait_vms(self): + for vm_cfg in self.host_cfg['vms']: + self.vm_mgr.wait_vm(vm_cfg['vm_name']) + self.vm_mgr.init_config_vm(vm_cfg['vm_name']) + + def check_vm_connectivity(self): + for vm_cfg in self.host_cfg['vms']: + vm_ip = vm_cfg['init_config']['ctrl_ip_setting'].split('/')[0] + for _ in range(3): + ret = ping(vm_ip) + if ret: + break + my_sleep(3) + else: + raise Exception("ping ip:%s failed." % vm_ip) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/model.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/model.py new file mode 100644 index 00000000..b19ceb96 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/model.py @@ -0,0 +1,58 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from abc import ABCMeta +from abc import abstractmethod + + +class EnvBuilderPlugin: + __metaclass__ = ABCMeta + + def __init__(self): + self.host_cfg = None + pass + + @abstractmethod + def clean(self): + pass + + @abstractmethod + def load_drivers(self): + pass + + @abstractmethod + def create_brs(self): + pass + + @abstractmethod + def config_br_ports(self): + pass + + @abstractmethod + def create_vms(self): + pass + + @abstractmethod + def wait_vms(self): + pass + + @abstractmethod + def check_vm_connectivity(self): + pass + + def build(self, cfg_intent): + self.host_cfg = cfg_intent + self.clean() + self.load_drivers() + self.create_brs() + self.create_vms() + self.wait_vms() + self.config_br_ports() + self.check_vm_connectivity() + return True diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/tester_env_plugin.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/tester_env_plugin.py new file mode 100644 index 00000000..0682aac8 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/tester_env_plugin.py @@ -0,0 +1,46 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import logging + +from vstf.agent.env.plugins.model import EnvBuilderPlugin +from vstf.agent.env.driver_plugins.manager import DriverPluginManager + +LOG = logging.getLogger(__name__) + + +class Plugin(EnvBuilderPlugin): + def __init__(self): + super(Plugin, self).__init__() + self.dr_mgr = DriverPluginManager() + + def clean(self): + self.dr_mgr.clean() + + def install(self): + pass + + def load_drivers(self): + drivers = self.host_cfg['drivers'] + self.dr_mgr.load(drivers) + + def create_brs(self): + pass + + def config_br_ports(self): + pass + + def create_vms(self): + pass + + def wait_vms(self): + pass + + def check_vm_connectivity(self): + pass diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/__init__.py new file mode 100644 index 00000000..83b8d15d --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/bridge_plugin.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/bridge_plugin.py new file mode 100644 index 00000000..21b8f82c --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/bridge_plugin.py @@ -0,0 +1,71 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from vstf.agent.env.vswitch_plugins import model +from vstf.common.utils import check_call, get_eth_by_bdf, check_output + + +class BridgePlugin(model.VswitchPlugin): + def __init__(self): + pass + + def clean(self): + """clean brs created before. + + """ + out = check_output(r"brctl show | grep -v '^\s' | awk '{print $1}'|sed '1,1d'", shell=True) + print out + for br in out.split(): + if br != 'br0': + self._del_br(br) + + return True + + def init(self): + pass + + def _del_br(self, name): + check_call('ip link set dev %s down' % name, shell=True) + check_call('brctl delbr %s' % name, shell=True) + + def create_br(self, br_cfg): + """Create a bridge(virtual switch). Return True for success, return False for failure. + + :param dict br_cfg: configuration for bridge creation like + { + "name": "br1", + "uplinks": [ + { + "bdf": "04:00.0", + }, + { + "bdf": "04:00.1", + } + ] + } + + """ + name, uplinks = br_cfg['name'], br_cfg['uplinks'] + check_call("brctl addbr %s" % name, shell=True) + for uplink in uplinks: + device = get_eth_by_bdf(uplink['bdf']) + check_call("ip link set dev %s up" % device, shell=True) + check_call("brctl addif %s %s" % (name, device), shell=True) + check_call("ip link set dev %s up" % name, shell=True) + return True + + def set_tap_vid(self, tap_cfg): + """linux bridge doesn't support vlan id setting. + """ + return True + + def set_fastlink(self, br_cfg): + """linux bridge doesn't support openflow protocol. + """ + return True diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/manager.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/manager.py new file mode 100644 index 00000000..785a1db8 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/manager.py @@ -0,0 +1,35 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import stevedore + + +class VswitchPluginManager(object): + def __init__(self): + self.plugin = None + self.mgr = stevedore.extension.ExtensionManager(namespace="vswitch.plugins", invoke_on_load=True) + + def clean(self): + if self.plugin: + self.plugin.clean() + self.plugin = None + for plugin in self.mgr.names(): + self.mgr[plugin].obj.clean() + return True + + def get_vs_plugin(self, plugin): + if plugin in self.mgr.names(): + ext = self.mgr[plugin] + self.plugin = ext.obj + return self.plugin + else: + raise Exception("unsupported vswitch plugin: %s" % plugin) + + def get_supported_plugins(self): + return self.mgr.names() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/model.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/model.py new file mode 100644 index 00000000..5d700411 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/model.py @@ -0,0 +1,67 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from abc import ABCMeta +from abc import abstractmethod + + +class VswitchPlugin: + __metaclass__ = ABCMeta + + @abstractmethod + def clean(self): + """implement this clean function to clean environment before and after calling any other functions. + + """ + pass + + @abstractmethod + def init(self): + """implements this init function to setup necessary Preconditions. + + """ + pass + + @abstractmethod + def create_br(self, br_cfg): + """Create a bridge(virtual switch). Return True for success, return False for failure. + + :param dict br_cfg: configuration for bridge creation like + { + "type": "ovs", + "name": "ovs1", + "uplinks": [ + { + "bdf": "04:00.0", + "vlan_mode": "access", + "vlan_id": "1" + } + ], + "vtep": {}, + } + + """ + pass + + @abstractmethod + def set_tap_vid(self, tap_cfg): + """set vlan id or vxlan id for tap device(virtual nic for vm). + + :param dict tap_cfg: dictionary config for tap device like + { + "tap_name": "tap_in", + "vlan_mode": "access", + "vlan_id": "1" + } + + """ + pass + + def set_fastlink(self, br_cfg): + return True
\ No newline at end of file diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/ovs_plugin.py b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/ovs_plugin.py new file mode 100644 index 00000000..7ea56d4a --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/ovs_plugin.py @@ -0,0 +1,187 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import shutil +import logging +import time +import re + +from vstf.agent.env.vswitch_plugins import model +from vstf.common.utils import check_and_kill, check_and_rmmod, check_call, check_output, \ + get_eth_by_bdf, my_mkdir, call + +LOG = logging.getLogger(__name__) + + +class OvsPlugin(model.VswitchPlugin): + + def __init__(self): + self.daemons = ['ovs-vswitchd', 'ovsdb-server'] + self.mods = ['openvswitch'] + self.dirs = {'db': "/usr/local/etc/openvswitch"} + self.cmds = [] + self.cmds.append("mkdir -p /usr/local/etc/openvswitch") + self.cmds.append("ovsdb-tool create /usr/local/etc/openvswitch/conf.db") + self.cmds.append("ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \ + --remote=db:Open_vSwitch,Open_vSwitch,manager_options \ + --private-key=db:Open_vSwitch,SSL,private_key \ + --certificate=db:Open_vSwitch,SSL,certificate \ + --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert \ + --pidfile --detach") + self.cmds.append("ovs-vsctl --no-wait init") + self.cmds.append("ovs-vswitchd --pidfile --detach") + self.initialized = False + + def init(self): + if not self.initialized: + self._start_servers() + self.initialized = True + + def clean(self): + """clean for ovs. Rmmod openvswitch.ko, kill openvswitch daemon process. + + """ + for process in self.daemons: + check_and_kill(process) + for mod in self.mods: + check_and_rmmod(mod) + for _, directory in self.dirs.items(): + if os.path.isdir(directory): + LOG.info('rm -rf %s', directory) + shutil.rmtree(directory, ignore_errors=True) + self.initialized = False + return True + + def create_br(self, br_cfg): + """Create a bridge(virtual switch). Return True for success, return False for failure. + + :param dict br_cfg: configuration for bridge creation like + { + "type": "ovs", + "name": "ovs1", + "uplinks": [ + { + "bdf": "04:00.0", + "vlan_mode": "access", + "vlan_id": "1" + } + ], + "vtep": {}, + } + + """ + self.init() + name, uplinks = br_cfg['name'], br_cfg['uplinks'] + + check_call("ovs-vsctl add-br %s" % (name), shell=True) + if br_cfg['vtep']: # vxlan supports + local_ip, remote_ip = br_cfg['vtep']['local_ip'], br_cfg['vtep']['remote_ip'] + assert len(uplinks) == 1 + uplink = uplinks[0] + device = get_eth_by_bdf(uplink['bdf']) + time.sleep(0.5) + vtep = 'vx1' + check_call("ifconfig %s %s up" % (device, local_ip), shell=True) + check_call("ovs-vsctl add-port %s %s" % (name, vtep), shell=True) + check_call("ovs-vsctl set interface %s type=vxlan options:remote_ip=%s" % (vtep, remote_ip), shell=True) + for uplink in uplinks: + device = get_eth_by_bdf(uplink['bdf']) + vlan_mode = uplink['vlan_mode'] + vlan_id = uplink['vlan_id'] + check_call("ip link set dev %s up" % device, shell=True) + call("ethtool -A %s rx off tx off " % device, shell=True) + check_call("ovs-vsctl add-port %s %s" % (name, device), shell=True) + if vlan_mode == 'trunk': + check_call("ovs-vsctl set port %s trunks=%s" % (device, vlan_id), shell=True) + elif vlan_mode == 'access': + check_call("ovs-vsctl set port %s tag=%s" % (device, vlan_id), shell=True) + else: + raise Exception("unreconized vlan_mode:%s" % vlan_mode) + return True + + def set_tap_vid(self, tap_cfg): + """set vlan id or vxlan id for tap device(virtual nic for vm). + return True for success, return False for failure. + + :param dict tap_cfg: dictionary config for tap device like + { + "tap_name": "tap_in", + "vlan_mode": "access", + "vlan_id": "1" + } + + """ + port, vlan_mode, vlan = tap_cfg['tap_name'], tap_cfg['vlan_mode'], tap_cfg['vlan_id'] + assert vlan_mode in ('access', 'vxlan') + if int(vlan) > '4095': + # vxlan setting + self.__set_tap_vid(port, "vxlan", vlan) + else: + # vlan setting + self.__set_tap_vid(port, vlan_mode, vlan) + return True + + def set_fastlink(self, br_cfg): + """connect two ports directly, so that packets comes from any one port be forwarded to the other. + return True for success, return False for failure. + + :param dict br_cfg: dictionary configuration for linking ports. + { + "name": "ovs1", + "fastlink": [ + { + "inport": "04:00.0", + "outport": "tap_in" + } + ] + } + """ + br_name = br_cfg['name'] + for fast_cfg in br_cfg['fastlink']: + p1, p2 = fast_cfg['inport'], fast_cfg['outport'] + self.__fastlink(br_name, p1, p2) + return True + + def _start_servers(self): + for _, directory in self.dirs.items(): + my_mkdir(directory) + for mod in self.mods: + check_call("modprobe %s" % mod, shell=True) + for cmd in self.cmds: + check_call(cmd, shell=True) + return True + + def __set_tap_vid(self, port, vlan_mode, vlan_id): + if vlan_mode == 'vxlan': + raise Exception("don't support vxlan setting right now.") + elif vlan_mode == 'trunk': + check_call("ovs-vsctl set port %s trunks=%s" % (port, vlan_id), shell=True) + else: + check_call("ovs-vsctl set port %s tag=%s" % (port, vlan_id), shell=True) + + def __fastlink(self, br, p1, p2): + LOG.info("_fastlink(%s,%s,%s)", br, p1, p2) + p1 = p1.replace(' ', '') + p2 = p2.replace(' ', '') + bdfs = check_output("lspci |grep Eth | awk '{print $1}'", shell=True).splitlines() + if p1 in bdfs: + p1 = get_eth_by_bdf(p1) + if p2 in bdfs: + p2 = get_eth_by_bdf(p2) + ovs_port = {} + buf = check_output("ovs-ofctl show %s" % br, shell=True) + port_info = re.compile(r"[0-9]+\(.*\)", re.IGNORECASE | re.MULTILINE) + for s in port_info.findall(buf): + port_num, interface = s.replace('(', ' ').replace(')', ' ').split() + ovs_port[interface] = port_num + pn1, pn2 = ovs_port[p1], ovs_port[p2] + check_call("ovs-ofctl add-flow %s in_port=%s,priority=100,action=output:%s" % (br, pn1, pn2), shell=True) + check_call("ovs-ofctl add-flow %s in_port=%s,priority=100,action=output:%s" % (br, pn2, pn1), shell=True) + return True diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/__init__.py new file mode 100644 index 00000000..df7d24d0 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/__init__.py @@ -0,0 +1,9 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/equalizer.py b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/equalizer.py new file mode 100644 index 00000000..30e1de1f --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/equalizer.py @@ -0,0 +1,74 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import re +import subprocess +import logging + +log = logging.getLogger(__name__) + + +def run_cmd(cmd, shell=True): + try: + ret = subprocess.check_output(cmd, shell=shell) + except subprocess.CalledProcessError as e: + raise e + return ret + + +class Resource(object): + def __init__(self): + super(Resource, self).__init__() + self.sysfs = "/sys/devices/system/node" + self.mapping = {} + for node in self._init_numa(): + self.mapping[node] = {} + + process_mapping = self._get_process_mapping(node) + for process_index in xrange(0, len(bin(process_mapping)) - 2): + if process_mapping & 1 << process_index != 0: + core = self._get_core_id(node, process_index) + if not self.mapping[node].has_key(core): + self.mapping[node][core] = [] + self.mapping[node][core].append(process_index) + + def _get_process_mapping(self, numa_node): + ret = run_cmd("cat " + self.sysfs + '/' + numa_node + '/cpumap').replace(',', '').lstrip('0') + return int(ret, 16) + + def _get_core_id(self, numa_node, process_index): + cmd = "cat " + self.sysfs + '/' + numa_node + '/cpu' + str(process_index) + '/topology/core_id' + return run_cmd(cmd).strip('\n') + + def _init_numa(self): + """the node name is node0, node1......""" + try: + node_list = os.listdir(self.sysfs) + except Exception as e: + raise e + ret = [] + partten = re.compile("^node[0-9]{,}$") + for node in node_list: + if partten.match(node) is None: + continue + ret.append(node) + return ret + + +class Equalizer(Resource): + def __init__(self): + super(Equalizer, self).__init__() + + def topology(self): + print self.mapping + + +e = Equalizer() +e.topology() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/get_info.py b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/get_info.py new file mode 100644 index 00000000..0c92f979 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/get_info.py @@ -0,0 +1,170 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import commands + +try: + import xml.etree.cElementTree as ET +except ImportError: + import xml.etree.ElementTree as ET + + +class GetPhyInfo(object): + def __init__(self): + pass + + def _get_range(self, temp): + topo = {} + phy_core_flag = True + for sub in temp.split(','): + r_list = [] + _start = sub.split('-')[0] + _end = sub.split('-')[1] + r_list.extend(range(int(_start), int(_end) + 1)) + if phy_core_flag: + topo['phy_cores'] = r_list + else: + topo['virt_cores'] = r_list + phy_core_flag = False + return topo + + def _get_numa_num(self): + flag, num = commands.getstatusoutput('lscpu | grep "NUMA node(s):"') + try: + num = num.split(':')[1] + except: + print('get numa %s value failed.' % (num)) + return num + + def get_numa_core(self): + numa = {} + num = self._get_numa_num() + for numa_id in range(0, int(num)): + flag, temp = commands.getstatusoutput('lscpu | grep "NUMA node%s"' % (str(numa_id))) + try: + temp = temp.split(':')[1].split()[0] + except: + print('get numa %s range %s failed.' % (str(numa_id), range)) + topo = self._get_range(temp) + numa['node' + str(numa_id)] = topo + return str(numa) + + def get_nic_numa(self, nic): + result = {} + try: + flag, id = commands.getstatusoutput('cat /sys/class/net/%s/device/numa_node' % (nic)) + except: + print('get nic numa id failed.') + return id + + def _get_main_pid(self, xml_file): + try: + tree = ET.ElementTree(file=xml_file) + root = tree.getroot() + _main_pid = root.attrib['pid'] + except: + print('[ERROR]Parse xml file failed, could not get qemu main pid') + return _main_pid + + def _get_qemu_threads(self, xml_file): + # import pdb + # pdb.set_trace() + _qemu_threads = [] + try: + tree = ET.ElementTree(file=xml_file) + root = tree.getroot() + for element in tree.iterfind('vcpus/vcpu'): + _qemu_threads.append(element.attrib['pid']) + except: + print('[ERROR]Parse xml file failed, could not get qemu threads.') + + return _qemu_threads + + def _get_mem_numa(self, xml_file): + try: + _mem_numa = None + tree = ET.ElementTree(file=xml_file) + root = tree.getroot() + for element in tree.iterfind('domain/numatune/memory'): + _mem_numa = element.attrib['nodeset'] + finally: + return _mem_numa + + def _get_vhost_threads(self, xml_file): + _vhost = [] + _main_pid = self._get_main_pid(xml_file) + + # get vhost info + proc_name = 'vhost-' + _main_pid + flag, temp = commands.getstatusoutput('ps -ef | grep %s | grep -v grep' % (proc_name)) + for line in temp.split('\n'): + try: + vhost = line.split()[1] + _vhost.append(vhost) + except: + print('get vhost %s proc id failed' % (line)) + + return _vhost + + def get_vm_info(self, vm_name): + vm = {} + src_path = '/var/run/libvirt/qemu/' + xml_file = src_path + vm_name + '.xml' + + # get vm main pid from file + _main_pid = self._get_main_pid(xml_file) + # get vm vcpu thread from the libvirt file + _qemu_threads = self._get_qemu_threads(xml_file) + # get vm bind mem numa id + _mem_numa = self._get_mem_numa(xml_file) + # get vhost thread + _vhosts = self._get_vhost_threads(xml_file) + + vm['main_pid'] = _main_pid + vm['qemu_thread'] = _qemu_threads + vm['mem_numa'] = _mem_numa + vm['vhost_thread'] = _vhosts + return vm + + def _get_proc_by_irq(self, irq): + try: + flag, info = commands.getstatusoutput('ps -ef | grep irq/%s | grep -v grep ' % (irq)) + proc_id = info.split('\n')[0].split()[1] + except: + print("[ERROR]grep process id failed.") + return proc_id + + def get_nic_interrupt_proc(self, nic): + _phy_nic_thread = [] + flag, info = commands.getstatusoutput('cat /proc/interrupts | grep %s' % (nic)) + for line in info.split('\n'): + try: + irq_num = line.split(':')[0].split()[0] + proc_id = self._get_proc_by_irq(irq_num) + _phy_nic_thread.append([irq_num, proc_id]) + except: + print("[ERROR]get irq num failed.") + return _phy_nic_thread + + def get_libvirt_vms(self): + vm_list = [] + flag, info = commands.getstatusoutput('virsh list') + list = info.split('\n') + if list[-1] == '': + list.pop() + del list[0] + del list[0] + + for line in list: + try: + vm_temp = line.split()[1] + vm_list.append(vm_temp) + except: + print("Get vm name failed from %s" % (line)) + return vm_list diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/optimize.py b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/optimize.py new file mode 100644 index 00000000..5a09900d --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/equalizer/optimize.py @@ -0,0 +1,62 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import commands +import re + + +# import pdb +# pdb.set_trace() + +class Optimize(object): + def __init__(self): + pass + + def bind_cpu(self, cpu_range, thread): + flag, num = commands.getstatusoutput('taskset -pc %s %s' % (cpu_range, thread)) + return flag + + def catch_thread_info(self): + thread_info = {'fwd_vhost': None, 'src_recv_irq': None, 'dst_send_irq': None} + # top -H get the usage info + flag, threads_usages = commands.getstatusoutput('top -bH -n1 -c -w 2000') + line_array = threads_usages.split('\n') + # get highest vhost line + for line in line_array: + if re.search('vhost-', line) and self._check_thread_usage(line): + thread_info['fwd_vhost'] = line.split()[0] + break + # get highest irq thread as src_recv_irq thread + for line in line_array: + if re.search('irq/', line) and self._check_thread_usage(line): + thread_info['src_recv_irq'] = line.split()[0] + line_array.remove(line) + break + # get the second highest irq thread as dst_send_irq + for line in line_array: + if re.search('irq/', line) and self._check_thread_usage(line): + thread_info['dst_send_irq'] = line.split()[0] + break + # check the data valid + + for key in thread_info.keys(): + if thread_info[key] is None: + return False, str(thread_info) + return True, str(thread_info) + + def _check_thread_usage(self, line): + try: + usage = line.split()[8] + if float(usage) >= 3.0: + return True + else: + print("[ERROR]The highest thread %s is less than 0.05" % usage) + return False + except: + print("[ERROR]The thread usage get failed.") diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/__init__.py new file mode 100644 index 00000000..df7d24d0 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/__init__.py @@ -0,0 +1,9 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/affctl.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/affctl.py new file mode 100644 index 00000000..5b203632 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/affctl.py @@ -0,0 +1,21 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from vstf.common.utils import check_call, call, check_output + + +def affctl_load(policy): + cmd = "affctl load %s" % policy + return check_call(cmd, shell=True) + + +def affctl_list(): + cmd = "affctl list" + return check_output(cmd, shell=True) + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/ethtool.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/ethtool.py new file mode 100644 index 00000000..3f4a3728 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/ethtool.py @@ -0,0 +1,59 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import vstf.common.utils as utils + +__all__ = ["autoneg_on", "autoneg_off", "autoneg_query"] + +_para_map = { + "Autonegotiate": ("-A", "-a", "autoneg"), + "RX": ("-A", "-a", "rx"), + "TX": ("-A", "-a", "tx"), +} + + +def autoneg_on(iface, nspace=None): + return _set(nspace, iface, Autonegotiate="on", RX="on", TX="on") + + +def autoneg_off(iface, nspace=None): + return _set(nspace, iface, Autonegotiate="off", RX="off", TX="off") + + +def autoneg_query(iface, nspace=None): + return _query(nspace, iface, "-a") + + +def _set(nspace, iface, **kwargs): + cmds = {} + for item, value in kwargs.items(): + opt, _, key = _para_map[item] + cmds.setdefault(opt, []) + cmds[opt].append(key) + cmds[opt].append(value) + + for key, value in cmds.items(): + cmd = _namespace(nspace) + cmd += ["ethtool", key, iface] + value + utils.call(cmd) + + return True + + +def _query(nspace, iface, item): + cmd = _namespace(nspace) + cmd += ["ethtool", item, iface] + return utils.check_output(cmd) + + +def _namespace(nspace): + result = "" + if nspace: + result = "ip netns exec %(namespace)s " % {"namespace": nspace} + return result.split() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/iperf.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/iperf.py new file mode 100644 index 00000000..3105be4b --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/iperf.py @@ -0,0 +1,155 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess +import signal +import os +import time +import logging + +import vstf.common.decorator as deco +import vstf.agent.perf.utils as utils +from vstf.common.utils import kill_by_name + +LOG = logging.getLogger(__name__) + + +class Iperf(object): + def __init__(self): + self._send_processes = [] + self._receive_processes = [] + self._typemap = { + "tcp_bw": "", + "udp_bw": " -u ", + } + + @deco.check("protocol", choices=['tcp_bw', 'udp_bw']) + @deco.check("namespace", defaults=None) + @deco.check("dst") + @deco.check("time", defaults=600) + @deco.check("size", defaults=64) + @deco.check("threads", defaults=1) + def send_start(self, **kwargs): + + cmd = self.format_send_start(**kwargs) + LOG.debug("cmd:%s", cmd) + + process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(1) + ret = process.poll() + if ret is None: + ret = 0 + error_str = "start iperf send success" + self._send_processes.append(process) + else: + print ret + error_str = "start iperf send failed, %s", (str(kwargs)) + + return ret, error_str + + @deco.namespace() + def format_send_start(self, **kwargs): + cmd = "iperf %(type)s -c %(dst_ip)s -i 1 -l %(pkt_size)s -t %(time)s -P %(threads)s " + context = { + 'type': self._typemap[kwargs['protocol']], + 'dst_ip': kwargs['dst'][0]['ip'], + 'time': kwargs['time'], + 'pkt_size': kwargs['size'], + 'threads': kwargs['threads'], + } + cmd = cmd % context + return cmd + + def send_stop(self): + results = [] + for process in self._send_processes: + poll = process.poll() + if poll is None: + process.kill() + ret = 0 + read = "process is stopped by killed" + results.append((ret, read)) + + self._send_processes = [] + return results + + @deco.namespace() + def format_receive_start(self, **kwargs): + cmd = 'iperf %s -s ' % (self._typemap[kwargs['protocol']]) + return cmd + + @deco.check("protocol", choices=['tcp_bw', 'udp_bw']) + @deco.check("namespace", defaults=None) + def receive_start(self, **kwargs): + cmd = self.format_receive_start(**kwargs) + LOG.debug("cmd:%s", cmd) + + process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(1) + ret = process.poll() + if ret is None: + ret = 0 + error_str = "start iperf receive success" + self._receive_processes.append(process) + else: + print ret + error_str = "start iperf receive failed, %s" % (str(kwargs)) + return ret, error_str + + def receive_stop(self): + ret = 0 + for process in self._receive_processes: + process.kill() + ret = process.wait() + self._receive_processes = [] + return ret + + def receive_kill(self): + ret = 0 + receive_pids = utils.get_pid_by_name('iperf') + for pid in receive_pids: + os.kill(pid, signal.SIGKILL) + time.sleep(0.5) + error_str = "stop iperf receive success" + LOG.debug(error_str) + return ret, error_str + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + kill_by_name('iperf') + self._send_processes = [] + self._receive_processes = [] + return True + + +def unit_test(): + perf = Iperf() + pro = 'udp_bw' + print perf.receive_start(namespace='receive', protocol=pro) + + send = { + "namespace": "send", + "protocol": "udp_bw", + "dst": [ + {"ip": "192.168.1.102"} + ], + "size": 64, + "time": 5, + } + print perf.send_start(**send) + time.sleep(10) + print perf.send_stop() + print perf.receive_stop() + + +if __name__ == "__main__": + from vstf.common.log import setup_logging + + setup_logging(level=logging.DEBUG, log_file="/var/log/vstf-iperf.log", clevel=logging.DEBUG) + unit_test() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/netmap.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/netmap.py new file mode 100644 index 00000000..88a25444 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/netmap.py @@ -0,0 +1,168 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import time +import subprocess +import vstf.common.decorator as deco +from vstf.common.utils import kill_by_name, my_popen + +import logging + +LOG = logging.getLogger(__name__) + + +class Netmap(object): + def __init__(self): + self._send_processes = [] + self._receive_processes = [] + + @deco.check("protocol", choices=['udp_bw'], defaults='udp_bw') + @deco.check("namespace", defaults=None) + @deco.check("dst") + @deco.check("src") + @deco.check("size", defaults=64) + @deco.check("threads", defaults=1) + @deco.check("ratep", defaults=0) + def send_start(self, **kwargs): + cmd = self.format_send_start(**kwargs) + LOG.info("cmd:%s", cmd) + + process = my_popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self._send_processes.append(process) + time.sleep(0.5) + + ret = process.poll() + if ret is None: + ret = 0 + error_str = "start netmap send success" + else: + error_str = "start netmap send failed, %s" % (str(kwargs)) + process.wait() + self._send_processes.remove(process) + + return ret, error_str + + def send_stop(self, **kwargs): + LOG.info("send_stop") + results = [] + ret = 0 + for process in self._send_processes: + process.kill() + process.wait() + error_str = "stop netmap send success" + results.append((ret, error_str)) + self._send_processes = [] + return results + + def format_send_start(self, **kwargs): + cmd = "pkt-gen -i %(src_iface)s -f tx -l %(pkt_size)s -p %(threads)s -D %(dst_mac)s -R %(ratep)s" + context = { + 'src_iface': kwargs['src'][0]['iface'], + 'dst_mac': kwargs['dst'][0]['mac'], + 'pkt_size': kwargs['size'], + 'threads': kwargs['threads'], + 'ratep': kwargs['ratep'] + } + cmd = cmd % context + return cmd + + @deco.namespace() + def format_receive_start(self, **kwargs): + cmd = "pkt-gen -i %(iface)s -f rx" + context = { + 'iface': kwargs['dst'][0]['iface'] + } + cmd = cmd % context + return cmd + + @deco.check("protocol", choices=['udp_bw'], defaults='udp_bw') + @deco.check("namespace", defaults=None) + @deco.check("dst") + def receive_start(self, **kwargs): + + cmd = self.format_receive_start(**kwargs) + LOG.info("cmd:%s", cmd) + + process = my_popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self._receive_processes.append(process) + time.sleep(0.5) + + ret = process.poll() + if ret is None: + ret = 0 + error_str = "start netmap receive success" + else: + error_str = "start netmap receive failed, %s" % (str(kwargs)) + process.wait() + self._receive_processes.remove(process) + + return ret, error_str + + def receive_stop(self, **kwargs): + LOG.info("receive_stop") + ret = 0 + for process in self._receive_processes: + process.kill() + process.wait() + self._receive_processes = [] + error_str = "stop netmap receive success" + self._receive_processes = [] + return ret, error_str + + def clean(self): + self.send_stop() + self.receive_stop() + return True + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + kill_by_name('pkt-gen') + self._send_processes = [] + self._receive_processes = [] + return True + + +def unit_test(): + perf = Netmap() + receive = { + "protocol": "udp_bw", + # "namespace": "receive", + "dst": [ + {"iface": "p57p2"} + ], + } + ret = perf.receive_start(**receive) + LOG.info("*********receive_start***********") + LOG.info("ret") + send = { + # "namespace": "send", + "protocol": "udp_bw", + "src": [ + {"iface": "eth4", "mac": "90:e2:ba:20:1f:d8"} + ], + "dst": [ + {"mac": "90:e2:ba:20:1f:d9"} + ], + "size": 64, + "threads": 1, + "ratep": 0 + } + print perf.send_start(**send) + print perf._send_processes + time.sleep(10) + + print perf.send_stop() + print perf.receive_stop() + + +if __name__ == "__main__": + from vstf.common.log import setup_logging + + setup_logging(level=logging.DEBUG, log_file="/var/log/vstf/vstf-netmap.log", clevel=logging.INFO) + unit_test() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/netns.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/netns.py new file mode 100644 index 00000000..c3b73860 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/netns.py @@ -0,0 +1,107 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import logging +from vstf.common.utils import IPCommandHelper +from vstf.agent.perf import ethtool +from vstf.common.utils import check_call, check_output, ns_cmd, my_popen, my_sleep + +LOG = logging.getLogger(__name__) + + +class Netns(object): + def __init__(self): + super(Netns, self).__init__() + self.netns_add_str = "ip netns add %s" + self.netns_del_str = " ip netns del %s" + self.netns_add_device_str = " ip link set %s netns %s" + self.set_link_up_str = "ip link set dev %s up" + self.set_link_addr_str = "ip addr replace %s dev %s" + self.netns_remove_device_str = "ip netns exec %s ip link set %s netns 1" + # self.set_link_addr_str = "ifconfig %s %s up" + self.ns_devices = {} + + def clean_all_namespace(self): + out = check_output("ip netns list", shell=True) + for ns in out.splitlines(): + self.remove_namespace(ns) + return True + + def create_namespace(self, name): + if name in (None, 'None', 'none'): + return True + cmd = self.netns_add_str % name + check_call(cmd, shell=True) + return True + + def remove_namespace(self, ns): + if ns in (None, 'None', 'none'): + return True + ip_helper = IPCommandHelper(ns) + for dev in ip_helper.device_mac_map: + cmd = self.netns_remove_device_str % (ns, dev) + check_call(cmd, shell=True) + self.activate_device(None, dev) + cmd = self.netns_del_str % ns + check_call(cmd, shell=True) + return True + + def add_device(self, ns, device): + if ns is None: + return True + cmd = self.netns_add_device_str % (device, ns) + check_call(cmd, shell=True) + return True + + def config_ip(self, ns, device, ip): + self.activate_device(ns, device) + cmd = self.set_link_addr_str % (ip, device) + cmd = ns_cmd(ns, cmd) + check_call(cmd, shell=True) + return True + + def activate_device(self, ns, device): + cmd = self.set_link_up_str % device + cmd = ns_cmd(ns, cmd) + check_call(cmd, shell=True) + return True + + +class NetnsManager(object): + def __init__(self): + super(NetnsManager, self).__init__() + self._netns = Netns() + + def config_dev(self, netdev): + ns, device, ip = netdev["namespace"], netdev["iface"], netdev['ip_setting'] if "ip_setting" in netdev else \ + netdev['ip'] + self._netns.create_namespace(ns) + self._netns.add_device(ns, device) + self._netns.config_ip(ns, device, ip) + my_sleep(1) + ethtool.autoneg_off(device, ns) + return True + + def recover_dev(self, netdev): + ns = netdev["namespace"] + return self._netns.remove_namespace(ns) + + def clean_all_namespace(self): + return self._netns.clean_all_namespace() + + @staticmethod + def ping(ns, ip): + cmd = "ping -w2 -c1 %s" % ip + cmd = ns_cmd(ns, cmd) + child = my_popen(cmd, shell=True) + return 0 == child.wait() + + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/netperf.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/netperf.py new file mode 100644 index 00000000..99f1c904 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/netperf.py @@ -0,0 +1,181 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import time +import subprocess +import vstf.common.constants as cst +import vstf.common.decorator as deco +from vstf.common import perfmark as mark +from vstf.common.utils import kill_by_name, my_popen + +import logging + +LOG = logging.getLogger(__name__) + + +class Netperf(object): + def __init__(self): + self._send_processes = [] + self._islat = False + self._typemap = { + "tcp_lat": "TCP_STREAM", + "tcp_bw": "TCP_STREAM", + "udp_lat": "UDP_STREAM", + "udp_bw": "UDP_STREAM", + } + + @deco.check("protocol", choices=cst.PROTOCOLS) + @deco.check("namespace", defaults=None) + @deco.check("dst") + @deco.check("time", defaults=0) + @deco.check("size", defaults=64) + @deco.check("threads", defaults=1) + def send_start(self, **kwargs): + threads = kwargs.pop('threads') + kwargs['buf'] = cst.SOCKET_BUF + if kwargs['protocol'] in ['tcp_lat', 'udp_lat']: + self._islat = True + else: + kwargs['time'] = 0 + + cmd = self.format_send_start(**kwargs) + LOG.info("cmd:%s", cmd) + + for _ in range(threads): + process = my_popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self._send_processes.append(process) + time.sleep(0.5) + for process in self._send_processes: + ret = process.poll() + if ret is None: + ret = 0 + error_str = "start netperf send success" + else: + error_str = "start netperf send failed, %s" % (str(kwargs)) + process.wait() + self._send_processes.remove(process) + + return ret, error_str + + def send_stop(self, **kwargs): + LOG.info("send_stop") + results = [] + ret = 0 + for process in self._send_processes: + poll = process.poll() + if poll is None: + if not self._islat: + process.kill() + read = "process is stopped by killed" + else: + ret = process.wait() + read = process.stdout.read() + read = self._parse_data(read) + results.append((ret, read)) + self._send_processes = [] + self._islat = False + return results + + @staticmethod + def _parse_data(data): + buf = data.splitlines() + data = buf[2].strip().split(',') + result = { + mark.minLatency: float(data[0]), + mark.avgLatency: float(data[1]), + mark.maxLatency: float(data[2]) + } + return result + + @deco.namespace() + def format_send_start(self, **kwargs): + # cmd = "netperf -H %(dst_ip)s -t %(type)s -l %(time)s -- -m %(pkt_size)s " + cmd = "netperf -H %(dst_ip)s -t %(type)s -l %(time)s " \ + "-- -m %(pkt_size)s -s %(buf)s -S %(buf)s -o MIN_LATENCY,MEAN_LATENCY,MAX_LATENCY" + context = { + 'dst_ip': kwargs['dst'][0]['ip'], + 'type': self._typemap[kwargs['protocol']], + 'time': kwargs['time'], + 'pkt_size': kwargs['size'], + 'buf': kwargs['buf'], + } + cmd = cmd % context + return cmd + + @deco.namespace() + def format_receive_start(self, **kwargs): + cmd = 'netserver' + return cmd + + @deco.check("namespace") + def receive_start(self, **kwargs): + + cmd = self.format_receive_start(**kwargs) + LOG.info("cmd:%s", cmd) + + process = my_popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(0.5) + ret = process.poll() + if ret: + error_str = "start netserver failed, %s" % (str(kwargs)) + else: + ret = 0 + error_str = "start netserver success" + + return ret, error_str + + def receive_stop(self, **kwargs): + LOG.info("receive_stop") + ret = 0 + kill_by_name('netserver') + time.sleep(0.5) + error_str = "stop netserver success" + return ret, error_str + + def clean(self): + self.send_stop() + self.receive_stop() + return True + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + kill_by_name('netserver') + kill_by_name('netperf') + self._send_processes = [] + self._receive_processes = [] + return True + + +def unit_test(): + perf = Netperf() + ret = perf.receive_start(namespace='receive') + print "*********receive_start***********" + print ret + send = { + "namespace": "send", + "protocol": "udp_lat", + "dst": [ + {"ip": "192.168.1.102"} + ], + "size": 64, + "threads": 1, + "time": 10, + } + print perf.send_start(**send) + print perf._send_processes + time.sleep(10) + print perf.send_stop() + print perf.receive_stop() + + +if __name__ == "__main__": + from vstf.common.log import setup_logging + + setup_logging(level=logging.DEBUG, log_file="/var/log/vstf/vstf-netperf.log", clevel=logging.DEBUG) + unit_test() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/pktgen.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/pktgen.py new file mode 100644 index 00000000..671e1aa7 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/pktgen.py @@ -0,0 +1,153 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess +import time +import logging +import vstf.agent.perf.utils as utils +import vstf.common.decorator as deco +from vstf.common.utils import my_popen + +LOG = logging.getLogger(__name__) + + +class Pktgen(object): + def __init__(self): + utils.modprobe_pktgen() + self._send_processes = [] + + def _psetpg(self, dev): + self._dev = dev + + def _vsetpg(self, key, value=''): + with open(self._dev, 'w') as f: + txt = "%(key)s %(value)s\n" % {'key': key, 'value': value} + f.write(txt) + LOG.info("write(%s) to %s", txt.strip(), self._dev) + + def _start(self): + cmd = 'echo start > /proc/net/pktgen/pgctrl' + process = my_popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + LOG.info('running pid:%s', process.pid) + time.sleep(0.5) + ret = process.poll() + if ret is None: + ret = 0 + self._send_processes.append(process) + error_str = "start pktgen send success" + else: + error_str = "start pktgen send failed, stdout:%s,stderr:%s" % (process.stdout.read(), process.stderr.read()) + LOG.info(error_str) + return ret, error_str + + def _rem_device_all(self): + cpu_num = utils.get_cpu_num() + for thread in range(0, cpu_num - 1): + self._psetpg("/proc/net/pktgen/kpktgend_%s" % thread) + self._vsetpg('rem_device_all') + return True + + @deco.check("protocol", choices=['udp_bw'], defaults='udp_bw') + @deco.check("namespace", defaults=None) + @deco.check("dst") + @deco.check("src") + @deco.check("size", defaults=64) + @deco.check("threads", defaults=utils.get_default_threads()) + @deco.check("clone_skb", defaults=1) + @deco.check("count", defaults=0) + @deco.check("ratep", defaults=0) + def send_start(self, **kwargs): + # ensure that all sends is exit + self.send_stop() + + interface_num = len(kwargs['src']) + interfaces = [] + for i in range(interface_num): + device = kwargs['src'][i]['iface'] + interfaces.append(device) + utils.iface_up(device) + + self._rem_device_all() + + threads = kwargs['threads'] + for i in range(interface_num): + dev_min = i * threads + dev_max = (i + 1) * threads + device = interfaces[i] + for dev_id in range(dev_min, dev_max): + queue_id = dev_id % threads + self._psetpg("/proc/net/pktgen/kpktgend_%s" % dev_id) + self._vsetpg('add_device', "%s@%s" % (device, queue_id)) + self._psetpg("/proc/net/pktgen/%s@%s" % (device, queue_id)) + self._vsetpg('pkt_size', kwargs['size']) + self._vsetpg('clone_skb', kwargs['clone_skb']) + self._vsetpg('dst_mac', kwargs['dst'][i]['mac']) + self._vsetpg('src_mac', kwargs['src'][i]['mac']) + self._vsetpg('count', kwargs['count']) + if kwargs['ratep']: + self._vsetpg('ratep', kwargs['ratep']) + return self._start() + + def send_stop(self, **kwargs): + results = [] + ret = 0 + for process in self._send_processes: + process.kill() + process.wait() + LOG.info("process.kill(pktgen:%s)", process.pid) + results.append((ret, process.stdout.read())) + self._send_processes = [] + return results + + def receive_start(self, **kwargs): + ret = 0 + error_str = "%s pktgen neednt receive start" % (self.__class__) + LOG.debug(error_str) + return ret, error_str + + def receive_stop(self, **kwargs): + ret = 0 + error_str = "pktgen neednt receive stop" + LOG.debug(error_str) + return ret, error_str + + def clean(self): + self.send_stop() + return True + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + return self.clean() + + +def unit_test(): + perf = Pktgen() + print perf.receive_start() + send = { + "src": [ + {"iface": 'eth4', "mac": "90:e2:ba:20:1f:d8"} + ], + "dst": [ + {"mac": '90:e2:ba:20:1f:d9'} + ], + "size": 64, + "threads": 1, + 'ratep': 0 + } + print perf.send_start(**send) + time.sleep(30) + print perf.send_stop() + print perf.receive_stop() + + +if __name__ == "__main__": + from vstf.common.log import setup_logging + + setup_logging(level=logging.DEBUG, log_file="/var/log/vstf/vstf-pktgen.log", clevel=logging.DEBUG) + unit_test() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/qperf.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/qperf.py new file mode 100644 index 00000000..afdf44d7 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/qperf.py @@ -0,0 +1,167 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess +import time +import logging +import vstf.common.decorator as deco +from vstf.common import perfmark as mark +from vstf.common.utils import kill_by_name, my_popen + +LOG = logging.getLogger(__name__) + + +class Qperf(object): + def __init__(self): + self._send_processes = [] + self._receive_processes = [] + + @deco.check("protocol", choices=['tcp_lat', 'udp_lat']) + @deco.check("namespace", defaults=None) + @deco.check("dst") + @deco.check("time", defaults=10) + @deco.check("size", defaults=64) + def send_start(self, **kwargs): + cmd = self.format_send_start(**kwargs) + LOG.info("cmd:%s", cmd) + process = my_popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(0.5) + ret = process.poll() + if ret is None: + ret = 0 + error_str = "start qperf send success" + self._send_processes.append(process) + else: + print ret + error_str = "start qperf send failed, %s" % (str(kwargs)) + process.wait() + + return ret, error_str + + @deco.namespace() + def format_send_start(self, **kwargs): + cmd = "qperf %(dst_ip)s -t %(time)s -m %(pkt_size)s -vu %(type)s " + context = { + 'dst_ip': kwargs['dst'][0]['ip'], + 'type': kwargs['protocol'], + 'time': kwargs['time'], + 'pkt_size': kwargs['size'], + } + cmd = cmd % context + return cmd + + def send_stop(self, **kwargs): + results = [] + for process in self._send_processes: + process.wait() + read = process.stdout.read() + read = self._parse_data(read) + ret = 0 + results.append((ret, read)) + self._send_processes = [] + return results + + @deco.namespace() + def format_receive_start(self, **kwargs): + cmd = 'qperf' + return cmd + + def receive_start(self, **kwargs): + cmd = self.format_receive_start(**kwargs) + LOG.info("cmd:%s", cmd) + + process = my_popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(0.5) + ret = process.poll() + if ret is None: + ret = 0 + error_str = "start qperf receive success" + self._receive_processes.append(process) + else: + print ret + error_str = "start qperf receive failed, %s" % (str(kwargs)) + process.wait() + raise Exception(error_str) + return ret, error_str + + def receive_stop(self, **kwargs): + ret = 0 + for process in self._receive_processes: + process.kill() + process.wait() + self._receive_processes = [] + error_str = "stop qperf receive success" + return ret, error_str + + def receive_kill(self): + kill_by_name('qperf') + self._receive_processes = [] + return True + + def clean(self): + for process in self._receive_processes: + process.kill() + process.wait() + LOG.info("process.kill(qperf daemon:%s)", process.pid) + for process in self._send_processes: + LOG.info("process.wait(qperf client:%s)", process.pid) + process.wait() + self._receive_processes = [] + self._send_processes = [] + return True + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + kill_by_name('qperf') + self._send_processes = [] + self._receive_processes = [] + return True + + def _parse_data(self, data): + LOG.info(data) + latency = 0 + if data: + buf = data.splitlines() + if "latency" in buf[1]: + data = buf[1].strip().split() + if data[3] == "us": + latency = float(data[2]) / 1000 + else: + latency = float(data[2]) + result = { + mark.minLatency: latency, + mark.avgLatency: latency, + mark.maxLatency: latency + } + return result + + +def unit_test(): + perf = Qperf() + perf.receive_start(namespace='receive') + + send = { + "namespace": "send", + "protocol": "udp_lat", + "dst": [ + {"ip": "192.168.1.102"} + ], + "size": 64, + } + print perf.send_start(**send) + time.sleep(10) + print perf.send_stop() + print perf.receive_stop() + + +if __name__ == "__main__": + from vstf.common.log import setup_logging + + setup_logging(level=logging.DEBUG, log_file="/var/log/vstf/vstf-qperf.log", clevel=logging.DEBUG) + unit_test() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/sar.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/sar.py new file mode 100644 index 00000000..0231d5c1 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/sar.py @@ -0,0 +1,82 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess +import logging +import time +import os +from signal import SIGINT + +from vstf.common.utils import check_output, my_popen, kill_by_name +from vstf.agent.env.basic import collect + +LOG = logging.getLogger(__name__) + + +class Sar(object): + def __init__(self): + self.sar_cmd_str = "sar -u %(interval)s" + self.child_process = {} + + def start(self, interval=2): + cmd = self.sar_cmd_str % {'interval': interval} + child = my_popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(1) + if child.poll() is not None: + print child.poll() + raise Exception("start vnstat error, vnstat is not running") + self.child_process[child.pid] = child + return child.pid + + def stop(self, pid): + assert pid in self.child_process + os.kill(pid, SIGINT) + process = self.child_process.pop(pid) + out = process.stdout.read() + process.wait() + data = {'raw_data': out, 'tool': 'sar', 'type': 'cpu'} + cpu_info = collect.Collect().collect_host_info()[1] + cpu_num = cpu_info['CPU INFO']['CPU(s)'] + cpu_mhz = cpu_info['CPU INFO']['CPU MHz'] + data.update({'cpu_num': float(cpu_num), 'cpu_mhz': float(cpu_mhz)}) + return data + + def process(self, raw): + lines = raw.splitlines() + # print lines + head = lines[2].split()[3:] + average = lines[-1].split()[2:] + data = {} + for h, d in zip(head, average): + data[h.strip('%')] = float(d) + cpu_num = check_output('cat /proc/cpuinfo | grep processor | wc -l', shell=True).strip() + data.update({'cpu_num': int(cpu_num)}) + return data + + def clean(self): + for _, process in self.child_process.items(): + process.kill() + process.wait() + self.child_process = {} + return True + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + kill_by_name("sar") + self.child_process = {} + return True + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + q = Sar() + pid = q.start() + time.sleep(10) + raw = q.stop(pid) + print raw + print q.process(raw['raw_data']) diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/utils.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/utils.py new file mode 100644 index 00000000..4f7ddb6a --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/utils.py @@ -0,0 +1,46 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import logging +import subprocess +from vstf.common.utils import check_call, check_output + +LOG = logging.getLogger(__name__) + + +def get_pid_by_name(process_name): + out = check_output(['ps', '-A']) + pids = [] + for line in out.splitlines(): + values = line.split() + pid, name = values[0], values[3] + if process_name == name: + pids.append(int(pid)) + return pids + + +def get_cpu_num(): + cpu_num = check_output('cat /proc/cpuinfo | grep processor | wc -l', shell=True).strip() + cpu_num = int(cpu_num) + return cpu_num + + +def get_default_threads(): + cpu_num = get_cpu_num() + return 2 if cpu_num > 3 * 3 else 1 + + +def modprobe_pktgen(): + check_call('modprobe pktgen', shell=True) + return True + + +def iface_up(device): + check_call("ifconfig %s up" % device, shell=True) + return True diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/vnstat.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/vnstat.py new file mode 100644 index 00000000..b12ac1af --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/vnstat.py @@ -0,0 +1,110 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess +import time +import re +from signal import SIGINT +import os +import logging +from vstf.common.utils import check_call, my_popen, kill_by_name + +LOG = logging.getLogger(__name__) + + +class VnStat(object): + def __init__(self): + self.netns_exec_str = "ip netns exec %s " + self.vnstat_cmd_str = "vnstat -l -i %s" + self.child_process = {} + + def run_vnstat(self, device, namespace=None): + cmd = self.vnstat_cmd_str + if namespace: + cmd1 = (self.netns_exec_str + "ifconfig %s") % (namespace, device) + check_call(cmd1, shell=True) + cmd = self.netns_exec_str + cmd + cmd = cmd % (namespace, device) + else: + cmd = cmd % device + check_call("which vnstat", shell=True) + child = my_popen(cmd.split(), stdout=subprocess.PIPE) + self.child_process[child.pid] = child + return child.pid + + def kill_vnstat(self, pid, namespace=None): + assert pid in self.child_process + os.kill(pid, SIGINT) + process = self.child_process.pop(pid) + out = process.stdout.read() + process.wait() + LOG.info("os.kill(pid = %s)", pid) + data = {'tool': 'vnstat', 'type': 'nic', 'raw_data': out} + return data + + def clean(self): + for _, process in self.child_process.items(): + process.kill() + process.wait() + LOG.info("process.kill(vnstat:%s)", process.pid) + self.child_process = {} + return True + + def process(self, raw): + buf = raw.splitlines() + buf = buf[9:] + buf = ' '.join(buf) + m = {} + + digits = re.compile(r"\d+\.?\d*") + units = re.compile("(?:gib|mib|kib|kbit/s|gbits/s|mbit/s|p/s)", re.IGNORECASE | re.MULTILINE) + units_arr = units.findall(buf) + + LOG.debug(units_arr) + + digits_arr = digits.findall(buf) + + for i in range(len(digits_arr)): + digits_arr[i] = round(float(digits_arr[i]), 2) + + m['rxpck'], m['txpck'] = digits_arr[8], digits_arr[9] + m['time'] = digits_arr[-1] + digits_arr = digits_arr[:8] + digits_arr[10:-1] + index = 0 + for unit in units_arr: + unit = unit.lower() + if unit == 'gib': + digits_arr[index] *= 1024 + elif unit == 'kib': + digits_arr[index] /= 1024 + elif unit == 'gbit/s': + digits_arr[index] *= 1000 + elif unit == 'kbit/s': + digits_arr[index] /= 1000 + else: + pass + index += 1 + + for i in range(len(digits_arr)): + digits_arr[i] = round(digits_arr[i], 2) + + m['rxmB'], m['txmB'] = digits_arr[0:2] + m['rxmB_max/s'], m['txmB_max/s'] = digits_arr[2:4] + m['rxmB/s'], m['txmB/s'] = digits_arr[4:6] + m['rxmB_min/s'], m['txmB_min/s'] = digits_arr[6:8] + m['rxpck_max/s'], m['txpck_max/s'] = digits_arr[8:10] + m['rxpck/s'], m['txpck/s'] = digits_arr[10:12] + m['rxpck_min/s'], m['txpck_min/s'] = digits_arr[12:14] + return m + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + kill_by_name("vnstat") + self.child_process = {} + return True diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/perf/vstfperf.py b/testsuites/vstf/vstf_scripts/vstf/agent/perf/vstfperf.py new file mode 100644 index 00000000..8be3c4e5 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/perf/vstfperf.py @@ -0,0 +1,107 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +__doc__ = """ +operation: [start, stop, restart] +action: [send, receive] +tool: [pktgen, netperf, qperf, iperf, netmap] +params: + protocol: [tcp_lat, udp_lat, tcp_bw, udp_bw] + namespace: None + src:[ + { "iface":"eth0", "ip":"xxx.xxx.xxx.xxx", "mac":"FF:FF:FF:FF:FF:FF"} + ] + dst:[ + { "iface":"eth0", "ip":"xxx.xxx.xxx.xxx", "mac":"FF:FF:FF:FF:FF:FF"} + ] + size: 64 + threads: 1 + ratep: 100000 (pps) + time: 100 (s) +""" + +import sys +import logging +import vstf.common.constants as cst +import vstf.common.decorator as deco +import vstf.agent.perf.pktgen as vstf_pktgen +import vstf.agent.perf.netmap as vstf_netmap +import vstf.agent.perf.qperf as vstf_qperf +import vstf.agent.perf.iperf as vstf_iperf +import vstf.agent.perf.netperf as vstf_netperf + +LOG = logging.getLogger(__name__) + + +class Vstfperf(object): + def __init__(self): + for tool in cst.TOOLS: + obj_name = 'vstf_' + tool + obj = getattr(sys.modules[__name__], obj_name) + cls_name = tool.title() + cls = getattr(obj, tool.title()) + self.__dict__.update({tool: cls()}) + + @deco.check("operation", choices=cst.OPERATIONS) + @deco.check("action", choices=cst.ACTIONS) + @deco.check("tool", choices=cst.TOOLS) + @deco.check("params", defaults={}) + def run(self, **kwargs): + print "_run in" + operation = kwargs.pop("operation") + tool = kwargs.pop("tool") + instance = getattr(self, tool) + action = kwargs.pop("action") + func_name = "%s_%s" % (action, operation) + func = getattr(instance, func_name) + LOG.info(kwargs['params']) + LOG.info(func) + ret = func(**kwargs['params']) + return ret + + def force_clean(self): + LOG.info("%s %s start", self.__class__, self.force_clean.__name__) + for tool in cst.TOOLS: + instance = getattr(self, tool) + instance.force_clean() + return True + + +def unit_test(): + from vstf.common.log import setup_logging + setup_logging(level=logging.DEBUG, log_file="/var/log/vstf/vstf-vstfperf.log", clevel=logging.INFO) + + perf = Vstfperf() + start = { + "operation": "start", + "action": "send", + "tool": "netperf", + "params": { + "namespace": "vnet_name1", + "protocol": "udp_lat", + "dst": [ + {"ip": "192.168.1.102"} + ], + "size": 64, + "threads": 1, + "time": 100, + }, + } + perf.run(**start) + + stop = { + "operation": "stop", + "action": "send", + "tool": "netperf", + } + perf.run(**stop) + + +if __name__ == '__main__': + unit_test() diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/softagent.py b/testsuites/vstf/vstf_scripts/vstf/agent/softagent.py new file mode 100644 index 00000000..6271a097 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/softagent.py @@ -0,0 +1,133 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +import logging +import time +from vstf.agent.env.basic.image_manager import ImageManager +from vstf.agent.env.basic.source_manager import SourceCodeManager +from vstf.agent.env.basic import commandline +from vstf.agent.env.basic.device_manager import DeviceManager +from vstf.agent.env.basic import collect as coll +from vstf.agent.perf import netns, vnstat, vstfperf, sar, ethtool, affctl +from vstf.agent.env import builder +from vstf.agent.equalizer.get_info import GetPhyInfo +from vstf.agent.equalizer.optimize import Optimize +from vstf.agent.env.driver_plugins.manager import DriverPluginManager + +LOG = logging.getLogger(__name__) + + +class ENV(object): + def __init__(self): + super(ENV, self).__init__() + self.builder = builder.PluginManager() + + def build_env(self, cfg_intent): + return self.builder.build(cfg_intent) + + def clean_env(self): + return self.builder.clean() + + @staticmethod + def create_images(cfg): + return ImageManager(cfg).create_all() + + @staticmethod + def clean_images(cfg): + return ImageManager(cfg).clean_all() + + +class Drivers(object): + def __init__(self): + super(Drivers, self).__init__() + self.dr_mgr = DriverPluginManager() + + def install_drivers(self, drivers): + LOG.info("install drivers:%s", drivers) + self.dr_mgr.clean() + ret = self.dr_mgr.load(drivers) + return ret + + def clean_drivers(self): + return self.dr_mgr.clean() + + def autoneg_on(self, iface, nspace): + return ethtool.autoneg_on(iface, nspace) + + def autoneg_off(self, iface, nspace): + return ethtool.autoneg_off(iface, nspace) + + def autoneg_query(self, iface, nspace): + return ethtool.autoneg_query(iface, nspace) + + +class Cpu(object): + def affctl_load(self, policy): + return affctl.affctl_load(policy) + + def affctl_list(self): + return affctl.affctl_list() + + +class Perf(object): + def __init__(self): + super(Perf, self).__init__() + self._vnstat = vnstat.VnStat() + self._vstfperf = vstfperf.Vstfperf() + self._sar = sar.Sar() + + def run_vnstat(self, device, namespace=None): + return self._vnstat.run_vnstat(device, namespace) + + def kill_vnstat(self, pid, namespace=None): + return self._vnstat.kill_vnstat(pid, namespace) + + def perf_run(self, **kwargs): + return self._vstfperf.run(**kwargs) + + def run_cpuwatch(self, interval = 2): + return self._sar.start(interval) + + def kill_cpuwatch(self, pid): + return self._sar.stop(pid) + + def force_clean(self): + self._vstfperf.force_clean() + self._sar.force_clean() + self._vnstat.force_clean() + return True + + +class EqualizerOps(GetPhyInfo, Optimize): + def __init__(self): + super(EqualizerOps, self).__init__() + + +class BaseAgent(coll.Collect, + ENV, + Cpu, + Drivers, + DeviceManager, + commandline.CommandLine, + netns.NetnsManager, + SourceCodeManager + ): + def __init__(self): + super(BaseAgent, self).__init__() + + +class softAgent(BaseAgent, Perf, EqualizerOps): + def __init__(self): + super(softAgent, self).__init__() + + +if __name__ == '__main__': + softAgent() + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/spirent/__init__.py b/testsuites/vstf/vstf_scripts/vstf/agent/spirent/__init__.py new file mode 100644 index 00000000..83b8d15d --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/spirent/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/spirent/spirent.py b/testsuites/vstf/vstf_scripts/vstf/agent/spirent/spirent.py new file mode 100644 index 00000000..904de736 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/spirent/spirent.py @@ -0,0 +1,243 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +import Tkinter + + +def build_cmd(*args): + cmd = '' + for arg in args: + cmd = cmd+str(arg)+' ' + #import pdb + #pdb.set_trace() + return cmd + + +class stcPython(): + def __init__(self): + self.tclsh = Tkinter.Tcl() + self.stcpkg = '/home/Spirent_TestCenter_4.46/Spirent_TestCenter_Application_Linux' + self.tclsh.eval("set auto_path [ linsert $auto_path 0 %s ]" %(self.stcpkg)) + self.tclsh.eval("package require SpirentTestCenter") + + def build_cmd(self, *args): + cmd = '' + for arg in args: + cmd = cmd+str(arg)+' ' + return cmd + + # [ stc base interface ] + def stc_init(self, *args): + cmd = build_cmd('stc::init', *args) + return self.tclsh.eval(cmd) + # stc connect + def stc_connect(self,*args): + cmd = build_cmd('stc::connect', *args) + return self.tclsh.eval(cmd) + # stc disconnect + def stc_disconnect(self,*args): + cmd = build_cmd('stc::disconnect', *args) + return self.tclsh.eval(cmd) + # stc create + def stc_create(self,*args): + cmd = build_cmd('stc::create', *args) + return self.tclsh.eval(cmd) + # stc delete + def stc_delete(self,*args): + cmd = build_cmd('stc::delete', *args) + return self.tclsh.eval(cmd) + # stc config + def stc_config(self,*args): + cmd = build_cmd('stc::config', *args) + return self.tclsh.eval(cmd) + # stc get + def stc_get(self,*args): + cmd = build_cmd('stc::get', *args) + return self.tclsh.eval(cmd) + # stc apply + def stc_apply(self,*args): + cmd = build_cmd('stc::apply', *args) + return self.tclsh.eval(cmd) + # stc perform + def stc_perform(self,*args): + cmd = build_cmd('stc::perform', *args) + return self.tclsh.eval(cmd) + # stc reserve + def stc_reserve(self,*args): + cmd = build_cmd('stc::reserve', *args) + return self.tclsh.eval(cmd) + # stc release + def stc_release(self,*args): + cmd = build_cmd('stc::release', *args) + return self.tclsh.eval(cmd) + # stc subscribe + def stc_subscribe(self,*args): + cmd = build_cmd('stc::subscribe',*args) + return self.tclsh.eval(cmd) + # stc unsubscribe + def stc_unsubscribe(self,*args): + cmd = build_cmd('stc::unsubscribe', *args) + return self.tclsh.eval(cmd) + # stc wait until sequencer complete + def stc_waituntilcomplete(self,*args): + cmd = build_cmd('stc::waituntilcomplete', *args) + return self.tclsh.eval(cmd) + # stc help + def stc_help(self, *args): + cmd = build_cmd('stc::help',*args) + return self.tclsh.eval(cmd) + + # [ stc expand interface ] + # get one dict-key's value + # return value + def stc_get_value(self,stc_dict,stc_key): + cmd = stc_dict+' -'+stc_key + return self.stc_get(cmd) + # create project + # return: project_name + def stc_create_project(self): + return self.stc_create('project') + # create port under project + # return: port name + def stc_create_port(self,project_name): + cmd = 'port -under '+project_name + return self.stc_create(cmd) + # config port location + # return: None + def stc_config_port_location(self,port_name,chassisAddress,slot,port): + #import pdb + #pdb.set_trace() + cmd = port_name+' -location //'+chassisAddress+'/'+slot+'/'+port+' -UseDefaultHost False' + return self.stc_config(cmd) + # create streamblock under port + # return: streamblock name + def stc_create_streamblock(self,port_name,vlan_tag,ExpectedRxPort,srcMac,dstMac,sourceAddr,destAddr): + #import pdb + #pdb.set_trace() + if vlan_tag == None or vlan_tag == 'None': + frameStruc = '"EthernetII IPv4 Udp"' + if ExpectedRxPort == '' : + return self.stc_create( 'streamBlock -under ',port_name, + '-frameConfig ',frameStruc, + '-frame "EthernetII.srcMac',srcMac,'EthernetII.dstMac',dstMac, + 'IPv4.1.sourceAddr',sourceAddr,'IPv4.1.destAddr',destAddr,'"') + else : + return self.stc_create( 'streamBlock -under ',port_name, + '-ExpectedRxPort',ExpectedRxPort, + '-frameConfig ',frameStruc, + '-frame "EthernetII.srcMac',srcMac,'EthernetII.dstMac',dstMac, + 'IPv4.1.sourceAddr',sourceAddr,'IPv4.1.destAddr',destAddr,'"') + else : + frameStruc = '"EthernetII Vlan IPv4 Udp"' + if ExpectedRxPort == '' : + return self.stc_create( 'streamBlock -under ',port_name, + '-frameConfig '+frameStruc, + '-frame "EthernetII.srcMac',srcMac,'EthernetII.dstMac',dstMac, + 'Vlan.1.id',vlan_tag, + 'IPv4.1.sourceAddr',sourceAddr,'IPv4.1.destAddr',destAddr,'"') + else : + return self.stc_create( 'streamBlock -under ',port_name, + '-ExpectedRxPort',ExpectedRxPort, + '-frameConfig '+frameStruc, + '-frame "EthernetII.srcMac',srcMac,'EthernetII.dstMac',dstMac, + 'Vlan.1.id',vlan_tag, + 'IPv4.1.sourceAddr',sourceAddr,'IPv4.1.destAddr',destAddr,'"') + # config streamblock with part arguments + # argument list use args dictionary + def stc_config_streamblock(self,streamblock_name,args_dict): + cmd = '' + for key in args_dict.keys() : + temp_cmd = '-'+key+' '+str(args_dict[key]) + cmd = cmd + temp_cmd + return self.stc_config(streamblock_name,cmd) + # get generator name from port name + # return: generator name + def stc_get_generator(self,port_name): + cmd = port_name+' -children-generator' + return self.stc_get(cmd) + # config generator with part arguments + # argument list use args dictionary + # return none + def stc_config_generator(self,generator_name,args_dict): + cmd = '' + for key in args_dict.keys() : + temp_cmd = '-'+key+' '+str(args_dict[key]) + cmd = cmd + temp_cmd + return self.stc_config(generator_name,cmd) + # attach port + # return: port's parent project info + def stc_attach_ports(self,portList): + cmd = 'AttachPorts -portList {' + for port in portList : + cmd = cmd+' '+port + cmd = cmd+'} -autoConnect TRUE' + return self.stc_perform(cmd) + # config src mac and dst mac + # return: none + def stc_config_ethII(self,ethII,src_mac,dst_mac): + cmd = ethII+' -srcMac '+src_mac+' -dstMac '+dst_mac + return self.stc_config(cmd) + # config src ip and dst ip + # return: none + def stc_config_ethIII(self,ethIII,src_ip,dst_ip): + cmd = ethIII+' -sourceAddr '+src_ip+' -destAddr '+dst_ip + return self.stc_config(cmd) + # start streamblock + # return: none + def stc_streamblock_start(self,streamblock_list): + cmd = 'StreamBlockStart -StreamBlockList {' + for streamblock in streamblock_list : + cmd = cmd+' '+streamblock + cmd = cmd+' } -ExecuteSynchronous TRUE' + return self.stc_perform(cmd) + # stop streamblock + def stc_streamblock_stop(self,streamblock_list): + cmd = 'StreamBlockStop -StreamBlockList {' + for streamblock in streamblock_list : + cmd = cmd+' '+streamblock + cmd = cmd+' } -ExecuteSynchronous TRUE' + return self.stc_perform(cmd) + # start generator + # return: none + def stc_generator_start(self,generator_List): + cmd = 'GeneratorStart -generatorList {' + for generator in generator_List : + cmd = cmd+' '+generator + cmd = cmd+' }' + return self.stc_perform(cmd) + # stop generator + # return: none + def stc_generator_stop(self,generator_List): + cmd = 'GeneratorStop -generatorList {' + for generator in generator_List : + cmd = cmd+' '+generator + cmd = cmd+' }' + return self.stc_perform(cmd) + # create rfc2544 throughput test + def stc_setup_rfc2544_throughput(self): + pass + # create rfc2544 frameloss test + def stc_setup_rfc2544_frameloss(self): + pass + # create rfc2544 latency test + def stc_setup_rfc2544_latency(self): + pass + # start Sequence start + def stc_sequence_start(self): + return self.stc_perform('SequencerStart') + # output rfc2544 throughput result + def stc_get_rfc2544_throughput_result(self): + pass + # output rfc2544 frameloss result + def stc_get_rfc2544_frameloss_result(self): + pass + # output rfc2544 latency result + def stc_get_rfc2544_latency_result(self): + pass diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/spirent/tools.py b/testsuites/vstf/vstf_scripts/vstf/agent/spirent/tools.py new file mode 100644 index 00000000..088a7b13 --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/spirent/tools.py @@ -0,0 +1,334 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +import time +from spirent import stcPython + +class Spirent_Tools(object): + baseAPI = stcPython() + def __init__(self): + """This class provide API of Spirent + + """ + super(Spirent_Tools, self).__init__() + + def send_packet(self,flow): + try: + #import pdb + #pdb.set_trace() + flow = eval(flow) + #stc init action + self.baseAPI.stc_perform(' ResetConfig -config system1') + self.baseAPI.stc_init() + #create project + project = self.baseAPI.stc_create_project() + #create port + port_handle = self.baseAPI.stc_create_port(project) + #config port + slot = flow['send_port'].split('/')[0] + port = flow['send_port'].split('/')[1] + self.baseAPI.stc_config_port_location(port_handle,flow['tester_ip'],slot,port) + #create streamblock + streamblock_handle = self.baseAPI.stc_create_streamblock( + port_name = port_handle, + ExpectedRxPort = '', + vlan_tag = flow['vlan'], + srcMac = flow['src_mac'], + dstMac = flow['dst_mac'], + sourceAddr = flow['src_ip'], + destAddr =flow['dst_ip'] + ) + # attach port + port_list = [port_handle] + self.baseAPI.stc_attach_ports(port_list) + #start streamblock + streamblock_list = [streamblock_handle] + flag = self.baseAPI.stc_streamblock_start(streamblock_list) + return str(streamblock_list).strip('[]') + except : + print("[ERROR]create stream block and send packet failed.") + return False + + def mac_learning(self,flowA,flowB): + try: + #import pdb + #pdb.set_trace() + flowA = eval(flowA) + flowB = eval(flowB) + port_list = [] + streamblock_list = [] + #stc init action + self.baseAPI.stc_perform(' ResetConfig -config system1') + self.baseAPI.stc_init() + #create project + project = self.baseAPI.stc_create_project() + #create port and config port + for flow in [ flowA,flowB ]: + flow['port_handle'] = self.baseAPI.stc_create_port(project) + tmp_test_ip = flow['tester_ip'] + tmp_slot = flow['send_port'].split('/')[0] + tmp_port = flow['send_port'].split('/')[1] + self.baseAPI.stc_config_port_location(flow['port_handle'],tmp_test_ip,tmp_slot,tmp_port) + #create streamblock + flow['streamblock'] = self.baseAPI.stc_create_streamblock(port_name = flow['port_handle'], + ExpectedRxPort = '', + vlan_tag = flow['vlan'], + srcMac = flow['src_mac'], + dstMac = flow['dst_mac'], + sourceAddr = flow['src_ip'], + destAddr =flow['dst_ip']) + #create port and stream block list + port_list.append(flow['port_handle']) + streamblock_list.append(flow['streamblock']) + + #attach port + self.baseAPI.stc_attach_ports(port_list) + #start streamblock + flag = self.baseAPI.stc_streamblock_start(streamblock_list) + # mac learning + time.sleep(2) + # stop stream block + self.baseAPI.stc_streamblock_stop(streamblock_list) + # delete streamblock and release port + for flow in [ flowA,flowB ]: + tmp_test_ip = flow['tester_ip'] + tmp_slot = flow['send_port'].split('/')[0] + tmp_port = flow['send_port'].split('/')[1] + self.baseAPI.stc_delete(flow['streamblock']) + self.baseAPI.stc_release('%s/%s/%s' %(tmp_test_ip,tmp_slot,tmp_port)) + # delete project + self.baseAPI.stc_delete('project1') + ret = self.baseAPI.stc_perform('ResetConfig -config system1') + return True + except : + print("[ERROR]mac learning failed") + return False + + def stop_flow(self,streamblock_list,flow): + flow = eval(flow) + streamblock_list = streamblock_list.strip('\'').split(',') + #stop streamblock list + try : + ret = self.baseAPI.stc_streamblock_stop(streamblock_list) + except : + print("[ERROR]Stop the streamblock list failed.") + return False + #delete streamblock + try : + for streamblock in streamblock_list : + ret = self.baseAPI.stc_delete(streamblock) + except : + print("[ERROR]delete stream block.") + return False + #release port + try : + slot = flow['send_port'].split('/')[0] + port = flow['send_port'].split('/')[1] + ret = self.baseAPI.stc_release('%s/%s/%s' %(flow['tester_ip'],slot,port)) + except : + print("[ERROR]Release port failed") + return False + ##delete project + try : + ret = self.baseAPI.stc_delete('project1') + ret = self.baseAPI.stc_perform('ResetConfig -config system1') + return True + except : + print("[ERROR]Delete project1 failed.") + return False + + def run_rfc2544_throughput(self,forward_init_flows,reverse_init_flows): + #import pdb + #pdb.set_trace() + #rebuild the flows + forward_init_flows = eval(forward_init_flows) + reverse_init_flows = eval(reverse_init_flows) + #stc init action + self.baseAPI.stc_perform(' ResetConfig -config system1') + self.baseAPI.stc_init() + #create project + project = self.baseAPI.stc_create_project() + #create sequencer + seq_handle = self.baseAPI.stc_create('Sequencer -under %s' %(project)) + #create port handle + forward_port_handle = self.baseAPI.stc_create_port(project) + reverse_port_handle = self.baseAPI.stc_create_port(project) + #create forward flow streamblock + for key in forward_init_flows.keys(): + forward_init_flows[key]['port_handle'] = forward_port_handle + tmp_test_ip = forward_init_flows[key]['tester_ip'] + tmp_slot = forward_init_flows[key]['send_port'].split('/')[0] + tmp_port = forward_init_flows[key]['send_port'].split('/')[1] + self.baseAPI.stc_config_port_location(forward_init_flows[key]['port_handle'],tmp_test_ip,tmp_slot,tmp_port) + #create streamblock + forward_init_flows[key]['streamblock'] = self.baseAPI.stc_create_streamblock(port_name = forward_init_flows[key]['port_handle'], + vlan_tag = forward_init_flows[key]['vlan'], + ExpectedRxPort = reverse_port_handle, + srcMac = forward_init_flows[key]['src_mac'], + dstMac = forward_init_flows[key]['dst_mac'], + sourceAddr = forward_init_flows[key]['src_ip'], + destAddr = forward_init_flows[key]['dst_ip']) + #create reverse flow streamblock + for key in reverse_init_flows.keys(): + reverse_init_flows[key]['port_handle'] = reverse_port_handle + tmp_test_ip = reverse_init_flows[key]['tester_ip'] + tmp_slot = reverse_init_flows[key]['send_port'].split('/')[0] + tmp_port = reverse_init_flows[key]['send_port'].split('/')[1] + self.baseAPI.stc_config_port_location(reverse_init_flows[key]['port_handle'],tmp_test_ip,tmp_slot,tmp_port) + #create streamblock + reverse_init_flows[key]['streamblock'] = self.baseAPI.stc_create_streamblock(port_name = reverse_init_flows[key]['port_handle'], + vlan_tag = reverse_init_flows[key]['vlan'], + ExpectedRxPort = forward_port_handle, + srcMac = reverse_init_flows[key]['src_mac'], + dstMac = reverse_init_flows[key]['dst_mac'], + sourceAddr = reverse_init_flows[key]['src_ip'], + destAddr = reverse_init_flows[key]['dst_ip']) + #Create the RFC 2544 throughput test + throughput_config = self.baseAPI.stc_create('Rfc2544ThroughputConfig -under ',project, + '-AcceptableFrameLoss 0.01', + '-NumOfTrials 1', + '-DurationSeconds 60', + '-SearchMode BINARY', + '-RateLowerLimit 1', + '-RateUpperLimit 100', + '-RateInitial 10', + '-UseExistingStreamBlocks True', + '-EnableLearning False', + '-FrameSizeIterationMode CUSTOM', + '-CustomFrameSizeList "70 128 256 512 1024 1280 1518"', + '-LatencyType LIFO', + '-EnableJitterMeasurement TRUE' + ) + #import pdb + #pdb.set_trace() + # list streamblocks + streamblock_list = '" ' + for key in forward_init_flows.keys(): + streamblock_list = streamblock_list+forward_init_flows[key]['streamblock']+' ' + for key in reverse_init_flows.keys(): + streamblock_list = streamblock_list+reverse_init_flows[key]['streamblock']+' ' + streamblock_list = streamblock_list+'"' + + throughput_sbProfile= self.baseAPI.stc_create('Rfc2544StreamBlockProfile -under '+throughput_config+' -Active TRUE -LocalActive TRUE') + self.baseAPI.stc_config(throughput_sbProfile,'-StreamBlockList '+streamblock_list) + self.baseAPI.stc_perform('ExpandBenchmarkConfigCommand','-config ',throughput_config) + + #attach the port before testing + port_list = [ forward_port_handle,reverse_port_handle] + self.baseAPI.stc_attach_ports(port_list) + + #stc apply and begin to sequence test + self.baseAPI.stc_apply() + self.baseAPI.stc_perform("SequencerStart") + + #wait until complete + self.baseAPI.stc_waituntilcomplete() + + #get result db + resultsdb = self.baseAPI.stc_get("system1.project.TestResultSetting", "-CurrentResultFileName") + results_dict = self.baseAPI.stc_perform('QueryResult','-DatabaseConnectionString',resultsdb,'-ResultPath RFC2544ThroughputTestResultDetailedSummaryView') + #print results_dict + return True,results_dict + + def run_rfc2544_frameloss(self,forward_init_flows,reverse_init_flows): + #import pdb + #pdb.set_trace() + #rebuild the flows + forward_init_flows = eval(forward_init_flows) + reverse_init_flows = eval(reverse_init_flows) + #stc init action + self.baseAPI.stc_perform(' ResetConfig -config system1') + self.baseAPI.stc_init() + #create project + project = self.baseAPI.stc_create_project() + #create sequencer + seq_handle = self.baseAPI.stc_create('Sequencer -under %s' %(project)) + #create port handle + forward_port_handle = self.baseAPI.stc_create_port(project) + reverse_port_handle = self.baseAPI.stc_create_port(project) + #create forward flow streamblock + for key in forward_init_flows.keys(): + forward_init_flows[key]['port_handle'] = forward_port_handle + tmp_test_ip = forward_init_flows[key]['tester_ip'] + tmp_slot = forward_init_flows[key]['send_port'].split('/')[0] + tmp_port = forward_init_flows[key]['send_port'].split('/')[1] + self.baseAPI.stc_config_port_location(forward_init_flows[key]['port_handle'],tmp_test_ip,tmp_slot,tmp_port) + #create streamblock + forward_init_flows[key]['streamblock'] = self.baseAPI.stc_create_streamblock(port_name = forward_init_flows[key]['port_handle'], + vlan_tag = forward_init_flows[key]['vlan'], + ExpectedRxPort = reverse_port_handle, + srcMac = forward_init_flows[key]['src_mac'], + dstMac = forward_init_flows[key]['dst_mac'], + sourceAddr = forward_init_flows[key]['src_ip'], + destAddr = forward_init_flows[key]['dst_ip']) + #create reverse flow streamblock + for key in reverse_init_flows.keys(): + reverse_init_flows[key]['port_handle'] = reverse_port_handle + tmp_test_ip = reverse_init_flows[key]['tester_ip'] + tmp_slot = reverse_init_flows[key]['send_port'].split('/')[0] + tmp_port = reverse_init_flows[key]['send_port'].split('/')[1] + self.baseAPI.stc_config_port_location(reverse_init_flows[key]['port_handle'],tmp_test_ip,tmp_slot,tmp_port) + #create streamblock + reverse_init_flows[key]['streamblock'] = self.baseAPI.stc_create_streamblock(port_name = reverse_init_flows[key]['port_handle'], + vlan_tag = reverse_init_flows[key]['vlan'], + ExpectedRxPort = forward_port_handle, + srcMac = reverse_init_flows[key]['src_mac'], + dstMac = reverse_init_flows[key]['dst_mac'], + sourceAddr = reverse_init_flows[key]['src_ip'], + destAddr = reverse_init_flows[key]['dst_ip']) + #Create the RFC 2544 frameloss test + frameloss_config = self.baseAPI.stc_create('Rfc2544FrameLossConfig -under ',project, + '-NumOfTrials 1 ', + '-DurationSeconds 60 ', + '-LoadUnits PERCENT_LINE_RATE ', + '-LoadType CUSTOM ' + '-CustomLoadList 100 ' + '-UseExistingStreamBlocks True ', + '-EnableLearning False ', + '-FrameSizeIterationMode CUSTOM ', + '-CustomFrameSizeList "70 128 256 512 1024 1280 1518"', + '-LatencyType LIFO', + '-EnableJitterMeasurement TRUE' + ) + #import pdb + #pdb.set_trace() + # list streamblocks + streamblock_list = '" ' + for key in forward_init_flows.keys(): + streamblock_list = streamblock_list+forward_init_flows[key]['streamblock']+' ' + for key in reverse_init_flows.keys(): + streamblock_list = streamblock_list+reverse_init_flows[key]['streamblock']+' ' + streamblock_list = streamblock_list+'"' + + frameloss_sbProfile= self.baseAPI.stc_create('Rfc2544StreamBlockProfile -under '+frameloss_config+' -Active TRUE -LocalActive TRUE') + self.baseAPI.stc_config(frameloss_sbProfile,'-StreamBlockList '+streamblock_list) + self.baseAPI.stc_perform('ExpandBenchmarkConfigCommand','-config ',frameloss_config) + + #attach the port before testing + port_list = [ forward_port_handle,reverse_port_handle] + self.baseAPI.stc_attach_ports(port_list) + + #stc apply and begin to sequence test + self.baseAPI.stc_apply() + self.baseAPI.stc_perform("SequencerStart") + + #wait until complete + self.baseAPI.stc_waituntilcomplete() + + #get result db + resultsdb = self.baseAPI.stc_get("system1.project.TestResultSetting", "-CurrentResultFileName") + results_dict = self.baseAPI.stc_perform('QueryResult','-DatabaseConnectionString',resultsdb,'-ResultPath RFC2544FrameLossTestResultDetailedSummaryView') + #import pdb + #pdb.set_trace() + return True,results_dict + + def run_rfc2544_latency(self,forward_init_flows,reverse_init_flows): + pass + diff --git a/testsuites/vstf/vstf_scripts/vstf/agent/spirentagent.py b/testsuites/vstf/vstf_scripts/vstf/agent/spirentagent.py new file mode 100644 index 00000000..8951f96d --- /dev/null +++ b/testsuites/vstf/vstf_scripts/vstf/agent/spirentagent.py @@ -0,0 +1,16 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +from vstf.agent.spirent.tools import Spirent_Tools as Spirent + + +class agentSpirent(Spirent): + def __init__(self): + super(agentSpirent, self).__init__() |