summaryrefslogtreecommitdiffstats
path: root/VNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py
diff options
context:
space:
mode:
Diffstat (limited to 'VNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py')
-rwxr-xr-xVNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py453
1 files changed, 453 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py
new file mode 100755
index 00000000..b421c709
--- /dev/null
+++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py
@@ -0,0 +1,453 @@
+#!/usr/bin/python
+
+##
+## Copyright (c) 2010-2017 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.
+##
+
+from __future__ import print_function
+
+import os
+import stat
+import sys
+import time
+import subprocess
+import getopt
+import re
+import logging
+from logging.handlers import RotatingFileHandler
+from logging import handlers
+from prox_ctrl import prox_ctrl
+import ConfigParser
+
+version="17.09.03"
+stack = "rapidTestEnv" #Default string for stack
+loglevel="DEBUG" # sets log level for writing to file
+runtime=10 # time in seconds for 1 test run
+
+def usage():
+ print("usage: rapid [--version] [-v]")
+ print(" [--stack STACK_NAME]")
+ print(" [--runtime TIME_FOR_TEST]")
+ print(" [--log DEBUG|INFO|WARNING|ERROR|CRITICAL")
+ print(" [-h] [--help]")
+ print("")
+ print("Command-line interface to RAPID")
+ print("")
+ print("optional arguments:")
+ print(" -v, --version Show program's version number and exit")
+ print(" --stack STACK_NAME Parameters will be read from STACK_NAME.cfg Default is rapidTestEnv.")
+ print(" --runtime Specify time in seconds for 1 test run")
+ print(" --log Specify logging level for log file output, screen output level is hard coded")
+ print(" -h, --help Show help message and exit.")
+ print("")
+ print("To delete the rapid stack, type the following command")
+ print(" openstack stack delete --yes --wait rapidTestEnv")
+ print("Note that rapidTestEnv is the default stack name. Replace with STACK_NAME if needed")
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "stack=","runtime=","log="])
+except getopt.GetoptError as err:
+ print("===========================================")
+ print(str(err))
+ print("===========================================")
+ usage()
+ sys.exit(2)
+if args:
+ usage()
+ sys.exit(2)
+for opt, arg in opts:
+ if opt in ("-h", "--help"):
+ usage()
+ sys.exit()
+ if opt in ("-v", "--version"):
+ print("Rapid Automated Performance Indication for Dataplane "+version)
+ sys.exit()
+ if opt in ("--stack"):
+ stack = arg
+ print ("Using '"+stack+"' as name for the stack")
+ elif opt in ("--runtime"):
+ runtime = arg
+ print ("Runtime: "+ runtime)
+ elif opt in ("--log"):
+ loglevel = arg
+ print ("Log level: "+ loglevel)
+
+
+# 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)
+console_handler.setFormatter(screen_formatter)
+
+# create a file handler
+# and set its log level to DEBUG
+#
+log_file = 'RUN' +stack +'.log'
+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
+ log.handlers[0].doRollover()
+
+# Add timestamp
+log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
+
+log.debug("runrapid.py version: "+version)
+#========================================================================
+def connect_socket(client):
+ attempts = 1
+ log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
+ sock = None
+ while True:
+ sock = client.prox_sock()
+ if sock is not None:
+ break
+ attempts += 1
+ if attempts > 20:
+ log.exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
+ raise Exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
+ time.sleep(8)
+ log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
+ log.info("Connected to PROX on %s" % client.ip())
+ return sock
+
+def connect_client(client):
+ attempts = 1
+ log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
+ while True:
+ try:
+ client.connect()
+ break
+ except RuntimeWarning, ex:
+ attempts += 1
+ if attempts > 20:
+ log.exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
+ raise Exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
+ time.sleep(8)
+ log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
+ log.info("Connected to VM on %s" % client.ip())
+
+def run_iteration(gensock,sutsock,cores,gencores):
+ if sutAdminIP!='none':
+ old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats([1])
+ old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(cores)
+ time.sleep(float(runtime))
+ # Get statistics after some execution time
+ new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(cores)
+ if sutAdminIP!='none':
+ new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats([1])
+ time.sleep(1)
+ #Stop generating
+ gensock.stop(gencores)
+ drop = new_drop-old_drop # drop is all packets dropped by all tasks. This includes packets dropped at the generator task + packets dropped by the nop task. In steady state, this equals to the number of packets received by this VM
+ rx = new_rx - old_rx # rx is all packets received by the nop task = all packets received in the gen VM
+ tx = new_tx - old_tx # tx is all generated packets actually accepted by the interface
+ tsc = new_tsc - old_tsc # time difference between the 2 measurements, expressed in cycles.
+ pps_req_tx = round((tx+drop-rx)*tsc_hz*1.0/(tsc*1000000),3)
+ pps_tx = round(tx*tsc_hz*1.0/(tsc*1000000),3)
+ pps_rx = round(rx*tsc_hz*1.0/(tsc*1000000),3)
+ if sutAdminIP!='none':
+ sut_rx = new_sut_rx - old_sut_rx
+ sut_tx = new_sut_tx - old_sut_tx
+ sut_tsc = new_sut_tsc - old_sut_tsc
+ pps_sut_tx = round(sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000),3)
+ pps_sut_tx_str = str(pps_sut_tx)
+ else:
+ pps_sut_tx = 0
+ pps_sut_tx_str = 'NO MEAS.'
+ if (tx == 0):
+ raise Exception("TX = 0")
+ drop_rate = round(((pps_req_tx-pps_rx) * 100.0)/pps_req_tx,1)
+ return(drop_rate,pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx)
+
+def new_speed(speed,drop_rate):
+ # Following calculates the ratio for the new speed to be applied
+ # On the Y axis, we will find the ratio, a number between 0 and 1
+ # On the x axis, we find the % of dropped packets, a number between 0 and 100
+ # 2 lines are drawn and we take the minumun of these lines to calculate the ratio
+ # One line goes through (0,y0) and (p,q)
+ # The second line goes through (p,q) and (100,y100)
+ y0=0.99
+ y100=0.1
+ p=15
+ q=.9
+ ratio = min((q-y0)/p*drop_rate+y0,(q-y100)/(p-100)*drop_rate+q-p*(q-y100)/(p-100))
+ return (int(speed*ratio*100)+0.5)/100
+
+def run_speedtest():
+ global genclient
+ global sutclient
+ log.info("Starting PROX")
+ speed = 100
+ attempts = 0
+ cores = [1]
+ gencores = [1]
+ cmd = '/root/prox/build/prox -e -t -o cli -f /root/gen.cfg'
+ genclient.fork_cmd(cmd, 'PROX GEN speed Test')
+ gensock = connect_socket(genclient)
+ gensock.reset_stats()
+ if sutAdminIP!='none':
+ cmd = '/root/prox/build/prox -t -o cli -f /root/sut.cfg'
+ sutclient.fork_cmd(cmd, 'PROX SUT speed Test')
+ sutsock = connect_socket(sutclient)
+ sutsock.reset_stats()
+ else:
+ sutsock = 'none'
+ log.info("+-----------------------------------------------------------------------------------------------------------+")
+ log.info("| Generator is sending UDP (1 flow) packets (64 bytes) to SUT. SUT sends packets back |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+------------+")
+ log.info("| Test | Speed requested | Req to Generate| Sent by Gen | Forward by SUT | Rec. by Gen | Result |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+------------+")
+ while (speed > 0.1):
+ attempts += 1
+ print('Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
+ sys.stdout.flush()
+ # Start generating packets at requested speed (in % of a 10Gb/s link)
+ gensock.speed(speed, gencores)
+ gensock.start(gencores)
+ time.sleep(1)
+ # Get statistics now that the generation is stable and NO ARP messages any more
+ drop_rate,pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx = run_iteration(gensock,sutsock,cores,gencores)
+ if ((drop_rate) < 1):
+ # This will stop the test when number of dropped packets is below a certain percentage
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+------------+")
+ log.info('|{:>7}'.format(str(attempts))+" | "+ '{:>14}'.format(str(round(speed,2))) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps | SUCCESS |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+------------+")
+ break
+ else:
+ log.info('|{:>7}'.format(str(attempts))+" | "+ '{:>14}'.format(str(round(speed,2))) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps | FAILED |")
+ speed = new_speed(speed,drop_rate)
+ gensock.quit()
+ if sutAdminIP!='none':
+ sutsock.quit()
+ time.sleep(2)
+
+
+# print("")
+
+def run_flowtest():
+ global genclient
+ global sutclient
+ log.info("Starting PROX")
+ speed = 100
+ attempts = 0
+ cores = [1]
+ gencores = [1]
+ cmd = '/root/prox/build/prox -e -t -o cli -f /root/gen.cfg'
+ genclient.fork_cmd(cmd, 'PROX GEN flow Test')
+ gensock = connect_socket(genclient)
+ gensock.reset_stats()
+ if sutAdminIP!='none':
+ cmd = '/root/prox/build/prox -t -o cli -f /root/sut.cfg'
+ sutclient.fork_cmd(cmd, 'PROX SUT flow Test')
+ sutsock = connect_socket(sutclient)
+ sutsock.reset_stats()
+ else:
+ sutsock = 'none'
+ log.info("+----------------------------------------------------------------------------------------------+")
+ log.info("| UDP, 64 bytes, different number of flows by randomizing SRC & DST UDP port |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+")
+ log.info("| Flows | Speed requested | Req to Generate| Sent by Gen | Forward by SUT | Rec. by Gen |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+")
+ cores = [1]
+ gencores = [1]
+ speed = 100
+ # To generate a desired number of flows, PROX will randomize the bits in source and destination ports, as specified by the bit masks in the flows variable.
+ flows={128:['1000000000000XXX','100000000000XXXX'],1024:['10000000000XXXXX','10000000000XXXXX'],8192:['1000000000XXXXXX','100000000XXXXXXX'],65535:['10000000XXXXXXXX','10000000XXXXXXXX'],524280:['1000000XXXXXXXXX','100000XXXXXXXXXX']}
+ for flow_number in sorted(flows.iterkeys()):
+ #speed = 100 Commented out: Not starting from 100% since we are trying more flows, so speed will not be higher than the speed achieved in previous loop
+ attempts = 0
+ gensock.reset_stats()
+ if sutAdminIP!='none':
+ sutsock.reset_stats()
+ source_port,destination_port = flows[flow_number]
+ gensock.set_random(gencores,0,34,source_port,2)
+ gensock.set_random(gencores,0,36,destination_port,2)
+ while (speed > 0.1):
+ print(str(flow_number)+' flows: Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
+ sys.stdout.flush()
+ attempts += 1
+ # Start generating packets at requested speed (in % of a 10Gb/s link)
+ gensock.speed(speed, gencores)
+ gensock.start(gencores)
+ time.sleep(1)
+ # Get statistics now that the generation is stable and NO ARP messages any more
+ drop_rate,pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx = run_iteration(gensock,sutsock,cores,gencores)
+ if ((drop_rate) < 1):
+ # This will stop the test when number of dropped packets is below a certain percentage
+ log.info('|{:>7}'.format(str(flow_number))+" | "+ '{:>14}'.format(str(round(speed,2))) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+")
+ break
+ speed = new_speed(speed,drop_rate)
+ gensock.quit()
+ if sutAdminIP!='none':
+ sutsock.quit()
+ time.sleep(2)
+# print("")
+
+def run_sizetest():
+ global genclient
+ global sutclient
+ log.info("Starting PROX")
+ speed = 100
+ attempts = 0
+ cores = [1]
+ gencores = [1]
+ cmd = '/root/prox/build/prox -e -t -o cli -f /root/gen.cfg'
+ genclient.fork_cmd(cmd, 'PROX GEN size Test')
+ gensock = connect_socket(genclient)
+ gensock.reset_stats()
+ if sutAdminIP!='none':
+ cmd = '/root/prox/build/prox -t -o cli -f /root/sut.cfg'
+ sutclient.fork_cmd(cmd, 'PROX SUT size Test')
+ sutsock = connect_socket(sutclient)
+ sutsock.reset_stats()
+ else:
+ sutsock = 'none'
+ log.info("+----------------------------------------------------------------------------------------------+")
+ log.info("| UDP, 1 flow, different packet sizes |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+")
+ log.info("| Pktsize| Speed requested | Req to Generate| Sent by Gen | Forward by SUT | Rec. by Gen |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+")
+ cores = [1]
+ gencores = [1]
+ speed = 100
+ # To generate a desired number of flows, PROX will randomize the bits in source and destination ports, as specified by the bit masks in the flows variable.
+ sizes=[1500,1024,512,256,128,64]
+ for size in sizes:
+ #speed = 100 Commented out: Not starting from 100% since we are trying smaller packets, so speed will not be higher than the speed achieved in previous loop
+ attempts = 0
+ gensock.reset_stats()
+ if sutAdminIP!='none':
+ sutsock.reset_stats()
+ gensock.set_size(gencores,0,size) # This is setting the frame size
+ gensock.set_value(gencores,0,16,(size-18),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
+ gensock.set_value(gencores,0,38,(size-38),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
+ # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
+ while (speed > 0.1):
+ print(str(size)+' bytes: Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
+ sys.stdout.flush()
+ attempts += 1
+ # Start generating packets at requested speed (in % of a 10Gb/s link)
+ gensock.speed(speed, gencores)
+ gensock.start(gencores)
+ time.sleep(1)
+ # Get statistics now that the generation is stable and NO ARP messages any more
+ drop_rate,pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx = run_iteration(gensock,sutsock,cores,gencores)
+ if ((drop_rate) < 1):
+ # This will stop the test when number of dropped packets is below a certain percentage
+ log.info('|{:>7}'.format(str(size))+" | "+ '{:>14}'.format(str(round(speed,2))) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps |")
+ log.info("+--------+-----------------+----------------+----------------+----------------+----------------+")
+ break
+ speed = new_speed(speed,drop_rate)
+ gensock.quit()
+ if sutAdminIP!='none':
+ sutsock.quit()
+ time.sleep(2)
+#========================================================================
+config = ConfigParser.RawConfigParser()
+config.read(stack+'.cfg')
+
+genAdminIP = config.get('Generator', 'admin_ip')
+genDPmac = config.get('Generator', 'dp_mac')
+genDPIP = config.get('Generator', 'dp_ip')
+sutAdminIP = config.get('SUT', 'admin_ip')
+sutDPmac = config.get('SUT', 'dp_mac')
+sutDPIP = config.get('SUT', 'dp_ip')
+key = config.get('OpenStack', 'key')
+ip = genDPIP.split('.')
+hexgenDPIP=hex(int(ip[0]))[2:].zfill(2) + ' ' + hex(int(ip[1]))[2:].zfill(2) + ' ' + hex(int(ip[2]))[2:].zfill(2) + ' ' + hex(int(ip[3]))[2:].zfill(2)
+ip = sutDPIP.split('.')
+hexsutDPIP=hex(int(ip[0]))[2:].zfill(2) + ' ' + hex(int(ip[1]))[2:].zfill(2) + ' ' + hex(int(ip[2]))[2:].zfill(2) + ' ' + hex(int(ip[3]))[2:].zfill(2)
+with open("parameters.lua", "w") as f:
+ f.write('gen_hex_ip="'+hexgenDPIP+'"\n')
+ f.write('sut_hex_ip="'+hexsutDPIP+'"\n')
+ f.write('gen_ip="'+genDPIP+'"\n')
+ f.write('sut_ip="'+sutDPIP+'"\n')
+ f.close
+
+#####################################################################################
+genclient = prox_ctrl(genAdminIP, key+'.pem','root')
+connect_client(genclient)
+genclient.scp_put('./gen.cfg', '/root/gen.cfg')
+genclient.scp_put('./parameters.lua', '/root/parameters.lua')
+# Creating script to bind the right network interface to the poll mode driver
+with open("devbind.sh") as f:
+ newText=f.read().replace('MACADDRESS', genDPmac)
+with open("gendevbind.sh", "w") as f:
+ f.write(newText)
+st = os.stat('gendevbind.sh')
+os.chmod('gendevbind.sh', st.st_mode | stat.S_IEXEC)
+genclient.scp_put('./gendevbind.sh', '/root/gendevbind.sh')
+cmd = '/root/gendevbind.sh'
+genclient.run_cmd(cmd)
+log.info("Generator Config files copied & running devbind.sh")
+
+#####################################################################################
+if sutAdminIP!='none':
+ sutclient = prox_ctrl(sutAdminIP, key+'.pem','root')
+ connect_client(sutclient)
+ sutclient.scp_put('./sut.cfg', '/root/sut.cfg')
+ sutclient.scp_put('./parameters.lua', '/root/parameters.lua')
+# Creating script to bind the right network interface to the poll mode driver
+ with open("devbind.sh") as f:
+ newText=f.read().replace('MACADDRESS', sutDPmac)
+ with open("sutdevbind.sh", "w") as f:
+ f.write(newText)
+ st = os.stat('sutdevbind.sh')
+ os.chmod('sutdevbind.sh', st.st_mode | stat.S_IEXEC)
+ sutclient.scp_put('./sutdevbind.sh', '/root/sutdevbind.sh')
+ cmd = '/root/sutdevbind.sh'
+ sutclient.run_cmd(cmd)
+ log.info("SUT Config files copied & running devbind.sh")
+run_speedtest()
+run_flowtest()
+run_sizetest()
+#####################################################################################
+genclient.close()
+if sutAdminIP!='none':
+ sutclient.close()