summaryrefslogtreecommitdiffstats
path: root/testsuites/vstf/vstf_scripts/vstf/agent/env
diff options
context:
space:
mode:
Diffstat (limited to 'testsuites/vstf/vstf_scripts/vstf/agent/env')
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/__init__.py9
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/__init__.py9
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/collect.py110
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/commandline.py55
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/device_manager.py147
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/image_manager.py128
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/source_manager.py78
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm9pfs.py158
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_manager.py222
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/basic/vm_xml_help.py85
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/builder.py55
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/__init__.py8
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/manager.py44
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/model.py42
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/driver_plugins/origin_driver.py50
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/FSMonitor.py220
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/__init__.py8
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/constant.py21
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/fsmonitor/utils.py114
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/__init__.py8
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/libvirt_plugin.py70
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/model.py58
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/plugins/tester_env_plugin.py46
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/__init__.py8
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/bridge_plugin.py71
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/manager.py35
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/model.py67
-rw-r--r--testsuites/vstf/vstf_scripts/vstf/agent/env/vswitch_plugins/ovs_plugin.py187
28 files changed, 2113 insertions, 0 deletions
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