Merge "Add smoke, components, features and performance test suite for Yatdstick"
[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
137         self.client._put_file_shell(self.vsperf_conf, '~/vsperf.conf')
138
139         # execute external setup script
140         if self.setup_script:
141             cmd = "%s setup" % (self.setup_script)
142             LOG.info("Execute setup script \"%s\"", cmd)
143             subprocess.call(cmd, shell=True)
144
145         self.setup_done = True
146
147     def run(self, result):
148         """ execute the vsperf benchmark and return test results
149             within result dictionary
150         """
151         def add_test_params(options, option, default_value):
152             """return parameter and its value as a string to be passed
153                to the VSPERF inside --test-params argument
154
155                Parameters:
156                 options - dictionary with scenario options
157                 option  - a name of option to be added to the string
158                 default_value - value to be used in case that option
159                     is not defined inside scenario options
160             """
161             if option in options:
162                 return "%s=%s" % (option, options[option])
163             elif default_value is not None:
164                 return "%s=%s" % (option, default_value)
165             else:
166                 return None
167
168         if not self.setup_done:
169             self.setup()
170
171         # remove results from previous tests
172         self.client.execute("rm -rf /tmp/results*")
173
174         # get vsperf options
175         options = self.scenario_cfg['options']
176         test_params = []
177         test_params.append(add_test_params(options, "traffic_type", "rfc2544"))
178         test_params.append(add_test_params(options, "bidirectional", "False"))
179         test_params.append(add_test_params(options, "iload", 100))
180         test_params.append(add_test_params(options, "multistream", None))
181         test_params.append(add_test_params(options, "stream_type", None))
182         if 'frame_size' in options:
183             test_params.append("%s=(%s,)" % ('TRAFFICGEN_PKT_SIZES',
184                                              options['frame_size']))
185         if 'test_params' in options:
186             test_params.append(options['test_params'])
187
188         # filter empty parameters and escape quotes and double quotes
189         test_params = [tp.replace('"', '\\"').replace("'", "\\'")
190                        for tp in test_params if tp]
191
192         # execute vsperf
193         cmd = "source ~/vsperfenv/bin/activate ; cd vswitchperf ; "
194         cmd += "./vsperf --mode trafficgen "
195         if self.vsperf_conf:
196             cmd += "--conf-file ~/vsperf.conf "
197         cmd += "--test-params=\"%s\"" % (';'.join(test_params))
198         LOG.debug("Executing command: %s", cmd)
199         status, stdout, stderr = self.client.execute(cmd)
200
201         if status:
202             raise RuntimeError(stderr)
203
204         # get test results
205         cmd = "cat /tmp/results*/result.csv"
206         LOG.debug("Executing command: %s", cmd)
207         status, stdout, stderr = self.client.execute(cmd)
208
209         if status:
210             raise RuntimeError(stderr)
211
212         # convert result.csv to JSON format
213         reader = csv.DictReader(stdout.split('\r\n'))
214         result.update(reader.next())
215
216         # sla check; go through all defined SLAs and check if values measured
217         # by VSPERF are higher then those defined by SLAs
218         if 'sla' in self.scenario_cfg and \
219            'metrics' in self.scenario_cfg['sla']:
220             for metric in self.scenario_cfg['sla']['metrics'].split(','):
221                 assert metric in result, \
222                     '%s is not collected by VSPERF' % (metric)
223                 assert metric in self.scenario_cfg['sla'], \
224                     '%s is not defined in SLA' % (metric)
225                 vs_res = float(result[metric])
226                 sla_res = float(self.scenario_cfg['sla'][metric])
227                 assert vs_res >= sla_res, \
228                     'VSPERF_%s(%f) < SLA_%s(%f)' % \
229                     (metric, vs_res, metric, sla_res)
230
231     def teardown(self):
232         """cleanup after the test execution"""
233         # remove trafficgen interfaces from the external bridge
234         if self.tg_port1:
235             subprocess.call('sudo bash -c "ovs-vsctl del-port %s %s"' %
236                             (self.br_ex, self.tg_port1), shell=True)
237         if self.tg_port2:
238             subprocess.call('sudo bash -c "ovs-vsctl del-port %s %s"' %
239                             (self.br_ex, self.tg_port2), shell=True)
240
241         # execute external setup script
242         if self.setup_script:
243             cmd = "%s teardown" % (self.setup_script)
244             LOG.info("Execute setup script \"%s\"", cmd)
245             subprocess.call(cmd, shell=True)
246
247         self.setup_done = False