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