Merge "Use context name instead of key_uuid"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / prox_helpers.py
index ac5abfb..29f9c7b 100644 (file)
@@ -11,7 +11,6 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-from __future__ import absolute_import
 
 import array
 import io
@@ -31,7 +30,6 @@ import six
 from six.moves import cStringIO
 from six.moves import zip, StringIO
 
-from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file
 from yardstick.common import utils
 from yardstick.common.utils import SocketTopology, join_non_strings, try_int
 from yardstick.network_services.helpers.iniparser import ConfigParser
@@ -363,9 +361,9 @@ class ProxSocketHelper(object):
         """ send data to the remote instance """
         LOG.debug("Sending data to socket: [%s]", to_send.rstrip('\n'))
         try:
-            # TODO: sendall will block, we need a timeout
+            # NOTE: sendall will block, we need a timeout
             self._sock.sendall(to_send.encode('utf-8'))
-        except:
+        except:  # pylint: disable=bare-except
             pass
 
     def get_packet_dump(self):
@@ -509,11 +507,6 @@ class ProxSocketHelper(object):
     def hz(self):
         return self.get_all_tot_stats()[3]
 
-    # Deprecated
-    # TODO: remove
-    def rx_stats(self, cores, task=0):
-        return self.core_stats(cores, task)
-
     def core_stats(self, cores, task=0):
         """Get the receive statistics from the remote system"""
         rx = tx = drop = tsc = 0
@@ -544,7 +537,7 @@ class ProxSocketHelper(object):
         finally:
             container['end_tot'] = end = self.get_all_tot_stats()
 
-        container['delta'] = TotStatsTuple(end - start for start, end in zip(start, end))
+        container['delta'] = TotStatsTuple(e - s for s, e in zip(start, end))
 
     def tot_stats(self):
         """Get the total statistics from the remote system"""
@@ -642,13 +635,6 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
             raise KeyError(template.format(section_key, section_name))
         return result
 
-    def _build_pipeline_kwargs(self):
-        tool_path = self.ssh_helper.provision_tool(tool_file=self.APP_NAME)
-        self.pipeline_kwargs = {
-            'tool_path': tool_path,
-            'tool_dir': os.path.dirname(tool_path),
-        }
-
     def copy_to_target(self, config_file_path, prox_file):
         remote_path = os.path.join("/tmp", prox_file)
         self.ssh_helper.put(config_file_path, remote_path)
@@ -690,14 +676,13 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
                 if port_section_name != section_name:
                     continue
 
-                for index, section_data in enumerate(section):
+                for section_data in section:
                     if section_data[0] == "mac":
                         section_data[1] = "hardware"
 
         # search for dst mac
         for _, section in sections:
-            # for index, (item_key, item_val) in enumerate(section):
-            for index, section_data in enumerate(section):
+            for section_data in section:
                 item_key, item_val = section_data
                 if item_val.startswith("@@dst_mac"):
                     tx_port_iter = re.finditer(r'\d+', item_val)
@@ -718,14 +703,14 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
             return sections
 
         for section_name, section in sections:
-            for index, section_data in enumerate(section):
+            for section_data in section:
                 try:
                     if section_data[0].startswith("dofile"):
                         section_data[0] = self._insert_additional_file(section_data[0])
 
                     if section_data[1].startswith("dofile"):
                         section_data[1] = self._insert_additional_file(section_data[1])
-                except:
+                except:  # pylint: disable=bare-except
                     pass
 
         return sections
