Improved PROX cleanup when exiting 52/71752/5
authorLuc Provoost <luc.provoost@intel.com>
Wed, 16 Dec 2020 14:47:49 +0000 (15:47 +0100)
committerLuc Provoost <luc.provoost@intel.com>
Mon, 28 Dec 2020 11:11:29 +0000 (12:11 +0100)
Sending now a 'quit' command to PROX, so that the thread running PROX in
an ssh session is now cleaned up correctly.
Also fixed a problem with the logging when running the code more than
once. We only create handlers now, when they do not yet exist.
Updated the rapid version.
Also added some more tests in the testcases.yaml for xtesting.
At the end of a run, the PROX.log files are copied in the results
directory.
The success criterium for a test 'pass_threshold' has been removed from
the test files and is to be controlled by the xtesting testcases.yaml
file.

Change-Id: Ifbbb1c91f32c9176f52025d9ae4c495b432a94c9
Signed-off-by: Luc Provoost <luc.provoost@intel.com>
23 files changed:
VNFs/DPPD-PROX/helper-scripts/rapid/TST009_Throughput.test
VNFs/DPPD-PROX/helper-scripts/rapid/TST009_Throughput_64B_64F.test
VNFs/DPPD-PROX/helper-scripts/rapid/TST009_Throughput_acaeab_16384F.test [new file with mode: 0644]
VNFs/DPPD-PROX/helper-scripts/rapid/basicrapid.test
VNFs/DPPD-PROX/helper-scripts/rapid/format.yaml
VNFs/DPPD-PROX/helper-scripts/rapid/increment_till_fail.test
VNFs/DPPD-PROX/helper-scripts/rapid/ipv6.test
VNFs/DPPD-PROX/helper-scripts/rapid/l2framerate.test
VNFs/DPPD-PROX/helper-scripts/rapid/l2zeroloss.test
VNFs/DPPD-PROX/helper-scripts/rapid/l3framerate.test
VNFs/DPPD-PROX/helper-scripts/rapid/prox_ctrl.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_defaults.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_flowsizetest.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_generator_machine.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_irqtest.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_log.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_machine.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_parser.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapid_test.py
VNFs/DPPD-PROX/helper-scripts/rapid/rapidxt.py
VNFs/DPPD-PROX/helper-scripts/rapid/runrapid.py
VNFs/DPPD-PROX/helper-scripts/rapid/xtesting/Dockerfile
VNFs/DPPD-PROX/helper-scripts/rapid/xtesting/testcases.yaml

index ce9ba72..6b1e677 100644 (file)
@@ -42,11 +42,6 @@ warmuptime=2
 
 [test2]
 test=TST009test
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.1
 imixs=[[64],[128]]
 # the number of flows in the list need to be powers of 2, max 2^20
 # Select from following numbers: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576
index 866db5c..c10c50e 100644 (file)
@@ -35,11 +35,6 @@ cores = [1]
 
 [test1]
 test=TST009test
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.001
 imixs=[[64]]
 # the number of flows in the list need to be powers of 2, max 2^20
 # Select from following numbers: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576
diff --git a/VNFs/DPPD-PROX/helper-scripts/rapid/TST009_Throughput_acaeab_16384F.test b/VNFs/DPPD-PROX/helper-scripts/rapid/TST009_Throughput_acaeab_16384F.test
new file mode 100644 (file)
index 0000000..9b6585a
--- /dev/null
@@ -0,0 +1,54 @@
+##
+## Copyright (c) 2010-2020 Intel Corporation
+##
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## 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.
+##
+
+[TestParameters]
+name = Rapid_ETSINFV_TST009
+number_of_tests = 1
+total_number_of_test_machines = 2
+lat_percentile = 99
+
+[TestM1]
+name = Generator
+config_file = gen.cfg
+dest_vm = 2
+gencores = [1]
+latcores = [3]
+#bucket_size_exp = 12
+
+[TestM2]
+name = Swap
+config_file = swap.cfg
+cores = [1]
+
+[test1]
+test=TST009test
+# Following parameter defines the success criterium for the test.
+# When this test uses multiple combinations of packet size and flows,
+# all combinations must be meeting the same threshold
+# The threshold is expressed in Mpps
+pass_threshold=0.001
+imixs=[[64,256,64,1024,64,128]]
+# the number of flows in the list need to be powers of 2, max 2^20
+# Select from following numbers: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576
+flows=[16384]
+drop_rate_threshold = 0
+lat_avg_threshold = 120
+lat_perc_threshold = 220
+lat_max_threshold = inf
+MAXr = 3
+MAXz = 5000
+MAXFramesPerSecondAllIngress = 12000000
+StepSize = 10000
index fbf75a0..f27e498 100644 (file)
@@ -44,11 +44,6 @@ warmuptime=2
 
 [test2]
 test=flowsizetest
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.1
 # Each element in the imix list will result in a separate test. Each element
 # is on its turn a list of packet sizes which will be used during one test
 # execution. If you only want to test 1 size, define a list with only one
