Add send socket commands function
[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         try:
118             protocol = options.get("protocol")
119             bandwidth = options.get('bandwidth')
120             use_UDP = protocol == 'udp'
121             if protocol:
122                 cmd += " --" + protocol
123             if use_UDP and bandwidth:
124                 cmd += " --bandwidth " + bandwidth
125             # if nodelay in the option, protocal maybe null or 'tcp'
126             if "nodelay" in options:
127                 cmd += " --nodelay"
128         except AttributeError:
129             LOG.warning("Can't parser the options in your config file!!!")
130
131         # these options are mutually exclusive in iperf3
132         if time:
133             cmd += " %d" % time
134         elif "bytes" in options:
135             # number of bytes to transmit (instead of --time)
136             cmd += " --bytes %d" % options["bytes"]
137         elif "blockcount" in options:
138             cmd += " --blockcount %d" % options["blockcount"]
139
140         if "length" in options:
141             cmd += " --length %s" % options["length"]
142
143         if "window" in options:
144             cmd += " --window %s" % options["window"]
145
146         LOG.debug("Executing command: %s", cmd)
147
148         status, stdout, stderr = self.host.execute(cmd)
149         if status:
150             # error cause in json dict on stdout
151             raise RuntimeError(stdout)
152
153         # Note: convert all ints to floats in order to avoid
154         # schema conflicts in influxdb. We probably should add
155         # a format func in the future.
156         iperf_result = jsonutils.loads(stdout, parse_int=float)
157         result.update(utils.flatten_dict_key(iperf_result))
158
159         if "sla" in self.scenario_cfg:
160             sla_iperf = self.scenario_cfg["sla"]
161             if not use_UDP:
162                 sla_bytes_per_second = int(sla_iperf["bytes_per_second"])
163
164                 # convert bits per second to bytes per second
165                 bit_per_second = \
166                     int(iperf_result["end"]["sum_received"]["bits_per_second"])
167                 bytes_per_second = bit_per_second / 8
168                 assert bytes_per_second >= sla_bytes_per_second, \
169                     "bytes_per_second %d < sla:bytes_per_second (%d); " % \
170                     (bytes_per_second, sla_bytes_per_second)
171             else:
172                 sla_jitter = float(sla_iperf["jitter"])
173
174                 jitter_ms = float(iperf_result["end"]["sum"]["jitter_ms"])
175                 assert jitter_ms <= sla_jitter, \
176                     "jitter_ms  %f > sla:jitter %f; " % \
177                     (jitter_ms, sla_jitter)
178
179
180 def _test():
181     """internal test function"""
182     key_filename = pkg_resources.resource_filename('yardstick.resources',
183                                                    'files/yardstick_key')
184     ctx = {
185         'host': {
186             'ip': '10.229.47.137',
187             'user': 'root',
188             'key_filename': key_filename
189         },
190         'target': {
191             'ip': '10.229.47.137',
192             'user': 'root',
193             'key_filename': key_filename,
194             'ipaddr': '10.229.47.137',
195         }
196     }
197
198     logger = logging.getLogger('yardstick')
199     logger.setLevel(logging.DEBUG)
200
201     options = {'packetsize': 120}
202     args = {'options': options}
203     result = {}
204
205     p = Iperf(args, ctx)
206     p.run(result)
207     print(result)
208
209
210 if __name__ == '__main__':
211     _test()