Merge "Bugfix: task_id parameter from API can not pass to yardstick core"
[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.benchmark.scenarios import base
23
24 LOG = logging.getLogger(__name__)
25
26
27 class Iperf(base.Scenario):
28     """Execute iperf3 between two hosts
29
30 By default TCP is used but UDP can also be configured.
31 For more info see http://software.es.net/iperf
32
33   Parameters
34     bytes - number of bytes to transmit
35       only valid with a non duration runner, mutually exclusive with blockcount
36         type:    int
37         unit:    bytes
38         default: 56
39     udp - use UDP rather than TCP
40         type:    bool
41         unit:    na
42         default: false
43     nodelay - set TCP no delay, disabling Nagle's Algorithm
44         type:    bool
45         unit:    na
46         default: false
47     blockcount - number of blocks (packets) to transmit,
48       only valid with a non duration runner, mutually exclusive with bytes
49         type:    int
50         unit:    bytes
51         default: -
52     """
53     __scenario_type__ = "Iperf3"
54
55     def __init__(self, scenario_cfg, context_cfg):
56         self.scenario_cfg = scenario_cfg
57         self.context_cfg = context_cfg
58         self.setup_done = False
59
60     def setup(self):
61         host = self.context_cfg['host']
62         host_user = host.get('user', 'ubuntu')
63         host_ssh_port = host.get('ssh_port', ssh.DEFAULT_PORT)
64         host_ip = host.get('ip', None)
65         host_key_filename = host.get('key_filename', '~/.ssh/id_rsa')
66         target = self.context_cfg['target']
67         target_user = target.get('user', 'ubuntu')
68         target_ssh_port = target.get('ssh_port', ssh.DEFAULT_PORT)
69         target_ip = target.get('ip', None)
70         target_key_filename = target.get('key_filename', '~/.ssh/id_rsa')
71
72         LOG.info("user:%s, target:%s", target_user, target_ip)
73         self.target = ssh.SSH(target_user, target_ip,
74                               key_filename=target_key_filename,
75                               port=target_ssh_port)
76         self.target.wait(timeout=600)
77
78         LOG.info("user:%s, host:%s", host_user, host_ip)
79         self.host = ssh.SSH(host_user, host_ip,
80                             key_filename=host_key_filename, port=host_ssh_port)
81         self.host.wait(timeout=600)
82
83         cmd = "iperf3 -s -D"
84         LOG.debug("Starting iperf3 server with command: %s", cmd)
85         status, _, stderr = self.target.execute(cmd)
86         if status:
87             raise RuntimeError(stderr)
88
89         self.setup_done = True
90
91     def teardown(self):
92         LOG.debug("teardown")
93         self.host.close()
94         status, stdout, stderr = self.target.execute("pkill iperf3")
95         if status:
96             LOG.warning(stderr)
97         self.target.close()
98
99     def run(self, result):
100         """execute the benchmark"""
101         if not self.setup_done:
102             self.setup()
103
104         # if run by a duration runner, get the duration time and setup as arg
105         time = self.scenario_cfg["runner"].get("duration", None) \
106             if "runner" in self.scenario_cfg else None
107         options = self.scenario_cfg['options']
108
109         cmd = "iperf3 -c %s --json" % (self.context_cfg['target']['ipaddr'])
110
111         # If there are no options specified
112         if not options:
113             options = ""
114
115         use_UDP = False
116         if "udp" in options:
117             cmd += " --udp"
118             use_UDP = True
119             if "bandwidth" in options:
120                 cmd += " --bandwidth %s" % options["bandwidth"]
121         else:
122             # tcp obviously
123             if "nodelay" in options:
124                 cmd += " --nodelay"
125
126         # these options are mutually exclusive in iperf3
127         if time:
128             cmd += " %d" % time
129         elif "bytes" in options:
130             # number of bytes to transmit (instead of --time)
131             cmd += " --bytes %d" % options["bytes"]
132         elif "blockcount" in options:
133             cmd += " --blockcount %d" % options["blockcount"]
134
135         LOG.debug("Executing command: %s", cmd)
136
137         status, stdout, stderr = self.host.execute(cmd)
138         if status:
139             # error cause in json dict on stdout
140             raise RuntimeError(stdout)
141
142         # Note: convert all ints to floats in order to avoid
143         # schema conflicts in influxdb. We probably should add
144         # a format func in the future.
145         result.update(
146             jsonutils.loads(stdout, parse_int=float))
147
148         if "sla" in self.scenario_cfg:
149             sla_iperf = self.scenario_cfg["sla"]
150             if not use_UDP:
151                 sla_bytes_per_second = int(sla_iperf["bytes_per_second"])
152
153                 # convert bits per second to bytes per second
154                 bit_per_second = \
155                     int(result["end"]["sum_received"]["bits_per_second"])
156                 bytes_per_second = bit_per_second / 8
157                 assert bytes_per_second >= sla_bytes_per_second, \
158                     "bytes_per_second %d < sla:bytes_per_second (%d); " % \
159                     (bytes_per_second, sla_bytes_per_second)
160             else:
161                 sla_jitter = float(sla_iperf["jitter"])
162
163                 jitter_ms = float(result["end"]["sum"]["jitter_ms"])
164                 assert jitter_ms <= sla_jitter, \
165                     "jitter_ms  %f > sla:jitter %f; " % \
166                     (jitter_ms, sla_jitter)
167
168
169 def _test():
170     """internal test function"""
171     key_filename = pkg_resources.resource_filename('yardstick.resources',
172                                                    'files/yardstick_key')
173     ctx = {
174         'host': {
175             'ip': '10.229.47.137',
176             'user': 'root',
177             'key_filename': key_filename
178         },
179         'target': {
180             'ip': '10.229.47.137',
181             'user': 'root',
182             'key_filename': key_filename,
183             'ipaddr': '10.229.47.137',
184         }
185     }
186
187     logger = logging.getLogger('yardstick')
188     logger.setLevel(logging.DEBUG)
189
190     options = {'packetsize': 120}
191     args = {'options': options}
192     result = {}
193
194     p = Iperf(args, ctx)
195     p.run(result)
196     print(result)
197
198
199 if __name__ == '__main__':
200     _test()