index 6b1eb45..d9c5540 100644 (file)
@@ -22,6 +22,8 @@
   PacketsReceived: PacketsReceived
   PacketsLost: PacketsLost
 rapid_flowsizetest:
+  Environment: environment_file
+  Test: test
   Flows: Flows 
   Size: Size
   Speed (Mpps):
@@ -42,17 +44,7 @@ rapid_flowsizetest:
     PacketsReceived: PacketsReceived
     PacketsLost: PacketsLost
 rapid_irqtest:
-  Core: Core 
-  LessThan1us   : B1
-  LessThan5us   : B5
-  LessThan10us  : B10
-  LessThan50us  : B50
-  LessThan100us : B100
-  LessThan500us : B500
-  LessThan1ms   : B1000
-  LessThan5ms   : B5000
-  LessThan10ms  : B10000
-  LessThan50ms  : B50000
-  LessThan100ms : B100000
-  LessThan500ms : B500000
-  MoreThan500ms : BM500000
+  Environment: environment_file
+  Test: test
+  Buckets: buckets
+  Machine_data: machine_data 
index 29e36bf..d07876b 100644 (file)
@@ -44,11 +44,6 @@ warmuptime=2
 
 [test2]
 test=increment_till_fail
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.1
 # Each element in the imix list will result in a separate test. Each element
 # is on its turn a list of packet sizes which will be used during one test
 # execution. If you only want to test 1 size, define a list with only one
index b9f00f7..7dd586a 100644 (file)
@@ -45,12 +45,8 @@ warmuptime=2
 
 [test2]
 test=flowsizetest
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.1
-# DO NOT USE IMIX FOR IPV6 TESTING
+# DO NOT USE IMIX FOR IPV6 TESTING. THE LIST OF IMIXS CAN ONLY CONTAIN LISTS
+# WITH ONE ELEMENT!!!
 # PACKET SIZE NEEDS TO BE AT LEAST 84 (66 + 18) FOR IPV6
 # 18 bytes needed for UDP LATENCY AND COUNTER CONTENT
 imixs=[[84],[250]]
index e09b80f..0c86800 100644 (file)
@@ -33,11 +33,6 @@ cores = [1]
 
 [test1]
 test=fixed_rate
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.1
 startspeed = 10
 imixs=[[256]]
 flows=[64]
index 2f61df5..bc1a1bc 100644 (file)
@@ -40,11 +40,6 @@ warmuptime=2
 
 [test2]
 test=flowsizetest
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.1
 # Each element in the imix list will result in a separate test. Each element
 # is on its turn a list of packet sizes which will be used during one test
 # execution. If you only want to test 1 size, define a list with only one
index 1d890d1..67a57ce 100644 (file)
@@ -40,11 +40,6 @@ warmuptime=2
 
 [test2]
 test=fixed_rate
-# Following parameter defines the success criterium for the test.
-# When this test uses multiple combinations of packet size and flows,
-# all combinations must be meeting the same threshold
-# The threshold is expressed in Mpps
-pass_threshold=0.1
 imixs=[[64],[128]]
 # the number of flows in the list need to be powers of 2, max 2^20
 # If not a power of 2, we will use the lowest power of 2 that is larger than
index 586731e..5fc98db 100644 (file)
@@ -183,9 +183,8 @@ class prox_sock(object):
         self._sock = sock
         self._rcvd = b''
 
