New reliability/availability testcase - IP datagram error rate and etc. 63/26963/8
authorJingLu5 <lvjing5@huawei.com>
Fri, 13 Jan 2017 14:48:25 +0000 (14:48 +0000)
committerJingLu5 <lvjing5@huawei.com>
Mon, 13 Feb 2017 02:27:00 +0000 (02:27 +0000)
JIRA: YARDSTICK-534

This test case uses nstat to monitor network metrics provided by the kernel in
a host and calculate IP datagram error rate, ICMP message error rate, TCP
segment error rate and UDP datagram error rate.

Change-Id: I2fe6457bb5c95d0446c1463991ae31cc664b09f8
Signed-off-by: JingLu5 <lvjing5@huawei.com>
samples/nstat.yaml [new file with mode: 0644]
tests/opnfv/test_cases/opnfv_yardstick_tc076.yaml [new file with mode: 0644]
tests/unit/benchmark/scenarios/networking/test_nstat.py [new file with mode: 0644]
tools/ubuntu-server-cloudimg-dpdk-modify.sh
tools/ubuntu-server-cloudimg-modify.sh
yardstick/benchmark/scenarios/networking/nstat.py [new file with mode: 0644]

diff --git a/samples/nstat.yaml b/samples/nstat.yaml
new file mode 100644 (file)
index 0000000..0a5aa80
--- /dev/null
@@ -0,0 +1,35 @@
+---
+
+schema: "yardstick:task:0.1"
+
+description: >
+    Sample benchmark task config file;
+    Monitor network metrics provided by the kernel in a host and calculate
+    IP datagram error rate, ICMP message error rate, TCP segment error rate and
+    UDP datagram error rate.
+
+scenarios:
+-
+  type: Nstat
+  options:
+    duration: 60
+
+  host: poseidon.demo
+
+  runner:
+    type: Iteration
+    iterations: 1
+
+context:
+  name: demo
+  image: yardstick-image
+  flavor: yardstick-flavor
+  user: ubuntu
+
+  servers:
+    poseidon:
+      floating_ip: true
+
+  networks:
+    test:
+      cidr: '10.0.1.0/24'
diff --git a/tests/opnfv/test_cases/opnfv_yardstick_tc076.yaml b/tests/opnfv/test_cases/opnfv_yardstick_tc076.yaml
new file mode 100644 (file)
index 0000000..c23ee97
--- /dev/null
@@ -0,0 +1,56 @@
+---
+
+schema: "yardstick:task:0.1"
+
+description: >
+    Yardstick TC076 config file;
+    Monitor network metrics provided by the kernel in a host and calculate
+    IP datagram error rate, ICMP message error rate, TCP segment error rate and
+    UDP datagram error rate.
+
+scenarios:
+-
+  type: Ping
+  run_in_background: true
+  options:
+    packetsize: 200
+
+  host: demeter.yardstick-TC076
+  target: poseidon.yardstick-TC076
+
+-
+  type: Nstat
+  options:
+    duration: 300
+
+  host: poseidon.yardstick-TC076
+
+  runner:
+    type: Iteration
+    iterations: 1
+
+  sla:
+    IP_datagram_error_rate: 0.01
+    action: monitor
+
+context:
+  name: yardstick-TC076
+  image: yardstick-image
+  flavor: yardstick-flavor
+  user: ubuntu
+
+  placement_groups:
+    pgrp1:
+      policy: "availability"
+
+  servers:
+    demeter:
+      floating_ip: true
+      placement: "pgrp1"
+    poseidon:
+      floating_ip: true
+      placement: "pgrp1"
+
+  networks:
+    test:
+      cidr: '10.0.1.0/24'
diff --git a/tests/unit/benchmark/scenarios/networking/test_nstat.py b/tests/unit/benchmark/scenarios/networking/test_nstat.py
new file mode 100644 (file)
index 0000000..87a7663
--- /dev/null
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Unittest for yardstick.benchmark.scenarios.networking.nstat.Nstat
+
+from __future__ import absolute_import
+
+import unittest
+
+import mock
+
+from yardstick.benchmark.scenarios.networking import nstat
+
+@mock.patch('yardstick.benchmark.scenarios.networking.nstat.ssh')
+class NstatTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.ctx = {
+            "host": {
+                "ip": "192.168.50.28",
+                "user": "root",
+                "key_filename": "mykey.key"
+            }
+        }
+
+    def test_nstat_successful_setup(self, mock_ssh):
+
+        n = nstat.Nstat({}, self.ctx)
+        n.setup()
+
+        mock_ssh.SSH().execute.return_value = (0, '', '')
+        self.assertIsNotNone(n.client)
+        self.assertEqual(n.setup_done, True)
+
+    def test_nstat_successful_no_sla(self, mock_ssh):
+
+        options = {
+            "duration": 60
+        }
+        args = {
+            "options": options,
+        }
+        n = nstat.Nstat(args, self.ctx)
+        result = {}
+
+        sample_output = '#kernel\nIpInReceives                    1837               0.0\nIpInHdrErrors                   0                  0.0\nIpInAddrErrors                  2                  0.0\nIcmpInMsgs                      319                  0.0\nIcmpInErrors                    0                0.0\nTcpInSegs                       36               0.0\nTcpInErrs                       0                  0.0\nUdpInDatagrams                  1318                  0.0\nUdpInErrors                     0                  0.0\n'
+
+        mock_ssh.SSH().execute.return_value = (0, sample_output, '')
+
+        n.run(result)
+        expected_result = {"TcpInErrs": 0, "UdpInDatagrams": 1318,
+            "Tcp_segment_error_rate": 0.0, "IpInAddrErrors": 2,
+            "IpInHdrErrors": 0, "IcmpInErrors": 0, "IpErrors": 2,
+            "TcpInSegs": 36, "IpInReceives": 1837, "IcmpInMsgs": 319,
+            "IP_datagram_error_rate": 0.001, "Udp_datagram_error_rate": 0.0,
+            "Icmp_message_error_rate": 0.0, "UdpInErrors": 0}
+        self.assertEqual(result, expected_result)
+
+    def test_nstat_successful_sla(self, mock_ssh):
+
+        options = {
+            "duration": 60
+        }
+        sla = {
+            "IP_datagram_error_rate": 0.1
+        }
+        args = {
+            "options": options,
+            "sla": sla
+        }
+        n = nstat.Nstat(args, self.ctx)
+        result = {}
+
+        sample_output = '#kernel\nIpInReceives                    1837               0.0\nIpInHdrErrors                   0                  0.0\nIpInAddrErrors                  2                  0.0\nIcmpInMsgs                      319                  0.0\nIcmpInErrors                    0                0.0\nTcpInSegs                       36               0.0\nTcpInErrs                       0                  0.0\nUdpInDatagrams                  1318                  0.0\nUdpInErrors                     0                  0.0\n'
+
+        mock_ssh.SSH().execute.return_value = (0, sample_output, '')
+
+        n.run(result)
+        expected_result = {"TcpInErrs": 0, "UdpInDatagrams": 1318,
+            "Tcp_segment_error_rate": 0.0, "IpInAddrErrors": 2,
+            "IpInHdrErrors": 0, "IcmpInErrors": 0, "IpErrors": 2,
+            "TcpInSegs": 36, "IpInReceives": 1837, "IcmpInMsgs": 319,
+            "IP_datagram_error_rate": 0.001, "Udp_datagram_error_rate": 0.0,
+            "Icmp_message_error_rate": 0.0, "UdpInErrors": 0}
+        self.assertEqual(result, expected_result)
+
+    def test_nstat_unsuccessful_cmd_error(self, mock_ssh):
+
+        options = {
+            "duration": 60
+        }
+        sla = {
+            "IP_datagram_error_rate": 0.1
+        }
+        args = {
+            "options": options,
+            "sla": sla
+        }
+        n = nstat.Nstat(args, self.ctx)
+        result = {}
+
+        mock_ssh.SSH().execute.return_value = (1, '', 'FOOBAR')
+        self.assertRaises(RuntimeError, n.run, result)
+
+
+def main():
+    unittest.main()
+
+if __name__ == '__main__':
+    main()
index aa4e252..9a3857e 100755 (executable)
@@ -63,10 +63,13 @@ linuxheadersversion=`echo ls boot/vmlinuz* | cut -d- -f2-`
 
 apt-get update
 apt-get install -y \
