Add send socket commands function
[yardstick.git] / yardstick / benchmark / scenarios / networking / moongen_testpmd.py
1 # Copyright (c) 2018 Huawei Technologies Co.,Ltd and others.
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 """ VsperfDPDK specific scenario definition """
15
16 from __future__ import absolute_import
17 import pkg_resources
18 import logging
19 import subprocess
20 import time
21 import re
22 from oslo_serialization import jsonutils
23
24 import yardstick.ssh as ssh
25 import yardstick.common.utils as utils
26 from yardstick.benchmark.scenarios import base
27
28 LOG = logging.getLogger(__name__)
29
30
31 class MoongenTestPMD(base.Scenario):
32     """Execute vsperf with defined parameters
33
34   Parameters:
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     multistream - the number of simulated streams
41         type:    string
42         default: 0 (disabled)
43     testpmd_queue - specifies how many queues you will use the VM
44                     only useful when forward_type is true.
45         type:    int
46         default: 1(one queue)
47     trafficgen_port1 - specifies device name of 1st interface connected to
48         the trafficgen
49         type:   string
50         default: NA
51     trafficgen_port2 - specifies device name of 2nd interface connected to
52         the trafficgen
53         type:   string
54         default: NA
55     moongen_host_user - specifies moongen host ssh user name
56         type: string
57         default: root
58     moongen_host_passwd - specifies moongen host ssh user password
59         type: string
60         default: root
61     moongen_host_ip - specifies moongen host ssh ip address
62         type: string
63         default NA
64     moongen_dir - specifies where is the moongen installtion dir
65         type: string
66         default NA
67     moongen_runBidirec - specifies moongen will run in one traffic
68                          or two traffic.
69         type: string
70         default true
71     Package_Loss - specifies the package_Loss number in moongen server.
72         type: int
73         default 0(0%)
74     SearchRuntime - specifies the SearchRuntime and validation time
75                     on moongen server.
76         type: int
77         default 60(s)
78     moongen_port1_mac - moongen server port1 mac address.
79         type: string
80         default NA
81     moongen_port2_mac - moongen server port2 mac address.
82         type: string
83         default NA
84     forward_type - VM forward type is l2fwd or testpmd.
85         type: string
86         default: testpmd
87     """
88     __scenario_type__ = "MoongenTestPMD"
89
90     TESTPMD_SCRIPT = 'moongen_testpmd.bash'
91     VSPERF_CONFIG = '/tmp/opnfv-vsperf-cfg.lua'
92
93     def __init__(self, scenario_cfg, context_cfg):
94         self.scenario_cfg = scenario_cfg
95         self.context_cfg = context_cfg
96         self.forward_setup_done = False
97         self.options = scenario_cfg.get('options', {})
98         self.moongen_host_user = \
99             self.options.get('moongen_host_user', "root")
100         self.moongen_host_passwd = \
101             self.options.get('moongen_host_passwd', "r00t")
102         self.moongen_dir = \
103             self.options.get('moongen_dir', '~/moongen.py')
104         self.testpmd_queue = \
105             self.options.get('testpmd_queue', 1)
106         self.moongen_host_ip = \
107             self.options.get('moongen_host_ip', "127.0.0.1")
108         self.moongen_port1_mac = \
109             self.options.get('moongen_port1_mac', None)
110         self.moongen_port2_mac = \
111             self.options.get('moongen_port2_mac', None)
112         self.tg_port1 = \
113             self.options.get('trafficgen_port1', "enp2s0f0")
114         self.tg_port2 = \
115             self.options.get('trafficgen_port2', "enp2s0f1")
116         self.forward_type = \
117             self.options.get('forward_type', 'testpmd')
118         self.tgen_port1_mac = None
119         self.tgen_port2_mac = None
120
121     def setup(self):
122         """scenario setup"""
123         host = self.context_cfg['host']
124
125         task_id = self.scenario_cfg['task_id']
126         context_number = task_id.split('-')[0]
127         self.tg_port1_nw = 'demo' + \
128             "-" + context_number + "-" + \
129             self.options.get('trafficgen_port1_nw', 'test2')
130         self.tg_port2_nw = 'demo' + \
131             "-" + context_number + "-" + \
132             self.options.get('trafficgen_port2_nw', 'test3')
133
134         # copy vsperf conf to VM
135         self.client = ssh.SSH.from_node(host, defaults={"user": "ubuntu"})
136         # traffic generation could last long
137         self.client.wait(timeout=1800)
138
139         self.server = ssh.SSH(
140             self.moongen_host_user,
141             self.moongen_host_ip,
142             password=self.moongen_host_passwd
143         )
144         # traffic generation could last long
145         self.server.wait(timeout=1800)
146
147         self.setup_done = True
148
149     def forward_setup(self):
150         """forward tool setup"""
151
152         # setup forward loopback in VM
153         self.testpmd_script = pkg_resources.resource_filename(
154             'yardstick.benchmark.scenarios.networking',
155             self.TESTPMD_SCRIPT)
156
157         self.client._put_file_shell(self.testpmd_script,
158                                     '~/testpmd_vsperf.sh')
159
160         # disable Address Space Layout Randomization (ASLR)
161         cmd = "echo 0 | sudo tee /proc/sys/kernel/randomize_va_space"
162         self.client.send_command(cmd)
163
164         if not self._is_forward_setup():
165             self.tgen_port1_ip = \
166                 utils.get_port_ip(self.client, self.tg_port1)
167             self.tgen_port1_mac = \
168                 utils.get_port_mac(self.client, self.tg_port1)
169             self.client.run("tee ~/.testpmd.ipaddr.port1 > /dev/null",
170                             stdin=self.tgen_port1_ip)
171             self.client.run("tee ~/.testpmd.macaddr.port1 > /dev/null",
172                             stdin=self.tgen_port1_mac)
173             self.tgen_port2_ip = \
174                 utils.get_port_ip(self.client, self.tg_port2)
175             self.tgen_port2_mac = \
176                 utils.get_port_mac(self.client, self.tg_port2)
177             self.client.run("tee ~/.testpmd.ipaddr.port2 > /dev/null",
178                             stdin=self.tgen_port2_ip)
179             self.client.run("tee ~/.testpmd.macaddr.port2 > /dev/null",
180                             stdin=self.tgen_port2_mac)
181         else:
182             cmd = "cat ~/.testpmd.macaddr.port1"
183             status, stdout, stderr = self.client.execute(cmd)
184             if status:
185                 raise RuntimeError(stderr)
186             self.tgen_port1_mac = stdout
187             cmd = "cat ~/.testpmd.ipaddr.port1"
188             status, stdout, stderr = self.client.execute(cmd)
189             if status:
190                 raise RuntimeError(stderr)
191             self.tgen_port1_ip = stdout
192             cmd = "cat ~/.testpmd.macaddr.port2"
193             status, stdout, stderr = self.client.execute(cmd)
194             if status:
195                 raise RuntimeError(stderr)
196             self.tgen_port2_mac = stdout
197             cmd = "cat ~/.testpmd.ipaddr.port2"
198             status, stdout, stderr = self.client.execute(cmd)
199             if status:
200                 raise RuntimeError(stderr)
201             self.tgen_port2_ip = stdout
202
203         LOG.info("forward type is %s", self.forward_type)
204         if self.forward_type == 'testpmd':
205             cmd = "sudo ip link set %s down" % (self.tg_port1)
206             LOG.debug("Executing command: %s", cmd)
207             self.client.execute(cmd)
208             cmd = "sudo ip link set %s down" % (self.tg_port2)
209             LOG.debug("Executing command: %s", cmd)
210             self.client.execute(cmd)
211             cmd = "screen -d -m sudo -E bash ~/testpmd_vsperf.sh %s %s %d" % \
212                 (self.moongen_port1_mac, self.moongen_port2_mac,
213                  self.testpmd_queue)
214             LOG.debug("Executing command: %s", cmd)
215             status, stdout, stderr = self.client.execute(cmd)
216             if status:
217                 raise RuntimeError(stderr)
218
219         elif self.forward_type == 'l2fwd':
220             cmd = ('sed -i "s/static char *net1 = \\\"eth1\\\";'
221                    '/static char *net1 = \\\"%s %s %s\\\";/g" /home/l2fwd/l2fwd.c'
222                    % (self.tg_port1, self.tgen_port1_ip, self.moongen_port1_mac))
223             LOG.debug("Executing command: %s", cmd)
224             status, stdout, stderr = self.client.execute(cmd)
225
226             cmd = ('sed -i "s/static char *net2 = \\\"eth2\\\";'
227                    '/static char *net2 = \\\"%s %s %s\\\";/g" /home/l2fwd/l2fwd.c'
228                    % (self.tg_port2, self.tgen_port2_ip, self.moongen_port2_mac))
229             LOG.debug("Executing command: %s", cmd)
230             status, stdout, stderr = self.client.execute(cmd)
231
232             cmd = ('cd /home/l2fwd/;make;./gen_debian_package.sh;'
233                    'sudo dpkg -i *.deb;'
234                    'sudo modprobe l2fwd')
235             LOG.debug("Executing command: %s", cmd)
236             status, stdout, stderr = self.client.execute(cmd)
237
238         time.sleep(1)
239
240         self.forward_setup_done = True
241
242     def _is_forward_setup(self):
243         """Is forward already setup in the host?"""
244         if self.forward_type is 'testpmd':
245             is_run = True
246             cmd = "ip a | grep %s 2>/dev/null" % (self.tg_port1)
247             LOG.debug("Executing command: %s", cmd)
248             _, stdout, _ = self.client.execute(cmd)
249             if stdout:
250                 is_run = False
251             return is_run
252         elif self.forward_type is 'l2fwd':
253             cmd = ('sudo lsmod |grep l2fwd')
254             LOG.debug("Executing command: %s", cmd)
255             _, stdout, _ = self.client.execute(cmd)
256             if stdout:
257                 return True
258             else:
259                 return False
260
261     def generate_config_file(self, frame_size, multistream,
262                              runBidirec, tg_port1_vlan, tg_port2_vlan,
263                              SearchRuntime, Package_Loss):
264         out_text = """\
265 VSPERF {
266 testType = 'throughput',
267 nrFlows = %d,
268 runBidirec = %s,
269 frameSize = %d,
270 srcMacs = {\'%s\', \'%s\'},
271 dstMacs = {\'%s\', \'%s\'},
272 vlanIds = {%d, %d},
273 searchRunTime = %d,
274 validationRunTime = %d,
275 acceptableLossPct = %d,
276 ports = {0,1},
277 }
278 """ % (multistream, runBidirec, frame_size, self.moongen_port1_mac,
279        self.moongen_port2_mac, self.tgen_port1_mac, self.tgen_port2_mac,
280        tg_port1_vlan, tg_port2_vlan, SearchRuntime, SearchRuntime, Package_Loss)
281         with open(self.VSPERF_CONFIG, "wt") as out_file:
282            out_file.write(out_text)
283         self.CONFIG_FILE = True
284
285     def result_to_data(self, result):
286         search_pattern = re.compile(
287             r'\[REPORT\]\s+total\:\s+'
288             r'Tx\s+frames\:\s+(\d+)\s+'
289             r'Rx\s+Frames\:\s+(\d+)\s+'
290             r'frame\s+loss\:\s+(\d+)\,'
291             r'\s+(\d+\.\d+|\d+)%\s+'
292             r'Tx\s+Mpps\:\s+(\d+.\d+|\d+)\s+'
293             r'Rx\s+Mpps\:\s+(\d+\.\d+|\d+)',
294             re.IGNORECASE)
295         results_match = search_pattern.search(result)
296         if results_match:
297             rx_mpps = float(results_match.group(6))
298             tx_mpps = float(results_match.group(5))
299         else:
300             rx_mpps = 0
301             tx_mpps = 0
302         test_result = {"rx_mpps": rx_mpps, "tx_mpps": tx_mpps}
303         self.TO_DATA = True
304         return test_result
305
306     def run(self, result):
307         """ execute the vsperf benchmark and return test results
308             within result dictionary
309         """
310
311         if not self.setup_done:
312             self.setup()
313
314         # get vsperf options
315         multistream = self.options.get("multistream", 1)
316
317         if not self.forward_setup_done:
318             self.forward_setup()
319
320         if 'frame_size' in self.options:
321             frame_size = self.options.get("frame_size", 64)
322         Package_Loss = self.options.get("Package_Loss", 0)
323         runBidirec = self.options.get("moongen_runBidirec",
324                                                       "true")
325         SearchRuntime = self.options.get("SearchRuntime", 10)
326
327         cmd = "openstack network show %s --format json -c " \
328               "provider:segmentation_id" % (self.tg_port1_nw)
329         LOG.debug("Executing command: %s", cmd)
330         output = subprocess.check_output(cmd, shell=True)
331         try:
332             tg_port1_vlan = jsonutils.loads(output).get("provider:segmentation_id", 1)
333         except TypeError:
334             tg_port1_vlan = 1
335
336         cmd = "openstack network show %s --format json -c " \
337               "provider:segmentation_id" % (self.tg_port2_nw)
338         LOG.debug("Executing command: %s", cmd)
339         output = subprocess.check_output(cmd, shell=True)
340         try:
341             tg_port2_vlan = jsonutils.loads(output).get("provider:segmentation_id", 2)
342         except TypeError:
343             tg_port2_vlan = 2
344
345         self.generate_config_file(frame_size, multistream,
346                                   runBidirec, tg_port1_vlan,
347                                   tg_port2_vlan, SearchRuntime, Package_Loss)
348
349         self.server.execute("rm -f -- %s/opnfv-vsperf-cfg.lua" %
350                             (self.moongen_dir))
351         self.server._put_file_shell(self.VSPERF_CONFIG,
352                                     "%s/opnfv-vsperf-cfg.lua"
353                                     % (self.moongen_dir))
354
355         # execute moongen
356         cmd = ("cd %s;./MoonGen/build/MoonGen ./trafficgen.lua"
357                % (self.moongen_dir))
358         status, stdout, stderr = self.server.execute(cmd)
359         if status:
360             raise RuntimeError(stderr)
361
362         moongen_result = self.result_to_data(stdout)
363         LOG.info(moongen_result)
364         result.update(moongen_result)
365
366         if "sla" in self.scenario_cfg:
367             throughput_rx_mpps = int(
368                 self.scenario_cfg["sla"]["throughput_rx_mpps"])
369
370             assert throughput_rx_mpps <= moongen_result["tx_mpps"], \
371                 "sla_throughput_rx_mpps %f > throughput_rx_mpps(%f); " % \
372                 (throughput_rx_mpps, moongen_result["tx_mpps"])
373
374     def teardown(self):
375         """cleanup after the test execution"""
376
377         # execute external setup script
378         self.setup_done = False