@@ -758,9 +743,9 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
         a custom method
         """
         out = []
-        for i, (section_name, section) in enumerate(prox_config):
+        for (section_name, section) in prox_config:
             out.append("[{}]".format(section_name))
-            for index, item in enumerate(section):
+            for item in section:
                 key, value = item
                 if key == "__name__":
                     continue
@@ -812,7 +797,7 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
         options = self.scenario_helper.options
         config_path = options['prox_config']
         config_file = os.path.basename(config_path)
-        config_path = find_relative_file(config_path, task_path)
+        config_path = utils.find_relative_file(config_path, task_path)
         self.additional_files = {}
 
         try:
@@ -821,7 +806,7 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
                 self.lua = self.generate_prox_lua_file()
                 if len(self.lua) > 0:
                     self.upload_prox_lua("parameters.lua", self.lua)
-        except:
+        except:  # pylint: disable=bare-except
             pass
 
         prox_files = options.get('prox_files', [])
@@ -829,7 +814,7 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
             prox_files = [prox_files]
         for key_prox_file in prox_files:
             base_prox_file = os.path.basename(key_prox_file)
-            key_prox_path = find_relative_file(key_prox_file, task_path)
+            key_prox_path = utils.find_relative_file(key_prox_file, task_path)
             remote_prox_file = self.copy_to_target(key_prox_path, base_prox_file)
             self.additional_files[base_prox_file] = remote_prox_file
 
@@ -842,17 +827,20 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper):
         self.build_config_file()
 
         options = self.scenario_helper.options
-
         prox_args = options['prox_args']
-        LOG.info("Provision and start the %s", self.APP_NAME)
-        self._build_pipeline_kwargs()
-        self.pipeline_kwargs["args"] = " ".join(
-            " ".join([k, v if v else ""]) for k, v in prox_args.items())
-        self.pipeline_kwargs["cfg_file"] = self.remote_path
+        tool_path = self.ssh_helper.join_bin_path(self.APP_NAME)
+
+        self.pipeline_kwargs = {
+            'tool_path': tool_path,
+            'tool_dir': os.path.dirname(tool_path),
+            'cfg_file': self.remote_path,
+            'args': ' '.join(' '.join([str(k), str(v) if v else ''])
+                             for k, v in prox_args.items())
+        }
 
-        cmd_template = "sudo bash -c 'cd {tool_dir}; {tool_path} -o cli {args} -f {cfg_file} '"
-        prox_cmd = cmd_template.format(**self.pipeline_kwargs)
-        return prox_cmd
+        cmd_template = ("sudo bash -c 'cd {tool_dir}; {tool_path} -o cli "
+                        "{args} -f {cfg_file} '")
+        return cmd_template.format(**self.pipeline_kwargs)
 
 
 # this might be bad, sometimes we want regular ResourceHelper methods, like collect_kpi
@@ -940,6 +928,7 @@ class ProxResourceHelper(ClientResourceHelper):
         func = getattr(self.sut, cmd, None)
         if func:
             return func(*args, **kwargs)
+        return None
 
     def _connect(self, client=None):
         """Run and connect to prox on the remote system """
@@ -1016,11 +1005,18 @@ class ProxDataHelper(object):
     def samples(self):
         samples = {}
         for port_name, port_num in self.vnfd_helper.ports_iter():
-            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,
-            }
+            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,
+                }
         return samples
 
     def __enter__(self):
@@ -1062,7 +1058,7 @@ class ProxDataHelper(object):
         self.tsc_hz = float(self.sut.hz())
 
     def line_rate_to_pps(self):
-        # FIXME Don't hardcode 10Gb/s
+        # NOTE: to fix, don't hardcode 10Gb/s
         return self.port_count * TEN_GIGABIT / BITS_PER_BYTE / (self.pkt_size + 20)
 
 
@@ -1138,7 +1134,7 @@ class ProxProfileHelper(object):
             for key, value in section:
                 if key == "mode" and value == mode:
                     core_tuple = CoreSocketTuple(section_name)
-                    core = core_tuple.find_in_topology(self.cpu_topology)
+                    core = core_tuple.core_id
                     cores.append(core)
 
         return cores
@@ -1160,6 +1156,10 @@ class ProxProfileHelper(object):
         :return: return lat_min, lat_max, lat_avg
         :rtype: list
         """
+
+        if not self._latency_cores:
+            self._latency_cores = self.get_cores(self.PROX_CORE_LAT_MODE)
+
         if self._latency_cores:
             return self.sut.lat_stats(self._latency_cores)
         return []
@@ -1209,12 +1209,12 @@ class ProxMplsProfileHelper(ProxProfileHelper):
 
                 if item_value.startswith("tag"):
                     core_tuple = CoreSocketTuple(section_name)
-                    core_tag = core_tuple.find_in_topology(self.cpu_topology)
+                    core_tag = core_tuple.core_id
                     cores_tagged.append(core_tag)
 
                 elif item_value.startswith("udp"):
                     core_tuple = CoreSocketTuple(section_name)
-                    core_udp = core_tuple.find_in_topology(self.cpu_topology)
+                    core_udp = core_tuple.core_id
                     cores_plain.append(core_udp)
 
         return cores_tagged, cores_plain
@@ -1287,23 +1287,23 @@ class ProxBngProfileHelper(ProxProfileHelper):
 
                 if item_value.startswith("cpe"):
                     core_tuple = CoreSocketTuple(section_name)
-                    cpe_core = core_tuple.find_in_topology(self.cpu_topology)
+                    cpe_core = core_tuple.core_id
                     cpe_cores.append(cpe_core)
 
                 elif item_value.startswith("inet"):
                     core_tuple = CoreSocketTuple(section_name)
-                    inet_core = core_tuple.find_in_topology(self.cpu_topology)
+                    inet_core = core_tuple.core_id
                     inet_cores.append(inet_core)
 
                 elif item_value.startswith("arp"):
                     core_tuple = CoreSocketTuple(section_name)
