Addition of IRQ Mode to NSB NFVI (PROX)
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / prox_helpers.py
1 # Copyright (c) 2018 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
15 import array
16 import io
17 import logging
18 import operator
19 import os
20 import re
21 import select
22 import socket
23 import time
24
25 from collections import OrderedDict, namedtuple
26 from contextlib import contextmanager
27 from itertools import repeat, chain
28 from multiprocessing import Queue
29
30 import six
31 from six.moves import cStringIO
32 from six.moves import zip, StringIO
33
34 from yardstick.common import utils
35 from yardstick.common.utils import SocketTopology, join_non_strings, try_int
36 from yardstick.network_services.helpers.iniparser import ConfigParser
37 from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper
38 from yardstick.network_services.vnf_generic.vnf.sample_vnf import DpdkVnfSetupEnvHelper
39 from yardstick.network_services import constants
40
41 PROX_PORT = 8474
42
43 SECTION_NAME = 0
44 SECTION_CONTENTS = 1
45
46 LOG = logging.getLogger(__name__)
47 LOG.setLevel(logging.DEBUG)
48 LOG_RESULT = logging.getLogger('yardstick')
49 LOG_RESULT.setLevel(logging.DEBUG)
50
51 BITS_PER_BYTE = 8
52 RETRY_SECONDS = 60
53 RETRY_INTERVAL = 1
54
55 CONFIGURATION_OPTIONS = (
56     # dict key           section     key               default value
57     ('pktSizes', 'general', 'pkt_sizes', '64,128,256,512,1024,1280,1518'),
58     ('testDuration', 'general', 'test_duration', 5.0),
59     ('testPrecision', 'general', 'test_precision', 1.0),
60     ('tests', 'general', 'tests', None),
61     ('toleratedLoss', 'general', 'tolerated_loss', 0.0),
62
63     ('logFile', 'logging', 'file', 'dats.log'),
64     ('logDateFormat', 'logging', 'datefmt', None),
65     ('logLevel', 'logging', 'level', 'INFO'),
66     ('logOverwrite', 'logging', 'overwrite', 1),
67
68     ('testerIp', 'tester', 'ip', None),
69     ('testerUser', 'tester', 'user', 'root'),
70     ('testerDpdkDir', 'tester', 'rte_sdk', '/root/dpdk'),
71     ('testerDpdkTgt', 'tester', 'rte_target', 'x86_64-native-linuxapp-gcc'),
72     ('testerProxDir', 'tester', 'prox_dir', '/root/prox'),
73     ('testerSocketId', 'tester', 'socket_id', 0),
74
75     ('sutIp', 'sut', 'ip', None),
76     ('sutUser', 'sut', 'user', 'root'),
77     ('sutDpdkDir', 'sut', 'rte_sdk', '/root/dpdk'),
78     ('sutDpdkTgt', 'sut', 'rte_target', 'x86_64-native-linuxapp-gcc'),
79     ('sutProxDir', 'sut', 'prox_dir', '/root/prox'),
80     ('sutSocketId', 'sut', 'socket_id', 0),
81 )
82
83
84 class CoreSocketTuple(namedtuple('CoreTuple', 'core_id, socket_id, hyperthread')):
85     CORE_RE = re.compile(r"core\s+(\d+)(?:s(\d+))?(h)?$")
86
87     def __new__(cls, *args):
88         try:
89             matches = cls.CORE_RE.search(str(args[0]))
90             if matches:
91                 args = matches.groups()
92
93             return super(CoreSocketTuple, cls).__new__(cls, int(args[0]), try_int(args[1], 0),
94                                                        'h' if args[2] else '')
95
96         except (AttributeError, TypeError, IndexError, ValueError):
97             raise ValueError('Invalid core spec {}'.format(args))
98
99     def is_hyperthread(self):
100         return self.hyperthread == 'h'
101
102     @property
103     def index(self):
104         return int(self.is_hyperthread())
105
106     def find_in_topology(self, cpu_topology):
107         try:
108             socket_core_match = cpu_topology[self.socket_id][self.core_id]
109             sorted_match = sorted(socket_core_match.values())
110             return sorted_match[self.index][0]
111         except (KeyError, IndexError):
112             template = "Core {}{} on socket {} does not exist"
113             raise ValueError(template.format(self.core_id, self.hyperthread, self.socket_id))
114
115
116 class TotStatsTuple(namedtuple('TotStats', 'rx,tx,tsc,hz')):
117     def __new__(cls, *args):
118         try:
119             assert args[0] is not str(args[0])
120             args = tuple(args[0])
121         except (AssertionError, IndexError, TypeError):
122             pass
123
124         return super(TotStatsTuple, cls).__new__(cls, *args)
125
126
127 class ProxTestDataTuple(namedtuple('ProxTestDataTuple', 'tolerated,tsc_hz,delta_rx,'
128                                                         'delta_tx,delta_tsc,'
129                                                         'latency,rx_total,tx_total,'
130                                                         'requested_pps')):
131     @property
132     def pkt_loss(self):
133         try:
134             return 1e2 * self.drop_total / float(self.tx_total)
135         except ZeroDivisionError:
136             return 100.0
137
138     @property
139     def tx_mpps(self):
140         # calculate the effective throughput in Mpps
141         return float(self.delta_tx) * self.tsc_hz / self.delta_tsc / 1e6
142
143     @property
144     def rx_mpps(self):
145         # calculate the effective throughput in Mpps
146         return float(self.delta_rx) * self.tsc_hz / self.delta_tsc / 1e6
147
148     @property
149     def can_be_lost(self):
150         return int(self.tx_total * self.tolerated / 1e2)
151
152     @property
153     def drop_total(self):
154         return self.tx_total - self.rx_total
155
156     @property
157     def success(self):
158         return self.drop_total <= self.can_be_lost
159
160     def get_samples(self, pkt_size, pkt_loss=None, port_samples=None):
161         if pkt_loss is None:
162             pkt_loss = self.pkt_loss
163
164         if port_samples is None:
165             port_samples = {}
166
167         latency_keys = [
168             "LatencyMin",
169             "LatencyMax",
170             "LatencyAvg",
171         ]
172
173         samples = {
174             "Throughput": self.rx_mpps,
175             "RxThroughput": self.rx_mpps,
176             "DropPackets": pkt_loss,
177             "CurrentDropPackets": pkt_loss,
178             "RequestedTxThroughput": self.requested_pps / 1e6,
179             "TxThroughput": self.tx_mpps,
180             "PktSize": pkt_size,
181         }
182         if port_samples:
183             samples.update(port_samples)
184
185         samples.update((key, value) for key, value in zip(latency_keys, self.latency))
186         return samples
187
188     def log_data(self, logger=None):
189         if logger is None:
190             logger = LOG_RESULT
191
192         template = "RX: %d; TX: %d; dropped: %d (tolerated: %d)"
193         logger.info(template, self.rx_total, self.tx_total, self.drop_total, self.can_be_lost)
194         logger.info("Mpps configured: %f; Mpps generated %f; Mpps received %f",
195                     self.requested_pps / 1e6, self.tx_mpps, self.rx_mpps)
196
197
198 class PacketDump(object):
199     @staticmethod
200     def assert_func(func, value1, value2, template=None):
201         assert func(value1, value2), template.format(value1, value2)
202
203     def __init__(self, port_id, data_len, payload):
204         template = "Packet dump has specified length {}, but payload is {} bytes long"
205         self.assert_func(operator.eq, data_len, len(payload), template)
206         self._port_id = port_id
207         self._data_len = data_len
208         self._payload = payload
209
210     @property
211     def port_id(self):
212         """Get the port id of the packet dump"""
213         return self._port_id
214
215     @property
216     def data_len(self):
217         """Get the length of the data received"""
218         return self._data_len
219
220     def __str__(self):
221         return '<PacketDump port: {} payload: {}>'.format(self._port_id, self._payload)
222
223     def payload(self, start=None, end=None):
224         """Get part of the payload as a list of ordinals.
225
226         Returns a list of byte values, matching the contents of the packet dump.
227         Optional start and end parameters can be specified to retrieve only a
228         part of the packet contents.
229
230         The number of elements in the list is equal to end - start + 1, so end
231         is the offset of the last character.
232
233         Args:
234             start (pos. int): the starting offset in the payload. If it is not
235                 specified or None, offset 0 is assumed.
236             end (pos. int): the ending offset of the payload. If it is not
237                 specified or None, the contents until the end of the packet are
238                 returned.
239
240         Returns:
241             [int, int, ...]. Each int represents the ordinal value of a byte in
242             the packet payload.
243         """
244         if start is None:
245             start = 0
246
247         if end is None:
248             end = self.data_len - 1
249
250         # Bounds checking on offsets
251         template = "Start offset must be non-negative"
252         self.assert_func(operator.ge, start, 0, template)
253
254         template = "End offset must be less than {1}"
255         self.assert_func(operator.lt, end, self.data_len, template)
256
257         # Adjust for splice operation: end offset must be 1 more than the offset
258         # of the last desired character.
259         end += 1
260
261         return self._payload[start:end]
262
263
264 class ProxSocketHelper(object):
265
266     def __init__(self, sock=None):
267         """ creates new prox instance """
268         super(ProxSocketHelper, self).__init__()
269
270         if sock is None:
271             sock = socket.socket()
272
273         self._sock = sock
274         self._pkt_dumps = []
275         self.master_stats = None
276
277     def connect(self, ip, port):
278         """Connect to the prox instance on the remote system"""
279         self._sock.connect((ip, port))
280
281     def get_socket(self):
282         """ get the socket connected to the remote instance """
283         return self._sock
284
285     def _parse_socket_data(self, decoded_data, pkt_dump_only):
286         def get_newline_index():
287             return decoded_data.find('\n', index)
288
289         ret_str = ''
290         index = 0
291         for newline_index in iter(get_newline_index, -1):
292             ret_str = decoded_data[index:newline_index]
293
294             try:
295                 mode, port_id, data_len = ret_str.split(',', 2)
296             except ValueError:
297                 mode, port_id, data_len = None, None, None
298
299             if mode != 'pktdump':
300                 # Regular 1-line message. Stop reading from the socket.
301                 LOG.debug("Regular response read")
302                 return ret_str, True
303
304             LOG.debug("Packet dump header read: [%s]", ret_str)
305
306             # The line is a packet dump header. Parse it, read the
307             # packet payload, store the dump for later retrieval.
308             # Skip over the packet dump and continue processing: a
309             # 1-line response may follow the packet dump.
310
311             data_len = int(data_len)
312             data_start = newline_index + 1  # + 1 to skip over \n
313             data_end = data_start + data_len
314             sub_data = decoded_data[data_start:data_end]
315             pkt_payload = array.array('B', (ord(v) for v in sub_data))
316             pkt_dump = PacketDump(int(port_id), data_len, pkt_payload)
317             self._pkt_dumps.append(pkt_dump)
318
319             if pkt_dump_only:
320                 # Return boolean instead of string to signal
321                 # successful reception of the packet dump.
322                 LOG.debug("Packet dump stored, returning")
323                 return True, False
324
325             index = data_end + 1
326
327         return ret_str, False
328
329     def get_string(self, pkt_dump_only=False, timeout=0.01):
330
331         def is_ready_string():
332             # recv() is blocking, so avoid calling it when no data is waiting.
333             ready = select.select([self._sock], [], [], timeout)
334             return bool(ready[0])
335
336         status = False
337         ret_str = ""
338         while status is False:
339             for status in iter(is_ready_string, False):
340                 decoded_data = self._sock.recv(256).decode('utf-8')
341                 ret_str, done = self._parse_socket_data(decoded_data,
342                                                         pkt_dump_only)
343                 if (done):
344                     status = True
345                     break
346
347         LOG.debug("Received data from socket: [%s]", ret_str)
348         return status, ret_str
349
350     def get_data(self, pkt_dump_only=False, timeout=0.01):
351         """ read data from the socket """
352
353         # This method behaves slightly differently depending on whether it is
354         # called to read the response to a command (pkt_dump_only = 0) or if
355         # it is called specifically to read a packet dump (pkt_dump_only = 1).
356         #
357         # Packet dumps look like:
358         #   pktdump,<port_id>,<data_len>\n
359         #   <packet contents as byte array>\n
360         # This means the total packet dump message consists of 2 lines instead
361         # of 1 line.
362         #
363         # - Response for a command (pkt_dump_only = 0):
364         #   1) Read response from the socket until \n (end of message)
365         #   2a) If the response is a packet dump header (starts with "pktdump,"):
366         #     - Read the packet payload and store the packet dump for later
367         #       retrieval.
368         #     - Reset the state and restart from 1). Eventually state 2b) will
369         #       be reached and the function will return.
370         #   2b) If the response is not a packet dump:
371         #     - Return the received message as a string
372         #
373         # - Explicit request to read a packet dump (pkt_dump_only = 1):
374         #   - Read the dump header and payload
375         #   - Store the packet dump for later retrieval
376         #   - Return True to signify a packet dump was successfully read
377
378         def is_ready():
379             # recv() is blocking, so avoid calling it when no data is waiting.
380             ready = select.select([self._sock], [], [], timeout)
381             return bool(ready[0])
382
383         status = False
384         ret_str = ""
385         for status in iter(is_ready, False):
386             decoded_data = self._sock.recv(256).decode('utf-8')
387             ret_str, done = self._parse_socket_data(decoded_data, pkt_dump_only)
388             if (done):
389                 break
390
391         LOG.debug("Received data from socket: [%s]", ret_str)
392         return ret_str if status else ''
393
394     def put_command(self, to_send):
395         """ send data to the remote instance """
396         LOG.debug("Sending data to socket: [%s]", to_send.rstrip('\n'))
397         try:
398             # NOTE: sendall will block, we need a timeout
399             self._sock.sendall(to_send.encode('utf-8'))
400         except:  # pylint: disable=bare-except
401             pass
402
403     def get_packet_dump(self):
404         """ get the next packet dump """
405         if self._pkt_dumps:
406             return self._pkt_dumps.pop(0)
407         return None
408
409     def stop_all_reset(self):
410         """ stop the remote instance and reset stats """
411         LOG.debug("Stop all and reset stats")
412         self.stop_all()
413         self.reset_stats()
414
415     def stop_all(self):
416         """ stop all cores on the remote instance """
417         LOG.debug("Stop all")
418         self.put_command("stop all\n")
419
420     def stop(self, cores, task=''):
421         """ stop specific cores on the remote instance """
422
423         tmpcores = []
424         for core in cores:
425             if core not in tmpcores:
426                 tmpcores.append(core)
427
428         LOG.debug("Stopping cores %s", tmpcores)
429         self.put_command("stop {} {}\n".format(join_non_strings(',', tmpcores), task))
430
431     def start_all(self):
432         """ start all cores on the remote instance """
433         LOG.debug("Start all")
434         self.put_command("start all\n")
435
436     def start(self, cores):
437         """ start specific cores on the remote instance """
438
439         tmpcores = []
440         for core in cores:
441             if core not in tmpcores:
442                 tmpcores.append(core)
443
444         LOG.debug("Starting cores %s", tmpcores)
445         self.put_command("start {}\n".format(join_non_strings(',', tmpcores)))
446
447     def reset_stats(self):
448         """ reset the statistics on the remote instance """
449         LOG.debug("Reset stats")
450         self.put_command("reset stats\n")
451
452     def _run_template_over_cores(self, template, cores, *args):
453         for core in cores:
454             self.put_command(template.format(core, *args))
455
456     def set_pkt_size(self, cores, pkt_size):
457         """ set the packet size to generate on the remote instance """
458         LOG.debug("Set packet size for core(s) %s to %d", cores, pkt_size)
459         pkt_size -= 4
460         self._run_template_over_cores("pkt_size {} 0 {}\n", cores, pkt_size)
461
462     def set_value(self, cores, offset, value, length):
463         """ set value on the remote instance """
464         msg = "Set value for core(s) %s to '%s' (length %d), offset %d"
465         LOG.debug(msg, cores, value, length, offset)
466         template = "set value {} 0 {} {} {}\n"
467         self._run_template_over_cores(template, cores, offset, value, length)
468
469     def reset_values(self, cores):
470         """ reset values on the remote instance """
471         LOG.debug("Set value for core(s) %s", cores)
472         self._run_template_over_cores("reset values {} 0\n", cores)
473
474     def set_speed(self, cores, speed, tasks=None):
475         """ set speed on the remote instance """
476         if tasks is None:
477             tasks = [0] * len(cores)
478         elif len(tasks) != len(cores):
479             LOG.error("set_speed: cores and tasks must have the same len")
480         LOG.debug("Set speed for core(s)/tasks(s) %s to %g", list(zip(cores, tasks)), speed)
481         for (core, task) in list(zip(cores, tasks)):
482             self.put_command("speed {} {} {}\n".format(core, task, speed))
483
484     def slope_speed(self, cores_speed, duration, n_steps=0):
485         """will start to increase speed from 0 to N where N is taken from
486         a['speed'] for each a in cores_speed"""
487         # by default, each step will take 0.5 sec
488         if n_steps == 0:
489             n_steps = duration * 2
490
491         private_core_data = []
492         step_duration = float(duration) / n_steps
493         for core_data in cores_speed:
494             target = float(core_data['speed'])
495             private_core_data.append({
496                 'cores': core_data['cores'],
497                 'zero': 0,
498                 'delta': target / n_steps,
499                 'current': 0,
500                 'speed': target,
501             })
502
503         deltas_keys_iter = repeat(('current', 'delta'), n_steps - 1)
504         for key1, key2 in chain(deltas_keys_iter, [('zero', 'speed')]):
505             time.sleep(step_duration)
506             for core_data in private_core_data:
507                 core_data['current'] = core_data[key1] + core_data[key2]
508                 self.set_speed(core_data['cores'], core_data['current'])
509
510     def set_pps(self, cores, pps, pkt_size,
511                 line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)):
512         """ set packets per second for specific cores on the remote instance """
513         msg = "Set packets per sec for core(s) %s to %g%% of line rate (packet size: %d)"
514         LOG.debug(msg, cores, pps, pkt_size)
515
516         # speed in percent of line-rate
517         speed = float(pps) * (pkt_size + 20) / line_speed / BITS_PER_BYTE
518         self._run_template_over_cores("speed {} 0 {}\n", cores, speed)
519
520     def lat_stats(self, cores, task=0):
521         """Get the latency statistics from the remote system"""
522         # 1-based index, if max core is 4, then 0, 1, 2, 3, 4  len = 5
523         lat_min = {}
524         lat_max = {}
525         lat_avg = {}
526         for core in cores:
527             self.put_command("lat stats {} {} \n".format(core, task))
528             ret = self.get_data()
529
530             try:
531                 lat_min[core], lat_max[core], lat_avg[core] = \
532                     tuple(int(n) for n in ret.split(",")[:3])
533
534             except (AttributeError, ValueError, TypeError):
535                 pass
536
537         return lat_min, lat_max, lat_avg
538
539     def get_all_tot_stats(self):
540         self.put_command("tot stats\n")
541         all_stats_str = self.get_data().split(",")
542         if len(all_stats_str) != 4:
543             all_stats = [0] * 4
544             return all_stats
545         all_stats = TotStatsTuple(int(v) for v in all_stats_str)
546         self.master_stats = all_stats
547         return all_stats
548
549     def hz(self):
550         return self.get_all_tot_stats()[3]
551
552     def core_stats(self, cores, task=0):
553         """Get the receive statistics from the remote system"""
554         rx = tx = drop = tsc = 0
555         for core in cores:
556             self.put_command("core stats {} {}\n".format(core, task))
557             ret = self.get_data().split(",")
558             rx += int(ret[0])
559             tx += int(ret[1])
560             drop += int(ret[2])
561             tsc = int(ret[3])
562         return rx, tx, drop, tsc
563
564     def irq_core_stats(self, cores_tasks):
565         """ get IRQ stats per core"""
566
567         stat = {}
568         core = 0
569         task = 0
570         for core, task in cores_tasks:
571             self.put_command("stats task.core({}).task({}).max_irq,task.core({}).task({}).irq(0),"
572                              "task.core({}).task({}).irq(1),task.core({}).task({}).irq(2),"
573                              "task.core({}).task({}).irq(3),task.core({}).task({}).irq(4),"
574                              "task.core({}).task({}).irq(5),task.core({}).task({}).irq(6),"
575                              "task.core({}).task({}).irq(7),task.core({}).task({}).irq(8),"
576                              "task.core({}).task({}).irq(9),task.core({}).task({}).irq(10),"
577                              "task.core({}).task({}).irq(11),task.core({}).task({}).irq(12)"
578                              "\n".format(core, task, core, task, core, task, core, task,
579                                          core, task, core, task, core, task, core, task,
580                                          core, task, core, task, core, task, core, task,
581                                          core, task, core, task))
582             in_data_str = self.get_data().split(",")
583             ret = [try_int(s, 0) for s in in_data_str]
584             key = "core_" + str(core)
585             try:
586                 stat[key] = {"cpu": core, "max_irq": ret[0], "bucket_0" : ret[1],
587                              "bucket_1" : ret[2], "bucket_2" : ret[3],
588                              "bucket_3" : ret[4], "bucket_4" : ret[5],
589                              "bucket_5" : ret[6], "bucket_6" : ret[7],
590                              "bucket_7" : ret[8], "bucket_8" : ret[9],
591                              "bucket_9" : ret[10], "bucket_10" : ret[11],
592                              "bucket_11" : ret[12], "bucket_12" : ret[13],
593                              "overflow": ret[10] + ret[11] + ret[12] + ret[13]}
594             except (KeyError, IndexError):
595                 LOG.error("Corrupted PACKET %s", in_data_str)
596
597         return stat
598
599     def multi_port_stats(self, ports):
600         """get counter values from all  ports at once"""
601
602         ports_str = ",".join(map(str, ports))
603         ports_all_data = []
604         tot_result = [0] * len(ports)
605
606         port_index = 0
607         while (len(ports) is not len(ports_all_data)):
608             self.put_command("multi port stats {}\n".format(ports_str))
609             status, ports_all_data_str = self.get_string()
610
611             if not status:
612                 return False, []
613
614             ports_all_data = ports_all_data_str.split(";")
615
616             if len(ports) is len(ports_all_data):
617                 for port_data_str in ports_all_data:
618
619                     tmpdata = []
620                     try:
621                         tmpdata = [try_int(s, 0) for s in port_data_str.split(",")]
622                     except (IndexError, TypeError):
623                         LOG.error("Unpacking data error %s", port_data_str)
624                         return False, []
625
626                     if (len(tmpdata) < 6) or tmpdata[0] not in ports:
627                         LOG.error("Corrupted PACKET %s - retrying", port_data_str)
628                         return False, []
629                     else:
630                         tot_result[port_index] = tmpdata
631                         port_index = port_index + 1
632             else:
633                 LOG.error("Empty / too much data - retry -%s-", ports_all_data)
634                 return False, []
635
636         LOG.debug("Multi port packet ..OK.. %s", tot_result)
637         return True, tot_result
638
639     def port_stats(self, ports):
640         """get counter values from a specific port"""
641         tot_result = [0] * 12
642         for port in ports:
643             self.put_command("port_stats {}\n".format(port))
644             ret = [try_int(s, 0) for s in self.get_data().split(",")]
645             tot_result = [sum(x) for x in zip(tot_result, ret)]
646         return tot_result
647
648     @contextmanager
649     def measure_tot_stats(self):
650         start = self.get_all_tot_stats()
651         container = {'start_tot': start}
652         try:
653             yield container
654         finally:
655             container['end_tot'] = end = self.get_all_tot_stats()
656
657         container['delta'] = TotStatsTuple(e - s for s, e in zip(start, end))
658
659     def tot_stats(self):
660         """Get the total statistics from the remote system"""
661         stats = self.get_all_tot_stats()
662         return stats[:3]
663
664     def tot_ierrors(self):
665         """Get the total ierrors from the remote system"""
666         self.put_command("tot ierrors tot\n")
667         recv = self.get_data().split(',')
668         tot_ierrors = int(recv[0])
669         tsc = int(recv[0])
670         return tot_ierrors, tsc
671
672     def set_count(self, count, cores):
673         """Set the number of packets to send on the specified core"""
674         self._run_template_over_cores("count {} 0 {}\n", cores, count)
675
676     def dump_rx(self, core_id, task_id=0, count=1):
677         """Activate dump on rx on the specified core"""
678         LOG.debug("Activating dump on RX for core %d, task %d, count %d", core_id, task_id, count)
679         self.put_command("dump_rx {} {} {}\n".format(core_id, task_id, count))
680         time.sleep(1.5)  # Give PROX time to set up packet dumping
681
682     def quit(self):
683         self.stop_all()
684         self._quit()
685         self.force_quit()
686
687     def _quit(self):
688         """ stop all cores on the remote instance """
689         LOG.debug("Quit prox")
690         self.put_command("quit\n")
691         time.sleep(3)
692
693     def force_quit(self):
694         """ stop all cores on the remote instance """
695         LOG.debug("Force Quit prox")
696         self.put_command("quit_force\n")
697         time.sleep(3)
698
699 _LOCAL_OBJECT = object()
700
701
702 class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
703     # the actual app is lowercase
704     APP_NAME = 'prox'
705     # not used for Prox but added for consistency
706     VNF_TYPE = "PROX"
707
708     LUA_PARAMETER_NAME = ""
709     LUA_PARAMETER_PEER = {
710         "gen": "sut",
711         "sut": "gen",
712     }
713
714     CONFIG_QUEUE_TIMEOUT = 120
715
716     def __init__(self, vnfd_helper, ssh_helper, scenario_helper):
717         self.remote_path = None
718         super(ProxDpdkVnfSetupEnvHelper, self).__init__(vnfd_helper, ssh_helper, scenario_helper)
719         self.remote_prox_file_name = None
720         self._prox_config_data = None
721         self.additional_files = {}
722         self.config_queue = Queue()
723         # allow_exit_without_flush
724         self.config_queue.cancel_join_thread()
725         self._global_section = None
726
727     @property
728     def prox_config_data(self):
729         if self._prox_config_data is None:
730             # this will block, but it needs too
731             self._prox_config_data = self.config_queue.get(True, self.CONFIG_QUEUE_TIMEOUT)
732         return self._prox_config_data
733
734     @property
735     def global_section(self):
736         if self._global_section is None and self.prox_config_data:
737             self._global_section = self.find_section("global")
738         return self._global_section
739
740     def find_section(self, name, default=_LOCAL_OBJECT):
741         result = next((value for key, value in self.prox_config_data if key == name), default)
742         if result is _LOCAL_OBJECT:
743             raise KeyError('{} not found in Prox config'.format(name))
744         return result
745
746     def find_in_section(self, section_name, section_key, default=_LOCAL_OBJECT):
747         section = self.find_section(section_name, [])
748         result = next((value for key, value in section if key == section_key), default)
749         if result is _LOCAL_OBJECT:
750             template = '{} not found in {} section of Prox config'
751             raise KeyError(template.format(section_key, section_name))
752         return result
753
754     def copy_to_target(self, config_file_path, prox_file):
755         remote_path = os.path.join("/tmp", prox_file)
756         self.ssh_helper.put(config_file_path, remote_path)
757         return remote_path
758
759     @staticmethod
760     def _get_tx_port(section, sections):
761         iface_port = [-1]
762         for item in sections[section]:
763             if item[0] == "tx port":
764                 iface_port = re.findall(r'\d+', item[1])
765                 # do we want the last one?
766                 #   if yes, then can we reverse?
767         return int(iface_port[0])
768
769     @staticmethod
770     def _replace_quoted_with_value(quoted, value, count=1):
771         new_string = re.sub('"[^"]*"', '"{}"'.format(value), quoted, count)
772         return new_string
773
774     def _insert_additional_file(self, value):
775         file_str = value.split('"')
776         base_name = os.path.basename(file_str[1])
777         file_str[1] = self.additional_files[base_name]
778         return '"'.join(file_str)
779
780     def generate_prox_config_file(self, config_path):
781         sections = []
782         prox_config = ConfigParser(config_path, sections)
783         prox_config.parse()
784
785         # Ensure MAC is set "hardware"
786         all_ports = self.vnfd_helper.port_pairs.all_ports
787         # use dpdk port number
788         for port_name in all_ports:
789             port_num = self.vnfd_helper.port_num(port_name)
790             port_section_name = "port {}".format(port_num)
791             for section_name, section in sections:
792                 if port_section_name != section_name:
793                     continue
794
795                 for section_data in section:
796                     if section_data[0] == "mac":
797                         section_data[1] = "hardware"
798
799         # search for dst mac
800         for _, section in sections:
801             for section_data in section:
802                 item_key, item_val = section_data
803                 if item_val.startswith("@@dst_mac"):
804                     tx_port_iter = re.finditer(r'\d+', item_val)
805                     tx_port_no = int(next(tx_port_iter).group(0))
806                     intf = self.vnfd_helper.find_interface_by_port(tx_port_no)
807                     mac = intf["virtual-interface"]["dst_mac"]
808                     section_data[1] = mac.replace(":", " ", 6)
809
810                 if item_key == "dst mac" and item_val.startswith("@@"):
811                     tx_port_iter = re.finditer(r'\d+', item_val)
812                     tx_port_no = int(next(tx_port_iter).group(0))
813                     intf = self.vnfd_helper.find_interface_by_port(tx_port_no)
814                     mac = intf["virtual-interface"]["dst_mac"]
815                     section_data[1] = mac
816
817                 if item_val.startswith("@@src_mac"):
818                     tx_port_iter = re.finditer(r'\d+', item_val)
819                     tx_port_no = int(next(tx_port_iter).group(0))
820                     intf = self.vnfd_helper.find_interface_by_port(tx_port_no)
821                     mac = intf["virtual-interface"]["local_mac"]
822                     section_data[1] = mac.replace(":", " ", 6)
823
824                 if item_key == "src mac" and item_val.startswith("@@"):
825                     tx_port_iter = re.finditer(r'\d+', item_val)
826                     tx_port_no = int(next(tx_port_iter).group(0))
827                     intf = self.vnfd_helper.find_interface_by_port(tx_port_no)
828                     mac = intf["virtual-interface"]["local_mac"]
829                     section_data[1] = mac
830
831         # if addition file specified in prox config
832         if not self.additional_files:
833             return sections
834
835         for section_name, section in sections:
836             for section_data in section:
837                 try:
838                     if section_data[0].startswith("dofile"):
839                         section_data[0] = self._insert_additional_file(section_data[0])
840
841                     if section_data[1].startswith("dofile"):
842                         section_data[1] = self._insert_additional_file(section_data[1])
843                 except:  # pylint: disable=bare-except
844                     pass
845
846         return sections
847
848     @staticmethod
849     def write_prox_lua(lua_config):
850         """
851         Write an .ini-format config file for PROX (parameters.lua)
852         PROX does not allow a space before/after the =, so we need
853         a custom method
854         """
855         out = []
856         for key in lua_config:
857             value = '"' + lua_config[key] + '"'
858             if key == "__name__":
859                 continue
860             if value is not None and value != '@':
861                 key = "=".join((key, str(value).replace('\n', '\n\t')))
862                 out.append(key)
863             else:
864                 key = str(key).replace('\n', '\n\t')
865                 out.append(key)
866         return os.linesep.join(out)
867
868     @staticmethod
869     def write_prox_config(prox_config):
870         """
871         Write an .ini-format config file for PROX
872         PROX does not allow a space before/after the =, so we need
873         a custom method
874         """
875         out = []
876         for (section_name, section) in prox_config:
877             out.append("[{}]".format(section_name))
878             for item in section:
879                 key, value = item
880                 if key == "__name__":
881                     continue
882                 if value is not None and value != '@':
883                     key = "=".join((key, str(value).replace('\n', '\n\t')))
884                     out.append(key)
885                 else:
886                     key = str(key).replace('\n', '\n\t')
887                     out.append(key)
888         return os.linesep.join(out)
889
890     def put_string_to_file(self, s, remote_path):
891         file_obj = cStringIO(s)
892         self.ssh_helper.put_file_obj(file_obj, remote_path)
893         return remote_path
894
895     def generate_prox_lua_file(self):
896         p = OrderedDict()
897         all_ports = self.vnfd_helper.port_pairs.all_ports
898         for port_name in all_ports:
899             port_num = self.vnfd_helper.port_num(port_name)
900             intf = self.vnfd_helper.find_interface(name=port_name)
901             vintf = intf['virtual-interface']
902             p["tester_mac{0}".format(port_num)] = vintf["dst_mac"]
903             p["src_mac{0}".format(port_num)] = vintf["local_mac"]
904
905         return p
906
907     def upload_prox_lua(self, config_file, lua_data):
908         # prox can't handle spaces around ' = ' so use custom method
909         out = StringIO(self.write_prox_lua(lua_data))
910         out.seek(0)
911         remote_path = os.path.join("/tmp", config_file)
912         self.ssh_helper.put_file_obj(out, remote_path)
913
914         return remote_path
915
916     def upload_prox_config(self, config_file, prox_config_data):
917         # prox can't handle spaces around ' = ' so use custom method
918         out = StringIO(self.write_prox_config(prox_config_data))
919         out.seek(0)
920         remote_path = os.path.join("/tmp", config_file)
921         self.ssh_helper.put_file_obj(out, remote_path)
922
923         return remote_path
924
925     def build_config_file(self):
926         task_path = self.scenario_helper.task_path
927         options = self.scenario_helper.options
928         config_path = options['prox_config']
929         config_file = os.path.basename(config_path)
930         config_path = utils.find_relative_file(config_path, task_path)
931         self.additional_files = {}
932
933         try:
934             if options['prox_generate_parameter']:
935                 self.lua = []
936                 self.lua = self.generate_prox_lua_file()
937                 if len(self.lua) > 0:
938                     self.upload_prox_lua("parameters.lua", self.lua)
939         except:  # pylint: disable=bare-except
940             pass
941
942         prox_files = options.get('prox_files', [])
943         if isinstance(prox_files, six.string_types):
944             prox_files = [prox_files]
945         for key_prox_file in prox_files:
946             base_prox_file = os.path.basename(key_prox_file)
947             key_prox_path = utils.find_relative_file(key_prox_file, task_path)
948             remote_prox_file = self.copy_to_target(key_prox_path, base_prox_file)
949             self.additional_files[base_prox_file] = remote_prox_file
950
951         self._prox_config_data = self.generate_prox_config_file(config_path)
952         # copy config to queue so we can read it from traffic_runner process
953         self.config_queue.put(self._prox_config_data)
954         self.remote_path = self.upload_prox_config(config_file, self._prox_config_data)
955
956     def build_config(self):
957         self.build_config_file()
958
959         options = self.scenario_helper.options
960         prox_args = options['prox_args']
961         tool_path = self.ssh_helper.join_bin_path(self.APP_NAME)
962
963         self.pipeline_kwargs = {
964             'tool_path': tool_path,
965             'tool_dir': os.path.dirname(tool_path),
966             'cfg_file': self.remote_path,
967             'args': ' '.join(' '.join([str(k), str(v) if v else ''])
968                              for k, v in prox_args.items())
969         }
970
971         cmd_template = ("sudo bash -c 'cd {tool_dir}; {tool_path} -o cli "
972                         "{args} -f {cfg_file} '")
973         return cmd_template.format(**self.pipeline_kwargs)
974
975
976 # this might be bad, sometimes we want regular ResourceHelper methods, like collect_kpi
977 class ProxResourceHelper(ClientResourceHelper):
978
979     RESOURCE_WORD = 'prox'
980
981     PROX_MODE = ""
982
983     WAIT_TIME = 3
984
985     @staticmethod
986     def find_pci(pci, bound_pci):
987         # we have to substring match PCI bus address from the end
988         return any(b.endswith(pci) for b in bound_pci)
989
990     def __init__(self, setup_helper):
991         super(ProxResourceHelper, self).__init__(setup_helper)
992         self.mgmt_interface = self.vnfd_helper.mgmt_interface
993         self._user = self.mgmt_interface["user"]
994         self._ip = self.mgmt_interface["ip"]
995
996         self.done = False
997         self._vpci_to_if_name_map = None
998         self.additional_file = {}
999         self.remote_prox_file_name = None
1000         self.lower = None
1001         self.upper = None
1002         self.step_delta = 1
1003         self.step_time = 0.5
1004         self._test_type = None
1005
1006     @property
1007     def sut(self):
1008         if not self.client:
1009             self.client = self._connect()
1010         return self.client
1011
1012     @property
1013     def test_type(self):
1014         if self._test_type is None:
1015             self._test_type = self.setup_helper.find_in_section('global', 'name', None)
1016         return self._test_type
1017
1018     def run_traffic(self, traffic_profile, *args):
1019         self._queue.cancel_join_thread()
1020         self.lower = 0.0
1021         self.upper = 100.0
1022
1023         traffic_profile.init(self._queue)
1024         # this frees up the run_traffic loop
1025         self.client_started.value = 1
1026
1027         while not self._terminated.value:
1028             # move it all to traffic_profile
1029             self._run_traffic_once(traffic_profile)
1030
1031     def _run_traffic_once(self, traffic_profile):
1032         traffic_profile.execute_traffic(self)
1033         if traffic_profile.done.is_set():
1034             self._queue.put({'done': True})
1035             LOG.debug("tg_prox done")
1036             self._terminated.value = 1
1037
1038     # For VNF use ResourceHelper method to collect KPIs directly.
1039     # for TG leave the superclass ClientResourceHelper collect_kpi_method intact
1040     def collect_collectd_kpi(self):
1041         return self._collect_resource_kpi()
1042
1043     def collect_kpi(self):
1044         result = super(ProxResourceHelper, self).collect_kpi()
1045         # add in collectd kpis manually
1046         if result:
1047             result['collect_stats'] = self._collect_resource_kpi()
1048         return result
1049
1050     def terminate(self):
1051         # should not be called, use VNF terminate
1052         raise NotImplementedError()
1053
1054     def up_post(self):
1055         return self.sut  # force connection
1056
1057     def execute(self, cmd, *args, **kwargs):
1058         func = getattr(self.sut, cmd, None)
1059         if func:
1060             return func(*args, **kwargs)
1061         return None
1062
1063     def _connect(self, client=None):
1064         """Run and connect to prox on the remote system """
1065         # De-allocating a large amount of hugepages takes some time. If a new
1066         # PROX instance is started immediately after killing the previous one,
1067         # it might not be able to allocate hugepages, because they are still
1068         # being freed. Hence the -w switch.
1069         # self.connection.execute("sudo killall -w Prox 2>/dev/null")
1070         # prox_cmd = "export TERM=xterm; cd "+ self.bin_path +"; ./Prox -t
1071         # -f ./handle_none-4.cfg"
1072         # prox_cmd = "export TERM=xterm; export RTE_SDK=" + self._dpdk_dir +
1073         #  "; " \
1074         #    + "export RTE_TARGET=" + self._dpdk_target + ";" \
1075         #    + " cd " + self._prox_dir + "; make HW_DIRECT_STATS=y -j50;
1076         # sudo " \
1077         #    + "./build/Prox " + prox_args
1078         # log.debug("Starting PROX with command [%s]", prox_cmd)
1079         # thread.start_new_thread(self.ssh_check_quit, (self, self._user,
1080         # self._ip, prox_cmd))
1081         if client is None:
1082             client = ProxSocketHelper()
1083
1084         # try connecting to Prox for 60s
1085         for _ in range(RETRY_SECONDS):
1086             time.sleep(RETRY_INTERVAL)
1087             try:
1088                 client.connect(self._ip, PROX_PORT)
1089             except (socket.gaierror, socket.error):
1090                 continue
1091             else:
1092                 return client
1093
1094         msg = "Failed to connect to prox, please check if system {} accepts connections on port {}"
1095         raise Exception(msg.format(self._ip, PROX_PORT))
1096
1097
1098 class ProxDataHelper(object):
1099
1100     def __init__(self, vnfd_helper, sut, pkt_size, value, tolerated_loss, line_speed):
1101         super(ProxDataHelper, self).__init__()
1102         self.vnfd_helper = vnfd_helper
1103         self.sut = sut
1104         self.pkt_size = pkt_size
1105         self.value = value
1106         self.line_speed = line_speed
1107         self.tolerated_loss = tolerated_loss
1108         self.port_count = len(self.vnfd_helper.port_pairs.all_ports)
1109         self.tsc_hz = None
1110         self.measured_stats = None
1111         self.latency = None
1112         self._totals_and_pps = None
1113         self.result_tuple = None
1114
1115     @property
1116     def totals_and_pps(self):
1117         if self._totals_and_pps is None:
1118             rx_total = tx_total = 0
1119             ok = False
1120             timeout = time.time() + constants.RETRY_TIMEOUT
1121             while not ok:
1122                 ok, all_ports = self.sut.multi_port_stats([
1123                     self.vnfd_helper.port_num(port_name)
1124                     for port_name in self.vnfd_helper.port_pairs.all_ports])
1125                 if time.time() > timeout:
1126                     break
1127             if ok:
1128                 for port in all_ports:
1129                     rx_total = rx_total + port[1]
1130                     tx_total = tx_total + port[2]
1131                 requested_pps = self.value / 100.0 * self.line_rate_to_pps()
1132                 self._totals_and_pps = rx_total, tx_total, requested_pps
1133         return self._totals_and_pps
1134
1135     @property
1136     def rx_total(self):
1137         try:
1138             ret_val = self.totals_and_pps[0]
1139         except (AttributeError, ValueError, TypeError, LookupError):
1140             ret_val = 0
1141         return ret_val
1142
1143     @property
1144     def tx_total(self):
1145         try:
1146             ret_val = self.totals_and_pps[1]
1147         except (AttributeError, ValueError, TypeError, LookupError):
1148             ret_val = 0
1149         return ret_val
1150
1151     @property
1152     def requested_pps(self):
1153         try:
1154             ret_val = self.totals_and_pps[2]
1155         except (AttributeError, ValueError, TypeError, LookupError):
1156             ret_val = 0
1157         return ret_val
1158
1159     @property
1160     def samples(self):
1161         samples = {}
1162         ports = []
1163         port_names = {}
1164         for port_name, port_num in self.vnfd_helper.ports_iter():
1165             ports.append(port_num)
1166             port_names[port_num] = port_name
1167
1168         ok = False
1169         timeout = time.time() + constants.RETRY_TIMEOUT
1170         while not ok:
1171             ok, results = self.sut.multi_port_stats(ports)
1172             if time.time() > timeout:
1173                 break
1174         if ok:
1175             for result in results:
1176                 port_num = result[0]
1177                 try:
1178                     samples[port_names[port_num]] = {
1179                         "in_packets": result[1],
1180                         "out_packets": result[2]}
1181                 except (IndexError, KeyError):
1182                     pass
1183         return samples
1184
1185     def __enter__(self):
1186         self.check_interface_count()
1187         return self
1188
1189     def __exit__(self, exc_type, exc_val, exc_tb):
1190         self.make_tuple()
1191
1192     def make_tuple(self):
1193         if self.result_tuple:
1194             return
1195
1196         self.result_tuple = ProxTestDataTuple(
1197             self.tolerated_loss,
1198             self.tsc_hz,
1199             self.measured_stats['delta'].rx,
1200             self.measured_stats['delta'].tx,
1201             self.measured_stats['delta'].tsc,
1202             self.latency,
1203             self.rx_total,
1204             self.tx_total,
1205             self.requested_pps,
1206         )
1207         self.result_tuple.log_data()
1208
1209     @contextmanager
1210     def measure_tot_stats(self):
1211         with self.sut.measure_tot_stats() as self.measured_stats:
1212             yield
1213
1214     def check_interface_count(self):
1215         # do this assert in init?  unless we expect interface count to
1216         # change from one run to another run...
1217         assert self.port_count in {1, 2, 4}, \
1218             "Invalid number of ports: 1, 2 or 4 ports only supported at this time"
1219
1220     def capture_tsc_hz(self):
1221         self.tsc_hz = float(self.sut.hz())
1222
1223     def line_rate_to_pps(self):
1224       return self.port_count * self.line_speed  / BITS_PER_BYTE / (self.pkt_size + 20)
1225
1226 class ProxProfileHelper(object):
1227
1228     __prox_profile_type__ = "Generic"
1229
1230     PROX_CORE_GEN_MODE = "gen"
1231     PROX_CORE_LAT_MODE = "lat"
1232
1233     @classmethod
1234     def get_cls(cls, helper_type):
1235         """Return class of specified type."""
1236         if not helper_type:
1237             return ProxProfileHelper
1238
1239         for profile_helper_class in utils.itersubclasses(cls):
1240             if helper_type == profile_helper_class.__prox_profile_type__:
1241                 return profile_helper_class
1242
1243         return ProxProfileHelper
1244
1245     @classmethod
1246     def make_profile_helper(cls, resource_helper):
1247         return cls.get_cls(resource_helper.test_type)(resource_helper)
1248
1249     def __init__(self, resource_helper):
1250         super(ProxProfileHelper, self).__init__()
1251         self.resource_helper = resource_helper
1252         self._cpu_topology = None
1253         self._test_cores = None
1254         self._latency_cores = None
1255
1256     @property
1257     def cpu_topology(self):
1258         if not self._cpu_topology:
1259             stdout = io.BytesIO()
1260             self.ssh_helper.get_file_obj("/proc/cpuinfo", stdout)
1261             self._cpu_topology = SocketTopology.parse_cpuinfo(stdout.getvalue().decode('utf-8'))
1262         return self._cpu_topology
1263
1264     @property
1265     def test_cores(self):
1266         if not self._test_cores:
1267             self._test_cores = self.get_cores(self.PROX_CORE_GEN_MODE)
1268         return self._test_cores
1269
1270     @property
1271     def latency_cores(self):
1272         if not self._latency_cores:
1273             self._latency_cores = self.get_cores(self.PROX_CORE_LAT_MODE)
1274         return self._latency_cores
1275
1276     @contextmanager
1277     def traffic_context(self, pkt_size, value):
1278         self.sut.stop_all()
1279         self.sut.reset_stats()
1280         try:
1281             self.sut.set_pkt_size(self.test_cores, pkt_size)
1282             self.sut.set_speed(self.test_cores, value)
1283             self.sut.start_all()
1284             time.sleep(1)
1285             yield
1286         finally:
1287             self.sut.stop_all()
1288
1289     def get_cores(self, mode):
1290         cores = []
1291
1292         for section_name, section in self.setup_helper.prox_config_data:
1293             if not section_name.startswith("core"):
1294                 continue
1295
1296             for key, value in section:
1297                 if key == "mode" and value == mode:
1298                     core_tuple = CoreSocketTuple(section_name)
1299                     core = core_tuple.core_id
1300                     cores.append(core)
1301
1302         return cores
1303
1304     def pct_10gbps(self, percent, line_speed):
1305         """Get rate in percent of 10 Gbps.
1306
1307         Returns the rate in percent of 10 Gbps.
1308         For instance 100.0 = 10 Gbps; 400.0 = 40 Gbps.
1309
1310         This helper method isrequired when setting interface_speed option in
1311         the testcase because NSB/PROX considers 10Gbps as 100% of line rate,
1312         this means that the line rate must be expressed as a percentage of
1313         10Gbps.
1314
1315         :param percent: (float) Percent of line rate (100.0 = line rate).
1316         :param line_speed: (int) line rate speed, in bits per second.
1317
1318         :return: (float) Represents the rate in percent of 10Gbps.
1319         """
1320         return (percent * line_speed / (
1321             constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT))
1322
1323     def run_test(self, pkt_size, duration, value, tolerated_loss=0.0,
1324                  line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)):
1325         data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size,
1326                                      value, tolerated_loss, line_speed)
1327
1328         with data_helper, self.traffic_context(pkt_size,
1329                                                self.pct_10gbps(value, line_speed)):
1330             with data_helper.measure_tot_stats():
1331                 time.sleep(duration)
1332                 # Getting statistics to calculate PPS at right speed....
1333                 data_helper.capture_tsc_hz()
1334                 data_helper.latency = self.get_latency()
1335
1336         return data_helper.result_tuple, data_helper.samples
1337
1338     def get_latency(self):
1339         """
1340         :return: return lat_min, lat_max, lat_avg
1341         :rtype: list
1342         """
1343
1344         if not self._latency_cores:
1345             self._latency_cores = self.get_cores(self.PROX_CORE_LAT_MODE)
1346
1347         if self._latency_cores:
1348             return self.sut.lat_stats(self._latency_cores)
1349         return []
1350
1351     def terminate(self):
1352         pass
1353
1354     def __getattr__(self, item):
1355         return getattr(self.resource_helper, item)
1356
1357
1358 class ProxMplsProfileHelper(ProxProfileHelper):
1359
1360     __prox_profile_type__ = "MPLS tag/untag"
1361
1362     def __init__(self, resource_helper):
1363         super(ProxMplsProfileHelper, self).__init__(resource_helper)
1364         self._cores_tuple = None
1365
1366     @property
1367     def mpls_cores(self):
1368         if not self._cores_tuple:
1369             self._cores_tuple = self.get_cores_mpls()
1370         return self._cores_tuple
1371
1372     @property
1373     def tagged_cores(self):
1374         return self.mpls_cores[0]
1375
1376     @property
1377     def plain_cores(self):
1378         return self.mpls_cores[1]
1379
1380     def get_cores_mpls(self):
1381         cores_tagged = []
1382         cores_plain = []
1383         for section_name, section in self.resource_helper.setup_helper.prox_config_data:
1384             if not section_name.startswith("core"):
1385                 continue
1386
1387             if all(key != "mode" or value != self.PROX_CORE_GEN_MODE for key, value in section):
1388                 continue
1389
1390             for item_key, item_value in section:
1391                 if item_key != 'name':
1392                     continue
1393
1394                 if item_value.startswith("tag"):
1395                     core_tuple = CoreSocketTuple(section_name)
1396                     core_tag = core_tuple.core_id
1397                     cores_tagged.append(core_tag)
1398
1399                 elif item_value.startswith("udp"):
1400                     core_tuple = CoreSocketTuple(section_name)
1401                     core_udp = core_tuple.core_id
1402                     cores_plain.append(core_udp)
1403
1404         return cores_tagged, cores_plain
1405
1406     @contextmanager
1407     def traffic_context(self, pkt_size, value):
1408         self.sut.stop_all()
1409         self.sut.reset_stats()
1410         try:
1411             self.sut.set_pkt_size(self.tagged_cores, pkt_size)
1412             self.sut.set_pkt_size(self.plain_cores, pkt_size - 4)
1413             self.sut.set_speed(self.tagged_cores, value)
1414             ratio = 1.0 * (pkt_size - 4 + 20) / (pkt_size + 20)
1415             self.sut.set_speed(self.plain_cores, value * ratio)
1416             self.sut.start_all()
1417             time.sleep(1)
1418             yield
1419         finally:
1420             self.sut.stop_all()
1421
1422
1423 class ProxBngProfileHelper(ProxProfileHelper):
1424
1425     __prox_profile_type__ = "BNG gen"
1426
1427     def __init__(self, resource_helper):
1428         super(ProxBngProfileHelper, self).__init__(resource_helper)
1429         self._cores_tuple = None
1430
1431     @property
1432     def bng_cores(self):
1433         if not self._cores_tuple:
1434             self._cores_tuple = self.get_cores_gen_bng_qos()
1435         return self._cores_tuple
1436
1437     @property
1438     def cpe_cores(self):
1439         return self.bng_cores[0]
1440
1441     @property
1442     def inet_cores(self):
1443         return self.bng_cores[1]
1444
1445     @property
1446     def arp_cores(self):
1447         return self.bng_cores[2]
1448
1449     @property
1450     def arp_task_cores(self):
1451         return self.bng_cores[3]
1452
1453     @property
1454     def all_rx_cores(self):
1455         return self.latency_cores
1456
1457     def get_cores_gen_bng_qos(self):
1458         cpe_cores = []
1459         inet_cores = []
1460         arp_cores = []
1461         arp_tasks_core = [0]
1462         for section_name, section in self.resource_helper.setup_helper.prox_config_data:
1463             if not section_name.startswith("core"):
1464                 continue
1465
1466             if all(key != "mode" or value != self.PROX_CORE_GEN_MODE for key, value in section):
1467                 continue
1468
1469             for item_key, item_value in section:
1470                 if item_key != 'name':
1471                     continue
1472
1473                 if item_value.startswith("cpe"):
1474                     core_tuple = CoreSocketTuple(section_name)
1475                     cpe_core = core_tuple.core_id
1476                     cpe_cores.append(cpe_core)
1477
1478                 elif item_value.startswith("inet"):
1479                     core_tuple = CoreSocketTuple(section_name)
1480                     inet_core = core_tuple.core_id
1481                     inet_cores.append(inet_core)
1482
1483                 elif item_value.startswith("arp"):
1484                     core_tuple = CoreSocketTuple(section_name)
1485                     arp_core = core_tuple.core_id
1486                     arp_cores.append(arp_core)
1487
1488                 # We check the tasks/core separately
1489                 if item_value.startswith("arp_task"):
1490                     core_tuple = CoreSocketTuple(section_name)
1491                     arp_task_core = core_tuple.core_id
1492                     arp_tasks_core.append(arp_task_core)
1493
1494         return cpe_cores, inet_cores, arp_cores, arp_tasks_core
1495
1496     @contextmanager
1497     def traffic_context(self, pkt_size, value):
1498         # Tester is sending packets at the required speed already after
1499         # setup_test(). Just get the current statistics, sleep the required
1500         # amount of time and calculate packet loss.
1501         inet_pkt_size = pkt_size
1502         cpe_pkt_size = pkt_size - 24
1503         ratio = 1.0 * (cpe_pkt_size + 20) / (inet_pkt_size + 20)
1504
1505         curr_up_speed = curr_down_speed = 0
1506         max_up_speed = max_down_speed = value
1507         if ratio < 1:
1508             max_down_speed = value * ratio
1509         else:
1510             max_up_speed = value / ratio
1511
1512         # Initialize cores
1513         self.sut.stop_all()
1514         time.sleep(0.5)
1515
1516         # Flush any packets in the NIC RX buffers, otherwise the stats will be
1517         # wrong.
1518         self.sut.start(self.all_rx_cores)
1519         time.sleep(0.5)
1520         self.sut.stop(self.all_rx_cores)
1521         time.sleep(0.5)
1522         self.sut.reset_stats()
1523
1524         self.sut.set_pkt_size(self.inet_cores, inet_pkt_size)
1525         self.sut.set_pkt_size(self.cpe_cores, cpe_pkt_size)
1526
1527         self.sut.reset_values(self.cpe_cores)
1528         self.sut.reset_values(self.inet_cores)
1529
1530         # Set correct IP and UDP lengths in packet headers
1531         # CPE
1532         # IP length (byte 24): 26 for MAC(12), EthType(2), QinQ(8), CRC(4)
1533         self.sut.set_value(self.cpe_cores, 24, cpe_pkt_size - 26, 2)
1534         # UDP length (byte 46): 46 for MAC(12), EthType(2), QinQ(8), IP(20), CRC(4)
1535         self.sut.set_value(self.cpe_cores, 46, cpe_pkt_size - 46, 2)
1536
1537         # INET
1538         # IP length (byte 20): 22 for MAC(12), EthType(2), MPLS(4), CRC(4)
1539         self.sut.set_value(self.inet_cores, 20, inet_pkt_size - 22, 2)
1540         # IP length (byte 48): 50 for MAC(12), EthType(2), MPLS(4), IP(20), GRE(8), CRC(4)
1541         self.sut.set_value(self.inet_cores, 48, inet_pkt_size - 50, 2)
1542         # UDP length (byte 70): 70 for MAC(12), EthType(2), MPLS(4), IP(20), GRE(8), IP(20), CRC(4)
1543         self.sut.set_value(self.inet_cores, 70, inet_pkt_size - 70, 2)
1544
1545         # Sending ARP to initialize tables - need a few seconds of generation
1546         # to make sure all CPEs are initialized
1547         LOG.info("Initializing SUT: sending ARP packets")
1548         self.sut.set_speed(self.arp_cores, 1, self.arp_task_cores)
1549         self.sut.set_speed(self.inet_cores, curr_up_speed)
1550         self.sut.set_speed(self.cpe_cores, curr_down_speed)
1551         self.sut.start(self.arp_cores)
1552         time.sleep(4)
1553
1554         # Ramp up the transmission speed. First go to the common speed, then
1555         # increase steps for the faster one.
1556         self.sut.start(self.cpe_cores + self.inet_cores + self.latency_cores)
1557
1558         LOG.info("Ramping up speed to %s up, %s down", max_up_speed, max_down_speed)
1559
1560         while (curr_up_speed < max_up_speed) or (curr_down_speed < max_down_speed):
1561             # The min(..., ...) takes care of 1) floating point rounding errors
1562             # that could make curr_*_speed to be slightly greater than
1563             # max_*_speed and 2) max_*_speed not being an exact multiple of
1564             # self._step_delta.
1565             if curr_up_speed < max_up_speed:
1566                 curr_up_speed = min(curr_up_speed + self.step_delta, max_up_speed)
1567             if curr_down_speed < max_down_speed:
1568                 curr_down_speed = min(curr_down_speed + self.step_delta, max_down_speed)
1569
1570             self.sut.set_speed(self.inet_cores, curr_up_speed)
1571             self.sut.set_speed(self.cpe_cores, curr_down_speed)
1572             time.sleep(self.step_time)
1573
1574         LOG.info("Target speeds reached. Starting real test.")
1575
1576         yield
1577
1578         self.sut.stop(self.arp_cores + self.cpe_cores + self.inet_cores)
1579         LOG.info("Test ended. Flushing NIC buffers")
1580         self.sut.start(self.all_rx_cores)
1581         time.sleep(3)
1582         self.sut.stop(self.all_rx_cores)
1583
1584     def run_test(self, pkt_size, duration, value, tolerated_loss=0.0,
1585                  line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)):
1586         data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size,
1587                                      value, tolerated_loss, line_speed)
1588
1589         with data_helper, self.traffic_context(pkt_size,
1590                                                self.pct_10gbps(value, line_speed)):
1591             with data_helper.measure_tot_stats():
1592                 time.sleep(duration)
1593                 # Getting statistics to calculate PPS at right speed....
1594                 data_helper.capture_tsc_hz()
1595                 data_helper.latency = self.get_latency()
1596
1597         return data_helper.result_tuple, data_helper.samples
1598
1599
1600 class ProxVpeProfileHelper(ProxProfileHelper):
1601
1602     __prox_profile_type__ = "vPE gen"
1603
1604     def __init__(self, resource_helper):
1605         super(ProxVpeProfileHelper, self).__init__(resource_helper)
1606         self._cores_tuple = None
1607         self._ports_tuple = None
1608
1609     @property
1610     def vpe_cores(self):
1611         if not self._cores_tuple:
1612             self._cores_tuple = self.get_cores_gen_vpe()
1613         return self._cores_tuple
1614
1615     @property
1616     def cpe_cores(self):
1617         return self.vpe_cores[0]
1618
1619     @property
1620     def inet_cores(self):
1621         return self.vpe_cores[1]
1622
1623     @property
1624     def all_rx_cores(self):
1625         return self.latency_cores
1626
1627     @property
1628     def vpe_ports(self):
1629         if not self._ports_tuple:
1630             self._ports_tuple = self.get_ports_gen_vpe()
1631         return self._ports_tuple
1632
1633     @property
1634     def cpe_ports(self):
1635         return self.vpe_ports[0]
1636
1637     @property
1638     def inet_ports(self):
1639         return self.vpe_ports[1]
1640
1641     def get_cores_gen_vpe(self):
1642         cpe_cores = []
1643         inet_cores = []
1644         for section_name, section in self.resource_helper.setup_helper.prox_config_data:
1645             if not section_name.startswith("core"):
1646                 continue
1647
1648             if all(key != "mode" or value != self.PROX_CORE_GEN_MODE for key, value in section):
1649                 continue
1650
1651             for item_key, item_value in section:
1652                 if item_key != 'name':
1653                     continue
1654
1655                 if item_value.startswith("cpe"):
1656                     core_tuple = CoreSocketTuple(section_name)
1657                     core_tag = core_tuple.core_id
1658                     cpe_cores.append(core_tag)
1659
1660                 elif item_value.startswith("inet"):
1661                     core_tuple = CoreSocketTuple(section_name)
1662                     inet_core = core_tuple.core_id
1663                     inet_cores.append(inet_core)
1664
1665         return cpe_cores, inet_cores
1666
1667     def get_ports_gen_vpe(self):
1668         cpe_ports = []
1669         inet_ports = []
1670
1671         for section_name, section in self.resource_helper.setup_helper.prox_config_data:
1672             if not section_name.startswith("port"):
1673                 continue
1674             tx_port_iter = re.finditer(r'\d+', section_name)
1675             tx_port_no = int(next(tx_port_iter).group(0))
1676
1677             for item_key, item_value in section:
1678                 if item_key != 'name':
1679                     continue
1680
1681                 if item_value.startswith("cpe"):
1682                     cpe_ports.append(tx_port_no)
1683
1684                 elif item_value.startswith("inet"):
1685                     inet_ports.append(tx_port_no)
1686
1687         return cpe_ports, inet_ports
1688
1689     @contextmanager
1690     def traffic_context(self, pkt_size, value):
1691         # Calculate the target upload and download speed. The upload and
1692         # download packets have different packet sizes, so in order to get
1693         # equal bandwidth usage, the ratio of the speeds has to match the ratio
1694         # of the packet sizes.
1695         cpe_pkt_size = pkt_size
1696         inet_pkt_size = pkt_size - 4
1697         ratio = 1.0 * (cpe_pkt_size + 20) / (inet_pkt_size + 20)
1698
1699         curr_up_speed = curr_down_speed = 0
1700         max_up_speed = max_down_speed = value
1701         if ratio < 1:
1702             max_down_speed = value * ratio
1703         else:
1704             max_up_speed = value / ratio
1705
1706         # Adjust speed when multiple cores per port are used to generate traffic
1707         if len(self.cpe_ports) != len(self.cpe_cores):
1708             max_down_speed *= 1.0 * len(self.cpe_ports) / len(self.cpe_cores)
1709         if len(self.inet_ports) != len(self.inet_cores):
1710             max_up_speed *= 1.0 * len(self.inet_ports) / len(self.inet_cores)
1711
1712         # Initialize cores
1713         self.sut.stop_all()
1714         time.sleep(2)
1715
1716         # Flush any packets in the NIC RX buffers, otherwise the stats will be
1717         # wrong.
1718         self.sut.start(self.all_rx_cores)
1719         time.sleep(2)
1720         self.sut.stop(self.all_rx_cores)
1721         time.sleep(2)
1722         self.sut.reset_stats()
1723
1724         self.sut.set_pkt_size(self.inet_cores, inet_pkt_size)
1725         self.sut.set_pkt_size(self.cpe_cores, cpe_pkt_size)
1726
1727         self.sut.reset_values(self.cpe_cores)
1728         self.sut.reset_values(self.inet_cores)
1729
1730         # Set correct IP and UDP lengths in packet headers
1731         # CPE: IP length (byte 24): 26 for MAC(12), EthType(2), QinQ(8), CRC(4)
1732         self.sut.set_value(self.cpe_cores, 24, cpe_pkt_size - 26, 2)
1733         # UDP length (byte 46): 46 for MAC(12), EthType(2), QinQ(8), IP(20), CRC(4)
1734         self.sut.set_value(self.cpe_cores, 46, cpe_pkt_size - 46, 2)
1735
1736         # INET: IP length (byte 20): 22 for MAC(12), EthType(2), MPLS(4), CRC(4)
1737         self.sut.set_value(self.inet_cores, 20, inet_pkt_size - 22, 2)
1738         # UDP length (byte 42): 42 for MAC(12), EthType(2), MPLS(4), IP(20), CRC(4)
1739         self.sut.set_value(self.inet_cores, 42, inet_pkt_size - 42, 2)
1740
1741         self.sut.set_speed(self.inet_cores, curr_up_speed)
1742         self.sut.set_speed(self.cpe_cores, curr_down_speed)
1743
1744         # Ramp up the transmission speed. First go to the common speed, then
1745         # increase steps for the faster one.
1746         self.sut.start(self.cpe_cores + self.inet_cores + self.all_rx_cores)
1747
1748         LOG.info("Ramping up speed to %s up, %s down", max_up_speed, max_down_speed)
1749
1750         while (curr_up_speed < max_up_speed) or (curr_down_speed < max_down_speed):
1751             # The min(..., ...) takes care of 1) floating point rounding errors
1752             # that could make curr_*_speed to be slightly greater than
1753             # max_*_speed and 2) max_*_speed not being an exact multiple of
1754             # self._step_delta.
1755             if curr_up_speed < max_up_speed:
1756                 curr_up_speed = min(curr_up_speed + self.step_delta, max_up_speed)
1757             if curr_down_speed < max_down_speed:
1758                 curr_down_speed = min(curr_down_speed + self.step_delta, max_down_speed)
1759
1760             self.sut.set_speed(self.inet_cores, curr_up_speed)
1761             self.sut.set_speed(self.cpe_cores, curr_down_speed)
1762             time.sleep(self.step_time)
1763
1764         LOG.info("Target speeds reached. Starting real test.")
1765
1766         yield
1767
1768         self.sut.stop(self.cpe_cores + self.inet_cores)
1769         LOG.info("Test ended. Flushing NIC buffers")
1770         self.sut.start(self.all_rx_cores)
1771         time.sleep(3)
1772         self.sut.stop(self.all_rx_cores)
1773
1774     def run_test(self, pkt_size, duration, value, tolerated_loss=0.0,
1775                  line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)):
1776         data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size,
1777                                      value, tolerated_loss, line_speed)
1778
1779         with data_helper, self.traffic_context(pkt_size,
1780                                                self.pct_10gbps(value, line_speed)):
1781             with data_helper.measure_tot_stats():
1782                 time.sleep(duration)
1783                 # Getting statistics to calculate PPS at right speed....
1784                 data_helper.capture_tsc_hz()
1785                 data_helper.latency = self.get_latency()
1786
1787         return data_helper.result_tuple, data_helper.samples
1788
1789
1790 class ProxlwAFTRProfileHelper(ProxProfileHelper):
1791
1792     __prox_profile_type__ = "lwAFTR gen"
1793
1794     def __init__(self, resource_helper):
1795         super(ProxlwAFTRProfileHelper, self).__init__(resource_helper)
1796         self._cores_tuple = None
1797         self._ports_tuple = None
1798         self.step_delta = 5
1799         self.step_time = 0.5
1800
1801     @property
1802     def _lwaftr_cores(self):
1803         if not self._cores_tuple:
1804             self._cores_tuple = self._get_cores_gen_lwaftr()
1805         return self._cores_tuple
1806
1807     @property
1808     def tun_cores(self):
1809         return self._lwaftr_cores[0]
1810
1811     @property
1812     def inet_cores(self):
1813         return self._lwaftr_cores[1]
1814
1815     @property
1816     def _lwaftr_ports(self):
1817         if not self._ports_tuple:
1818             self._ports_tuple = self._get_ports_gen_lw_aftr()
1819         return self._ports_tuple
1820
1821     @property
1822     def tun_ports(self):
1823         return self._lwaftr_ports[0]
1824
1825     @property
1826     def inet_ports(self):
1827         return self._lwaftr_ports[1]
1828
1829     @property
1830     def all_rx_cores(self):
1831         return self.latency_cores
1832
1833     def _get_cores_gen_lwaftr(self):
1834         tun_cores = []
1835         inet_cores = []
1836         for section_name, section in self.resource_helper.setup_helper.prox_config_data:
1837             if not section_name.startswith("core"):
1838                 continue
1839
1840             if all(key != "mode" or value != self.PROX_CORE_GEN_MODE for key, value in section):
1841                 continue
1842
1843             core_tuple = CoreSocketTuple(section_name)
1844             core_tag = core_tuple.core_id
1845             for item_value in (v for k, v in section if k == 'name'):
1846                 if item_value.startswith('tun'):
1847                     tun_cores.append(core_tag)
1848                 elif item_value.startswith('inet'):
1849                     inet_cores.append(core_tag)
1850
1851         return tun_cores, inet_cores
1852
1853     def _get_ports_gen_lw_aftr(self):
1854         tun_ports = []
1855         inet_ports = []
1856
1857         re_port = re.compile(r'port (\d+)')
1858         for section_name, section in self.resource_helper.setup_helper.prox_config_data:
1859             match = re_port.search(section_name)
1860             if not match:
1861                 continue
1862
1863             tx_port_no = int(match.group(1))
1864             for item_value in (v for k, v in section if k == 'name'):
1865                 if item_value.startswith('lwB4'):
1866                     tun_ports.append(tx_port_no)
1867                 elif item_value.startswith('inet'):
1868                     inet_ports.append(tx_port_no)
1869
1870         return tun_ports, inet_ports
1871
1872     @staticmethod
1873     def _resize(len1, len2):
1874         if len1 == len2:
1875             return 1.0
1876         return 1.0 * len1 / len2
1877
1878     @contextmanager
1879     def traffic_context(self, pkt_size, value):
1880         # Tester is sending packets at the required speed already after
1881         # setup_test(). Just get the current statistics, sleep the required
1882         # amount of time and calculate packet loss.
1883         tun_pkt_size = pkt_size
1884         inet_pkt_size = pkt_size - 40
1885         ratio = 1.0 * (tun_pkt_size + 20) / (inet_pkt_size + 20)
1886
1887         curr_up_speed = curr_down_speed = 0
1888         max_up_speed = max_down_speed = value
1889
1890         max_up_speed = value / ratio
1891
1892         # Adjust speed when multiple cores per port are used to generate traffic
1893         if len(self.tun_ports) != len(self.tun_cores):
1894             max_down_speed *= self._resize(len(self.tun_ports), len(self.tun_cores))
1895         if len(self.inet_ports) != len(self.inet_cores):
1896             max_up_speed *= self._resize(len(self.inet_ports), len(self.inet_cores))
1897
1898         # Initialize cores
1899         self.sut.stop_all()
1900         time.sleep(0.5)
1901
1902         # Flush any packets in the NIC RX buffers, otherwise the stats will be
1903         # wrong.
1904         self.sut.start(self.all_rx_cores)
1905         time.sleep(0.5)
1906         self.sut.stop(self.all_rx_cores)
1907         time.sleep(0.5)
1908         self.sut.reset_stats()
1909
1910         self.sut.set_pkt_size(self.inet_cores, inet_pkt_size)
1911         self.sut.set_pkt_size(self.tun_cores, tun_pkt_size)
1912
1913         self.sut.reset_values(self.tun_cores)
1914         self.sut.reset_values(self.inet_cores)
1915
1916         # Set correct IP and UDP lengths in packet headers
1917         # tun
1918         # IPv6 length (byte 18): 58 for MAC(12), EthType(2), IPv6(40) , CRC(4)
1919         self.sut.set_value(self.tun_cores, 18, tun_pkt_size - 58, 2)
1920         # IP length (byte 56): 58 for MAC(12), EthType(2), CRC(4)
1921         self.sut.set_value(self.tun_cores, 56, tun_pkt_size - 58, 2)
1922         # UDP length (byte 78): 78 for MAC(12), EthType(2), IP(20), UDP(8), CRC(4)
1923         self.sut.set_value(self.tun_cores, 78, tun_pkt_size - 78, 2)
1924
1925         # INET
1926         # IP length (byte 20): 22 for MAC(12), EthType(2), CRC(4)
1927         self.sut.set_value(self.inet_cores, 16, inet_pkt_size - 18, 2)
1928         # UDP length (byte 42): 42 for MAC(12), EthType(2), IP(20), UPD(8), CRC(4)
1929         self.sut.set_value(self.inet_cores, 38, inet_pkt_size - 38, 2)
1930
1931         LOG.info("Initializing SUT: sending lwAFTR packets")
1932         self.sut.set_speed(self.inet_cores, curr_up_speed)
1933         self.sut.set_speed(self.tun_cores, curr_down_speed)
1934         time.sleep(4)
1935
1936         # Ramp up the transmission speed. First go to the common speed, then
1937         # increase steps for the faster one.
1938         self.sut.start(self.tun_cores + self.inet_cores + self.latency_cores)
1939
1940         LOG.info("Ramping up speed to %s up, %s down", max_up_speed, max_down_speed)
1941
1942         while (curr_up_speed < max_up_speed) or (curr_down_speed < max_down_speed):
1943             # The min(..., ...) takes care of 1) floating point rounding errors
1944             # that could make curr_*_speed to be slightly greater than
1945             # max_*_speed and 2) max_*_speed not being an exact multiple of
1946             # self._step_delta.
1947             if curr_up_speed < max_up_speed:
1948                 curr_up_speed = min(curr_up_speed + self.step_delta, max_up_speed)
1949             if curr_down_speed < max_down_speed:
1950                 curr_down_speed = min(curr_down_speed + self.step_delta, max_down_speed)
1951
1952             self.sut.set_speed(self.inet_cores, curr_up_speed)
1953             self.sut.set_speed(self.tun_cores, curr_down_speed)
1954             time.sleep(self.step_time)
1955
1956         LOG.info("Target speeds reached. Starting real test.")
1957
1958         yield
1959
1960         self.sut.stop(self.tun_cores + self.inet_cores)
1961         LOG.info("Test ended. Flushing NIC buffers")
1962         self.sut.start(self.all_rx_cores)
1963         time.sleep(3)
1964         self.sut.stop(self.all_rx_cores)
1965
1966     def run_test(self, pkt_size, duration, value, tolerated_loss=0.0,
1967                  line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)):
1968         data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size,
1969                                      value, tolerated_loss, line_speed)
1970
1971         with data_helper, self.traffic_context(pkt_size,
1972                                                self.pct_10gbps(value, line_speed)):
1973             with data_helper.measure_tot_stats():
1974                 time.sleep(duration)
1975                 # Getting statistics to calculate PPS at right speed....
1976                 data_helper.capture_tsc_hz()
1977                 data_helper.latency = self.get_latency()
1978
1979         return data_helper.result_tuple, data_helper.samples
1980
1981
1982 class ProxIrqProfileHelper(ProxProfileHelper):
1983
1984     __prox_profile_type__ = "IRQ Query"
1985
1986     def __init__(self, resource_helper):
1987         super(ProxIrqProfileHelper, self).__init__(resource_helper)
1988         self._cores_tuple = None
1989         self._ports_tuple = None
1990         self.step_delta = 5
1991         self.step_time = 0.5