Merge "bugfix: hosts should be clean if update_hosts twice"
[yardstick.git] / yardstick / benchmark / scenarios / networking / iperf3.py
1 ##############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 # iperf3 scenario
11 # iperf3 homepage at: http://software.es.net/iperf/
12
13 from __future__ import absolute_import
14 from __future__ import print_function
15
16 import logging
17
18 import pkg_resources
19 from oslo_serialization import jsonutils
20
21 import yardstick.ssh as ssh
22 from yardstick.common import utils
23 from yardstick.benchmark.scenarios import base
24
25 LOG = logging.getLogger(__name__)
26
27
28 class Iperf(base.Scenario):
29     """Execute iperf3 between two hosts
30
31 By default TCP is used but UDP can also be configured.
32 For more info see http://software.es.net/iperf
33
34   Parameters
35     bytes - number of bytes to transmit
36       only valid with a non duration runner, mutually exclusive with blockcount
37         type:    int
38         unit:    bytes
39         default: 56
40     udp - use UDP rather than TCP
41         type:    bool
42         unit:    na
43         default: false
44     nodelay - set TCP no delay, disabling Nagle's Algorithm
45         type:    bool
46         unit:    na
47         default: false
48     blockcount - number of blocks (packets) to transmit,
49       only valid with a non duration runner, mutually exclusive with bytes
50         type:    int
51         unit:    bytes
52         default: -
53     length - length of buffer to read or write,
54       (default 128 KB for TCP, 8 KB for UDP)
55         type:    int
56         unit:    k
57         default: -
58     window - set window size / socket buffer size
59       set TCP windows size. for UDP way to test, this will set to accept UDP
60       packet buffer size, limit the max size of acceptable data packet.
61         type:    int
62         unit:    k
63         default: -
64     """
65     __scenario_type__ = "Iperf3"
66
67     def __init__(self, scenario_cfg, context_cfg):
68         self.scenario_cfg = scenario_cfg
69         self.context_cfg = context_cfg
70         self.setup_done = False
71
72     def setup(self):
73         host = self.context_cfg['host']
74         target = self.context_cfg['target']
75
76         LOG.info("user:%s, target:%s", target['user'], target['ip'])
77         self.target = ssh.SSH.from_node(target, defaults={"user": "ubuntu"})
78         self.target.wait(timeout=600)
79
80         LOG.info("user:%s, host:%s", host['user'], host['ip'])
81         self.host = ssh.SSH.from_node(host, defaults={"user": "ubuntu"})
82         self.host.wait(timeout=600)
83
84         cmd = "iperf3 -s -D"
85         LOG.debug("Starting iperf3 server with command: %s", cmd)
86         status, _, stderr = self.target.execute(cmd)
87         if status:
88             raise RuntimeError(stderr)
89
90         self.setup_done = True
91
92     def teardown(self):
93         LOG.debug("teardown")
94         self.host.close()
95         status, stdout, stderr = self.target.execute("pkill iperf3")
96         if status:
97             LOG.warning(stderr)
98         self.target.close()
99
100     def run(self, result):
101         """execute the benchmark"""
102         if not self.setup_done:
103             self.setup()
104
105         # if run by a duration runner, get the duration time and setup as arg
106         time = self.scenario_cfg["runner"].get("duration", None) \
107             if "runner" in self.scenario_cfg else None
108         options = self.scenario_cfg['options']
109
110         cmd = "iperf3 -c %s --json" % (self.context_cfg['target']['ipaddr'])
111
112         # If there are no options specified
113         if not options:
114             options = ""
115
116         use_UDP = False
117         if "udp" in options:
118             cmd += " --udp"
119             use_UDP = True
120             if "bandwidth" in options:
121                 cmd += " --bandwidth %s" % options["bandwidth"]
122         else:
123             # tcp obviously
124             if "nodelay" in options:
125                 cmd += " --nodelay"
126
127         # these options are mutually exclusive in iperf3
128         if time:
129             cmd += " %d" % time
130         elif "bytes" in options:
131             # number of bytes to transmit (instead of --time)
132             cmd += " --bytes %d" % options["bytes"]
133         elif "blockcount" in options:
134             cmd += " --blockcount %d" % options["blockcount"]
135
136         if "length" in options:
137             cmd += " --length %s" % options["length"]
138
139         if "window" in options:
140             cmd += " --window %s" % options["window"]
141
142         LOG.debug("Executing command: %s", cmd)
143
144         status, stdout, stderr = self.host.execute(cmd)
145         if status:
146             # error cause in json dict on stdout
147             raise RuntimeError(stdout)
148
149         # Note: convert all ints to floats in order to avoid
150         # schema conflicts in influxdb. We probably should add
151         # a format func in the future.
152         iperf_result = jsonutils.loads(stdout, parse_int=float)
153         result.update(utils.flatten_dict_key(iperf_result))
154
155         if "sla" in self.scenario_cfg:
156             sla_iperf = self.scenario_cfg["sla"]
157             if not use_UDP:
158                 sla_bytes_per_second = int(sla_iperf["bytes_per_second"])
159
160                 # convert bits per second to bytes per second
161                 bit_per_second = \
162                     int(iperf_result["end"]["sum_received"]["bits_per_second"])
163                 bytes_per_second = bit_per_second / 8
164                 assert bytes_per_second >= sla_bytes_per_second, \
165                     "bytes_per_second %d < sla:bytes_per_second (%d); " % \
166                     (bytes_per_second, sla_bytes_per_second)
167             else:
168                 sla_jitter = float(sla_iperf["jitter"])
169
170                 jitter_ms = float(iperf_result["end"]["sum"]["jitter_ms"])
171                 assert jitter_ms <= sla_jitter, \
172                     "jitter_ms  %f > sla:jitter %f; " % \
173                     (jitter_ms, sla_jitter)
174
175
176 def _test():
177     """internal test function"""
178     key_filename = pkg_resources.resource_filename('yardstick.resources',
179                                                    'files/yardstick_key')
180     ctx = {
181         'host': {
182             'ip': '10.229.47.137',
183             'user': 'root',
184             'key_filename': key_filename
185         },
186         'target': {
187             'ip': '10.229.47.137',
188             'user': 'root',
189             'key_filename': key_filename,
190             'ipaddr': '10.229.47.137',
191         }
192     }
193
194     logger = logging.getLogger('yardstick')
195     logger.setLevel(logging.DEBUG)
196
197     options = {'packetsize': 120}
198     args = {'options': options}
199     result = {}
200
201     p = Iperf(args, ctx)
202     p.run(result)
203     print(result)
204
205
206 if __name__ == '__main__':
207     _test()