-                    arp_core = core_tuple.find_in_topology(self.cpu_topology)
+                    arp_core = core_tuple.core_id
                     arp_cores.append(arp_core)
 
                 # We check the tasks/core separately
                 if item_value.startswith("arp_task"):
                     core_tuple = CoreSocketTuple(section_name)
-                    arp_task_core = core_tuple.find_in_topology(self.cpu_topology)
+                    arp_task_core = core_tuple.core_id
                     arp_tasks_core.append(arp_task_core)
 
         return cpe_cores, inet_cores, arp_cores, arp_tasks_core
@@ -1466,12 +1466,12 @@ class ProxVpeProfileHelper(ProxProfileHelper):
 
                 if item_value.startswith("cpe"):
                     core_tuple = CoreSocketTuple(section_name)
-                    core_tag = core_tuple.find_in_topology(self.cpu_topology)
+                    core_tag = core_tuple.core_id
                     cpe_cores.append(core_tag)
 
                 elif item_value.startswith("inet"):
                     core_tuple = CoreSocketTuple(section_name)
-                    inet_core = core_tuple.find_in_topology(self.cpu_topology)
+                    inet_core = core_tuple.core_id
                     inet_cores.append(inet_core)
 
         return cpe_cores, inet_cores
@@ -1490,7 +1490,6 @@ class ProxVpeProfileHelper(ProxProfileHelper):
                 if item_key != 'name':
                     continue
 
-            for item_key, item_value in section:
                 if item_value.startswith("cpe"):
                     cpe_ports.append(tx_port_no)
 
@@ -1595,3 +1594,192 @@ class ProxVpeProfileHelper(ProxProfileHelper):
                 data_helper.latency = self.get_latency()
 
         return data_helper.result_tuple, data_helper.samples
