1 # Copyright 2016 Intel Corporation.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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 """ VsperfDPDK specific scenario definition """
16 from __future__ import absolute_import
24 import yardstick.ssh as ssh
25 import yardstick.common.utils as utils
26 from yardstick.benchmark.scenarios import base
28 LOG = logging.getLogger(__name__)
31 class VsperfDPDK(base.Scenario):
32 """Execute vsperf with defined parameters
35 traffic_type - to specify the type of traffic executed by traffic generator
36 the valid values are "rfc2544", "continuous", "back2back"
39 frame_size - a frame size for which test should be executed;
40 Multiple frame sizes can be tested by modification of sequence runner
41 section inside TC YAML definition.
44 bidirectional - speficies if traffic will be uni (False) or bi-directional
48 iload - specifies frame rate
51 multistream - the number of simulated streams
54 stream_type - specifies network layer used for multistream simulation
55 the valid values are "L4", "L3" and "L2"
58 test_params - specifies a string with a list of vsperf configuration
59 parameters, which will be passed to the '--test-params' CLI argument;
60 Parameters should be stated in the form of 'param=value' and separated
61 by a semicolon. Please check VSPERF documentation for details about
62 available configuration parameters and their data types.
63 In case that both 'test_params' and 'conf_file' are specified,
64 then values from 'test_params' will override values defined
65 in the configuration file.
68 conf_file - path to the vsperf configuration file, which will be uploaded
70 In case that both 'test_params' and 'conf_file' are specified,
71 then values from 'test_params' will override values defined
72 in configuration file.
75 setup_script - path to the setup script, which will be executed during
76 setup and teardown phases
79 trafficgen_port1 - specifies device name of 1st interface connected to
83 trafficgen_port2 - specifies device name of 2nd interface connected to
87 external_bridge - specifies name of external bridge configured in OVS
92 __scenario_type__ = "VsperfDPDK"
94 TESTPMD_SCRIPT = 'testpmd_vsperf.bash'
96 def __init__(self, scenario_cfg, context_cfg):
97 self.scenario_cfg = scenario_cfg
98 self.context_cfg = context_cfg
99 self.moongen_host_ip = \
100 scenario_cfg['options'].get('moongen_host_ip', "127.0.0.1")
101 self.moongen_port1_mac = \
102 scenario_cfg['options'].get('moongen_port1_mac', None)
103 self.moongen_port2_mac = \
104 scenario_cfg['options'].get('moongen_port2_mac', None)
105 self.dpdk_setup_done = False
106 self.setup_done = False
109 self.scenario_cfg['options'].get('trafficgen_port1', None)
111 self.scenario_cfg['options'].get('trafficgen_port2', None)
112 self.tgen_port1_mac = None
113 self.tgen_port2_mac = None
114 self.br_ex = self.scenario_cfg['options'].get('external_bridge',
116 self.vsperf_conf = self.scenario_cfg['options'].get('conf_file', None)
118 self.vsperf_conf = os.path.expanduser(self.vsperf_conf)
120 self.moongen_helper = \
121 self.scenario_cfg['options'].get('moongen_helper_file', None)
122 if self.moongen_helper:
123 self.moongen_helper = os.path.expanduser(self.moongen_helper)
125 self.setup_script = self.scenario_cfg['options'].get('setup_script',
127 if self.setup_script:
128 self.setup_script = os.path.expanduser(self.setup_script)
130 self.test_params = self.scenario_cfg['options'].get('test-params',
135 vsperf = self.context_cfg['host']
137 task_id = self.scenario_cfg['task_id']
138 context_number = task_id.split('-')[0]
139 self.tg_port1_nw = vsperf.get('name', 'demo') + \
140 "-" + context_number + "-" + \
141 self.scenario_cfg['options'].get('trafficgen_port1_nw', 'test2')
142 self.tg_port2_nw = vsperf.get('name', 'demo') + \
143 "-" + context_number + "-" + \
144 self.scenario_cfg['options'].get('trafficgen_port2_nw', 'test3')
146 # copy vsperf conf to VM
147 self.client = ssh.SSH.from_node(vsperf, defaults={
148 "user": "ubuntu", "password": "ubuntu"
150 # traffic generation could last long
151 self.client.wait(timeout=1800)
153 # copy script to host
154 self.client._put_file_shell(self.vsperf_conf, '~/vsperf.conf')
156 self.client._put_file_shell(
158 '~/vswitchperf/tools/pkt_gen/moongen/moongen.py')
160 # execute external setup script
161 if self.setup_script:
162 cmd = "%s setup" % (self.setup_script)
163 LOG.info("Execute setup script \"%s\"", cmd)
164 subprocess.call(cmd, shell=True)
166 self.setup_done = True
168 def dpdk_setup(self):
171 # setup dpdk loopback in VM
172 self.testpmd_script = pkg_resources.resource_filename(
173 'yardstick.benchmark.scenarios.networking',
174 VsperfDPDK.TESTPMD_SCRIPT)
176 self.client._put_file_shell(self.testpmd_script,
177 '~/testpmd_vsperf.sh')
179 # disable Address Space Layout Randomization (ASLR)
180 cmd = "echo 0 | sudo tee /proc/sys/kernel/randomize_va_space"
181 self.client.send_command(cmd)
183 if not self._is_dpdk_setup():
184 self.tgen_port1_ip = \
185 utils.get_port_ip(self.client, self.tg_port1)
186 self.tgen_port1_mac = \
187 utils.get_port_mac(self.client, self.tg_port1)
188 self.client.run("tee ~/.testpmd.ipaddr.port1 > /dev/null",
189 stdin=self.tgen_port1_ip)
190 self.client.run("tee ~/.testpmd.macaddr.port1 > /dev/null",
191 stdin=self.tgen_port1_mac)
192 self.tgen_port2_ip = \
193 utils.get_port_ip(self.client, self.tg_port2)
194 self.tgen_port2_mac = \
195 utils.get_port_mac(self.client, self.tg_port2)
196 self.client.run("tee ~/.testpmd.ipaddr.port2 > /dev/null",
197 stdin=self.tgen_port2_ip)
198 self.client.run("tee ~/.testpmd.macaddr.port2 > /dev/null",
199 stdin=self.tgen_port2_mac)
200 cmd = "ip link set %s down" % (self.tg_port1)
201 LOG.debug("Executing command: %s", cmd)
202 self.client.send_command(cmd)
203 cmd = "ip link set %s down" % (self.tg_port2)
204 LOG.debug("Executing command: %s", cmd)
205 self.client.send_command(cmd)
207 cmd = "cat ~/.testpmd.macaddr.port1"
208 status, stdout, stderr = self.client.execute(cmd)
210 raise RuntimeError(stderr)
211 self.tgen_port1_mac = stdout
212 cmd = "cat ~/.testpmd.macaddr.port2"
213 status, stdout, stderr = self.client.execute(cmd)
215 raise RuntimeError(stderr)
216 self.tgen_port2_mac = stdout
218 cmd = "screen -d -m sudo -E bash ~/testpmd_vsperf.sh %s %s" % \
219 (self.moongen_port1_mac, self.moongen_port2_mac)
220 LOG.debug("Executing command: %s", cmd)
221 status, stdout, stderr = self.client.execute(cmd)
223 raise RuntimeError(stderr)
227 self.dpdk_setup_done = True
229 def _is_dpdk_setup(self):
230 """Is dpdk already setup in the host?"""
232 cmd = "ip a | grep %s 2>/dev/null" % (self.tg_port1)
233 LOG.debug("Executing command: %s", cmd)
234 status, stdout, stderr = self.client.execute(cmd)
239 def run(self, result):
240 """ execute the vsperf benchmark and return test results
241 within result dictionary
244 if not self.setup_done:
247 # remove results from previous tests
248 self.client.execute("rm -rf /tmp/results*")
251 options = self.scenario_cfg['options']
253 traffic_type = self.scenario_cfg['options'].\
254 get("traffic_type", "rfc2544_throughput")
255 multistream = self.scenario_cfg['options'].get("multistream", 1)
257 if not self.dpdk_setup_done:
260 if 'frame_size' in options:
261 test_params.append("%s=(%s,)" % ('TRAFFICGEN_PKT_SIZES',
262 options['frame_size']))
264 cmd = "openstack network show %s | grep segmentation_id | " \
265 "cut -d '|' -f 3" % (self.tg_port1_nw)
266 LOG.debug("Executing command: %s", cmd)
267 tg_port1_vlan = subprocess.check_output(cmd, shell=True)
269 cmd = "openstack network show %s | grep segmentation_id | " \
270 "cut -d '|' -f 3" % (self.tg_port2_nw)
271 LOG.debug("Executing command: %s", cmd)
272 tg_port2_vlan = subprocess.check_output(cmd, shell=True)
274 additional_params = \
275 'TRAFFIC={"traffic_type":"%s", "multistream":%d, ' \
276 '"l2":{"srcmac":"{\'%s\',\'%s\'}", "dstmac":"{\'%s\',\'%s\'}"}, ' \
277 '"vlan":{"enabled":"True", "id":"{%d,%d}"}}' \
278 % (traffic_type, multistream,
279 self.moongen_port1_mac, self.moongen_port2_mac,
280 self.tgen_port1_mac, self.tgen_port2_mac,
281 int(tg_port1_vlan), int(tg_port2_vlan))
283 if 'test_params' in options:
284 test_params.append(options['test_params'] + additional_params)
286 # filter empty parameters and escape quotes and double quotes
287 test_params = [tp.replace('"', '\\"').replace("'", "\\'")
288 for tp in test_params if tp]
290 # Set password less access to MoonGen
291 cmd = "sshpass -p yardstick ssh-copy-id -o StrictHostKeyChecking=no " \
292 "root@%s -p 22" % (self.moongen_host_ip)
293 LOG.debug("Executing command: %s", cmd)
294 status, stdout, stderr = self.client.execute(cmd)
296 raise RuntimeError(stderr)
299 cmd = "source ~/vsperfenv/bin/activate ; cd vswitchperf ; "
300 cmd += "./vsperf --mode trafficgen "
302 cmd += "--conf-file ~/vsperf.conf "
303 cmd += "--test-params=\"%s\"" % (';'.join(test_params))
304 LOG.debug("Executing command: %s", cmd)
305 status, stdout, stderr = self.client.execute(cmd)
308 raise RuntimeError(stderr)
311 cmd = "cat /tmp/results*/result.csv"
312 LOG.debug("Executing command: %s", cmd)
313 status, stdout, stderr = self.client.execute(cmd)
316 raise RuntimeError(stderr)
318 # convert result.csv to JSON format
319 reader = csv.DictReader(stdout.split('\r\n'))
320 result.update(next(reader))
321 result['nrFlows'] = multistream
323 # sla check; go through all defined SLAs and check if values measured
324 # by VSPERF are higher then those defined by SLAs
325 if 'sla' in self.scenario_cfg and \
326 'metrics' in self.scenario_cfg['sla']:
327 for metric in self.scenario_cfg['sla']['metrics'].split(','):
328 assert metric in result, \
329 '%s is not collected by VSPERF' % (metric)
330 assert metric in self.scenario_cfg['sla'], \
331 '%s is not defined in SLA' % (metric)
332 vs_res = float(result[metric])
333 sla_res = float(self.scenario_cfg['sla'][metric])
334 assert vs_res >= sla_res, \
335 'VSPERF_%s(%f) < SLA_%s(%f)' % \
336 (metric, vs_res, metric, sla_res)
339 """cleanup after the test execution"""
341 # execute external setup script
342 if self.setup_script:
343 cmd = "%s teardown" % (self.setup_script)
344 LOG.info("Execute setup script \"%s\"", cmd)
345 subprocess.call(cmd, shell=True)
347 self.setup_done = False