Merge "import new _put_file_shell method from upstream rally"
[yardstick.git] / yardstick / benchmark / scenarios / networking / vsperf.py
1 # Copyright 2016 Intel Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """ Vsperf specific scenario definition """
15
16 import logging
17 import os
18 import subprocess
19 import csv
20
21 import yardstick.ssh as ssh
22 from yardstick.benchmark.scenarios import base
23
24 LOG = logging.getLogger(__name__)
25
26
27 class Vsperf(base.Scenario):
28     """Execute vsperf with defined parameters
29
30   Parameters:
31     traffic_type - to specify the type of traffic executed by traffic generator
32     the valid values are "rfc2544", "continuous", "back2back"
33         type:    string
34         default: "rfc2544"
35     frame_size - a frame size for which test should be executed;
36         Multiple frame sizes can be tested by modification of sequence runner
37         section inside TC YAML definition.
38         type:    string
39         default: "64"
40     bidirectional - speficies if traffic will be uni (False) or bi-directional
41         (True)
42         type:    string
43         default: False
44     iload - specifies frame rate
45         type:    string
46         default: 100
47     multistream - the number of simulated streams
48         type:    string
49         default: 0 (disabled)
50     stream_type - specifies network layer used for multistream simulation
51         the valid values are "L4", "L3" and "L2"
52         type:    string
53         default: "L4"
54     test_params - specifies a string with a list of vsperf configuration
55         parameters, which will be passed to the '--test-params' CLI argument;
56         Parameters should be stated in the form of 'param=value' and separated
57         by a semicolon. Please check VSPERF documentation for details about
58         available configuration parameters and their data types.
59         In case that both 'test_params' and 'conf_file' are specified,
60         then values from 'test_params' will override values defined
61         in the configuration file.
62         type:    string
63         default: NA
64     conf_file - path to the vsperf configuration file, which will be uploaded
65         to the VM;
66         In case that both 'test_params' and 'conf_file' are specified,
67         then values from 'test_params' will override values defined
68         in configuration file.
69         type:   string
70         default: NA
71     setup_script - path to the setup script, which will be executed during
72         setup and teardown phases
73         type:   string
74         default: NA
75     trafficgen_port1 - specifies device name of 1st interface connected to
76         the trafficgen
77         type:   string
78         default: NA
79     trafficgen_port2 - specifies device name of 2nd interface connected to
80         the trafficgen
81         type:   string
82         default: NA
83     external_bridge - specifies name of external bridge configured in OVS
84         type:   string
85         default: "br-ex"
86
87     """
88     __scenario_type__ = "Vsperf"
89
90     def __init__(self, scenario_cfg, context_cfg):
91         self.scenario_cfg = scenario_cfg
92         self.context_cfg = context_cfg
93         self.setup_done = False
94         self.client = None
95         self.tg_port1 = self.scenario_cfg['options'].get('trafficgen_port1',
96                                                          None)
97         self.tg_port2 = self.scenario_cfg['options'].get('trafficgen_port2',
98                                                          None)
99         self.br_ex = self.scenario_cfg['options'].get('external_bridge',
100                                                       'br-ex')
101         self.vsperf_conf = self.scenario_cfg['options'].get('conf_file', None)
102         if self.vsperf_conf:
103             self.vsperf_conf = os.path.expanduser(self.vsperf_conf)
104
105         self.setup_script = self.scenario_cfg['options'].get('setup_script',
106                                                              None)
107         if self.setup_script:
108             self.setup_script = os.path.expanduser(self.setup_script)
109
110         self.test_params = self.scenario_cfg['options'].get('test-params',
111                                                             None)
112
113     def setup(self):
114         '''scenario setup'''
115         vsperf = self.context_cfg['host']
116         vsperf_user = vsperf.get('user', 'ubuntu')
117         vsperf_ssh_port = vsperf.get('ssh_port', ssh.DEFAULT_PORT)
118         vsperf_password = vsperf.get('password', 'ubuntu')
119         vsperf_ip = vsperf.get('ip', None)
120
121         # add trafficgen interfaces to the external bridge
122         if self.tg_port1:
123             subprocess.call('sudo bash -c "ovs-vsctl add-port %s %s"' %
124                             (self.br_ex, self.tg_port1), shell=True)
125         if self.tg_port2:
126             subprocess.call('sudo bash -c "ovs-vsctl add-port %s %s"' %
127                             (self.br_ex, self.tg_port2), shell=True)
128
129         # copy vsperf conf to VM
130         LOG.info("user:%s, host:%s", vsperf_user, vsperf_ip)
131         self.client = ssh.SSH(vsperf_user, vsperf_ip,
132                               password=vsperf_password, port=vsperf_ssh_port)
133         # traffic generation could last long
134         self.client.wait(timeout=1800)
135
136         # copy script to host if needed
137         if self.vsperf_conf:
138             self.client.run("cat > ~/vsperf.conf",
139                             stdin=open(self.vsperf_conf, "rb"))
140
141         # execute external setup script
142         if self.setup_script:
143             cmd = "%s setup" % (self.setup_script)
144             LOG.info("Execute setup script \"%s\"", cmd)
145             subprocess.call(cmd, shell=True)
146
147         self.setup_done = True
148
149     def run(self, result):
150         """ execute the vsperf benchmark and return test results
151             within result dictionary
152         """
153         def add_test_params(options, option, default_value):
154             """return parameter and its value as a string to be passed
155                to the VSPERF inside --test-params argument
156
157                Parameters:
158                 options - dictionary with scenario options
159                 option  - a name of option to be added to the string
160                 default_value - value to be used in case that option
161                     is not defined inside scenario options
162             """
163             if option in options:
164                 return "%s=%s" % (option, options[option])
165             elif default_value is not None:
166                 return "%s=%s" % (option, default_value)
167             else:
168                 return None
169
170         if not self.setup_done:
171             self.setup()
172
173         # remove results from previous tests
174         self.client.execute("rm -rf /tmp/results*")
175
176         # get vsperf options
177         options = self.scenario_cfg['options']
178         test_params = []
179         test_params.append(add_test_params(options, "traffic_type", "rfc2544"))
180         test_params.append(add_test_params(options, "bidirectional", "False"))
181         test_params.append(add_test_params(options, "iload", 100))
182         test_params.append(add_test_params(options, "multistream", None))
183         test_params.append(add_test_params(options, "stream_type", None))
184         if 'frame_size' in options:
185             test_params.append("%s=(%s,)" % ('TRAFFICGEN_PKT_SIZES',
186                                              options['frame_size']))
187         if 'test_params' in options:
188             test_params.append(options['test_params'])
189
190         # filter empty parameters and escape quotes and double quotes
191         test_params = [tp.replace('"', '\\"').replace("'", "\\'")
192                        for tp in test_params if tp]
193
194         # execute vsperf
195         cmd = "source ~/vsperfenv/bin/activate ; cd vswitchperf ; "
196         cmd += "./vsperf --mode trafficgen "
197         if self.vsperf_conf:
198             cmd += "--conf-file ~/vsperf.conf "
199         cmd += "--test-params=\"%s\"" % (';'.join(test_params))
200         LOG.debug("Executing command: %s", cmd)
201         status, stdout, stderr = self.client.execute(cmd)
202
203         if status:
204             raise RuntimeError(stderr)
205
206         # get test results
207         cmd = "cat /tmp/results*/result.csv"
208         LOG.debug("Executing command: %s", cmd)
209         status, stdout, stderr = self.client.execute(cmd)
210
211         if status:
212             raise RuntimeError(stderr)
213
214         # convert result.csv to JSON format
215         reader = csv.DictReader(stdout.split('\r\n'))
216         result.update(reader.next())
217
218         # sla check; go through all defined SLAs and check if values measured
219         # by VSPERF are higher then those defined by SLAs
220         if 'sla' in self.scenario_cfg and \
221            'metrics' in self.scenario_cfg['sla']:
222             for metric in self.scenario_cfg['sla']['metrics'].split(','):
223                 assert metric in result, \
224                     '%s is not collected by VSPERF' % (metric)
225                 assert metric in self.scenario_cfg['sla'], \
226                     '%s is not defined in SLA' % (metric)
227                 vs_res = float(result[metric])
228                 sla_res = float(self.scenario_cfg['sla'][metric])
229                 assert vs_res >= sla_res, \
230                     'VSPERF_%s(%f) < SLA_%s(%f)' % \
231                     (metric, vs_res, metric, sla_res)
232
233     def teardown(self):
234         """cleanup after the test execution"""
235         # remove trafficgen interfaces from the external bridge
236         if self.tg_port1:
237             subprocess.call('sudo bash -c "ovs-vsctl del-port %s %s"' %
238                             (self.br_ex, self.tg_port1), shell=True)
239         if self.tg_port2:
240             subprocess.call('sudo bash -c "ovs-vsctl del-port %s %s"' %
241                             (self.br_ex, self.tg_port2), shell=True)
242
243         # execute external setup script
244         if self.setup_script:
245             cmd = "%s teardown" % (self.setup_script)
246             LOG.info("Execute setup script \"%s\"", cmd)
247             subprocess.call(cmd, shell=True)
248
249         self.setup_done = False