Merge "Add how to add/modify Yardstick Grafana dashboard in user guide"
[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_ssh_port = vsperf.get('ssh_port', ssh.DEFAULT_PORT)
108         vsperf_password = vsperf.get('password', 'ubuntu')
109         vsperf_ip = vsperf.get('ip', None)
110
111         # add trafficgen interfaces to the external bridge
112         if self.tg_port1:
113             subprocess.call('sudo bash -c "ovs-vsctl add-port %s %s"' %
114                             (self.br_ex, self.tg_port1), shell=True)
115         if self.tg_port2:
116             subprocess.call('sudo bash -c "ovs-vsctl add-port %s %s"' %
117                             (self.br_ex, self.tg_port2), shell=True)
118
119         # copy vsperf conf to VM
120         LOG.info("user:%s, host:%s", vsperf_user, vsperf_ip)
121         self.client = ssh.SSH(vsperf_user, vsperf_ip,
122                               password=vsperf_password, port=vsperf_ssh_port)
123         # traffic generation could last long
124         self.client.wait(timeout=1800)
125
126         # copy script to host
127         self.client.run("cat > ~/vsperf.conf",
128                         stdin=open(self.vsperf_conf, "rb"))
129
130         # execute external setup script
131         if self.setup_script:
132             cmd = "%s setup" % (self.setup_script)
133             LOG.info("Execute setup script \"%s\"", cmd)
134             subprocess.call(cmd, shell=True)
135
136         self.setup_done = True
137
138     def run(self, result):
139         """ execute the vsperf benchmark and return test results
140             within result dictionary
141         """
142         def add_test_params(options, option, default_value):
143             """return parameter and its value as a string to be passed
144                to the VSPERF inside --test-params argument
145
146                Parameters:
147                 options - dictionary with scenario options
148                 option  - a name of option to be added to the string
149                 default_value - value to be used in case that option
150                     is not defined inside scenario options
151             """
152             if option in options:
153                 return "%s=%s" % (option, options[option])
154             elif default_value is not None:
155                 return "%s=%s" % (option, default_value)
156             else:
157                 return None
158
159         if not self.setup_done:
160             self.setup()
161
162         # remove results from previous tests
163         self.client.execute("rm -rf /tmp/results*")
164
165         # get vsperf options
166         options = self.scenario_cfg['options']
167         test_params = []
168         test_params.append(add_test_params(options, "traffic_type", "rfc2544"))
169         test_params.append(add_test_params(options, "pkt_sizes", "64"))
170         test_params.append(add_test_params(options, "duration", None))
171         test_params.append(add_test_params(options, "bidirectional", "False"))
172         test_params.append(add_test_params(options, "iload", 100))
173         test_params.append(add_test_params(options, "rfc2544_trials", None))
174         test_params.append(add_test_params(options, "multistream", None))
175         test_params.append(add_test_params(options, "stream_type", None))
176
177         # execute vsperf
178         cmd = "source ~/vsperfenv/bin/activate ; cd vswitchperf ; "
179         cmd += "./vsperf --mode trafficgen --conf-file ~/vsperf.conf "
180         cmd += "--test-params=\"%s\"" % (';'.join(filter(None, test_params)))
181         LOG.debug("Executing command: %s", cmd)
182         status, stdout, stderr = self.client.execute(cmd)
183
184         if status:
185             raise RuntimeError(stderr)
186
187         # get test results
188         cmd = "cat /tmp/results*/result.csv"
189         LOG.debug("Executing command: %s", cmd)
190         status, stdout, stderr = self.client.execute(cmd)
191
192         if status:
193             raise RuntimeError(stderr)
194
195         # convert result.csv to JSON format
196         reader = csv.DictReader(stdout.split('\r\n'))
197         result.update(reader.next())
198
199         # sla check; go through all defined SLAs and check if values measured
200         # by VSPERF are higher then those defined by SLAs
201         if 'sla' in self.scenario_cfg and \
202            'metrics' in self.scenario_cfg['sla']:
203             for metric in self.scenario_cfg['sla']['metrics'].split(','):
204                 assert metric in result, \
205                     '%s is not collected by VSPERF' % (metric)
206                 assert metric in self.scenario_cfg['sla'], \
207                     '%s is not defined in SLA' % (metric)
208                 vs_res = float(result[metric])
209                 sla_res = float(self.scenario_cfg['sla'][metric])
210                 assert vs_res >= sla_res, \
211                     'VSPERF_%s(%f) < SLA_%s(%f)' % \
212                     (metric, vs_res, metric, sla_res)
213
214     def teardown(self):
215         """cleanup after the test execution"""
216         # remove trafficgen interfaces from the external bridge
217         if self.tg_port1:
218             subprocess.call('sudo bash -c "ovs-vsctl del-port %s %s"' %
219                             (self.br_ex, self.tg_port1), shell=True)
220         if self.tg_port2:
221             subprocess.call('sudo bash -c "ovs-vsctl del-port %s %s"' %
222                             (self.br_ex, self.tg_port2), shell=True)
223
224         # execute external setup script
225         if self.setup_script:
226             cmd = "%s teardown" % (self.setup_script)
227             LOG.info("Execute setup script \"%s\"", cmd)
228             subprocess.call(cmd, shell=True)
229
230         self.setup_done = False