summaryrefslogtreecommitdiffstats
path: root/vstf/vstf/agent
diff options
context:
space:
mode:
Diffstat (limited to 'vstf/vstf/agent')
-rwxr-xr-xvstf/vstf/agent/__init__.py14
-rwxr-xr-xvstf/vstf/agent/agent.py132
-rwxr-xr-xvstf/vstf/agent/env/Readme9
-rwxr-xr-xvstf/vstf/agent/env/__init__.py14
-rwxr-xr-xvstf/vstf/agent/env/basic/Readme39
-rwxr-xr-xvstf/vstf/agent/env/basic/__init__.py14
-rwxr-xr-xvstf/vstf/agent/env/basic/collect.py101
-rwxr-xr-xvstf/vstf/agent/env/basic/commandline.py46
-rwxr-xr-xvstf/vstf/agent/env/basic/device_manager.py144
-rwxr-xr-xvstf/vstf/agent/env/basic/image_manager.py124
-rwxr-xr-xvstf/vstf/agent/env/basic/source_manager.py74
-rwxr-xr-xvstf/vstf/agent/env/basic/vm9pfs.py154
-rwxr-xr-xvstf/vstf/agent/env/basic/vm_manager.py218
-rwxr-xr-xvstf/vstf/agent/env/basic/vm_xml_help.py81
-rwxr-xr-xvstf/vstf/agent/env/builder.py51
-rwxr-xr-xvstf/vstf/agent/env/driver_plugins/__init__.py0
-rwxr-xr-xvstf/vstf/agent/env/driver_plugins/manager.py40
-rwxr-xr-xvstf/vstf/agent/env/driver_plugins/model.py38
-rwxr-xr-xvstf/vstf/agent/env/driver_plugins/origin_driver.py46
-rwxr-xr-xvstf/vstf/agent/env/fsmonitor/FSMonitor.py215
-rwxr-xr-xvstf/vstf/agent/env/fsmonitor/Readme13
-rwxr-xr-xvstf/vstf/agent/env/fsmonitor/__init__.py14
-rwxr-xr-xvstf/vstf/agent/env/fsmonitor/constant.py17
-rwxr-xr-xvstf/vstf/agent/env/fsmonitor/utils.py110
-rwxr-xr-xvstf/vstf/agent/env/plugins/Readme49
-rwxr-xr-xvstf/vstf/agent/env/plugins/__init__.py14
-rwxr-xr-xvstf/vstf/agent/env/plugins/libvirt_plugin.py66
-rwxr-xr-xvstf/vstf/agent/env/plugins/model.py54
-rwxr-xr-xvstf/vstf/agent/env/plugins/tester_env_plugin.py43
-rwxr-xr-xvstf/vstf/agent/env/vswitch_plugins/__init__.py0
-rwxr-xr-xvstf/vstf/agent/env/vswitch_plugins/bridge_plugin.py67
-rwxr-xr-xvstf/vstf/agent/env/vswitch_plugins/manager.py31
-rwxr-xr-xvstf/vstf/agent/env/vswitch_plugins/model.py63
-rwxr-xr-xvstf/vstf/agent/env/vswitch_plugins/ovs_plugin.py183
-rwxr-xr-xvstf/vstf/agent/equalizer/README1
-rwxr-xr-xvstf/vstf/agent/equalizer/__init__.py14
-rwxr-xr-xvstf/vstf/agent/equalizer/equalizer.py67
-rwxr-xr-xvstf/vstf/agent/equalizer/get_info.py162
-rwxr-xr-xvstf/vstf/agent/equalizer/optimize.py54
-rwxr-xr-xvstf/vstf/agent/perf/__init__.py14
-rwxr-xr-xvstf/vstf/agent/perf/affctl.py18
-rwxr-xr-xvstf/vstf/agent/perf/ethtool.py56
-rwxr-xr-xvstf/vstf/agent/perf/iperf.py152
-rwxr-xr-xvstf/vstf/agent/perf/netmap.py166
-rwxr-xr-xvstf/vstf/agent/perf/netns.py103
-rwxr-xr-xvstf/vstf/agent/perf/netperf.py177
-rwxr-xr-xvstf/vstf/agent/perf/pktgen.py149
-rwxr-xr-xvstf/vstf/agent/perf/qperf.py164
-rwxr-xr-xvstf/vstf/agent/perf/sar.py78
-rwxr-xr-xvstf/vstf/agent/perf/utils.py42
-rwxr-xr-xvstf/vstf/agent/perf/vnstat.py106
-rwxr-xr-xvstf/vstf/agent/perf/vstfperf.py105
-rwxr-xr-xvstf/vstf/agent/softagent.py123
-rwxr-xr-xvstf/vstf/agent/spirent/__init__.py14
-rwxr-xr-xvstf/vstf/agent/spirent/spirent.py234
-rwxr-xr-xvstf/vstf/agent/spirent/tools.py325
-rwxr-xr-xvstf/vstf/agent/spirentagent.py6
-rwxr-xr-xvstf/vstf/agent/unittest/__init__.py14
-rwxr-xr-xvstf/vstf/agent/unittest/configuration.py28
-rwxr-xr-xvstf/vstf/agent/unittest/env/Readme13
-rwxr-xr-xvstf/vstf/agent/unittest/env/__init__.py14
-rwxr-xr-xvstf/vstf/agent/unittest/env/configuration/Ti.json58
-rwxr-xr-xvstf/vstf/agent/unittest/env/configuration/Tn-ovs.json36
-rwxr-xr-xvstf/vstf/agent/unittest/env/configuration/Tn.json37
-rwxr-xr-xvstf/vstf/agent/unittest/env/configuration/Tn1v.json85
-rwxr-xr-xvstf/vstf/agent/unittest/env/configuration/Tu.json71
-rwxr-xr-xvstf/vstf/agent/unittest/env/model.py24
-rwxr-xr-xvstf/vstf/agent/unittest/env/run_test.py33
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_bridge_plugin.py56
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_builder.py74
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_devicemanager.py38
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_drivermanager.py50
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_origin_driver.py44
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_sourcemanager.py53
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_vm9pfs.py71
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_vm_manager.py69
-rwxr-xr-xvstf/vstf/agent/unittest/env/test_vs_plugin_manager.py68
-rwxr-xr-xvstf/vstf/agent/unittest/perf/Readme7
-rwxr-xr-xvstf/vstf/agent/unittest/perf/__init__.py14
-rwxr-xr-xvstf/vstf/agent/unittest/perf/model.py46
-rwxr-xr-xvstf/vstf/agent/unittest/perf/run_test.py32
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_ethtool.py42
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_netns.py67
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_netperf.py105
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_pktgen.py90
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_qperf.py102
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_utils.py31
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_vnstat.py57
-rwxr-xr-xvstf/vstf/agent/unittest/perf/test_vstfperf.py98
-rwxr-xr-xvstf/vstf/agent/unittest/test_callback.py80
90 files changed, 6315 insertions, 0 deletions
diff --git a/vstf/vstf/agent/__init__.py b/vstf/vstf/agent/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/agent.py b/vstf/vstf/agent/agent.py
new file mode 100755
index 00000000..396b571e
--- /dev/null
+++ b/vstf/vstf/agent/agent.py
@@ -0,0 +1,132 @@
+#!/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/vstf/vstf/agent/env/Readme b/vstf/vstf/agent/env/Readme
new file mode 100755
index 00000000..d178a6fd
--- /dev/null
+++ b/vstf/vstf/agent/env/Readme
@@ -0,0 +1,9 @@
+Modules in the directory are for "Environment Building".
+"Environment Building" is for creating "virtual network" for Performance Evaluation.
+The builder.py contains the quick test code for creates a "virtual network" from a config file.
+
+Usage:
+ python builder.py --config /etc/vstf/env/Tn.json
+
+Please modify example config file "/etc/vstf/model/Tn.json" to suit your own Environment.
+
diff --git a/vstf/vstf/agent/env/__init__.py b/vstf/vstf/agent/env/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/env/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/env/basic/Readme b/vstf/vstf/agent/env/basic/Readme
new file mode 100755
index 00000000..cf7a877f
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/Readme
@@ -0,0 +1,39 @@
+the modules in this directory include some basic common purpose functions that can be used for other plugins.
+you can see the modules in this directory as libraries that you can import for ease of creating your own plugin.
+
+the main functions of the modules are listed as follows:
+
+ImageManger:
+ role:
+ virtual machine images manager. used by VmManager.
+ wraps 'qemu-img' command as underlying mechanism.
+ features:
+ provides a function to create child image from a parent image.
+ provides a function to delete child image.
+
+SourceManager:
+ role:
+ source code manager.
+ using 'pramiko' as underlying mechanism.
+ features:
+ provides a function to download source code
+ provides a function which wrap 'make clean;make;make install' to compile and install.
+
+VmManager:
+ role:
+ virtual machine manager which can create vm and configure vm on bootstrap.
+ use libvirt as underlying mechanism. wrap 'virsh' command for creating vm.
+ features:
+ provides a function to create vm from a vm configuration context.
+ provides functions to detect if vm boots up successfully and onfigure bootstrap options for vm like ip and rabbitmq conf file.
+
+vm9pfs:
+ used by VmManager.
+ host side 9pfs operation (mainly file operations) to communicate with vm by libvirt 9pfs.
+ see as communication protocol on client side. The vm runs a agent for receiving the commands.
+
+VmXmlHelp:
+ used by VmManager.
+ just some divided libvirt xml string templates for building a complete libvirt xml file on demand by VmManager.
+
+
diff --git a/vstf/vstf/agent/env/basic/__init__.py b/vstf/vstf/agent/env/basic/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/env/basic/collect.py b/vstf/vstf/agent/env/basic/collect.py
new file mode 100755
index 00000000..bc4f1ee4
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/collect.py
@@ -0,0 +1,101 @@
+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/vstf/vstf/agent/env/basic/commandline.py b/vstf/vstf/agent/env/basic/commandline.py
new file mode 100755
index 00000000..0df037d8
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/commandline.py
@@ -0,0 +1,46 @@
+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/vstf/vstf/agent/env/basic/device_manager.py b/vstf/vstf/agent/env/basic/device_manager.py
new file mode 100755
index 00000000..3209d7c9
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/device_manager.py
@@ -0,0 +1,144 @@
+"""
+Created on 2015-9-25
+
+@author: y00228926
+"""
+
+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/vstf/vstf/agent/env/basic/image_manager.py b/vstf/vstf/agent/env/basic/image_manager.py
new file mode 100755
index 00000000..6c7df709
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/image_manager.py
@@ -0,0 +1,124 @@
+"""
+Created on 2015-7-28
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/basic/source_manager.py b/vstf/vstf/agent/env/basic/source_manager.py
new file mode 100755
index 00000000..4267cbd2
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/source_manager.py
@@ -0,0 +1,74 @@
+"""
+Created on 2015-8-27
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/basic/vm9pfs.py b/vstf/vstf/agent/env/basic/vm9pfs.py
new file mode 100755
index 00000000..f3a2c2ce
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/vm9pfs.py
@@ -0,0 +1,154 @@
+"""
+Created on 2015-8-27
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/basic/vm_manager.py b/vstf/vstf/agent/env/basic/vm_manager.py
new file mode 100755
index 00000000..e59d6c71
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/vm_manager.py
@@ -0,0 +1,218 @@
+"""
+Created on 2015-8-27
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/basic/vm_xml_help.py b/vstf/vstf/agent/env/basic/vm_xml_help.py
new file mode 100755
index 00000000..d3116259
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/vm_xml_help.py
@@ -0,0 +1,81 @@
+"""
+Created on 2015-7-2
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/builder.py b/vstf/vstf/agent/env/builder.py
new file mode 100755
index 00000000..ebd3d3d7
--- /dev/null
+++ b/vstf/vstf/agent/env/builder.py
@@ -0,0 +1,51 @@
+"""
+Created on 2015-7-8
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/driver_plugins/__init__.py b/vstf/vstf/agent/env/driver_plugins/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/vstf/vstf/agent/env/driver_plugins/__init__.py
diff --git a/vstf/vstf/agent/env/driver_plugins/manager.py b/vstf/vstf/agent/env/driver_plugins/manager.py
new file mode 100755
index 00000000..3bb3fadd
--- /dev/null
+++ b/vstf/vstf/agent/env/driver_plugins/manager.py
@@ -0,0 +1,40 @@
+"""
+Created on 2015-9-15
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/driver_plugins/model.py b/vstf/vstf/agent/env/driver_plugins/model.py
new file mode 100755
index 00000000..3a0b1845
--- /dev/null
+++ b/vstf/vstf/agent/env/driver_plugins/model.py
@@ -0,0 +1,38 @@
+"""
+Created on 2015-9-15
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/driver_plugins/origin_driver.py b/vstf/vstf/agent/env/driver_plugins/origin_driver.py
new file mode 100755
index 00000000..850d7851
--- /dev/null
+++ b/vstf/vstf/agent/env/driver_plugins/origin_driver.py
@@ -0,0 +1,46 @@
+"""
+Created on 2015-10-12
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/fsmonitor/FSMonitor.py b/vstf/vstf/agent/env/fsmonitor/FSMonitor.py
new file mode 100755
index 00000000..71029705
--- /dev/null
+++ b/vstf/vstf/agent/env/fsmonitor/FSMonitor.py
@@ -0,0 +1,215 @@
+"""
+Created on 2015-7-13
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/fsmonitor/Readme b/vstf/vstf/agent/env/fsmonitor/Readme
new file mode 100755
index 00000000..f2bf9a8e
--- /dev/null
+++ b/vstf/vstf/agent/env/fsmonitor/Readme
@@ -0,0 +1,13 @@
+FSMonitor is a daemon process which runs in the vm.
+FSMonitor receive "commands" from 'VmManager' by libvirt 9pfs.
+
+Basically the process works like this:
+ 1.The 'VmManager' writes 'command string' to 'command' file on the libvirt 9pfs used by vm.
+ 2.The FSMonitor constantly detects file changes on the libvirt 9pfs.
+ 3.The FSMonitor finds the newly created file, it then reads the file and execute the command.
+
+All the dependencies of FSMonitor should be satisfied by modules in this directory.
+
+When deploying FSMonitor, just copy this directory into the vm.
+
+
diff --git a/vstf/vstf/agent/env/fsmonitor/__init__.py b/vstf/vstf/agent/env/fsmonitor/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/env/fsmonitor/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/env/fsmonitor/constant.py b/vstf/vstf/agent/env/fsmonitor/constant.py
new file mode 100755
index 00000000..d30bb439
--- /dev/null
+++ b/vstf/vstf/agent/env/fsmonitor/constant.py
@@ -0,0 +1,17 @@
+"""
+Created on 2015-8-27
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/fsmonitor/utils.py b/vstf/vstf/agent/env/fsmonitor/utils.py
new file mode 100755
index 00000000..e6eb1536
--- /dev/null
+++ b/vstf/vstf/agent/env/fsmonitor/utils.py
@@ -0,0 +1,110 @@
+"""
+Created on 2015-7-8
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/plugins/Readme b/vstf/vstf/agent/env/plugins/Readme
new file mode 100755
index 00000000..a2879ba0
--- /dev/null
+++ b/vstf/vstf/agent/env/plugins/Readme
@@ -0,0 +1,49 @@
+All the plugins should subclass EnvBuilderPlugin from "model.py".
+
+The EnvBuilderPlugin is a template class with a template algorithm:
+
+ def __init__(self, ):
+ pass
+ @abstractmethod
+ def clean(self):
+ #clean Environment before goes further.
+ @abstractmethod
+ def install(self):
+ #install network virtualization software from source code.
+ @abstractmethod
+ def load_drivers(self):
+ #loads drivers for network card.
+ @abstractmethod
+ def create_brs(self):
+ #creates virtual switches.
+ @abstractmethod
+ def config_br_ports(self):
+ #config the vlan property for vswitch ports.
+ def create_vms(self):
+ #create vms
+ def wait_vms(self):
+ #wait vm to boot up and config vm for ips and other configurations.
+ def check_vm_connectivity(self):
+ #check if the vms correctly setup the control panel ips.
+ def build(self, cfg_intent):
+ self.host_cfg = cfg_intent #please retrieve options from self.host_cfg for your use in other methods.
+ self.clean()
+ self.download_and_compile()
+ self.load_drivers()
+ self.create_brs()
+ self.create_vms()
+ self.wait_vms()
+ self.config_tap_vlans()
+ self.check_vm_connectivity()
+
+You should implements the abstract methods left empty, however you can make some methods do nothing to skip steps..
+
+The plugin receives a "cfg_intent", The "cfg_intent" is a python dict parsed from a env-build configuration file.
+
+It contains the detail configurations for the plugin to build a "virtual network" for testing.
+
+There are some example json config files for building different type of "virtual network" under "etc/vstf/env" that you can refer to.
+
+Before you creates a new plugin, you should make sure you understand these json config file properly.
+
+
diff --git a/vstf/vstf/agent/env/plugins/__init__.py b/vstf/vstf/agent/env/plugins/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/env/plugins/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/env/plugins/libvirt_plugin.py b/vstf/vstf/agent/env/plugins/libvirt_plugin.py
new file mode 100755
index 00000000..e7fefb6f
--- /dev/null
+++ b/vstf/vstf/agent/env/plugins/libvirt_plugin.py
@@ -0,0 +1,66 @@
+"""
+Created on 2015-7-8
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/plugins/model.py b/vstf/vstf/agent/env/plugins/model.py
new file mode 100755
index 00000000..5485f970
--- /dev/null
+++ b/vstf/vstf/agent/env/plugins/model.py
@@ -0,0 +1,54 @@
+"""
+Created on 2015-9-15
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/plugins/tester_env_plugin.py b/vstf/vstf/agent/env/plugins/tester_env_plugin.py
new file mode 100755
index 00000000..0fd4b9a2
--- /dev/null
+++ b/vstf/vstf/agent/env/plugins/tester_env_plugin.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author: wly
+# date: 2015/11/17
+# see license for license details
+
+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/vstf/vstf/agent/env/vswitch_plugins/__init__.py b/vstf/vstf/agent/env/vswitch_plugins/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/vstf/vstf/agent/env/vswitch_plugins/__init__.py
diff --git a/vstf/vstf/agent/env/vswitch_plugins/bridge_plugin.py b/vstf/vstf/agent/env/vswitch_plugins/bridge_plugin.py
new file mode 100755
index 00000000..252f190d
--- /dev/null
+++ b/vstf/vstf/agent/env/vswitch_plugins/bridge_plugin.py
@@ -0,0 +1,67 @@
+"""
+Created on 2015-10-12
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/vswitch_plugins/manager.py b/vstf/vstf/agent/env/vswitch_plugins/manager.py
new file mode 100755
index 00000000..00115dfd
--- /dev/null
+++ b/vstf/vstf/agent/env/vswitch_plugins/manager.py
@@ -0,0 +1,31 @@
+"""
+Created on 2015-9-15
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/vswitch_plugins/model.py b/vstf/vstf/agent/env/vswitch_plugins/model.py
new file mode 100755
index 00000000..a4d8b3b5
--- /dev/null
+++ b/vstf/vstf/agent/env/vswitch_plugins/model.py
@@ -0,0 +1,63 @@
+"""
+Created on 2015-9-15
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/env/vswitch_plugins/ovs_plugin.py b/vstf/vstf/agent/env/vswitch_plugins/ovs_plugin.py
new file mode 100755
index 00000000..46045950
--- /dev/null
+++ b/vstf/vstf/agent/env/vswitch_plugins/ovs_plugin.py
@@ -0,0 +1,183 @@
+"""
+Created on 2015-10-10
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/equalizer/README b/vstf/vstf/agent/equalizer/README
new file mode 100755
index 00000000..6c688a1f
--- /dev/null
+++ b/vstf/vstf/agent/equalizer/README
@@ -0,0 +1 @@
+equalizer of the performance of network
diff --git a/vstf/vstf/agent/equalizer/__init__.py b/vstf/vstf/agent/equalizer/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/equalizer/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/equalizer/equalizer.py b/vstf/vstf/agent/equalizer/equalizer.py
new file mode 100755
index 00000000..8db35df9
--- /dev/null
+++ b/vstf/vstf/agent/equalizer/equalizer.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# coding=utf-8
+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/vstf/vstf/agent/equalizer/get_info.py b/vstf/vstf/agent/equalizer/get_info.py
new file mode 100755
index 00000000..873f0caf
--- /dev/null
+++ b/vstf/vstf/agent/equalizer/get_info.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python
+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/vstf/vstf/agent/equalizer/optimize.py b/vstf/vstf/agent/equalizer/optimize.py
new file mode 100755
index 00000000..941769a3
--- /dev/null
+++ b/vstf/vstf/agent/equalizer/optimize.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+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/vstf/vstf/agent/perf/__init__.py b/vstf/vstf/agent/perf/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/perf/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/perf/affctl.py b/vstf/vstf/agent/perf/affctl.py
new file mode 100755
index 00000000..e9b96924
--- /dev/null
+++ b/vstf/vstf/agent/perf/affctl.py
@@ -0,0 +1,18 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author: wly
+# date: 2015/11/26
+# see license for license details
+
+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/vstf/vstf/agent/perf/ethtool.py b/vstf/vstf/agent/perf/ethtool.py
new file mode 100755
index 00000000..c214a568
--- /dev/null
+++ b/vstf/vstf/agent/perf/ethtool.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author: wly
+# date: 2015/11/12
+# see license for license details
+
+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/vstf/vstf/agent/perf/iperf.py b/vstf/vstf/agent/perf/iperf.py
new file mode 100755
index 00000000..25728b7e
--- /dev/null
+++ b/vstf/vstf/agent/perf/iperf.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author:
+# date: 2015-09-15
+# see license for license details
+
+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/vstf/vstf/agent/perf/netmap.py b/vstf/vstf/agent/perf/netmap.py
new file mode 100755
index 00000000..c61d2577
--- /dev/null
+++ b/vstf/vstf/agent/perf/netmap.py
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author: wly
+# date: 2015-11-09
+# see license for license details
+
+
+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/vstf/vstf/agent/perf/netns.py b/vstf/vstf/agent/perf/netns.py
new file mode 100755
index 00000000..d5552fa2
--- /dev/null
+++ b/vstf/vstf/agent/perf/netns.py
@@ -0,0 +1,103 @@
+"""
+Created on 2015-8-6
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/perf/netperf.py b/vstf/vstf/agent/perf/netperf.py
new file mode 100755
index 00000000..fab1fc11
--- /dev/null
+++ b/vstf/vstf/agent/perf/netperf.py
@@ -0,0 +1,177 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author:
+# date: 2015-09-15
+# see license for license details
+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/vstf/vstf/agent/perf/pktgen.py b/vstf/vstf/agent/perf/pktgen.py
new file mode 100755
index 00000000..58c0e6c8
--- /dev/null
+++ b/vstf/vstf/agent/perf/pktgen.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author:
+# date: 2015-09-15
+# see license for license details
+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/vstf/vstf/agent/perf/qperf.py b/vstf/vstf/agent/perf/qperf.py
new file mode 100755
index 00000000..3cb9eafd
--- /dev/null
+++ b/vstf/vstf/agent/perf/qperf.py
@@ -0,0 +1,164 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author:
+# date: 2015-09-15
+# see license for license details
+
+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]) / 1000
+ 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/vstf/vstf/agent/perf/sar.py b/vstf/vstf/agent/perf/sar.py
new file mode 100755
index 00000000..c4688c9c
--- /dev/null
+++ b/vstf/vstf/agent/perf/sar.py
@@ -0,0 +1,78 @@
+"""
+Created on 2015-8-6
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/perf/utils.py b/vstf/vstf/agent/perf/utils.py
new file mode 100755
index 00000000..1fb4b92c
--- /dev/null
+++ b/vstf/vstf/agent/perf/utils.py
@@ -0,0 +1,42 @@
+"""
+Created on 2015-8-6
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/perf/vnstat.py b/vstf/vstf/agent/perf/vnstat.py
new file mode 100755
index 00000000..7a47af14
--- /dev/null
+++ b/vstf/vstf/agent/perf/vnstat.py
@@ -0,0 +1,106 @@
+"""
+Created on 2015-8-6
+
+@author: y00228926
+"""
+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/vstf/vstf/agent/perf/vstfperf.py b/vstf/vstf/agent/perf/vstfperf.py
new file mode 100755
index 00000000..224380fe
--- /dev/null
+++ b/vstf/vstf/agent/perf/vstfperf.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author: wly
+# date: 2015-09-08
+# see license for license details
+
+
+__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/vstf/vstf/agent/softagent.py b/vstf/vstf/agent/softagent.py
new file mode 100755
index 00000000..6f97239d
--- /dev/null
+++ b/vstf/vstf/agent/softagent.py
@@ -0,0 +1,123 @@
+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/vstf/vstf/agent/spirent/__init__.py b/vstf/vstf/agent/spirent/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/spirent/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/spirent/spirent.py b/vstf/vstf/agent/spirent/spirent.py
new file mode 100755
index 00000000..3b073cbf
--- /dev/null
+++ b/vstf/vstf/agent/spirent/spirent.py
@@ -0,0 +1,234 @@
+#!/usr/bin/python
+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/vstf/vstf/agent/spirent/tools.py b/vstf/vstf/agent/spirent/tools.py
new file mode 100755
index 00000000..6d0d429c
--- /dev/null
+++ b/vstf/vstf/agent/spirent/tools.py
@@ -0,0 +1,325 @@
+#!/usr/bin/python
+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/vstf/vstf/agent/spirentagent.py b/vstf/vstf/agent/spirentagent.py
new file mode 100755
index 00000000..04a9dc90
--- /dev/null
+++ b/vstf/vstf/agent/spirentagent.py
@@ -0,0 +1,6 @@
+from vstf.agent.spirent.tools import Spirent_Tools as Spirent
+
+
+class agentSpirent(Spirent):
+ def __init__(self):
+ super(agentSpirent, self).__init__()
diff --git a/vstf/vstf/agent/unittest/__init__.py b/vstf/vstf/agent/unittest/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/unittest/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/unittest/configuration.py b/vstf/vstf/agent/unittest/configuration.py
new file mode 100755
index 00000000..3f3b3665
--- /dev/null
+++ b/vstf/vstf/agent/unittest/configuration.py
@@ -0,0 +1,28 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+
+eth_for_test = [
+ "eth4",
+ "p57p2"
+]
+
+mac_of_eth = [
+ "90:e2:ba:20:1f:d8",
+ '90:e2:ba:20:1f:d9'
+]
+
+bdf_of_eth = [
+ "04:00.0",
+ "04:00.1"
+]
+
+source_repo = {
+ "vnx-bin":{
+ "install": False,
+ "url": "root@192.168.188.10:/root/src/vnx-bin",
+ "repo_type": "git"
+ }
+} \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/Readme b/vstf/vstf/agent/unittest/env/Readme
new file mode 100755
index 00000000..eb2a78d5
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/Readme
@@ -0,0 +1,13 @@
+To enable Env unittest, please make sure:
+
+ 1. br0 exists on TargetHost and connects with 'management network'.
+ 2. a vm image exists on Target Host.
+ 3. change options in configuration files in the "configuration" directory.
+ 4. copy public key to git server, for example:
+ ssh-copy-id root@192.168.188.10
+ note:
+ 192.168.188.10 is the server you config in "../configuration.py"
+ this is needed for source manager which use 'git' to pull down sources.
+
+run tests:
+ python run_test.py \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/__init__.py b/vstf/vstf/agent/unittest/env/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/unittest/env/configuration/Ti.json b/vstf/vstf/agent/unittest/env/configuration/Ti.json
new file mode 100755
index 00000000..89236612
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/configuration/Ti.json
@@ -0,0 +1,58 @@
+{
+ "scenario": "ti",
+ "env-build": [
+ {
+ "ip": "192.168.188.16",
+ "drivers": [
+ "ixgbe",
+ "vhost_net"
+ ],
+ "vms": [
+ {
+ "vm_cpu": 3,
+ "vm_name": "test1",
+ "vm_memory": 4194304,
+ "image_path": "/mnt/sdb/test1.qcow2",
+ "image_type": "qcow2",
+ "init_config": {
+ "amqp_server": "192.168.188.10",
+ "ctrl_ip_setting": "192.168.188.200/23",
+ "ctrl_gw": "192.168.188.1"
+ },
+ "taps":[
+ {
+ "tap_name": "tap_in",
+ "br_type": "ovs",
+ "br_name": "ovs1",
+ "tap_mac": "56:6f:44:a5:3f:a2",
+ "vlan_mode": "access",
+ "vlan_id": "100"
+ }
+ ],
+ "ctrl_mac": "16:6f:44:a5:3f:a2",
+ "ctrl_br": "br0"
+ }
+ ],
+ "bridges": [
+ {
+ "type": "ovs",
+ "name": "ovs1",
+ "uplinks": [
+ {
+ "bdf": "04:00.0",
+ "vlan_mode": "trunk",
+ "vlan_id": "100,200,300,400"
+ }
+ ],
+ "vtep":{},
+ "fastlink":[
+ {
+ "inport":"04: 00.0",
+ "outport":"tap_in"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/configuration/Tn-ovs.json b/vstf/vstf/agent/unittest/env/configuration/Tn-ovs.json
new file mode 100755
index 00000000..73be1611
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/configuration/Tn-ovs.json
@@ -0,0 +1,36 @@
+{
+ "env-build": [
+ {
+ "ip": "192.168.188.16",
+ "drivers": [
+ "ixgbe"
+ ],
+ "bridges": [
+ {
+ "type": "ovs",
+ "name": "ovs1",
+ "uplinks": [
+ {
+ "bdf": "04:00.0",
+ "vlan_mode": "trunk",
+ "vlan_id": "1,100,200"
+ },
+ {
+ "bdf": "04:00.1",
+ "vlan_mode": "trunk",
+ "vlan_id": "1,100,200"
+ }
+ ],
+ "vtep": {},
+ "fastlink": [
+ {
+ "inport": "04: 00.0",
+ "outport": "04: 00.1"
+ }
+ ]
+ }
+ ],
+ "vms": []
+ }
+ ]
+}
diff --git a/vstf/vstf/agent/unittest/env/configuration/Tn.json b/vstf/vstf/agent/unittest/env/configuration/Tn.json
new file mode 100755
index 00000000..04c160da
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/configuration/Tn.json
@@ -0,0 +1,37 @@
+{
+ "scenario": "tn",
+ "env-build": [
+ {
+ "ip": "192.168.188.16",
+ "drivers": [
+ "ixgbe"
+ ],
+ "vms": [],
+ "bridges": [
+ {
+ "type": "ovs",
+ "name": "ovs1",
+ "uplinks": [
+ {
+ "bdf": "04:00.0",
+ "vlan_mode": "trunk",
+ "vlan_id": "1,100,200"
+ },
+ {
+ "bdf": "04:00.1",
+ "vlan_mode": "trunk",
+ "vlan_id": "1,100,200"
+ }
+ ],
+ "vtep": {},
+ "fastlink": [
+ {
+ "inport": "04:00.0",
+ "outport": "04:00.1"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/configuration/Tn1v.json b/vstf/vstf/agent/unittest/env/configuration/Tn1v.json
new file mode 100755
index 00000000..5a25efda
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/configuration/Tn1v.json
@@ -0,0 +1,85 @@
+{
+ "scenario": "tnv",
+ "env-build": [
+ {
+ "ip": "192.168.188.16",
+ "drivers": [
+ "ixgbe",
+ "tap_vhost"
+ ],
+ "vms": [
+ {
+ "vm_cpu": 3,
+ "vm_name": "test1",
+ "vm_memory": 4194304,
+ "image_path": "/mnt/sdb/test1.qcow2",
+ "image_type": "qcow2",
+ "init_config": {
+ "amqp_server": "192.168.188.10",
+ "ctrl_ip_setting": "192.168.188.200/23",
+ "tap_pktloop_config": "dpdk",
+ "ctrl_gw": "192.168.188.1"
+ },
+ "taps": [
+ {
+ "tap_name": "tap_in",
+ "br_type": "ovs",
+ "br_name": "ovs1",
+ "tap_mac": "56:6f:44:a5:3f:a2",
+ "vlan_mode": "access",
+ "vlan_id": "100"
+ },
+ {
+ "tap_name": "tap_out",
+ "br_type": "ovs",
+ "br_name": "ovs22",
+ "tap_mac": "56:6f:44:a5:3f:a3",
+ "vlan_mode": "access",
+ "vlan_id": "200"
+ }
+ ],
+ "ctrl_mac": "16:6f:44:a5:3f:a2",
+ "ctrl_br": "br0"
+ }
+ ],
+ "bridges": [
+ {
+ "type": "ovs",
+ "name": "ovs1",
+ "uplinks": [
+ {
+ "bdf": "04: 00.0",
+ "vlan_mode": "trunk",
+ "vlan_id": "100"
+ }
+ ],
+ "vtep":{},
+ "fastlink":[
+ {
+ "inport":"04: 00.0",
+ "outport":"tap_in"
+ }
+ ]
+ },
+ {
+ "type": "ovs",
+ "name": "ovs2",
+ "uplinks": [
+ {
+ "bdf": "04: 00.1",
+ "vlan_mode": "trunk",
+ "vlan_id": "200"
+ }
+ ],
+ "vtep":{},
+ "fastlink":[
+ {
+ "inport":"04: 00.1",
+ "outport":"tap_out"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/configuration/Tu.json b/vstf/vstf/agent/unittest/env/configuration/Tu.json
new file mode 100755
index 00000000..15a508c0
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/configuration/Tu.json
@@ -0,0 +1,71 @@
+{
+ "scenario": "tu",
+ "env-build": [
+ {
+ "ip": "192.168.188.16",
+ "drivers": [
+ "tap_vhost"
+ ],
+ "vms": [
+ {
+ "vm_cpu": 3,
+ "vm_name": "test1",
+ "vm_memory": 4194304,
+ "image_path": "/mnt/sdb/test1.qcow2",
+ "image_type": "qcow2",
+ "init_config": {
+ "amqp_server": "192.168.188.10",
+ "ctrl_ip_setting": "192.168.188.200/23",
+ "ctrl_gw": "192.168.188.1"
+ },
+ "taps": [
+ {
+ "tap_name": "tap1",
+ "br_type": "ovs",
+ "br_name": "ovs1",
+ "tap_mac": "56:6f:44:a5:3f:a2",
+ "vlan_mode": "access",
+ "vlan_id": "100"
+ }
+ ]
+ },
+ {
+ "vm_cpu": 3,
+ "vm_name": "test2",
+ "vm_memory": 4194304,
+ "image_path": "/mnt/sdb/test2.qcow2",
+ "image_type": "qcow2",
+ "init_config": {
+ "amqp_server": "192.168.188.10",
+ "ctrl_ip_setting": "192.168.188.201/23",
+ "ctrl_gw": "192.168.188.1"
+ },
+ "taps": [
+ {
+ "tap_name": "tap2",
+ "br_type": "ovs",
+ "br_name": "ovs1",
+ "tap_mac": "56:6f:44:a5:3f:a3",
+ "vlan_mode": "access",
+ "vlan_id": "100"
+ }
+ ]
+ }
+ ],
+ "bridges": [
+ {
+ "type": "ovs",
+ "name": "ovs1",
+ "uplinks": [],
+ "vtep": {},
+ "fastlink": [
+ {
+ "inport": "tap1",
+ "outport": "tap2"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/model.py b/vstf/vstf/agent/unittest/env/model.py
new file mode 100755
index 00000000..e3c2bca6
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/model.py
@@ -0,0 +1,24 @@
+"""
+Created on 2015-10-9
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest import configuration
+
+
+class Test(unittest.TestCase):
+ def setUp(self):
+ self.eth_for_test = configuration.eth_for_test
+ self.mac_of_eth = configuration.mac_of_eth
+ self.source_repo = configuration.source_repo
+ self.bdf_of_eth = configuration.bdf_of_eth
+
+ def tearDown(self):
+ pass
+
+
+if __name__ == "__main__":
+ # import sys;sys.argv = ['', 'Test.testName']
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/env/run_test.py b/vstf/vstf/agent/unittest/env/run_test.py
new file mode 100755
index 00000000..0f630c7d
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/run_test.py
@@ -0,0 +1,33 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+import importlib
+
+test_order_list = [
+ "vstf.agent.unittest.env.test_origin_driver",
+ "vstf.agent.unittest.env.test_bridge_plugin",
+ "vstf.agent.unittest.env.test_drivermanager",
+ "vstf.agent.unittest.env.test_devicemanager",
+ "vstf.agent.unittest.env.test_vs_plugin_manager",
+ "vstf.agent.unittest.env.test_builder",
+ "vstf.agent.unittest.env.test_sourcemanager",
+]
+
+
+def main():
+ import logging
+ logging.getLogger(__name__)
+ logging.basicConfig(level=logging.INFO)
+ suite = unittest.TestSuite()
+ for mod_name in test_order_list:
+ mod = importlib.import_module(mod_name)
+ suit = unittest.TestLoader().loadTestsFromModule(mod)
+ suite.addTest(suit)
+ unittest.TextTestRunner().run(suite)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/vstf/vstf/agent/unittest/env/test_bridge_plugin.py b/vstf/vstf/agent/unittest/env/test_bridge_plugin.py
new file mode 100755
index 00000000..ac4eb268
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_bridge_plugin.py
@@ -0,0 +1,56 @@
+"""
+Created on 2015-10-12
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.common.utils import check_call
+from vstf.agent.unittest.env import model
+from vstf.agent.env.vswitch_plugins import bridge_plugin
+from vstf.agent.env.driver_plugins import manager
+
+
+class Test(model.Test):
+ def setUp(self):
+ super(Test, self).setUp()
+ self.plugin = bridge_plugin.BridgePlugin()
+ self.dr_mgr = manager.DriverPluginManager()
+ self.br_cfg = {
+ "name": "br1",
+ "uplinks": [
+ {
+ "bdf": self.bdf_of_eth[0],
+ },
+ {
+ "bdf": self.bdf_of_eth[1],
+ }
+ ]
+ }
+ self.dr_mgr.clean()
+ self.dr_mgr.load(['ixgbe'])
+
+ def tearDown(self):
+ super(Test, self).tearDown()
+
+ def _check_br_exists(self, name):
+ try:
+ check_call('ifconfig %s' % name, shell=True)
+ except Exception, e:
+ return False
+ return True
+
+ def test_create_br(self):
+ self.plugin.clean()
+ self.plugin.create_br(self.br_cfg)
+ self.assertTrue(self._check_br_exists(self.br_cfg['name']))
+ self.plugin.clean()
+ self.assertFalse(self._check_br_exists(self.br_cfg['name']))
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.basicConfig(level=logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/env/test_builder.py b/vstf/vstf/agent/unittest/env/test_builder.py
new file mode 100755
index 00000000..fd2d4382
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_builder.py
@@ -0,0 +1,74 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+import os
+
+from vstf.controller.env_build.cfg_intent_parse import IntentParser
+from vstf.agent.env import builder
+
+
+class Test(unittest.TestCase):
+ def setUp(self):
+ self.mgr = builder.PluginManager()
+ self.dir = os.path.dirname(__file__)
+
+ def tearDown(self):
+ self.mgr.clean()
+
+ def __build(self, filepath, drivers=None):
+ parser = IntentParser(filepath)
+ cfg_intent = parser.parse_cfg_file()
+ host_cfg = cfg_intent['env-build'][0]
+ print filepath
+ print host_cfg
+ host_cfg["src-install"] = {}
+ if drivers:
+ host_cfg['drivers'] = drivers
+ return self.mgr.build(host_cfg)
+
+ def test_build_tn_using_origin_driver(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Tn.json'), drivers=['ixgbe'])
+ self.assertTrue(ret, "test_build_tn_using_origin_driver failed, ret = %s" % ret)
+
+ def test_build_tn1v_using_origin_driver(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Tn1v.json'), drivers=['ixgbe', 'vhost_net'])
+ self.assertTrue(ret, "test_build_tn1v_using_origin_driver failed, ret = %s" % ret)
+
+ def test_build_ti_using_origin_driver(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Ti.json'), drivers=['ixgbe', 'vhost_net'])
+ self.assertTrue(ret, "test_build_ti_using_origin_driver failed, ret = %s" % ret)
+
+ def test_build_tu_using_origin_driver(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Tu.json'), drivers=['vhost_net'])
+ self.assertTrue(ret, "test_build_ti_using_origin_driver failed, ret = %s" % ret)
+
+ @unittest.skip('can be tested by tn1v')
+ def test_build_tn(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Tn.json'))
+ self.assertTrue(ret, "test_build_tn failed, ret = %s" % ret)
+
+ @unittest.skip('can be tested by tn1v')
+ def test_build_tn1v(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Tn1v.json'))
+ self.assertTrue(ret, "test_build_tn failed,ret = %s" % ret)
+
+ @unittest.skip('can be tested by tn1v')
+ def test_build_ti(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Ti.json'))
+ self.assertTrue(ret, "test_build_tn failed, ret = %s" % ret)
+
+ @unittest.skip('can be tested by tn1v')
+ def test_build_tu(self):
+ ret = self.__build(os.path.join(self.dir, 'configuration/Tu.json'))
+ self.assertTrue(ret, "test_build_tn failed, ret = %s" % ret)
+
+
+if __name__ == "__main__":
+ import logging
+
+ LOG = logging.getLogger(__name__)
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/env/test_devicemanager.py b/vstf/vstf/agent/unittest/env/test_devicemanager.py
new file mode 100755
index 00000000..7e5bc3b1
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_devicemanager.py
@@ -0,0 +1,38 @@
+"""
+Created on 2015-9-25
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest.env import model
+from vstf.agent.env.basic.device_manager import DeviceManager
+
+
+class Test(model.Test):
+ def setUp(self):
+ super(Test, self).setUp()
+ self.dm = DeviceManager()
+ self.device_list = self.dm.list_nic_devices()
+ self.device_detail = self.device_list[0]
+
+ def tearDown(self):
+ super(Test, self).tearDown()
+
+ def test_get_device_detail(self):
+ detail1 = self.dm.get_device_detail(self.device_detail['bdf'])
+ detail2 = self.dm.get_device_detail(self.device_detail['mac'])
+ detail3 = self.dm.get_device_detail(self.device_detail['device'])
+ self.assertTrue(detail1 == detail2 == detail3 == self.device_detail)
+
+ def test_list_nic_devices(self):
+ import json
+ print json.dumps(self.device_list, indent=4)
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.basicConfig(level=logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/env/test_drivermanager.py b/vstf/vstf/agent/unittest/env/test_drivermanager.py
new file mode 100755
index 00000000..df6ad5df
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_drivermanager.py
@@ -0,0 +1,50 @@
+"""
+Created on 2015-10-9
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.common.utils import check_output
+from vstf.agent.unittest.env import model
+from vstf.agent.env.driver_plugins import manager
+
+
+class Test(model.Test):
+ def setUp(self):
+ super(Test, self).setUp()
+ self.driver_mgr = manager.DriverPluginManager()
+
+ def tearDown(self):
+ super(Test, self).tearDown()
+
+ def _driver_exists(self, drivers=[]):
+ all_drivers = check_output("lsmod | awk '{print $1}'", shell=True).split()
+ for mod in drivers:
+ if mod not in all_drivers:
+ return False
+ return True
+
+ def test_load(self):
+ self.driver_mgr.clean()
+ for _, drivers in self.driver_mgr.get_all_supported_drivers().items():
+ self.assertFalse(self._driver_exists(drivers))
+
+
+ self.driver_mgr.load(['ixgbe', 'vhost_net'])
+ self.assertTrue(self._driver_exists(['ixgbe', 'vhost_net']))
+
+ self.driver_mgr.clean()
+ self.assertFalse(self._driver_exists(['ixgbe', 'vhost_net']))
+
+ def test_load_unsuported_pair(self):
+ with self.assertRaises(Exception):
+ self.driver_mgr.load(['ixgbe', 'tap_vhost'])
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.basicConfig(level=logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/env/test_origin_driver.py b/vstf/vstf/agent/unittest/env/test_origin_driver.py
new file mode 100755
index 00000000..8daa481f
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_origin_driver.py
@@ -0,0 +1,44 @@
+"""
+Created on 2015-10-9
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.common.utils import check_output
+from vstf.agent.unittest.env import model
+from vstf.agent.env.driver_plugins import origin_driver
+
+
+class Test(model.Test):
+
+ def setUp(self):
+ super(Test, self).setUp()
+ self.driver_mgr = origin_driver.OriginDriverPlugin()
+
+ def tearDown(self):
+ super(Test, self).tearDown()
+
+ def _driver_exists(self, drivers=[]):
+ all_drivers = check_output("lsmod | awk '{print $1}'",shell = True).split()
+ for mod in drivers:
+ if mod not in all_drivers:
+ return False
+ return True
+
+ def test_load(self):
+ self.driver_mgr.clean()
+ self.assertFalse(self._driver_exists(self.driver_mgr.get_supported_drivers()))
+
+ self.driver_mgr.load(['ixgbe','vhost_net'])
+ self.assertTrue(self._driver_exists(['ixgbe','vhost_net']))
+
+ self.driver_mgr.clean()
+ self.assertFalse(self._driver_exists(self.driver_mgr.get_supported_drivers()))
+
+
+if __name__ == "__main__":
+ import logging
+ logging.basicConfig(level = logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main() \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/test_sourcemanager.py b/vstf/vstf/agent/unittest/env/test_sourcemanager.py
new file mode 100755
index 00000000..fda567d8
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_sourcemanager.py
@@ -0,0 +1,53 @@
+"""
+Created on 2015-10-9
+
+@author: y00228926
+"""
+import unittest
+import shutil
+import time
+import os
+
+from vstf.agent.env.basic.source_manager import SourceCodeManager
+from vstf.agent.unittest.env import model
+
+
+class TestSourceManager(model.Test):
+ def setUp(self):
+ super(TestSourceManager, self).setUp()
+ self.sm = SourceCodeManager()
+ self.dest_path = '/tmp/test_source_manager'
+ os.mkdir(self.dest_path)
+
+ def tearDown(self):
+ shutil.rmtree(self.dest_path, ignore_errors = True)
+
+ def _time(self,func):
+ def _deco(*args):
+ start_time = time.time()
+ func(*args)
+ end_time = time.time()
+ return end_time - start_time
+ return _deco
+
+ def test_download_source_code(self):
+ for key, item in self.source_repo.items():
+ print self.source_repo
+ url = item['url']
+ target = os.path.join(self.dest_path, key)
+ install = item['install']
+ if install:
+ self.sm._git_pull(url, target)
+ self.assertTrue(os.path.isdir(target))
+ my_download = self._time(self.sm._git_pull)
+ t = my_download(url, target)
+ self.assertTrue(t < 1.0)
+ else:
+ self.assertFalse(os.path.isdir(target))
+
+
+if __name__ == "__main__":
+ import logging
+ logging.basicConfig(level = logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main() \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/env/test_vm9pfs.py b/vstf/vstf/agent/unittest/env/test_vm9pfs.py
new file mode 100755
index 00000000..4f520133
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_vm9pfs.py
@@ -0,0 +1,71 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest.env import model
+from vstf.agent.env.basic.vm9pfs import VMConfigBy9pfs
+from vstf.agent.env.basic.vm_manager import VMControlOperation
+
+
+class TestVM9pfs(model.Test):
+ def setUp(self):
+ super(TestVM9pfs, self).setUp()
+ self.vm_config = {
+ 'vm_name': 'vm1',
+ 'vm_cpu': 5,
+ 'image_path': "/mnt/sdb/ubuntu_salt_master.img",
+ 'child_dir': '/mnt/sdb/',
+ 'image_type': 'qcow2',
+ 'ctrl_br': 'br0',
+ 'ctrl_mac': '56:6f:44:a5:3f:a4',
+ "taps": [
+ {
+ "tap_name": "tap_in",
+ "br_type": "bridge",
+ "br_name": "br0",
+ "tap_mac": "56:6f:44:a5:3f:a2",
+ },
+ {
+ "tap_name": "tap_out",
+ "br_type": "bridge",
+ "br_name": "br0",
+ "tap_mac": "56:6f:44:a5:3f:a3",
+ }
+ ],
+ 'init_config': {}
+ }
+ self.init_config = {
+ 'amqp_server': '192.168.188.10',
+ 'ctrl_ip_setting': '192.168.188.200/23',
+ 'tap_pktloop_config': 'dpdk',
+ 'ctrl_gw': '192.168.188.1'
+ }
+ self.mgr = VMControlOperation()
+ self.mgr.clean_all_vms()
+ self.mgr.create_vm(self.vm_config)
+ self.mgr.wait_vm(self.vm_config["vm_name"])
+ self.vm9pctrl = self.mgr.vm_9p_controllers[self.vm_config["vm_name"]]
+
+ def tearDown(self):
+ self.mgr.clean_all_vms()
+ super(TestVM9pfs, self).tearDown()
+
+ def test_init_config(self):
+ ret = self.vm9pctrl.config_ip(self.vm_config['ctrl_mac'], self.init_config['ctrl_ip_setting'])
+ self.assertTrue(ret)
+ ret = self.vm9pctrl.config_gw(self.init_config['ctrl_gw'])
+ self.assertTrue(ret)
+ ret = self.vm9pctrl.set_pktloop_dpdk([self.vm_config['taps'][0]['tap_mac'], self.vm_config['taps'][1]['tap_mac']])
+ self.assertTrue(ret)
+ ret = self.vm9pctrl.config_amqp(self.init_config['ctrl_ip_setting'].split('/')[0], self.init_config['amqp_server'])
+ self.assertTrue(ret)
+
+
+if __name__ == "__main__":
+ import logging
+ logging.basicConfig(level=logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/env/test_vm_manager.py b/vstf/vstf/agent/unittest/env/test_vm_manager.py
new file mode 100755
index 00000000..3c3deacc
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_vm_manager.py
@@ -0,0 +1,69 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest.env import model
+from vstf.agent.env.basic.vm9pfs import VMConfigBy9pfs
+from vstf.agent.env.basic.vm_manager import VMControlOperation
+
+
+class TestVM9pfs(model.Test):
+ def setUp(self):
+ super(TestVM9pfs, self).setUp()
+ self.vm_config = {
+ 'vm_name': 'vm1',
+ 'vm_cpu': 5,
+ 'image_path': "/mnt/sdb/ubuntu_salt_master.img",
+ 'child_dir': '/mnt/sdb/',
+ 'image_type': 'qcow2',
+ 'ctrl_br': 'br0',
+ 'ctrl_mac': '56:6f:44:a5:3f:a4',
+ "taps": [
+ {
+ "tap_name": "tap_in",
+ "br_type": "bridge",
+ "br_name": "br0",
+ "tap_mac": "56:6f:44:a5:3f:a2",
+ },
+ {
+ "tap_name": "tap_out",
+ "br_type": "bridge",
+ "br_name": "br0",
+ "tap_mac": "56:6f:44:a5:3f:a3",
+ }
+ ],
+ 'init_config': {
+ "amqp_passwd": "guest",
+ "amqp_user": "guest",
+ "amqp_server": "192.168.188.10",
+ "amqp_port": 5672,
+ 'ctrl_ip_setting': '192.168.188.200/23',
+ 'tap_pktloop_config': 'dpdk',
+ 'ctrl_gw': '192.168.188.1'
+ }
+ }
+ self.mgr = VMControlOperation()
+ self.mgr.clean_all_vms()
+
+ def tearDown(self):
+ self.mgr.clean_all_vms()
+ super(TestVM9pfs, self).tearDown()
+
+ def test_create_vm_bridge(self):
+ self.mgr.create_vm(self.vm_config)
+ self.mgr.wait_vm(self.vm_config["vm_name"])
+ self.mgr.init_config_vm(self.vm_config["vm_name"])
+
+ def _replace_opts(self, cfg, br_type):
+ for tap_cfg in cfg["taps"]:
+ tap_cfg["br_type"] = br_type
+
+
+if __name__ == "__main__":
+ import logging
+ logging.basicConfig(level=logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/env/test_vs_plugin_manager.py b/vstf/vstf/agent/unittest/env/test_vs_plugin_manager.py
new file mode 100755
index 00000000..f78c3e7d
--- /dev/null
+++ b/vstf/vstf/agent/unittest/env/test_vs_plugin_manager.py
@@ -0,0 +1,68 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest.env import model
+from vstf.agent.env.vswitch_plugins import manager
+from vstf.common.utils import check_call
+
+
+class TestVsPlugins(model.Test):
+ def setUp(self):
+ super(TestVsPlugins, self).setUp()
+ self.cfg = {
+ "type": "ovs",
+ "name": "ovs1",
+ "uplinks": [
+ {
+ "bdf": self.bdf_of_eth[0],
+ "vlan_mode": "trunk",
+ "vlan_id": "100,200,300,400"
+ },
+ {
+ "bdf": self.bdf_of_eth[1],
+ "vlan_mode": "trunk",
+ "vlan_id": "100,200,300,400"
+ }
+ ],
+ "vtep": {}
+ }
+ self.mgr = manager.VswitchPluginManager()
+
+ def tearDown(self):
+ super(TestVsPlugins, self).tearDown()
+
+ def _check_br_exists(self, name):
+ try:
+ check_call('ifconfig %s' % name, shell=True)
+ except Exception, e:
+ return False
+ return True
+
+ def test_create_bridge(self):
+ self.cfg['name'] = 'br1'
+ self.br = self.mgr.get_vs_plugin('bridge')
+ self.br.clean()
+ self.br.init()
+ self.br.create_br(self.cfg)
+ self.assertTrue(self._check_br_exists('br1'))
+ self.br.clean()
+ self.assertFalse(self._check_br_exists('br1'))
+
+ def test_clean(self):
+ self.mgr.clean()
+
+ def test_get_supported_plugins(self):
+ ret = self.mgr.get_supported_plugins()
+ self.assertEqual(set(ret), {'bridge', 'ovs'})
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.basicConfig(level=logging.INFO)
+ LOG = logging.getLogger(__name__)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/perf/Readme b/vstf/vstf/agent/unittest/perf/Readme
new file mode 100755
index 00000000..3ebe6c49
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/Readme
@@ -0,0 +1,7 @@
+To enable Perf unittest, please make sure:
+
+ 1. you have setup a "Tn virtual network" on Target Host, so you can ping from one nic to the other nic.
+ 2. change options in "unittest/configuration.py".
+
+run tests:
+ python run_test.py \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/perf/__init__.py b/vstf/vstf/agent/unittest/perf/__init__.py
new file mode 100755
index 00000000..89dcd4e2
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/__init__.py
@@ -0,0 +1,14 @@
+# Copyright Huawei Technologies Co., Ltd. 1998-2015.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the License); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an AS IS BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/vstf/vstf/agent/unittest/perf/model.py b/vstf/vstf/agent/unittest/perf/model.py
new file mode 100755
index 00000000..6383ae0c
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/model.py
@@ -0,0 +1,46 @@
+"""
+Created on 2015-9-25
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest import configuration
+from vstf.agent.perf import netns
+
+
+class LocalModel(unittest.TestCase):
+ def _ping(self):
+ device_list, ns_list, ip_setting_list, ip_list = self.device_list, self.ns_list, self.ip_setting_list, self.ip_list
+ for ns, dev, ip_setting in zip(ns_list, device_list, ip_setting_list):
+ netdev = {
+ "namespace": ns,
+ "iface": dev,
+ 'ip_setting': ip_setting
+ }
+ self.mgr.config_dev(netdev)
+ ip_list_copy = ip_list[:]
+ ip_list_copy.reverse()
+ for ns, ip in zip(ns_list, ip_list_copy):
+ self.assertTrue(sself.mgr.ping(ns, ip), True)
+ self.mgr.clean_all_namespace()
+
+ def setUp(self):
+ # make sure you have set up Tn loop on the "Target Host"
+ self.mgr = netns.NetnsManager()
+ self.mgr.clean_all_namespace()
+ self.device_list = configuration.eth_for_test
+ self.mac_list = configuration.mac_of_eth
+ self.ns_list = ['send', 'receive']
+ self.ip_setting_list = ['192.168.1.1/24', '192.168.1.2/24']
+ self.ip_list = ['192.168.1.1', '192.168.1.2']
+
+ def tearDown(self):
+ self.mgr.clean_all_namespace()
+
+
+class Model(LocalModel):
+ def setUp(self):
+ # make sure you have set up Tn loop on the "Target Host"
+ super(Model, self).setUp()
+ self._ping()
diff --git a/vstf/vstf/agent/unittest/perf/run_test.py b/vstf/vstf/agent/unittest/perf/run_test.py
new file mode 100755
index 00000000..684ca183
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/run_test.py
@@ -0,0 +1,32 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+import importlib
+
+test_order_list = [
+ "vstf.agent.unittest.perf.test_utils",
+ "vstf.agent.unittest.perf.test_netns",
+ "vstf.agent.unittest.perf.test_netperf",
+ "vstf.agent.unittest.perf.test_qperf",
+ "vstf.agent.unittest.perf.test_pktgen",
+ "vstf.agent.unittest.perf.test_vstfperf",
+]
+
+
+def main():
+ import logging
+ logging.getLogger(__name__)
+ logging.basicConfig(level=logging.DEBUG)
+ suite = unittest.TestSuite()
+ for mod_name in test_order_list:
+ mod = importlib.import_module(mod_name)
+ suit = unittest.TestLoader().loadTestsFromModule(mod)
+ suite.addTest(suit)
+ unittest.TextTestRunner().run(suite)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/vstf/vstf/agent/unittest/perf/test_ethtool.py b/vstf/vstf/agent/unittest/perf/test_ethtool.py
new file mode 100755
index 00000000..48e0d7f6
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_ethtool.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+# author: wly
+# date: 2015/11/13
+# see license for license details
+
+import unittest
+import logging
+from vstf.agent.perf import ethtool
+from vstf.agent.unittest import configuration
+from vstf.common.log import setup_logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Testethtool(unittest.TestCase):
+ def setUp(self):
+ LOG.info("start Testethtool unit test.")
+ self._devices = configuration.eth_for_test
+ super(Testethtool, self).setUp()
+
+ def teardown(self):
+ LOG.info("stop Testethtool unit test.")
+
+# @unittest.skip('for now')
+ def test_autoneg_on(self):
+ for dev in self._devices:
+ self.assertTrue(ethtool.autoneg_on(dev), True)
+
+ def test_autoneg_off(self):
+ for dev in self._devices:
+ self.assertTrue(ethtool.autoneg_off(dev), True)
+
+ def test_autoneg_query(self):
+ for dev in self._devices:
+ result = ethtool.autoneg_query(dev)
+ LOG.info(result)
+
+if __name__ == "__main__":
+ setup_logging(level=logging.INFO, log_file="/var/log/vstf/vstf-unit-test.log", clevel=logging.INFO)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/perf/test_netns.py b/vstf/vstf/agent/unittest/perf/test_netns.py
new file mode 100755
index 00000000..912358bd
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_netns.py
@@ -0,0 +1,67 @@
+"""
+Created on 2015-9-23
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest.perf import model
+from vstf.agent.perf import netns
+
+
+class TestNetnsManager(model.Model):
+ def setUp(self):
+ super(TestNetnsManager, self).setUp()
+ self.ns = netns.Netns()
+
+ def tearDown(self):
+ super(TestNetnsManager, self).tearDown()
+
+ def testNetns(self):
+ device_list,ns_list,ip_setting_list,ip_list = self.device_list,self.ns_list,self.ip_setting_list,self.ip_list
+ net = self.ns
+ for ns in ns_list:
+ self.assertTrue(net.create_namespace(ns),'create_namespace failed')
+ for ns,dev,ip_setting in zip(ns_list,device_list,ip_setting_list):
+ self.assertTrue(net.add_device(ns, dev),'add_device failed')
+ self.assertTrue(net.activate_device(ns,dev),'activate_device failed')
+ self.assertTrue(net.config_ip(ns,dev,ip_setting),'config_ip failed')
+ for ns in ns_list:
+ self.assertTrue(net.remove_namespace(ns),'remove_namespace failed')
+
+ def testNetNsManager(self):
+ mgr = self.mgr
+ device_list,ns_list,ip_setting_list,ip_list = self.device_list,self.ns_list,self.ip_setting_list,self.ip_list
+ for ns,dev,ip_setting in zip(ns_list,device_list,ip_setting_list):
+ netdev = {
+ "namespace":ns,
+ "iface":dev,
+ 'ip_setting':ip_setting
+ }
+ ret = mgr.config_dev(netdev)
+ self.assertTrue(ret,"config_dev failed, netdev=%s" % netdev)
+
+ for ns,dev,ip_setting in zip(ns_list,device_list,ip_setting_list):
+ netdev = {
+ "namespace":ns,
+ "iface":dev,
+ 'ip_setting':ip_setting
+ }
+ self.assertTrue(mgr.recover_dev(netdev),"recover_dev failed, netdev=%s" % netdev)
+
+ for ns,dev,ip_setting in zip(ns_list,device_list,ip_setting_list):
+ netdev = {
+ "namespace":ns,
+ "iface":dev,
+ 'ip_setting':ip_setting
+ }
+ self.assertTrue(mgr.config_dev(netdev),"config_dev failed, netdev=%s" % netdev)
+ self.assertTrue(mgr.clean_all_namespace(),'remove_namespace failed')
+
+
+if __name__ == "__main__":
+ import logging
+ LOG = logging.getLogger(__name__)
+ logging.basicConfig(level = logging.DEBUG)
+ #import sys;sys.argv = ['', 'Test.testName']
+ unittest.main() \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/perf/test_netperf.py b/vstf/vstf/agent/unittest/perf/test_netperf.py
new file mode 100755
index 00000000..b5f8cb2a
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_netperf.py
@@ -0,0 +1,105 @@
+'''
+Created on 2015-9-24
+
+@author: y00228926
+'''
+import unittest
+import time
+import subprocess
+
+from vstf.agent.unittest.perf import model
+from vstf.agent.perf import netperf
+from vstf.agent.perf.utils import get_pid_by_name
+
+
+class TestNetperf(model.Model):
+ '''
+ please make sure 'Tn' network on 'Target Host' is created.
+ '''
+ def setUp(self):
+ super(TestNetperf, self).setUp()
+ subprocess.call("killall netperf", shell = True)
+ subprocess.call("killall netserver",shell = True)
+ for ns, dev, ip_setting in zip(self.ns_list, self.device_list, self.ip_setting_list):
+ netdev = {
+ "namespace":ns,
+ "iface":dev,
+ 'ip_setting':ip_setting
+ }
+ self.mgr.config_dev(netdev)
+ self.send_cfg = {
+ "namespace": "send",
+ "protocol": "udp_bw",
+ "dst":[
+ {"ip": "192.168.1.2"}
+ ],
+ "size": 64,
+ "threads": 1,
+ "time": 10,
+ }
+
+ def tearDown(self):
+ super(TestNetperf, self).tearDown()
+
+ def test_netperf_start_success(self):
+ perf = netperf.Netperf()
+ ret = perf.receive_start(namespace='receive')
+ exp = (0, 'start netserver success')
+ self.assertEqual(ret, exp, "receive_start failed %s" % str(ret))
+
+ ret = perf.send_start(**self.send_cfg)
+ exp = (0,"start netperf send success")
+ self.assertEqual(ret, exp, "failed to start netperf")
+
+ time.sleep(3)
+
+ ret = perf.send_stop()
+ exp = [(0, "process is stopped by killed")]
+ self.assertEqual(ret, exp, "send_stop failed, ret = %s" % str(ret))
+
+ ret = perf.receive_stop()
+ exp = (0, "stop netserver success")
+ self.assertEqual(ret, exp, "receive_stop failedf, ret = %s" % str(ret))
+
+ def test_netperf_start_success_mutil_threads(self):
+ perf = netperf.Netperf()
+ ret = perf.receive_start(namespace='receive')
+ exp = (0, 'start netserver success')
+ self.assertEqual(ret, exp, "receive_start failed %s" % str(ret))
+
+ self.send_cfg.update({"threads":3})
+ exp = (0,"start netperf send success")
+ ret = perf.send_start(**self.send_cfg)
+ self.assertEqual(ret, exp, "failed to start netperf")
+
+ time.sleep(3)
+
+ rets = perf.send_stop()
+ exp = [(0, 'process is stopped by killed'), (0, 'process is stopped by killed'), (0, 'process is stopped by killed')]
+ self.assertEqual(rets, exp, "send_stop failed, rets = %s" % str(rets))
+
+ rets = perf.receive_stop()
+ self.assertEqual(rets, (0, "stop netserver success"), "receive_stop failedf, rets = %s" % str(rets))
+
+ def test_clean(self):
+ perf = netperf.Netperf()
+ ret = perf.receive_start(namespace='receive')
+ exp = (0, 'start netserver success')
+ self.assertEqual(ret, exp, "receive_start failed %s" % str(ret))
+
+ self.send_cfg.update({"threads":3})
+ exp = (0,"start netperf send success")
+ ret = perf.send_start(**self.send_cfg)
+ self.assertEqual(ret, exp, "failed to start netperf")
+ perf.clean()
+ ret = get_pid_by_name('netperf')
+ self.assertEqual(ret, [], "failed to clean netperf")
+ ret = get_pid_by_name('netserver')
+ self.assertEqual(ret, [], "failed to clean netserver")
+
+if __name__ == "__main__":
+ import logging
+ logging.getLogger(__name__)
+ logging.basicConfig(level = logging.DEBUG)
+ #import sys;sys.argv = ['', 'Test.testName']
+ unittest.main() \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/perf/test_pktgen.py b/vstf/vstf/agent/unittest/perf/test_pktgen.py
new file mode 100755
index 00000000..5e6cdf76
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_pktgen.py
@@ -0,0 +1,90 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+import time
+
+from vstf.agent.unittest.perf import model
+from vstf.agent.perf import pktgen
+
+
+class TestPktgen(model.Model):
+ def setUp(self):
+ super(TestPktgen, self).setUp()
+
+ def tearDown(self):
+ super(TestPktgen, self).tearDown()
+
+ def test_single_thread(self):
+ perf = pktgen.Pktgen()
+ print perf.receive_start()
+ send = {
+ "src": [
+ {"iface": self.device_list[0], "mac": self.mac_list[0]}
+ ],
+ "dst": [
+ {"mac": self.mac_list[1]}
+ ],
+ "size": 64,
+ "threads": 1,
+ 'ratep': 0
+ }
+ ret = perf.send_start(**send)
+ self.assertEqual((0, 'start pktgen send success'), ret, "send_start failed, ret=%s" % str(ret))
+ time.sleep(5)
+ ret = perf.send_stop()
+ self.assertEqual([(0, '')], ret, "send_start failed, ret=%s" % ret)
+ ret = perf.receive_stop()
+ self.assertEqual((0, 'pktgen neednt receive stop'), ret, "send_stop failed, ret=%s" % str(ret))
+
+ def test_single_thread_bidirectional(self):
+ perf = pktgen.Pktgen()
+ print perf.receive_start()
+ send = {
+ "src": [
+ {"iface": self.device_list[0], "mac": self.mac_list[0]},
+ {"iface": self.device_list[1], "mac": self.mac_list[1]}
+ ],
+ "dst": [
+ {"mac": self.mac_list[1]},
+ {"mac": self.mac_list[0]}
+ ],
+ "size": 64,
+ "threads": 1,
+ 'ratep': 0
+ }
+ ret = perf.send_start(**send)
+ self.assertEqual((0, 'start pktgen send success'), ret, "send_start failed, ret=%s" % str(ret))
+ time.sleep(5)
+ ret = perf.send_stop()
+ self.assertEqual([(0, '')], ret, "send_start failed, ret=%s" % ret)
+ ret = perf.receive_stop()
+ self.assertEqual((0, 'pktgen neednt receive stop'), ret, "send_stop failed, ret=%s" % str(ret))
+
+ def test_clean(self):
+ perf = pktgen.Pktgen()
+ print perf.receive_start()
+ send = {
+ "src": [
+ {"iface": self.device_list[0], "mac": self.mac_list[0]}
+ ],
+ "dst": [
+ {"mac": self.mac_list[1]}
+ ],
+ "size": 64,
+ "threads": 1,
+ 'ratep': 0
+ }
+ ret = perf.send_start(**send)
+ self.assertEqual((0, 'start pktgen send success'), ret, "send_start failed, ret=%s" % str(ret))
+ perf.clean()
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.getLogger(__name__)
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/perf/test_qperf.py b/vstf/vstf/agent/unittest/perf/test_qperf.py
new file mode 100755
index 00000000..ec9a290d
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_qperf.py
@@ -0,0 +1,102 @@
+'''
+Created on 2015-9-24
+
+@author: y00228926
+'''
+import unittest
+import subprocess
+import time
+
+from vstf.agent.unittest.perf import model
+from vstf.agent.perf import qperf
+from vstf.agent.perf.utils import get_pid_by_name
+
+
+class testQperf(model.Model):
+ def setUp(self):
+ super(testQperf, self).setUp()
+ subprocess.call("killall qperf", shell = True)
+ for ns, dev, ip_setting in zip(self.ns_list, self.device_list, self.ip_setting_list):
+ netdev = {
+ "namespace":ns,
+ "iface":dev,
+ 'ip_setting':ip_setting
+ }
+ self.mgr.config_dev(netdev)
+ self.send_cfg = {
+ "namespace": self.ns_list[0],
+ "time":1,
+ "protocol": "udp_lat",
+ "dst":[
+ {"ip": self.ip_list[1]}
+ ],
+ "size": 64,
+ }
+
+ def tearDown(self):
+ super(testQperf, self).tearDown()
+
+ def test_qperf_quick(self):
+ perf = qperf.Qperf()
+ ret = perf.receive_start(namespace=self.ns_list[1])
+ exp = (0, "start qperf receive success")
+ self.assertEqual(ret, exp, "receive_start failed %s" % str(ret))
+
+ ret = perf.send_start(**self.send_cfg)
+ exp = (0,"start qperf send success")
+ self.assertEqual(ret, exp, "send_start failed")
+
+ time.sleep(3)
+
+ ret = perf.send_stop()
+ for r in ret:
+ self.assertEqual(r[0], 0, "send_stop failed, ret = %s" % str(ret))
+ for key in ('MaximumLatency', 'AverageLatency', 'MinimumLatency'):
+ self.assertIn(key, r[1], "send_stop failed, ret = %s" % str(ret))
+
+ ret = perf.receive_stop()
+ exp = (0, "stop qperf receive success")
+ self.assertEqual(ret, exp, "receive_stop failed, ret = %s" % str(ret))
+
+ def test_qperf_quick_3s(self):
+ perf = qperf.Qperf()
+ self.send_cfg.update({"time":3})
+ ret = perf.receive_start(namespace='receive')
+ exp = (0, 'start qperf receive success')
+ self.assertEqual(ret, exp, "receive_start failed %s" % str(ret))
+
+ self.send_cfg.update({"threads":3})
+ exp = (0,"start qperf send success")
+ ret = perf.send_start(**self.send_cfg)
+ self.assertEqual(ret, exp, "send_start failed %s" % str(ret))
+
+ ret = perf.send_stop()
+ for r in ret:
+ self.assertEqual(r[0], 0, "send_stop failed, ret = %s" % str(ret))
+ for key in ('MaximumLatency', 'AverageLatency', 'MinimumLatency'):
+ self.assertIn(key, r[1], "send_stop failed, ret = %s" % str(ret))
+
+ ret = perf.receive_stop()
+ self.assertEqual(ret, (0, 'stop qperf receive success'), "receive_stop failedf, ret = %s" % str(ret))
+
+ def test_clean(self):
+ perf = qperf.Qperf()
+ self.send_cfg.update({"time":10})
+ ret = perf.receive_start(namespace=self.ns_list[1])
+ exp = (0, "start qperf receive success")
+ self.assertEqual(ret, exp, "receive_start failed %s" % str(ret))
+
+ ret = perf.send_start(**self.send_cfg)
+ exp = (0,"start qperf send success")
+ self.assertEqual(ret, exp, "send_start failed")
+
+ perf.clean()
+ ret = get_pid_by_name('qperf')
+ self.assertEqual(ret, [], "clean qperf failed")
+
+
+if __name__ == "__main__":
+ import logging
+ logging.getLogger(__name__)
+ logging.basicConfig(level = logging.DEBUG)
+ unittest.main() \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/perf/test_utils.py b/vstf/vstf/agent/unittest/perf/test_utils.py
new file mode 100755
index 00000000..83410c54
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_utils.py
@@ -0,0 +1,31 @@
+"""
+Created on 2015-9-24
+
+@author: y00228926
+"""
+import unittest
+from vstf.common import utils
+
+
+class TestUtils(unittest.TestCase):
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_ns_cmd(self):
+ cmd = "ls"
+ ns = "xx"
+ exp_cmd = "ip netns exec xx ls"
+ ret = utils.ns_cmd(ns, cmd)
+ self.assertEqual(ret, exp_cmd, "ns_cmd failed to add ns header prefix:%s != %s" % (ret, exp_cmd))
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.getLogger(__name__)
+ logging.basicConfig(level=logging.DEBUG)
+ # import sys;sys.argv = ['', 'Test.testName']
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/perf/test_vnstat.py b/vstf/vstf/agent/unittest/perf/test_vnstat.py
new file mode 100755
index 00000000..f7c31f5b
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_vnstat.py
@@ -0,0 +1,57 @@
+"""
+Created on 2015-9-23
+
+@author: y00228926
+"""
+import unittest
+import time
+
+from vstf.agent.perf.utils import get_pid_by_name
+from vstf.agent.unittest.perf import model
+from vstf.agent.perf import vnstat
+
+
+class TestVnstat(model.LocalModel):
+ def setUp(self):
+ super(TestVnstat, self).setUp()
+ self.vnstat = vnstat.VnStat()
+ self.namespace = self.ns_list[0]
+ self.device = self.device_list[0]
+ self.ip_setting = self.ip_setting_list[0]
+ netdev = {
+ "namespace": self.namespace,
+ "iface": self.device,
+ 'ip_setting': self.ip_setting
+ }
+ self.mgr.config_dev(netdev)
+
+ def tearDown(self):
+ super(TestVnstat, self).tearDown()
+
+ def test_run_vnstat(self):
+ logging.basicConfig(level=logging.DEBUG)
+ pid = self.vnstat.run_vnstat(self.device, self.namespace)
+ time.sleep(12)
+ raw = self.vnstat.kill_vnstat(pid, self.namespace)
+ print raw['raw_data']
+ data = self.vnstat.process(raw['raw_data'])
+ self.assertTrue(type(data) is dict)
+ for key in ('rxmB/s', 'txmB', 'rxpck', 'txpck', \
+ 'rxpck_min/s', 'txmB_max/s', 'txpck_max/s', \
+ 'txmB/s', 'rxmB', 'rxmB_max/s', 'rxpck/s', 'rxmB_min/s', \
+ 'time', 'rxpck_max/s', 'txpck_min/s', 'txpck/s', 'txmB_min/s'):
+ self.assertTrue(key in data)
+
+ def test_clean(self):
+ self.vnstat.run_vnstat(self.device, self.namespace)
+ self.vnstat.clean()
+ self.assertTrue(get_pid_by_name('vnstat') == [])
+
+
+if __name__ == "__main__":
+ import logging
+
+ LOG = logging.getLogger(__name__)
+ logging.basicConfig(level=logging.DEBUG)
+ # import sys;sys.argv = ['', 'Test.testName']
+ unittest.main()
diff --git a/vstf/vstf/agent/unittest/perf/test_vstfperf.py b/vstf/vstf/agent/unittest/perf/test_vstfperf.py
new file mode 100755
index 00000000..30d7c7fd
--- /dev/null
+++ b/vstf/vstf/agent/unittest/perf/test_vstfperf.py
@@ -0,0 +1,98 @@
+"""
+Created on 2015-9-28
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.perf.vstfperf import Vstfperf
+from vstf.agent.unittest.perf import model
+
+
+class Test(model.Model):
+ def setUp(self):
+ super(Test, self).setUp()
+
+ for ns, dev, ip_setting in zip(self.ns_list, self.device_list, self.ip_setting_list):
+ net_dev = {
+ "namespace":ns,
+ "iface":dev,
+ 'ip_setting':ip_setting
+ }
+ self.mgr.config_dev(net_dev)
+
+ self.start = {
+ "operation": "start",
+ "action": "send",
+ "tool": "netperf",
+ "params":{
+ "namespace": self.ns_list[0],
+ "protocol": "tcp_lat",
+ "dst":[
+ {"ip": self.ip_list[1]}
+ ],
+ "size": 64,
+ "threads": 1,
+ "time": 1,
+ },
+ }
+ self.stop = {
+ "operation": "stop",
+ "action": "send",
+ "tool": "netperf",
+ "params":{
+ "namespace": self.ns_list[1],
+ },
+ }
+
+ def tearDown(self):
+ super(Test, self).tearDown()
+
+ def testNetperf(self):
+ perf = Vstfperf()
+ self.start['tool'] = 'netperf'
+ self.stop['tool'] = 'netperf'
+ self.start['action'] = 'receive'
+ self.start['operation'] = 'start'
+ self.start['params']['namespace'] = self.ns_list[1]
+ self.start['params']['protocol'] = 'udp_bw'
+ perf.run(**self.start)
+ self.start['action'] = 'send'
+ self.start['operation'] = 'start'
+ self.start['params']['namespace'] = self.ns_list[0]
+ perf.run(**self.start)
+ self.stop['action'] = 'send'
+ self.stop['operation'] = 'stop'
+ self.stop['params']['namespace'] = self.ns_list[0]
+ perf.run(**self.stop)
+ self.stop['action'] = 'receive'
+ self.stop['operation'] = 'stop'
+ self.stop['params']['namespace'] = self.ns_list[1]
+ perf.run(**self.stop)
+
+ def testQperf(self):
+ perf = Vstfperf()
+ self.start['tool'] = 'qperf'
+ self.stop['tool'] = 'qperf'
+ self.start['action'] = 'receive'
+ self.start['operation'] = 'start'
+ self.start['params']['namespace'] = self.ns_list[1]
+ perf.run(**self.start)
+ self.start['action'] = 'send'
+ self.start['operation'] = 'start'
+ self.start['params']['namespace'] = self.ns_list[0]
+ perf.run(**self.start)
+ self.stop['action'] = 'send'
+ self.stop['operation'] = 'stop'
+ self.stop['params']['namespace'] = self.ns_list[0]
+ perf.run(**self.stop)
+ self.stop['action'] = 'receive'
+ self.stop['operation'] = 'stop'
+ self.stop['params']['namespace'] = self.ns_list[1]
+ perf.run(**self.stop)
+
+
+if __name__ == "__main__":
+ import logging
+ logging.basicConfig(level = logging.DEBUG)
+ unittest.main() \ No newline at end of file
diff --git a/vstf/vstf/agent/unittest/test_callback.py b/vstf/vstf/agent/unittest/test_callback.py
new file mode 100755
index 00000000..35336d9c
--- /dev/null
+++ b/vstf/vstf/agent/unittest/test_callback.py
@@ -0,0 +1,80 @@
+"""
+Created on 2015-9-28
+
+@author: y00228926
+"""
+import unittest
+
+from vstf.agent.unittest.perf import model
+from vstf.agent import softagent
+
+
+class TestCallback(model.Model):
+ def setUp(self):
+ super(TestCallback, self).setUp()
+ self.agent = softagent.softAgent()
+ for ns, dev, ip_setting in zip(self.ns_list, self.device_list, self.ip_setting_list):
+ netdev = {
+ "namespace": ns,
+ "iface": dev,
+ 'ip_setting': ip_setting
+ }
+ self.mgr.config_dev(netdev)
+
+ self.start = {
+ "operation": "start",
+ "action": "send",
+ "tool": "netperf",
+ "params": {
+ "namespace": self.ns_list[0],
+ "protocol": "tcp_lat",
+ "dst": [
+ {"ip": self.ip_list[1]}
+ ],
+ "size": 64,
+ "threads": 1,
+ "time": 1,
+ },
+ }
+ self.stop = {
+ "operation": "stop",
+ "action": "send",
+ "tool": "netperf",
+ "params": {
+ "namespace": self.ns_list[1],
+ },
+ }
+
+ def tearDown(self):
+ super(TestCallback, self).tearDown()
+
+ def test_clean(self):
+ agent = self.agent
+ agent.perf_clean()
+ self.start['tool'] = 'netperf'
+ self.stop['tool'] = 'netperf'
+ self.start['action'] = 'receive'
+ self.start['operation'] = 'start'
+ self.start['params']['namespace'] = self.ns_list[1]
+ self.start['params']['protocol'] = 'udp_bw'
+ agent.perf_run(**self.start)
+ self.start['action'] = 'send'
+ self.start['operation'] = 'start'
+ self.start['params']['namespace'] = self.ns_list[0]
+ agent.perf_run(**self.start)
+ agent.perf_clean()
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.getLogger(__name__)
+ logging.basicConfig(level=logging.DEBUG)
+ # import sys;sys.argv = ['', 'Test.testName']
+ unittest.main()
+
+if __name__ == "__main__":
+ import logging
+
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()