diff options
Diffstat (limited to 'yardstick/network_services/vnf_generic/vnf/prox_helpers.py')
-rw-r--r-- | yardstick/network_services/vnf_generic/vnf/prox_helpers.py | 442 |
1 files changed, 393 insertions, 49 deletions
diff --git a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py index 31ed30140..3507315f2 100644 --- a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py +++ b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017 Intel Corporation +# Copyright (c) 2018-2019 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import re import select import socket import time + from collections import OrderedDict, namedtuple from contextlib import contextmanager from itertools import repeat, chain @@ -44,6 +45,8 @@ SECTION_CONTENTS = 1 LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) +LOG_RESULT = logging.getLogger('yardstick') +LOG_RESULT.setLevel(logging.DEBUG) BITS_PER_BYTE = 8 RETRY_SECONDS = 60 @@ -123,7 +126,8 @@ class TotStatsTuple(namedtuple('TotStats', 'rx,tx,tsc,hz')): class ProxTestDataTuple(namedtuple('ProxTestDataTuple', 'tolerated,tsc_hz,delta_rx,' 'delta_tx,delta_tsc,' - 'latency,rx_total,tx_total,pps')): + 'latency,rx_total,tx_total,' + 'requested_pps')): @property def pkt_loss(self): try: @@ -132,11 +136,16 @@ class ProxTestDataTuple(namedtuple('ProxTestDataTuple', 'tolerated,tsc_hz,delta_ return 100.0 @property - def mpps(self): + def tx_mpps(self): # calculate the effective throughput in Mpps return float(self.delta_tx) * self.tsc_hz / self.delta_tsc / 1e6 @property + def rx_mpps(self): + # calculate the effective throughput in Mpps + return float(self.delta_rx) * self.tsc_hz / self.delta_tsc / 1e6 + + @property def can_be_lost(self): return int(self.tx_total * self.tolerated / 1e2) @@ -162,11 +171,12 @@ class ProxTestDataTuple(namedtuple('ProxTestDataTuple', 'tolerated,tsc_hz,delta_ ] samples = { - "Throughput": self.mpps, + "Throughput": self.rx_mpps, + "RxThroughput": self.rx_mpps, "DropPackets": pkt_loss, "CurrentDropPackets": pkt_loss, - "TxThroughput": self.pps / 1e6, - "RxThroughput": self.mpps, + "RequestedTxThroughput": self.requested_pps / 1e6, + "TxThroughput": self.tx_mpps, "PktSize": pkt_size, } if port_samples: @@ -177,11 +187,12 @@ class ProxTestDataTuple(namedtuple('ProxTestDataTuple', 'tolerated,tsc_hz,delta_ def log_data(self, logger=None): if logger is None: - logger = LOG + logger = LOG_RESULT template = "RX: %d; TX: %d; dropped: %d (tolerated: %d)" - logger.debug(template, self.rx_total, self.tx_total, self.drop_total, self.can_be_lost) - logger.debug("Mpps configured: %f; Mpps effective %f", self.pps / 1e6, self.mpps) + logger.info(template, self.rx_total, self.tx_total, self.drop_total, self.can_be_lost) + logger.info("Mpps configured: %f; Mpps generated %f; Mpps received %f", + self.requested_pps / 1e6, self.tx_mpps, self.rx_mpps) class PacketDump(object): @@ -288,7 +299,7 @@ class ProxSocketHelper(object): if mode != 'pktdump': # Regular 1-line message. Stop reading from the socket. LOG.debug("Regular response read") - return ret_str + return ret_str, True LOG.debug("Packet dump header read: [%s]", ret_str) @@ -309,13 +320,34 @@ class ProxSocketHelper(object): # Return boolean instead of string to signal # successful reception of the packet dump. LOG.debug("Packet dump stored, returning") - return True + return True, False index = data_end + 1 - return ret_str + return ret_str, False + + def get_string(self, pkt_dump_only=False, timeout=0.01): + + def is_ready_string(): + # recv() is blocking, so avoid calling it when no data is waiting. + ready = select.select([self._sock], [], [], timeout) + return bool(ready[0]) + + status = False + ret_str = "" + while status is False: + for status in iter(is_ready_string, False): + decoded_data = self._sock.recv(256).decode('utf-8') + ret_str, done = self._parse_socket_data(decoded_data, + pkt_dump_only) + if (done): + status = True + break + + LOG.debug("Received data from socket: [%s]", ret_str) + return status, ret_str - def get_data(self, pkt_dump_only=False, timeout=1): + def get_data(self, pkt_dump_only=False, timeout=10.0): """ read data from the socket """ # This method behaves slightly differently depending on whether it is @@ -352,7 +384,9 @@ class ProxSocketHelper(object): ret_str = "" for status in iter(is_ready, False): decoded_data = self._sock.recv(256).decode('utf-8') - ret_str = self._parse_socket_data(decoded_data, pkt_dump_only) + ret_str, done = self._parse_socket_data(decoded_data, pkt_dump_only) + if (done): + break LOG.debug("Received data from socket: [%s]", ret_str) return ret_str if status else '' @@ -382,13 +416,17 @@ class ProxSocketHelper(object): """ stop all cores on the remote instance """ LOG.debug("Stop all") self.put_command("stop all\n") - time.sleep(3) def stop(self, cores, task=''): """ stop specific cores on the remote instance """ - LOG.debug("Stopping cores %s", cores) - self.put_command("stop {} {}\n".format(join_non_strings(',', cores), task)) - time.sleep(3) + + tmpcores = [] + for core in cores: + if core not in tmpcores: + tmpcores.append(core) + + LOG.debug("Stopping cores %s", tmpcores) + self.put_command("stop {} {}\n".format(join_non_strings(',', tmpcores), task)) def start_all(self): """ start all cores on the remote instance """ @@ -397,15 +435,19 @@ class ProxSocketHelper(object): def start(self, cores): """ start specific cores on the remote instance """ - LOG.debug("Starting cores %s", cores) - self.put_command("start {}\n".format(join_non_strings(',', cores))) - time.sleep(3) + + tmpcores = [] + for core in cores: + if core not in tmpcores: + tmpcores.append(core) + + LOG.debug("Starting cores %s", tmpcores) + self.put_command("start {}\n".format(join_non_strings(',', tmpcores))) def reset_stats(self): """ reset the statistics on the remote instance """ LOG.debug("Reset stats") self.put_command("reset stats\n") - time.sleep(1) def _run_template_over_cores(self, template, cores, *args): for core in cores: @@ -416,7 +458,6 @@ class ProxSocketHelper(object): LOG.debug("Set packet size for core(s) %s to %d", cores, pkt_size) pkt_size -= 4 self._run_template_over_cores("pkt_size {} 0 {}\n", cores, pkt_size) - time.sleep(1) def set_value(self, cores, offset, value, length): """ set value on the remote instance """ @@ -520,6 +561,174 @@ class ProxSocketHelper(object): tsc = int(ret[3]) return rx, tx, drop, tsc + def irq_core_stats(self, cores_tasks): + """ get IRQ stats per core""" + + stat = {} + core = 0 + task = 0 + for core, task in cores_tasks: + self.put_command("stats task.core({}).task({}).max_irq,task.core({}).task({}).irq(0)," + "task.core({}).task({}).irq(1),task.core({}).task({}).irq(2)," + "task.core({}).task({}).irq(3),task.core({}).task({}).irq(4)," + "task.core({}).task({}).irq(5),task.core({}).task({}).irq(6)," + "task.core({}).task({}).irq(7),task.core({}).task({}).irq(8)," + "task.core({}).task({}).irq(9),task.core({}).task({}).irq(10)," + "task.core({}).task({}).irq(11),task.core({}).task({}).irq(12)" + "\n".format(core, task, core, task, core, task, core, task, + core, task, core, task, core, task, core, task, + core, task, core, task, core, task, core, task, + core, task, core, task)) + in_data_str = self.get_data().split(",") + ret = [try_int(s, 0) for s in in_data_str] + key = "core_" + str(core) + try: + stat[key] = {"cpu": core, "max_irq": ret[0], "bucket_0" : ret[1], + "bucket_1" : ret[2], "bucket_2" : ret[3], + "bucket_3" : ret[4], "bucket_4" : ret[5], + "bucket_5" : ret[6], "bucket_6" : ret[7], + "bucket_7" : ret[8], "bucket_8" : ret[9], + "bucket_9" : ret[10], "bucket_10" : ret[11], + "bucket_11" : ret[12], "bucket_12" : ret[13], + "overflow": ret[10] + ret[11] + ret[12] + ret[13]} + except (KeyError, IndexError): + LOG.error("Corrupted PACKET %s", in_data_str) + + return stat + + def multi_port_stats(self, ports): + """get counter values from all ports at once""" + + ports_str = ",".join(map(str, ports)) + ports_all_data = [] + tot_result = [0] * len(ports) + + port_index = 0 + while (len(ports) is not len(ports_all_data)): + self.put_command("multi port stats {}\n".format(ports_str)) + status, ports_all_data_str = self.get_string() + + if not status: + return False, [] + + ports_all_data = ports_all_data_str.split(";") + + if len(ports) is len(ports_all_data): + for port_data_str in ports_all_data: + + tmpdata = [] + try: + tmpdata = [try_int(s, 0) for s in port_data_str.split(",")] + except (IndexError, TypeError): + LOG.error("Unpacking data error %s", port_data_str) + return False, [] + + if (len(tmpdata) < 6) or tmpdata[0] not in ports: + LOG.error("Corrupted PACKET %s - retrying", port_data_str) + return False, [] + else: + tot_result[port_index] = tmpdata + port_index = port_index + 1 + else: + LOG.error("Empty / too much data - retry -%s-", ports_all_data) + return False, [] + + LOG.debug("Multi port packet ..OK.. %s", tot_result) + return True, tot_result + + @staticmethod + def multi_port_stats_tuple(stats, ports): + """ + Create a statistics tuple from port stats. + + Returns a dict with contains the port stats indexed by port name + + :param stats: (List) - List of List of port stats in pps + :param ports (Iterator) - to List of Ports + + :return: (Dict) of port stats indexed by port_name + """ + + samples = {} + port_names = {} + try: + port_names = {port_num: port_name for port_name, port_num in ports} + except (TypeError, IndexError, KeyError): + LOG.critical("Ports are not initialized or number of port is ZERO ... CRITICAL ERROR") + return {} + + try: + for stat in stats: + port_num = stat[0] + samples[port_names[port_num]] = { + "in_packets": stat[1], + "out_packets": stat[2]} + except (TypeError, IndexError, KeyError): + LOG.error("Ports data and samples data is incompatable ....") + return {} + + return samples + + @staticmethod + def multi_port_stats_diff(prev_stats, new_stats, hz): + """ + Create a statistics tuple from difference between prev port stats + and current port stats. And store results in pps. + + :param prev_stats: (List) - Previous List of port statistics + :param new_stats: (List) - Current List of port statistics + :param hz (float) - speed of system in Hz + + :return: sample (List) - Difference of prev_port_stats and + new_port_stats in pps + """ + + RX_TOTAL_INDEX = 1 + TX_TOTAL_INDEX = 2 + TSC_INDEX = 5 + + stats = [] + + if len(prev_stats) is not len(new_stats): + for port_index, stat in enumerate(new_stats): + stats.append([port_index, float(0), float(0), 0, 0, 0]) + return stats + + try: + for port_index, stat in enumerate(new_stats): + if stat[RX_TOTAL_INDEX] > prev_stats[port_index][RX_TOTAL_INDEX]: + rx_total = stat[RX_TOTAL_INDEX] - \ + prev_stats[port_index][RX_TOTAL_INDEX] + else: + rx_total = stat[RX_TOTAL_INDEX] + + if stat[TX_TOTAL_INDEX] > prev_stats[port_index][TX_TOTAL_INDEX]: + tx_total = stat[TX_TOTAL_INDEX] - prev_stats[port_index][TX_TOTAL_INDEX] + else: + tx_total = stat[TX_TOTAL_INDEX] + + if stat[TSC_INDEX] > prev_stats[port_index][TSC_INDEX]: + tsc = stat[TSC_INDEX] - prev_stats[port_index][TSC_INDEX] + else: + tsc = stat[TSC_INDEX] + + if tsc is 0: + rx_total = tx_total = float(0) + else: + if hz is 0: + LOG.error("HZ is ZERO ..") + rx_total = tx_total = float(0) + else: + rx_total = float(rx_total * hz / tsc) + tx_total = float(tx_total * hz / tsc) + + stats.append([port_index, rx_total, tx_total, 0, 0, tsc]) + except (TypeError, IndexError, KeyError): + stats = [] + LOG.info("Current Port Stats incompatable to previous Port stats .. Discarded") + + return stats + def port_stats(self, ports): """get counter values from a specific port""" tot_result = [0] * 12 @@ -580,7 +789,6 @@ class ProxSocketHelper(object): self.put_command("quit_force\n") time.sleep(3) - _LOCAL_OBJECT = object() @@ -662,6 +870,30 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper): file_str[1] = self.additional_files[base_name] return '"'.join(file_str) + def _make_core_list(self, inputStr): + + my_input = inputStr.split("core ", 1)[1] + ok_list = set() + + substrs = [x.strip() for x in my_input.split(',')] + for i in substrs: + try: + ok_list.add(int(i)) + + except ValueError: + try: + substr = [int(k.strip()) for k in i.split('-')] + if len(substr) > 1: + startstr = substr[0] + endstr = substr[len(substr) - 1] + for z in range(startstr, endstr + 1): + ok_list.add(z) + except ValueError: + LOG.error("Error in cores list ... resuming ") + return ok_list + + return ok_list + def generate_prox_config_file(self, config_path): sections = [] prox_config = ConfigParser(config_path, sections) @@ -681,6 +913,18 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper): if section_data[0] == "mac": section_data[1] = "hardware" + # adjust for range of cores + new_sections = [] + for section_name, section in sections: + if section_name.startswith('core') and section_name.find('$') == -1: + core_list = self._make_core_list(section_name) + for core in core_list: + new_sections.append(["core " + str(core), section]) + else: + new_sections.append([section_name, section]) + + sections = new_sections + # search for dst mac for _, section in sections: for section_data in section: @@ -887,6 +1131,8 @@ class ProxResourceHelper(ClientResourceHelper): self.step_delta = 1 self.step_time = 0.5 self._test_type = None + self.prev_multi_port = [] + self.prev_hz = 0 @property def sut(self): @@ -915,7 +1161,7 @@ class ProxResourceHelper(ClientResourceHelper): def _run_traffic_once(self, traffic_profile): traffic_profile.execute_traffic(self) - if traffic_profile.done: + if traffic_profile.done.is_set(): self._queue.put({'done': True}) LOG.debug("tg_prox done") self._terminated.value = 1 @@ -925,11 +1171,40 @@ class ProxResourceHelper(ClientResourceHelper): def collect_collectd_kpi(self): return self._collect_resource_kpi() + def collect_live_stats(self): + ports = [] + for _, port_num in self.vnfd_helper.ports_iter(): + ports.append(port_num) + + ok, curr_port_stats = self.sut.multi_port_stats(ports) + if not ok: + return False, {} + + hz = self.sut.hz() + if hz is 0: + hz = self.prev_hz + else: + self.prev_hz = hz + + new_all_port_stats = \ + self.sut.multi_port_stats_diff(self.prev_multi_port, curr_port_stats, hz) + + self.prev_multi_port = curr_port_stats + + live_stats = self.sut.multi_port_stats_tuple(new_all_port_stats, + self.vnfd_helper.ports_iter()) + return True, live_stats + def collect_kpi(self): result = super(ProxResourceHelper, self).collect_kpi() # add in collectd kpis manually if result: result['collect_stats'] = self._collect_resource_kpi() + + ok, live_stats = self.collect_live_stats() + if ok: + result.update({'live_stats': live_stats}) + return result def terminate(self): @@ -1000,39 +1275,71 @@ class ProxDataHelper(object): @property def totals_and_pps(self): if self._totals_and_pps is None: - rx_total, tx_total = self.sut.port_stats(range(self.port_count))[6:8] - pps = self.value / 100.0 * self.line_rate_to_pps() - self._totals_and_pps = rx_total, tx_total, pps + rx_total = tx_total = 0 + ok = False + timeout = time.time() + constants.RETRY_TIMEOUT + while not ok: + ok, all_ports = self.sut.multi_port_stats([ + self.vnfd_helper.port_num(port_name) + for port_name in self.vnfd_helper.port_pairs.all_ports]) + if time.time() > timeout: + break + if ok: + for port in all_ports: + rx_total = rx_total + port[1] + tx_total = tx_total + port[2] + requested_pps = self.value / 100.0 * self.line_rate_to_pps() + self._totals_and_pps = rx_total, tx_total, requested_pps return self._totals_and_pps @property def rx_total(self): - return self.totals_and_pps[0] + try: + ret_val = self.totals_and_pps[0] + except (AttributeError, ValueError, TypeError, LookupError): + ret_val = 0 + return ret_val @property def tx_total(self): - return self.totals_and_pps[1] + try: + ret_val = self.totals_and_pps[1] + except (AttributeError, ValueError, TypeError, LookupError): + ret_val = 0 + return ret_val @property - def pps(self): - return self.totals_and_pps[2] + def requested_pps(self): + try: + ret_val = self.totals_and_pps[2] + except (AttributeError, ValueError, TypeError, LookupError): + ret_val = 0 + return ret_val @property def samples(self): samples = {} + ports = [] + port_names = {} for port_name, port_num in self.vnfd_helper.ports_iter(): - try: - port_rx_total, port_tx_total = self.sut.port_stats([port_num])[6:8] - samples[port_name] = { - "in_packets": port_rx_total, - "out_packets": port_tx_total, - } - except (KeyError, TypeError, NameError, MemoryError, ValueError, - SystemError, BufferError): - samples[port_name] = { - "in_packets": 0, - "out_packets": 0, - } + ports.append(port_num) + port_names[port_num] = port_name + + ok = False + timeout = time.time() + constants.RETRY_TIMEOUT + while not ok: + ok, results = self.sut.multi_port_stats(ports) + if time.time() > timeout: + break + if ok: + for result in results: + port_num = result[0] + try: + samples[port_names[port_num]] = { + "in_packets": result[1], + "out_packets": result[2]} + except (IndexError, KeyError): + pass return samples def __enter__(self): @@ -1055,7 +1362,7 @@ class ProxDataHelper(object): self.latency, self.rx_total, self.tx_total, - self.pps, + self.requested_pps, ) self.result_tuple.log_data() @@ -1134,6 +1441,7 @@ class ProxProfileHelper(object): self.sut.set_pkt_size(self.test_cores, pkt_size) self.sut.set_speed(self.test_cores, value) self.sut.start_all() + time.sleep(1) yield finally: self.sut.stop_all() @@ -1153,12 +1461,32 @@ class ProxProfileHelper(object): return cores + def pct_10gbps(self, percent, line_speed): + """Get rate in percent of 10 Gbps. + + Returns the rate in percent of 10 Gbps. + For instance 100.0 = 10 Gbps; 400.0 = 40 Gbps. + + This helper method isrequired when setting interface_speed option in + the testcase because NSB/PROX considers 10Gbps as 100% of line rate, + this means that the line rate must be expressed as a percentage of + 10Gbps. + + :param percent: (float) Percent of line rate (100.0 = line rate). + :param line_speed: (int) line rate speed, in bits per second. + + :return: (float) Represents the rate in percent of 10Gbps. + """ + return (percent * line_speed / ( + constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)) + def run_test(self, pkt_size, duration, value, tolerated_loss=0.0, line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)): data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss, line_speed) - with data_helper, self.traffic_context(pkt_size, value): + with data_helper, self.traffic_context(pkt_size, + self.pct_10gbps(value, line_speed)): with data_helper.measure_tot_stats(): time.sleep(duration) # Getting statistics to calculate PPS at right speed.... @@ -1246,6 +1574,7 @@ class ProxMplsProfileHelper(ProxProfileHelper): ratio = 1.0 * (pkt_size - 4 + 20) / (pkt_size + 20) self.sut.set_speed(self.plain_cores, value * ratio) self.sut.start_all() + time.sleep(1) yield finally: self.sut.stop_all() @@ -1417,7 +1746,8 @@ class ProxBngProfileHelper(ProxProfileHelper): data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss, line_speed) - with data_helper, self.traffic_context(pkt_size, value): + with data_helper, self.traffic_context(pkt_size, + self.pct_10gbps(value, line_speed)): with data_helper.measure_tot_stats(): time.sleep(duration) # Getting statistics to calculate PPS at right speed.... @@ -1606,7 +1936,8 @@ class ProxVpeProfileHelper(ProxProfileHelper): data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss, line_speed) - with data_helper, self.traffic_context(pkt_size, value): + with data_helper, self.traffic_context(pkt_size, + self.pct_10gbps(value, line_speed)): with data_helper.measure_tot_stats(): time.sleep(duration) # Getting statistics to calculate PPS at right speed.... @@ -1797,7 +2128,8 @@ class ProxlwAFTRProfileHelper(ProxProfileHelper): data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss, line_speed) - with data_helper, self.traffic_context(pkt_size, value): + with data_helper, self.traffic_context(pkt_size, + self.pct_10gbps(value, line_speed)): with data_helper.measure_tot_stats(): time.sleep(duration) # Getting statistics to calculate PPS at right speed.... @@ -1805,3 +2137,15 @@ class ProxlwAFTRProfileHelper(ProxProfileHelper): data_helper.latency = self.get_latency() return data_helper.result_tuple, data_helper.samples + + +class ProxIrqProfileHelper(ProxProfileHelper): + + __prox_profile_type__ = "IRQ Query" + + def __init__(self, resource_helper): + super(ProxIrqProfileHelper, self).__init__(resource_helper) + self._cores_tuple = None + self._ports_tuple = None + self.step_delta = 5 + self.step_time = 0.5 |