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