-    def quit(self):
+    def __del__(self):
         if self._sock is not None:
-            self._send('quit')
             self._sock.close()
             self._sock = None
 
@@ -321,6 +320,9 @@ class prox_sock(object):
         self._send('set value %s %s %s %s %s' % (','.join(map(str, cores)), 
             task, offset, value, length))
 
+    def quit_prox(self):
+        self._send('quit')
+
     def _send(self, cmd):
         """Append LF and send command to the PROX instance."""
         if self._sock is None:
index 404e6e3..e648c01 100644 (file)
@@ -21,7 +21,7 @@ class RapidDefaults(object):
     Class to define the test defaults
     """
     test_params = {
-        'version' : '2020.09.23', # Please do NOT change, used for debugging
+        'version' : '2020.12.21', # Please do NOT change, used for debugging
         'environment_file' : 'rapid.env', #Default string for environment
         'test_file' : 'basicrapid.test', #Default string for test
         'machine_map_file' : 'machine.map', #Default string for machine map file
@@ -30,5 +30,6 @@ class RapidDefaults(object):
         'runtime' : 10, # time in seconds for 1 test run
         'configonly' : False, # If True, the system will upload all the necessary config fiels to the VMs, but not start PROX and the actual testing
         'rundir' : '/opt/rapid', # Directory where to find the tools in the machines running PROX
+        'resultsdir' : '.', # Directory where to store log files
         'lat_percentile' : 0.99
         }
index c4308b1..566e7cf 100644 (file)
@@ -99,7 +99,7 @@ class FlowSizeTest(RapidTest):
     def run(self):
         result_details = {'Details': 'Nothing'}
         self.gen_machine.start_latency_cores()
-        TestPassed = True
+        TestResult = 0
         for imix in self.test['imixs']:
             size = mean(imix)
             self.gen_machine.set_udp_packet_size(imix)
@@ -122,7 +122,6 @@ class FlowSizeTest(RapidTest):
                 self.set_background_flows(self.background_machines, flow_number)
                 endspeed = None
                 speed = self.get_start_speed_and_init(size)
-                self.record_start_time()
                 while True:
                     attempts += 1
                     endwarning = False
@@ -164,7 +163,7 @@ class FlowSizeTest(RapidTest):
                         if lat_warning or retry_warning:
                             endwarning = '|        | {:177.177} |'.format(retry_warning + lat_warning)
                         success = True
-                        TestPassed = False # fixed rate testing cannot be True, it is just reporting numbers every second
+                        TestResult = TestResult + pps_rx # fixed rate testing result is strange: we just report the pps received
                         speed_prefix = lat_avg_prefix = lat_perc_prefix = lat_max_prefix = abs_drop_rate_prefix = drop_rate_prefix = bcolors.ENDC
                     # The following if statement is testing if we pass the success criteria of a certain drop rate, average latency and maximum latency below the threshold
                     # The drop rate success can be achieved in 2 ways: either the drop rate is below a treshold, either we want that no packet has been lost during the test
@@ -234,10 +233,7 @@ class FlowSizeTest(RapidTest):
                             break
                     elif self.resolution_achieved():
                         break
-                self.record_stop_time()
                 if endspeed is not None:
-                    if TestPassed and (endpps_rx < self.test['pass_threshold']):
-                        TestPassed = False
                     speed_prefix = lat_avg_prefix = lat_perc_prefix = lat_max_prefix = abs_drop_rate_prefix = drop_rate_prefix = bcolors.ENDC
                     RapidLog.info(self.report_result(flow_number,size,endspeed,endpps_req_tx,endpps_tx,endpps_sut_tx,endpps_rx,endlat_avg,endlat_perc,endlat_perc_max,endlat_max,endabs_tx,endabs_rx,endabs_dropped,actual_duration,speed_prefix,lat_avg_prefix,lat_perc_prefix,lat_max_prefix,abs_drop_rate_prefix,drop_rate_prefix))
                     if endavg_bg_rate:
@@ -248,10 +244,9 @@ class FlowSizeTest(RapidTest):
                         RapidLog.info (endwarning)
                     RapidLog.info("+--------+------------------+-------------+-------------+-------------+------------------------+----------+----------+----------+-----------+-----------+-----------+-----------+-------+----+")
                     if self.test['test'] != 'fixed_rate':
+                        TestResult = TestResult + endpps_rx
                         result_details = {'test': self.test['testname'],
                                 'environment_file': self.test['environment_file'],
-                                'start_date': self.start,
-                                'stop_date': self.stop,
                                 'Flows': flow_number,
                                 'Size': size,
                                 'RequestedSpeed': RapidTest.get_pps(endspeed,size),
@@ -271,4 +266,4 @@ class FlowSizeTest(RapidTest):
                 else:
                     RapidLog.info('|{:>7}'.format(str(flow_number))+" | Speed 0 or close to 0")
         self.gen_machine.stop_latency_cores()
-        return (TestPassed, result_details)
+        return (TestResult, result_details)
index 293720d..99cb361 100644 (file)
@@ -49,7 +49,8 @@ class RapidGeneratorMachine(RapidMachine):
     """
     Class to deal with a generator PROX instance (VM, bare metal, container)
     """
-    def __init__(self, key, user, vim, rundir, machine_params, configonly, ipv6):
+    def __init__(self, key, user, vim, rundir, resultsdir, machine_params,
+            configonly, ipv6):
         mac_address_size = 6
         ethertype_size = 2
         FCS_size = 4
@@ -73,7 +74,8 @@ class RapidGeneratorMachine(RapidMachine):
         self.udp_dest_port_offset = udp_header_start_offset + 2
         self.udp_length_offset = udp_header_start_offset + 4
         self.ipv6 = ipv6
-        super().__init__(key, user, vim, rundir, machine_params, configonly)
+        super().__init__(key, user, vim, rundir, resultsdir, machine_params,
+                configonly)
 
     def get_cores(self):
         return (self.machine_params['gencores'] +
index 1afa0f1..f990a23 100644 (file)
@@ -34,16 +34,24 @@ class IrqTest(RapidTest):
         self.machines = machines
 
     def run(self):
-        RapidLog.info("+----------------------------------------------------------------------------------------------------------------------------")
-        RapidLog.info("| Measuring time probably spent dealing with an interrupt. Interrupting DPDK cores for more than 50us might be problematic   ")
-        RapidLog.info("| and result in packet loss. The first row shows the interrupted time buckets: first number is the bucket between 0us and    ")
-        RapidLog.info("| that number expressed in us and so on. The numbers in the other rows show how many times per second, the program was       ")
-        RapidLog.info("| interrupted for a time as specified by its bucket. '0' is printed when there are no interrupts in this bucket throughout   ")
-        RapidLog.info("| the duration of the test. 0.00 means there were interrupts in this bucket but very few. Due to rounding this shows as 0.00 ") 
-        RapidLog.info("+----------------------------------------------------------------------------------------------------------------------------")
+        RapidLog.info("+----------------------------------------------------------------------------------------------------------------------------+")
+        RapidLog.info("| Measuring time probably spent dealing with an interrupt. Interrupting DPDK cores for more than 50us might be problematic   |")
+        RapidLog.info("| and result in packet loss. The first row shows the interrupted time buckets: first number is the bucket between 0us and    |")
+        RapidLog.info("| that number expressed in us and so on. The numbers in the other rows show how many times per second, the program was       |")
+        RapidLog.info("| interrupted for a time as specified by its bucket. '0' is printed when there are no interrupts in this bucket throughout   |")
+        RapidLog.info("| the duration of the test. 0.00 means there were interrupts in this bucket but very few. Due to rounding this shows as 0.00 |") 
+        RapidLog.info("+----------------------------------------------------------------------------------------------------------------------------+")
         sys.stdout.flush()
+        max_loop_duration = 0
+        machine_details = {}
         for machine in self.machines:
             buckets=machine.socket.show_irq_buckets(1)
+            if max_loop_duration == 0:
+                # First time we go through the loop, we need to initialize
+                # result_details
+                result_details = {'test': self.test['testname'],
+                        'environment_file': self.test['environment_file'],
+                        'buckets': buckets}
             print('Measurement ongoing ... ',end='\r')
             machine.start() # PROX cores will be started within 0 to 1 seconds
             # That is why we sleep a bit over 1 second to make sure all cores
@@ -60,7 +68,7 @@ class IrqTest(RapidTest):
                     old_irq[i][j] = machine.socket.irq_stats(irqcore,j)
             # Measurements in the loop above, are updated by PROX every second
             # This means that taking the same measurement 0.5 second later
-            # might results in the same data or data from the next 1s window
+            # might result in the same data or data from the next 1s window
             time.sleep(float(self.test['runtime']))
             row_names = []
             for i,irqcore in enumerate(machine.get_cores()):
@@ -70,10 +78,13 @@ class IrqTest(RapidTest):
                     if diff == 0:
                         irq[i][j] = '0'
                     else:
-                        irq[i][j] = str(round(old_div(diff,float(self.test['runtime'])), 2))
+                        irq[i][j] = str(round(old_div(diff,
+                            float(self.test['runtime'])), 2))
+                        if max_loop_duration < int(bucket):
+                            max_loop_duration = int(bucket)
             # Measurements in the loop above, are updated by PROX every second
             # This means that taking the same measurement 0.5 second later
-            # might results in the same data or data from the next 1s window
+            # might result in the same data or data from the next 1s window
             # Conclusion: we don't know the exact window size.
             # Real measurement windows might be wrong by 1 second
             # This could be fixed in this script by checking this data every
@@ -81,17 +92,15 @@ class IrqTest(RapidTest):
             # a longer time and decrease the error. The absolute number of
             # interrupts is not so important.
             machine.stop()
+            core_details = {}
             RapidLog.info('Results for PROX instance %s'%machine.name)
-            RapidLog.info('{:>12}'.format('bucket us') + ''.join(['{:>12}'.format(item) for item in column_names]))
+            RapidLog.info('{:>12}'.format('bucket us') +
+                    ''.join(['{:>12}'.format(item) for item in column_names]))
             for j, row in enumerate(irq):
-                RapidLog.info('Core {:>7}'.format(row_names[j]) + ''.join(['{:>12}'.format(item) for item in row]))
-            variables = {}
-            variables['test'] = self.test['test']
-            variables['environment_file'] = self.test['environment_file']
-            variables['Machine'] = machine.name
-            for i,irqcore in enumerate(machine.get_cores()):
-                variables['Core'] = '{}'.format(row_names[i])
-                for j,bucket in enumerate(buckets):
-                    variables['B{}'.format(column_names[j].replace(">","M").replace("<","").replace(" ",""))] = irq[i][j]
-                self.post_data('rapid_irqtest', variables)
-        return (True, None)
+                RapidLog.info('Core {:>7}'.format(row_names[j]) +
+                        ''.join(['{:>12}'.format(item) for item in row]))
+                core_details['Core {}'.format(row_names[j])] = row
+            machine_details[machine.name] = core_details
+        result_details['machine_data'] = machine_details
+        result_details = self.post_data('rapid_irqtest', result_details)
+        return (500000 - max_loop_duration, result_details)
index d1460f5..f453c57 100644 (file)
@@ -42,56 +42,67 @@ class RapidLog(object):
 
     @staticmethod
     def log_init(log_file, loglevel, screenloglevel, version):
-        # create formatters
-        screen_formatter = logging.Formatter("%(message)s")
-        file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
-
-        # get a top-level logger,
-        # set its log level,
-        # BUT PREVENT IT from propagating messages to the root logger
-        #
-        log = logging.getLogger()
-        numeric_level = getattr(logging, loglevel.upper(), None)
-        if not isinstance(numeric_level, int):
-            raise ValueError('Invalid log level: %s' % loglevel)
-        log.setLevel(numeric_level)
-        log.propagate = 0
-
-        # create a console handler
-        # and set its log level to the command-line option 
-        # 
-        console_handler = logging.StreamHandler(sys.stdout)
-        #console_handler.setLevel(logging.INFO)
-        numeric_screenlevel = getattr(logging, screenloglevel.upper(), None)
-        if not isinstance(numeric_screenlevel, int):
-            raise ValueError('Invalid screenlog level: %s' % screenloglevel)
-        console_handler.setLevel(numeric_screenlevel)
-        console_handler.setFormatter(screen_formatter)
-
-        # create a file handler
-        # and set its log level
-        #
-        file_handler = logging.handlers.RotatingFileHandler(log_file, backupCount=10)
-        #file_handler = log.handlers.TimedRotatingFileHandler(log_file, 'D', 1, 5)
-        file_handler.setLevel(numeric_level)
-        file_handler.setFormatter(file_formatter)
-
-        # add handlers to the logger
-        #
-        log.addHandler(file_handler)
-        log.addHandler(console_handler)
-
-        # Check if log exists and should therefore be rolled
-        needRoll = os.path.isfile(log_file)
-
-
-        # This is a stale log, so roll it
-        if needRoll:    
-            # Add timestamp
-            log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
-
-            # Roll over on application start
-            file_handler.doRollover()
+        log = logging.getLogger(__name__)
+        makeFileHandler = True
+        makeStreamHandler = True
+        if len(log.handlers) > 0:
+            for handler in log.handlers:
+                if isinstance(handler, logging.FileHandler):
+                    makeFileHandler = False
+                elif isinstance(handler, logging.StreamHandler):
+                    makeStreamHandler = False
+        if makeStreamHandler:
+            # create formatters
+            screen_formatter = logging.Formatter("%(message)s")
+            # create a console handler
+            # and set its log level to the command-line option 
+            # 
+            console_handler = logging.StreamHandler(sys.stdout)
+            #console_handler.setLevel(logging.INFO)
+            numeric_screenlevel = getattr(logging, screenloglevel.upper(), None)
+            if not isinstance(numeric_screenlevel, int):
+                raise ValueError('Invalid screenlog level: %s' % screenloglevel)
+            console_handler.setLevel(numeric_screenlevel)
+            console_handler.setFormatter(screen_formatter)
+            # add handler to the logger
+            #
+            log.addHandler(console_handler)
+        if makeFileHandler:
+            # create formatters
+            file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
+            # get a top-level logger,
+            # set its log level,
+            # BUT PREVENT IT from propagating messages to the root logger
+            #
+            numeric_level = getattr(logging, loglevel.upper(), None)
+            if not isinstance(numeric_level, int):
+                raise ValueError('Invalid log level: %s' % loglevel)
+            log.setLevel(numeric_level)
+            log.propagate = 0
+
+
+            # create a file handler
+            # and set its log level
+            #
+            file_handler = logging.handlers.RotatingFileHandler(log_file, backupCount=10)
+            file_handler.setLevel(numeric_level)
+            file_handler.setFormatter(file_formatter)
+
+            # add handler to the logger
+            #
+            log.addHandler(file_handler)
+
+            # Check if log exists and should therefore be rolled
+            needRoll = os.path.isfile(log_file)
+
+
+            # This is a stale log, so roll it
+            if needRoll:    
+                # Add timestamp
+                log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
+
+                # Roll over on application start
+                file_handler.doRollover()
 
         # Add timestamp
         log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
@@ -99,6 +110,13 @@ class RapidLog(object):
         log.debug("rapid version: " + version)
         RapidLog.log = log
 
+    @staticmethod
+    def log_close():
+        for handler in RapidLog.log.handlers:
+            if isinstance(handler, logging.FileHandler):
+                handler.close()
+                RapidLog.log.removeHandler(handler)
+
     @staticmethod
     def exception(exception_info):
         RapidLog.log.exception(exception_info)
index 8466c85..e47c179 100644 (file)
@@ -24,12 +24,14 @@ class RapidMachine(object):
     """
     Class to deal with a PROX instance (VM, bare metal, container)
     """
-    def __init__(self, key, user, vim, rundir, machine_params, configonly):
+    def __init__(self, key, user, vim, rundir, resultsdir, machine_params,
+            configonly):
         self.name = machine_params['name']
         self.ip = machine_params['admin_ip']
         self.key = key
         self.user = user
         self.rundir = rundir
+        self.resultsdir = resultsdir
         self.dp_ports = []
         self.dpdk_port_index = []
         self.configonly = configonly
@@ -44,13 +46,13 @@ class RapidMachine(object):
                 index += 1
             else:
                 break
-        self.rundir = rundir
         self.machine_params = machine_params
         self.vim = vim
 
     def __del__(self):
         if ((not self.configonly) and self.machine_params['prox_socket']):
-            self._client.scp_get('/prox.log', './{}.prox.log'.format(self.name))
+            self._client.scp_get('/prox.log', '{}/{}.prox.log'.format(
+                self.resultsdir, self.name))
 
     def get_cores(self):
         return (self.machine_params['cores'])
@@ -113,7 +115,7 @@ class RapidMachine(object):
 
     def close_prox(self):
         if (not self.configonly) and self.machine_params['prox_socket'] and self.machine_params['prox_launch_exit']:
-            self.socket.quit()
+            self.socket.quit_prox()
 
     def connect_prox(self):
         if self.machine_params['prox_socket']:
index 29d8755..e5ed10d 100644 (file)
@@ -73,7 +73,7 @@ class RapidConfigParser(object):
                 elif option in ['startspeed', 'step', 'drop_rate_threshold',
                         'lat_avg_threshold','lat_perc_threshold',
                         'lat_max_threshold','accuracy','maxr','maxz',
-                        'pass_threshold','ramp_step']:
+                        'ramp_step']:
                     test[option] = float(testconfig.get(section, option))
                 else:
                     test[option] = testconfig.get(section, option)
index b89eb7b..a54caba 100644 (file)
@@ -105,12 +105,6 @@ class RapidTest(object):
                 if v in variables.keys():
                     data_format[k] = variables[v]
 
-    def record_start_time(self):
-        self.start = dt.now().strftime('%Y-%m-%d %H:%M:%S')
-
-    def record_stop_time(self):
-        self.stop = dt.now().strftime('%Y-%m-%d %H:%M:%S')
-
     def post_data(self, test, variables):
         var = copy.deepcopy(self.data_format)
         self.parse_data_format_dict(var, variables)
index b472f36..2a82df5 100644 (file)
@@ -36,6 +36,7 @@ class RapidXt(testcase.TestCase):
             for key in kwargs:
                 test_params[key] = kwargs[key]
             os.makedirs(self.res_dir, exist_ok=True)
+            test_params['resultsdir'] = self.res_dir
             log_file = '{}/RUN{}.{}.log'.format(self.res_dir,
                 test_params['environment_file'], test_params['test_file'])
             RapidLog.log_init(log_file, test_params['loglevel'],
@@ -43,9 +44,9 @@ class RapidXt(testcase.TestCase):
             test_manager = RapidTestManager()
             self.start_time = time.time()
             self.result, self.details = test_manager.run_tests(test_params)
-            self.result = 100 * self.result
-            RapidLog.info('Test result is : {}'.format(self.result))
             self.stop_time = time.time()
+            RapidLog.log_close()
+
         except Exception:  # pylint: disable=broad-except
             print("Unexpected error:", sys.exc_info()[0])
             self.result = 0
index 44f33c0..3bdb91d 100755 (executable)
@@ -62,8 +62,8 @@ class RapidTestManager(object):
             if 'gencores' in machine_params.keys():
                 machine = RapidGeneratorMachine(test_params['key'],
                         test_params['user'], test_params['vim_type'],
-                        test_params['rundir'], machine_params,
-                        configonly, test_params['ipv6'])
+                        test_params['rundir'], test_params['resultsdir'],
+                        machine_params, configonly, test_params['ipv6'])
                 if machine_params['monitor']:
                     if monitor_gen:
                         RapidLog.exception("Can only monitor 1 generator")
@@ -76,7 +76,7 @@ class RapidTestManager(object):
             else:
                 machine = RapidMachine(test_params['key'], test_params['user'],
                         test_params['vim_type'], test_params['rundir'],
-                        machine_params, configonly)
+                        test_params['resultsdir'], machine_params, configonly)
                 if machine_params['monitor']:
                     if monitor_sut:
                         RapidLog.exception("Can only monitor 1 sut")
@@ -94,7 +94,7 @@ class RapidTestManager(object):
         with concurrent.futures.ThreadPoolExecutor(max_workers=len(self.machines)) as executor:
             future_to_connect_prox = {executor.submit(machine.connect_prox): machine for machine in self.machines}
             concurrent.futures.wait(future_to_connect_prox,return_when=ALL_COMPLETED)
-        result = True
+        result = 0
         for test_param in test_params['tests']:
             RapidLog.info(test_param['test'])
             if test_param['test'] in ['flowsizetest', 'TST009test',
@@ -128,8 +128,10 @@ class RapidTestManager(object):
                 RapidLog.debug('Test name ({}) is not valid:'.format(
                     test_param['test']))
             single_test_result, result_details = test.run()
-            if not single_test_result:
-                result = False
+            result = result + single_test_result
+        for machine in self.machines:
+            machine.close_prox()
+        concurrent.futures.wait(self.future_to_prox,return_when=ALL_COMPLETED)
         return (result, result_details)
 
 def main():
@@ -145,7 +147,7 @@ def main():
             test_params['screenloglevel'] , test_params['version']  )
     test_manager = RapidTestManager()
     test_result, _ = test_manager.run_tests(test_params)
-    RapidLog.info('Test result is : {}'.format(test_result))
+    RapidLog.log_close()
 
 if __name__ == "__main__":
     main()
index b7bced8..299f8ef 100644 (file)
@@ -24,7 +24,6 @@ RUN git clone https://git.opnfv.org/samplevnf /samplevnf
 WORKDIR /samplevnf/VNFs/DPPD-PROX/helper-scripts/rapid
 COPY rapid.env /samplevnf/VNFs/DPPD-PROX/helper-scripts/rapid/.
 COPY rapid_key.pem /samplevnf/VNFs/DPPD-PROX/helper-scripts/rapid/.
-COPY TST009_Throughput_64B_64F.test /samplevnf/VNFs/DPPD-PROX/helper-scripts/rapid/.
 COPY testcases.yaml /usr/lib/python3.8/site-packages/xtesting/ci/testcases.yaml
 RUN apk add python3-dev openssh-client && cd /samplevnf/VNFs/DPPD-PROX/helper-scripts/rapid/ && git init && pip3 install .
 CMD ["run_tests", "-t", "all"]
index 2db064f..38ba131 100644 (file)
@@ -1,18 +1,51 @@
 ---
 tiers:
     -
-        name: rapid
+        name: IRQ_rapid_benchmarking
         order: 1
-        description: 'Rapid Testing'
+        description: 'IRQ Rapid Testing'
         testcases:
             -
-                case_name: rapid_tst009
+                case_name: rapid_irq
                 project_name: rapidxt
-                criteria: 100
+                criteria: 499500
+                # Criterium for irq is defined as 500000 -  the maximal allowed interrupt time per PMD loop (in us)
+                blocking: true
+                clean_flag: false
+                description: 'IRQ test'
+                run:
+                    name: rapidxt
+                    args:
+                        test_file: irq.test
+                        runtime: 5
+    -
+        name: TST009_rapid_benchmarking
+        order: 2
+        description: 'TST009 Rapid Testing'
+        testcases:
+            -
+                case_name: rapid_tst009_64b_64f
+                project_name: rapidxt
+                criteria: 0.5
+                # Criterium for TST009 testing is defined as the minimum packets per second received in the generator, expressed in Mpps
                 blocking: true
                 clean_flag: false
                 description: 'TST009 test, 64 byte packets, 64 flows'
                 run:
                     name: rapidxt
                     args:
-                        test_file: TST009_Throughput_64B_64F.test 
+                        test_file: TST009_Throughput_64B_64F.test
+                        runtime: 5
+            -
+                case_name: rapid_tst009_acaeab_16384f
+                project_name: rapidxt
+                criteria: 0.2
+                # Criterium for TST009 testing is defined as the minimum packets per second received in the generator, expressed in Mpps
+                blocking: true
+                clean_flag: false
+                description: 'TST009 test, imix acaeab, 16384 flows'
+                run:
+                    name: rapidxt
+                    args:
+                        test_file: TST009_Throughput_acaeab_16384F.test
+                        runtime: 5