diff options
Diffstat (limited to 'VNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py')
-rwxr-xr-x | VNFs/DPPD-PROX/helper-scripts/openstackrapid/runrapid.py | 453 |
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() |