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