+
+
+class ProxlwAFTRProfileHelper(ProxProfileHelper):
+
+    __prox_profile_type__ = "lwAFTR gen"
+
+    def __init__(self, resource_helper):
+        super(ProxlwAFTRProfileHelper, self).__init__(resource_helper)
+        self._cores_tuple = None
+        self._ports_tuple = None
+        self.step_delta = 5
+        self.step_time = 0.5
+
+    @property
+    def _lwaftr_cores(self):
+        if not self._cores_tuple:
+            self._cores_tuple = self._get_cores_gen_lwaftr()
+        return self._cores_tuple
+
+    @property
+    def tun_cores(self):
+        return self._lwaftr_cores[0]
+
+    @property
+    def inet_cores(self):
+        return self._lwaftr_cores[1]
+
+    @property
+    def _lwaftr_ports(self):
+        if not self._ports_tuple:
+            self._ports_tuple = self._get_ports_gen_lw_aftr()
+        return self._ports_tuple
+
+    @property
+    def tun_ports(self):
+        return self._lwaftr_ports[0]
+
+    @property
+    def inet_ports(self):
+        return self._lwaftr_ports[1]
+
+    @property
+    def all_rx_cores(self):
+        return self.latency_cores
+
+    def _get_cores_gen_lwaftr(self):
+        tun_cores = []
+        inet_cores = []
+        for section_name, section in self.resource_helper.setup_helper.prox_config_data:
+            if not section_name.startswith("core"):
+                continue
+
+            if all(key != "mode" or value != self.PROX_CORE_GEN_MODE for key, value in section):
+                continue
+
+            core_tuple = CoreSocketTuple(section_name)
+            core_tag = core_tuple.core_id
+            for item_value in (v for k, v in section if k == 'name'):
+                if item_value.startswith('tun'):
+                    tun_cores.append(core_tag)
+                elif item_value.startswith('inet'):
+                    inet_cores.append(core_tag)
+
+        return tun_cores, inet_cores
+
+    def _get_ports_gen_lw_aftr(self):
+        tun_ports = []
+        inet_ports = []
+
+        re_port = re.compile(r'port (\d+)')
+        for section_name, section in self.resource_helper.setup_helper.prox_config_data:
+            match = re_port.search(section_name)
+            if not match:
+                continue
+
+            tx_port_no = int(match.group(1))
+            for item_value in (v for k, v in section if k == 'name'):
+                if item_value.startswith('lwB4'):
+                    tun_ports.append(tx_port_no)
+                elif item_value.startswith('inet'):
+                    inet_ports.append(tx_port_no)
+
+        return tun_ports, inet_ports
+
+    @staticmethod
+    def _resize(len1, len2):
+        if len1 == len2:
+            return 1.0
+        return 1.0 * len1 / len2
+
+    @contextmanager
+    def traffic_context(self, pkt_size, value):
+        # Tester is sending packets at the required speed already after
+        # setup_test(). Just get the current statistics, sleep the required
+        # amount of time and calculate packet loss.
+        tun_pkt_size = pkt_size
+        inet_pkt_size = pkt_size - 40
+        ratio = 1.0 * (tun_pkt_size + 20) / (inet_pkt_size + 20)
+
+        curr_up_speed = curr_down_speed = 0
+        max_up_speed = max_down_speed = value
+
+        max_up_speed = value / ratio
+
+        # Adjust speed when multiple cores per port are used to generate traffic
+        if len(self.tun_ports) != len(self.tun_cores):
+            max_down_speed *= self._resize(len(self.tun_ports), len(self.tun_cores))
+        if len(self.inet_ports) != len(self.inet_cores):
+            max_up_speed *= self._resize(len(self.inet_ports), len(self.inet_cores))
+
+        # Initialize cores
+        self.sut.stop_all()
+        time.sleep(0.5)
+
+        # Flush any packets in the NIC RX buffers, otherwise the stats will be
+        # wrong.
+        self.sut.start(self.all_rx_cores)
+        time.sleep(0.5)
+        self.sut.stop(self.all_rx_cores)
+        time.sleep(0.5)
+        self.sut.reset_stats()
+
+        self.sut.set_pkt_size(self.inet_cores, inet_pkt_size)
+        self.sut.set_pkt_size(self.tun_cores, tun_pkt_size)
+
+        self.sut.reset_values(self.tun_cores)
+        self.sut.reset_values(self.inet_cores)
+
+        # Set correct IP and UDP lengths in packet headers
+        # tun
+        # IPv6 length (byte 18): 58 for MAC(12), EthType(2), IPv6(40) , CRC(4)
+        self.sut.set_value(self.tun_cores, 18, tun_pkt_size - 58, 2)
+        # IP length (byte 56): 58 for MAC(12), EthType(2), CRC(4)
+        self.sut.set_value(self.tun_cores, 56, tun_pkt_size - 58, 2)
+        # UDP length (byte 78): 78 for MAC(12), EthType(2), IP(20), UDP(8), CRC(4)
+        self.sut.set_value(self.tun_cores, 78, tun_pkt_size - 78, 2)
+
+        # INET
+        # IP length (byte 20): 22 for MAC(12), EthType(2), CRC(4)
+        self.sut.set_value(self.inet_cores, 16, inet_pkt_size - 18, 2)
+        # UDP length (byte 42): 42 for MAC(12), EthType(2), IP(20), UPD(8), CRC(4)
+        self.sut.set_value(self.inet_cores, 38, inet_pkt_size - 38, 2)
+
+        LOG.info("Initializing SUT: sending lwAFTR packets")
+        self.sut.set_speed(self.inet_cores, curr_up_speed)
+        self.sut.set_speed(self.tun_cores, curr_down_speed)
+        time.sleep(4)
+
+        # Ramp up the transmission speed. First go to the common speed, then
+        # increase steps for the faster one.
+        self.sut.start(self.tun_cores + self.inet_cores + self.latency_cores)
+
+        LOG.info("Ramping up speed to %s up, %s down", max_up_speed, max_down_speed)
+
+        while (curr_up_speed < max_up_speed) or (curr_down_speed < max_down_speed):
+            # The min(..., ...) takes care of 1) floating point rounding errors
+            # that could make curr_*_speed to be slightly greater than
+            # max_*_speed and 2) max_*_speed not being an exact multiple of
+            # self._step_delta.
+            if curr_up_speed < max_up_speed:
+                curr_up_speed = min(curr_up_speed + self.step_delta, max_up_speed)
+            if curr_down_speed < max_down_speed:
+                curr_down_speed = min(curr_down_speed + self.step_delta, max_down_speed)
+
+            self.sut.set_speed(self.inet_cores, curr_up_speed)
+            self.sut.set_speed(self.tun_cores, curr_down_speed)
+            time.sleep(self.step_time)
+
+        LOG.info("Target speeds reached. Starting real test.")
+
+        yield
+
+        self.sut.stop(self.tun_cores + self.inet_cores)
+        LOG.info("Test ended. Flushing NIC buffers")
+        self.sut.start(self.all_rx_cores)
+        time.sleep(3)
+        self.sut.stop(self.all_rx_cores)
+
+    def run_test(self, pkt_size, duration, value, tolerated_loss=0.0):
+        data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss)
+
+        with data_helper, self.traffic_context(pkt_size, value):
+            with data_helper.measure_tot_stats():
+                time.sleep(duration)
+                # Getting statistics to calculate PPS at right speed....
+                data_helper.capture_tsc_hz()
+                data_helper.latency = self.get_latency()
+
+        return data_helper.result_tuple, data_helper.samples