From 8f1101df131a4d3e03b377738507d88b745831c0 Mon Sep 17 00:00:00 2001 From: "Yiting.Li" Date: Tue, 22 Dec 2015 17:11:12 -0800 Subject: Upload the contribution of vstf as bottleneck network framework. End to End Performance test JIRA:BOTTLENECK-29 Change-Id: Ib2c553c8b60d6cda9e7a7b52b737c9139f706ebd Signed-off-by: Yiting.Li --- vstf/vstf/agent/perf/__init__.py | 14 ++++ vstf/vstf/agent/perf/affctl.py | 18 ++++ vstf/vstf/agent/perf/ethtool.py | 56 +++++++++++++ vstf/vstf/agent/perf/iperf.py | 152 +++++++++++++++++++++++++++++++++ vstf/vstf/agent/perf/netmap.py | 166 ++++++++++++++++++++++++++++++++++++ vstf/vstf/agent/perf/netns.py | 103 +++++++++++++++++++++++ vstf/vstf/agent/perf/netperf.py | 177 +++++++++++++++++++++++++++++++++++++++ vstf/vstf/agent/perf/pktgen.py | 149 ++++++++++++++++++++++++++++++++ vstf/vstf/agent/perf/qperf.py | 164 ++++++++++++++++++++++++++++++++++++ vstf/vstf/agent/perf/sar.py | 78 +++++++++++++++++ vstf/vstf/agent/perf/utils.py | 42 ++++++++++ vstf/vstf/agent/perf/vnstat.py | 106 +++++++++++++++++++++++ vstf/vstf/agent/perf/vstfperf.py | 105 +++++++++++++++++++++++ 13 files changed, 1330 insertions(+) create mode 100755 vstf/vstf/agent/perf/__init__.py create mode 100755 vstf/vstf/agent/perf/affctl.py create mode 100755 vstf/vstf/agent/perf/ethtool.py create mode 100755 vstf/vstf/agent/perf/iperf.py create mode 100755 vstf/vstf/agent/perf/netmap.py create mode 100755 vstf/vstf/agent/perf/netns.py create mode 100755 vstf/vstf/agent/perf/netperf.py create mode 100755 vstf/vstf/agent/perf/pktgen.py create mode 100755 vstf/vstf/agent/perf/qperf.py create mode 100755 vstf/vstf/agent/perf/sar.py create mode 100755 vstf/vstf/agent/perf/utils.py create mode 100755 vstf/vstf/agent/perf/vnstat.py create mode 100755 vstf/vstf/agent/perf/vstfperf.py (limited to 'vstf/vstf/agent/perf') 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() -- cgit 1.2.3-korg