+    bc \
     fio \
     gcc \
     git \
     iperf3 \
+    iproute2 \
+    ethtool \
     linux-tools-common \
     linux-tools-generic \
     lmbench \
index 49c842c..c0ae774 100755 (executable)
@@ -58,10 +58,13 @@ bootcmd:
 EOF
 fi
 apt-get install -y \
+    bc \
     fio \
     git \
     gcc \
     iperf3 \
+    ethtool \
+    iproute2 \
     linux-tools-common \
     linux-tools-generic \
     lmbench \
diff --git a/yardstick/benchmark/scenarios/networking/nstat.py b/yardstick/benchmark/scenarios/networking/nstat.py
new file mode 100644 (file)
index 0000000..df96dbd
--- /dev/null
@@ -0,0 +1,130 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from __future__ import print_function
+from __future__ import absolute_import
+
+import time
+import logging
+
+import yardstick.ssh as ssh
+from yardstick.benchmark.scenarios import base
+
+LOG = logging.getLogger(__name__)
+
+PRECISION = 3
+
+
+class Nstat(base.Scenario):
+    """Use nstat to monitor network metrics and measure IP datagram error rate
+    and etc.
+    """
+
+    __scenario_type__ = "Nstat"
+
+    def __init__(self, scenario_cfg, context_cfg):
+        """Scenario construction"""
+        self.scenario_cfg = scenario_cfg
+        self.context_cfg = context_cfg
+        self.setup_done = False
+
+    def setup(self):
+        """scenario setup"""
+        host = self.context_cfg["host"]
+        user = host.get("user", "ubuntu")
+        ssh_port = host.get("ssh_port", ssh.DEFAULT_PORT)
+        ip = host.get("ip", None)
+        key_filename = host.get('key_filename', "~/.ssh/id_rsa")
+
+        LOG.info("user:%s, host:%s", user, ip)
+        self.client = ssh.SSH(user, ip, key_filename=key_filename,
+                              port=ssh_port)
+        self.client.wait(timeout=600)
+
+        self.setup_done = True
+
+    def match(self, key, field, line, results):
+        """match data in the output"""
+        if key in line:
+            results[key] = int(line.split()[field])
+
+    def calculate_error_rate(self, x, y):
+        """calculate error rate"""
+        try:
+            return round(float(x) / float(y), PRECISION)
+        except ZeroDivisionError:
+            # If incoming Errors is non-zero, but incoming data is zero
+            # consider it as 100% error rate
+            if x:
+                return 1
+            else:
+                return 0
+
+    def process_output(self, out):
+        """process output"""
+        results = {}
+        for line in out.splitlines():
+            self.match('IpInReceives', 1, line, results)
+            self.match('IpInHdrErrors', 1, line, results)
+            self.match('IpInAddrErrors', 1, line, results)
+            self.match('IcmpInMsgs', 1, line, results)
+            self.match('IcmpInErrors', 1, line, results)
+            self.match('TcpInSegs', 1, line, results)
+            self.match('TcpInErrs', 1, line, results)
+            self.match('UdpInDatagrams', 1, line, results)
+            self.match('UdpInErrors', 1, line, results)
+        results['IpErrors'] = \
+            results['IpInHdrErrors'] + results['IpInAddrErrors']
+        results['IP_datagram_error_rate'] = \
+            self.calculate_error_rate(results['IpErrors'],
+                                      results['IpInReceives'])
+        results['Icmp_message_error_rate'] = \
+            self.calculate_error_rate(results['IcmpInErrors'],
+                                      results['IcmpInMsgs'])
+        results['Tcp_segment_error_rate'] = \
+            self.calculate_error_rate(results['TcpInErrs'],
+                                      results['TcpInSegs'])
+        results['Udp_datagram_error_rate'] = \
+            self.calculate_error_rate(results['UdpInErrors'],
+                                      results['UdpInDatagrams'])
+        return results
+
+    def run(self, result):
+        """execute the benchmark"""
+
+        if not self.setup_done:
+            self.setup()
+
+        options = self.scenario_cfg['options']
+        duration = options.get('duration', 60)
+
+        time.sleep(duration)
+
+        cmd = "nstat -z"
+
+        LOG.debug("Executing command: %s", cmd)
+        status, stdout, stderr = self.client.execute(cmd)
+
+        if status:
+            raise RuntimeError(stderr)
+
+        results = self.process_output(stdout)
+
+        result.update(results)
+
+        if "sla" in self.scenario_cfg:
+            sla_error = ""
+            for i, rate in result.items():
+                if i not in self.scenario_cfg['sla']:
+                    continue
+                sla_rate = float(self.scenario_cfg['sla'][i])
+                rate = float(rate)
+                if rate > sla_rate:
+                    sla_error += "%s rate %f > sla:%s_rate(%f); " % \
+                        (i, rate, i, sla_rate)
+            assert sla_error == "", sla_error