Merge "Change default vports type to int"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / sample_vnf.py
index 82a6eda..a369a3a 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2016-2018 Intel Corporation
+# Copyright (c) 2016-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.
@@ -14,7 +14,7 @@
 
 import logging
 import decimal
-from multiprocessing import Queue, Value, Process
+from multiprocessing import Queue, Value, Process, JoinableQueue
 import os
 import posixpath
 import re
@@ -231,6 +231,9 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper):
         exit_status = self.dpdk_bind_helper.check_dpdk_driver()
         if exit_status == 0:
             return
+        else:
+            LOG.critical("DPDK Driver not installed")
+            return
 
     def _setup_resources(self):
         # what is this magic?  how do we know which socket is for which port?
@@ -405,6 +408,10 @@ class ClientResourceHelper(ResourceHelper):
         try:
             self._build_ports()
             self.client = self._connect()
+            if self.client is None:
+                LOG.critical("Failure to Connect ... unable to continue")
+                return
+
             self.client.reset(ports=self.all_ports)
             self.client.remove_all_streams(self.all_ports)  # remove all streams
             traffic_profile.register_generator(self)
@@ -454,22 +461,35 @@ class ClientResourceHelper(ResourceHelper):
                                server=self.vnfd_helper.mgmt_interface["ip"],
                                verbose_level=LoggerApi.VERBOSE_QUIET)
 
-        # try to connect with 5s intervals, 30s max
+        # try to connect with 5s intervals
         for idx in range(6):
             try:
                 client.connect()
-                break
+                for idx2 in range(6):
+                    if client.is_connected():
+                        return client
+                    LOG.info("Waiting to confirm connection %s .. Attempt %s",
+                             idx, idx2)
+                    time.sleep(1)
+                client.disconnect(stop_traffic=True, release_ports=True)
             except STLError:
                 LOG.info("Unable to connect to Trex Server.. Attempt %s", idx)
                 time.sleep(5)
-        return client
 
+        if client.is_connected():
+            return client
+        else:
+            LOG.critical("Connection failure ..TRex username: %s server: %s",
+                         self.vnfd_helper.mgmt_interface["user"],
+                         self.vnfd_helper.mgmt_interface["ip"])
+            return None
 
 class Rfc2544ResourceHelper(object):
 
     DEFAULT_CORRELATED_TRAFFIC = False
     DEFAULT_LATENCY = False
     DEFAULT_TOLERANCE = '0.0001 - 0.0001'
+    DEFAULT_RESOLUTION = '0.1'
 
     def __init__(self, scenario_helper):
         super(Rfc2544ResourceHelper, self).__init__()
@@ -481,6 +501,7 @@ class Rfc2544ResourceHelper(object):
         self._tolerance_low = None
         self._tolerance_high = None
         self._tolerance_precision = None
+        self._resolution = None
 
     @property
     def rfc2544(self):
@@ -520,6 +541,13 @@ class Rfc2544ResourceHelper(object):
             self._latency = self.get_rfc2544('latency', self.DEFAULT_LATENCY)
         return self._latency
 
+    @property
+    def resolution(self):
+        if self._resolution is None:
+            self._resolution = float(self.get_rfc2544('resolution',
+                                                self.DEFAULT_RESOLUTION))
+        return self._resolution
+
     def get_rfc2544(self, name, default=None):
         return self.rfc2544.get(name, default)
 
@@ -691,8 +719,8 @@ class SampleVNF(GenericVNF):
         scenarios:
         - type: NSPerf
           nodes:
-            tg__0: trafficgen_1.yardstick
-            vnf__0: vnf.yardstick
+            tg__0: trafficgen_0.yardstick
+            vnf__0: vnf_0.yardstick
           options:
             collectd:
               <options>  # COLLECTD priority 3
@@ -755,6 +783,53 @@ class SampleVNF(GenericVNF):
             # by other VNF output
             self.q_in.put('\r\n')
 
+    def wait_for_initialize(self):
+        buf = []
+        vnf_prompt_found = False
+        prompt_command = '\r\n'
+        script_name = 'non_existent_script_name'
+        done_string = 'Cannot open file "{}"'.format(script_name)
+        time.sleep(self.WAIT_TIME)  # Give some time for config to load
+        while True:
+            if not self._vnf_process.is_alive():
+                raise RuntimeError("%s VNF process died." % self.APP_NAME)
+            while self.q_out.qsize() > 0:
+                buf.append(self.q_out.get())
+                message = ''.join(buf)
+
+                if self.VNF_PROMPT in message and not vnf_prompt_found:
+                    # Once we got VNF promt, it doesn't mean that the VNF is
+                    # up and running/initialized completely. But we can run
+                    # addition (any) VNF command and wait for it to complete
+                    # as it will be finished ONLY at the end of the VNF
+                    # initialization. So, this approach can be used to
+                    # indentify that VNF is completely initialized.
+                    LOG.info("Got %s VNF prompt.", self.APP_NAME)
+                    prompt_command = "run {}\r\n".format(script_name)
+                    self.q_in.put(prompt_command)
+                    # Cut the buffer since we are not interesting to find
+                    # the VNF prompt anymore
+                    prompt_pos = message.find(self.VNF_PROMPT)
+                    buf = [message[prompt_pos + len(self.VNF_PROMPT):]]
+                    vnf_prompt_found = True
+                    continue
+
+                if done_string in message:
+                    LOG.info("%s VNF is up and running.", self.APP_NAME)
+                    self._vnf_up_post()
+                    self.queue_wrapper.clear()
+                    return self._vnf_process.exitcode
+
+                if "PANIC" in message:
+                    raise RuntimeError("Error starting %s VNF." %
+                                       self.APP_NAME)
+
+            LOG.info("Waiting for %s VNF to start.. ", self.APP_NAME)
+            time.sleep(self.WAIT_TIME_FOR_SCRIPT)
+            # Send command again to display the expected prompt in case the
+            # expected text was corrupted by other VNF output
+            self.q_in.put(prompt_command)
+
     def start_collect(self):
         self.resource_helper.start_collect()
 
@@ -876,6 +951,39 @@ class SampleVNFTrafficGen(GenericTrafficGen):
         self.traffic_finished = False
         self._tg_process = None
         self._traffic_process = None
+        self._tasks_queue = JoinableQueue()
+        self._result_queue = Queue()
+
+    def _test_runner(self, traffic_profile, tasks, results):
+        self.resource_helper.run_test(traffic_profile, tasks, results)
+
+    def _init_traffic_process(self, traffic_profile):
+        name = '{}-{}-{}-{}'.format(self.name, self.APP_NAME,
+                                    traffic_profile.__class__.__name__,
+                                    os.getpid())
+        self._traffic_process = Process(name=name, target=self._test_runner,
+                                        args=(
+                                        traffic_profile, self._tasks_queue,
+                                        self._result_queue))
+
+        self._traffic_process.start()
+        while self.resource_helper.client_started.value == 0:
+            time.sleep(1)
+            if not self._traffic_process.is_alive():
+                break
+
+    def run_traffic_once(self, traffic_profile):
+        if self.resource_helper.client_started.value == 0:
+            self._init_traffic_process(traffic_profile)
+
+        # continue test - run next iteration
+        LOG.info("Run next iteration ...")
+        self._tasks_queue.put('RUN_TRAFFIC')
+
+    def wait_on_traffic(self):
+        self._tasks_queue.join()
+        result = self._result_queue.get()
+        return result
 
     def _start_server(self):
         # we can't share ssh paramiko objects to force new connection