diff options
Diffstat (limited to 'VNFs/DPPD-PROX/helper-scripts/openstackrapid')
8 files changed, 988 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/README b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/README new file mode 100644 index 00000000..49d819d8 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/README @@ -0,0 +1,57 @@ +## +## 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. +## + +rapid (Rapid Automated Performance Indication for Dataplane) +************************************************************ + +rapid is a set of files offering an easy way to do a sanity check of the +dataplane performance of an OpenStack environment. + +Copy the files in a directory on a machine that can run the OpenStack CLI +commands and that can reach the OpenStack public network. Also create a qcow2 +image in the same directory with the following characteristics: +* Name of the qcow2 file should be: rapidVM.qcow2 + This default name can be changed on the rapid command line +* Should have DPDK and PROX installed. PROX should be in /root/prox/ directory +* Image should have cloud-init installed + +Source the openrc file of the OpenStack environment so that the OpenStack CLI +commands can be run: + # source openrc +Now you can run the rapid.py file. Use help for more info on the usage: + # ./rapid.py --help + +rapid will use the OpenStack CLI to create the flavor, key-pair, network, image, +stack, ... +Then it will connect to the 2 VMs that have been instantiated and it will launch +PROX in both VMs. +Once that is done it will connect to the PROX tcp socket and start sending +commands to run the actual test. +It will print test results on the screen while running. +The PROX instance in the Generator VM will generate packets which will arrive in +the PROX instance running on the SUT (System Under Test) VM. The SUT will then +send the packets back to the generator by swapping source and destination. + +Notes about prox_gen_user_data.sh and prox_sut_user_data.sh scripts: +- These scripts contain commands that will be executed using cloud-init at + startup of the VMs. They contain a hard-coded PCI address for the DPDK + interface that will be used by PROX. You might want to check that this is + actually the right PCI address. +- These scripts also assume some specific DPDK directory and tools which might + change over different DPDK release. They have been tested with DPDK-17.02. +- These scripts are also assuming that this interface is on the "dpdk-network" + network managed by OpenStack. + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/gen.cfg b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/gen.cfg new file mode 100644 index 00000000..522eb801 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/gen.cfg @@ -0,0 +1,64 @@ +;; +;; 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. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[lua] +dofile("parameters.lua") + +[port 0] +name=p0 + +[variables] +$mbs=8 + +[defaults] +mempool size=4K + +[global] +name=Basic Gen + +[core 0] +mode=master + +[core 1] +name=p0 +task=0 +mode=gen +sub mode=l3 +rx ring=yes +tx port=p0 +bps=1250000000 +pkt inline=00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 00 1c 00 01 00 00 40 11 f7 7d ${gen_hex_ip} ${sut_hex_ip} 0b b8 0b b9 00 08 55 7b +gateway ipv4=${sut_ip} +local ipv4=${gen_ip} +min bulk size=$mbs +;random=XXXXXXXXXXXXXXXX +;random=0000000000XXXXXX ; 64 possibilities +;rand_offset=34 ; SOURCE UDP PORT +;random=XXXXXXXXXXXXXXXX +;random=000000000XXXXXXX ; 128 +;rand_offset=36 ; DESTINTAITON UDP PORT + +[core 2] +task=0 +mode=arp +rx port=p0,p0,p0,p0 +tx port=p0 +tx cores=1t0 + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_ctrl.py b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_ctrl.py new file mode 100644 index 00000000..b384e9f0 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_ctrl.py @@ -0,0 +1,218 @@ +## +## 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 subprocess +import socket + +class prox_ctrl(object): + def __init__(self, ip, key=None, user=None): + self._ip = ip + self._key = key + self._user = user + self._children = [] + self._proxsock = [] + + def ip(self): + return self._ip + + def connect(self): + """Simply try to run 'true' over ssh on remote system. + On failure, raise RuntimeWarning exception when possibly worth + retrying, and raise RuntimeError exception otherwise. + """ + return self.run_cmd('true', True) + + def close(self): + """Must be called before program termination.""" + for prox in self._proxsock: + prox.quit() + children = len(self._children) + if children == 0: + return + if children > 1: + print('Waiting for %d child processes to complete ...' % children) + for child in self._children: + ret = os.waitpid(child[0], os.WNOHANG) + if ret[0] == 0: + print("Waiting for child process '%s' to complete ..." % child[1]) + ret = os.waitpid(child[0], 0) + rc = ret[1] + if os.WIFEXITED(rc): + if os.WEXITSTATUS(rc) == 0: + print("Child process '%s' completed successfully" % child[1]) + else: + print("Child process '%s' returned exit status %d" % ( + child[1], os.WEXITSTATUS(rc))) + elif os.WIFSIGNALED(rc): + print("Child process '%s' exited on signal %d" % ( + child[1], os.WTERMSIG(rc))) + else: + print("Wait status for child process '%s' is 0x%04x" % ( + child[1], rc)) + + def run_cmd(self, command, _connect=False): + """Execute command over ssh on remote system. + Wait for remote command completion. + Return command output (combined stdout and stderr). + _connect argument is reserved for connect() method. + """ + cmd = self._build_ssh(command) + try: + return subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + if _connect and ex.returncode == 255: + raise RuntimeWarning(ex.output.strip()) + raise RuntimeError('ssh returned exit status %d:\n%s' + % (ex.returncode, ex.output.strip())) + + def fork_cmd(self, command, name=None): + """Execute command over ssh on remote system, in a child process. + Do not wait for remote command completion. + Return child process id. + """ + if name is None: + name = command + cmd = self._build_ssh(command) + pid = os.fork() + if (pid != 0): + # In the parent process + self._children.append((pid, name)) + return pid + # In the child process: use os._exit to terminate + try: + # Actually ignore output on success, but capture stderr on failure + subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + raise RuntimeError("Child process '%s' failed:\n" + 'ssh returned exit status %d:\n%s' + % (name, ex.returncode, ex.output.strip())) + os._exit(0) + + def prox_sock(self, port=8474): + """Connect to the PROX instance on remote system. + Return a prox_sock object on success, None on failure. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.connect((self._ip, port)) + prox = prox_sock(sock) + self._proxsock.append(prox) + return prox + except: + return None + + def scp_put(self, src, dst): + """Copy src file from local system to dst on remote system.""" + cmd = [ 'scp', + '-B', + '-oStrictHostKeyChecking=no', + '-oUserKnownHostsFile=/dev/null', + '-oLogLevel=ERROR' ] + if self._key is not None: + cmd.extend(['-i', self._key]) + cmd.append(src) + remote = '' + if self._user is not None: + remote += self._user + '@' + remote += self._ip + ':' + dst + cmd.append(remote) + try: + # Actually ignore output on success, but capture stderr on failure + subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + raise RuntimeError('scp returned exit status %d:\n%s' + % (ex.returncode, ex.output.strip())) + + def _build_ssh(self, command): + cmd = [ 'ssh', + '-oBatchMode=yes', + '-oStrictHostKeyChecking=no', + '-oUserKnownHostsFile=/dev/null', + '-oLogLevel=ERROR' ] + if self._key is not None: + cmd.extend(['-i', self._key]) + remote = '' + if self._user is not None: + remote += self._user + '@' + remote += self._ip + cmd.append(remote) + cmd.append(command) + return cmd + +class prox_sock(object): + def __init__(self, sock): + self._sock = sock + self._rcvd = b'' + + def quit(self): + if self._sock is not None: + self._send('quit') + self._sock.close() + self._sock = None + + def start(self, cores): + self._send('start %s' % ','.join(map(str, cores))) + + def stop(self, cores): + self._send('stop %s' % ','.join(map(str, cores))) + + def speed(self, speed, cores, tasks=None): + if tasks is None: + tasks = [ 0 ] * len(cores) + elif len(tasks) != len(cores): + raise ValueError('cores and tasks must have the same len') + for (core, task) in zip(cores, tasks): + self._send('speed %s %s %s' % (core, task, speed)) + + def reset_stats(self): + self._send('reset stats') + + def core_stats(self, cores, task=0): + rx = tx = drop = tsc = hz = 0 + self._send('core stats %s %s' % (','.join(map(str, cores)), task)) + for core in cores: + stats = self._recv().split(',') + rx += int(stats[0]) + tx += int(stats[1]) + drop += int(stats[2]) + tsc = int(stats[3]) + hz = int(stats[4]) + return rx, tx, drop, tsc, hz + + def set_random(self, cores, task, offset, mask, length): + self._send('set random %s %s %s %s %s' % (','.join(map(str, cores)), task, offset, mask, length)) + + def _send(self, cmd): + """Append LF and send command to the PROX instance.""" + if self._sock is None: + raise RuntimeError("PROX socket closed, cannot send '%s'" % cmd) + self._sock.sendall(cmd.encode() + b'\n') + + def _recv(self): + """Receive response from PROX instance, and return it with LF removed.""" + if self._sock is None: + raise RuntimeError("PROX socket closed, cannot receive anymore") + pos = self._rcvd.find(b'\n') + while pos == -1: + self._rcvd += self._sock.recv(256) + pos = self._rcvd.find(b'\n') + rsp = self._rcvd[:pos] + self._rcvd = self._rcvd[pos+1:] + return rsp.decode() + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_gen_user_data.sh b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_gen_user_data.sh new file mode 100644 index 00000000..e7f58a9f --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_gen_user_data.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +## +## 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. +## + +echo 128 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages +mount -t hugetlbfs nodev /mnt/huge +modprobe uio +insmod /root/dpdk/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko +/root/dpdk/usertools/dpdk-devbind.py --force --bind igb_uio 00:04.0 +iptables -F diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_sut_user_data.sh b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_sut_user_data.sh new file mode 100644 index 00000000..e7f58a9f --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/prox_sut_user_data.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +## +## 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. +## + +echo 128 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages +mount -t hugetlbfs nodev /mnt/huge +modprobe uio +insmod /root/dpdk/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko +/root/dpdk/usertools/dpdk-devbind.py --force --bind igb_uio 00:04.0 +iptables -F diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.py b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.py new file mode 100755 index 00000000..1a0ea41c --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.py @@ -0,0 +1,445 @@ +#!/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. +## + +import sys +import time +import subprocess +import getopt +from prox_ctrl import prox_ctrl + +version="17.04.19" +stack = "rapidTestEnv" #Default string for stack +yaml = "rapid.yaml" #Default string for yaml file +key = "prox" # This is also the default in the yaml file.... +flavor = "prox_flavor" # This is also the default in the yaml file.... +image = "rapidVM" # This is also the default in the yaml file.... +image_file = "rapidVM.qcow2" +network = "dpdk-network" # This is also the default in the yaml file.... +subnet = "dpdk-subnet" #Hardcoded at this moment + +def usage(): + print("usage: rapid [--version] [-v]") + print(" [--stack STACK_NAME]") + print(" [--yaml YAML_FILE]") + print(" [--key KEY_NAME]") + print(" [--flavor FLAVOR_NAME]") + print(" [--image IMAGE_NAME]") + print(" [--image_file IMAGE_FILE]") + print(" [--network NETWORK]") + 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 Specify a name for the heat stack. Default is rapidTestEnv.") + print(" --yaml YAML_FILE Specify the yaml file to be used. Default is rapid.yaml.") + print(" --key KEY_NAME Specify the key to be used. Default is prox.") + print(" --flavor FLAVOR_NAME Specify the flavor to be used. Default is prox_flavor.") + print(" --image IMAGE_NAME Specify the image to be used. Default is rapidVM.") + print(" --image_file IMAGE_FILE Specify the image qcow2 file to be used. Default is rapidVM.qcow2.") + print(" --network NETWORK Specify the network name to be used for the dataplane. Default is dpdk-network.") + 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 DPTestEnv") + 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", "yaml=","stack=","key=","flavor=","image=","network="]) +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 ("--yaml"): + yaml = arg + print ("Using stack: "+yaml) + elif opt in ("--key"): + key = arg + print ("Using key: "+key) + elif opt in ("--flavor"): + flavor = arg + print ("Using flavor: "+flavor) + elif opt in ("--image"): + image = arg + print ("Using image: "+image) + elif opt in ("--image_file"): + image_file = arg + print ("Using qcow2 file: "+image_file) + elif opt in ("--network"): + network = arg + print ("Using network: "+ network) + +print("Checking image: "+image) +cmd = 'openstack image show '+image+' |grep "status " | tr -s " " | cut -d" " -f 4' +ImageExist = subprocess.check_output(cmd , shell=True).strip() +if ImageExist == 'active': + print("Image already available") +else: + print('Creating image ...') + cmd = 'openstack image create --disk-format qcow2 --container-format bare --public --file ./'+image_file+ ' ' +image+' |grep "status " | tr -s " " | cut -d" " -f 4' + ImageExist = subprocess.check_output(cmd , shell=True).strip() + if ImageExist == 'active': + print('Image created and active') + cmd = 'openstack image set --property hw_vif_multiqueue_enabled="true" ' +image + subprocess.check_call(cmd , shell=True) + else : + raise Exception("Failed to create image") + +print("Checking key: "+key) +cmd = 'openstack keypair show '+key+' |grep "name " | tr -s " " | cut -d" " -f 4' +KeyExist = subprocess.check_output(cmd , shell=True).strip() +if KeyExist == key: + print("Key already installed") +else: + print('Creating key ...') + cmd = 'openstack keypair create '+ key + '>' +key+'.pem' + subprocess.check_call(cmd , shell=True) + cmd = 'chmod 600 ' +key+'.pem' + subprocess.check_call(cmd , shell=True) + cmd = 'openstack keypair show '+key+' |grep "name " | tr -s " " | cut -d" " -f 4' + KeyExist = subprocess.check_output(cmd , shell=True).strip() + if KeyExist == key: + print("Key created") + else : + raise Exception("Failed to create key: " + key) + +print("Checking flavor: "+flavor) +cmd = 'openstack flavor show '+flavor+' |grep "name " | tr -s " " | cut -d" " -f 4' +FlavorExist = subprocess.check_output(cmd , shell=True).strip() +if FlavorExist == flavor: + print("Flavor already installed") +else: + print('Creating flavor ...') + cmd = 'openstack flavor create '+flavor+' --ram 8192 --disk 80 --vcpus 4 |grep "name " | tr -s " " | cut -d" " -f 4' + FlavorExist = subprocess.check_output(cmd , shell=True).strip() + if FlavorExist == flavor: + cmd = 'openstack flavor set '+ flavor +' --property hw:mem_page_size="large" --property hw:cpu_policy="dedicated" --property hw:cpu_threads_policy="isolate"' + subprocess.check_call(cmd , shell=True) + print("Flavor created") + else : + raise Exception("Failed to create flavor: " + flavor) + +print("Checking network: "+network) +cmd = 'openstack network show '+network+' |grep "status " | tr -s " " | cut -d" " -f 4' +NetworkExist = subprocess.check_output(cmd , shell=True).strip() +if NetworkExist == 'ACTIVE': + print("Network already active") +else: + print('Creating network ...') + cmd = 'openstack network create '+network+' |grep "status " | tr -s " " | cut -d" " -f 4' + NetworkExist = subprocess.check_output(cmd , shell=True).strip() + if NetworkExist == 'ACTIVE': + print("Network created") + else : + raise Exception("Failed to create network: " + network) + +print("Checking subnet: "+subnet) +cmd = 'neutron subnet-show '+ subnet+' |grep "name " | tr -s " " | cut -d" " -f 4' +SubnetExist = subprocess.check_output(cmd , shell=True).strip() +if SubnetExist == subnet: + print("Subnet already exists") +else: + print('Creating subnet ...') + cmd = 'neutron subnet-create --name '+ subnet+ ' ' +network+' 10.10.10.0/24 |grep "name " | tr -s " " | cut -d" " -f 4' + SubnetExist = subprocess.check_output(cmd , shell=True).strip() + if SubnetExist == subnet: + print("Subnet created") + else : + raise Exception("Failed to create subnet: " + subnet) + +print("Checking Stack: "+stack) +cmd = 'openstack stack show '+stack+' |grep "stack_status " | tr -s " " | cut -d" " -f 4' +StackRunning = subprocess.check_output(cmd , shell=True).strip() +if StackRunning == '': + print('Creating Stack ...') + cmd = 'openstack stack create -t '+ yaml + ' --parameter flavor="'+flavor +'" --parameter key="'+ key + '" --parameter image="'+image + '" --parameter dpdk_network="'+network+'" --wait '+stack +' |grep "stack_status " | tr -s " " | cut -d" " -f 4' + StackRunning = subprocess.check_output(cmd , shell=True).strip() +if StackRunning != 'CREATE_COMPLETE': + raise Exception("Failed to create stack") + +print('Stack running') +genName=stack+'-gen' +sutName=stack+'-sut' +cmd = 'nova list | grep '+ genName +' | tr -s " " | cut -d " " -f 4' +genVMName = subprocess.check_output(cmd , shell=True).strip() +print('Generator: '+ genVMName) +cmd = 'nova list | grep '+ sutName +' | tr -s " " | cut -d " " -f 4' +sutVMName = subprocess.check_output(cmd , shell=True).strip() +print('SUT: '+ sutVMName) +cmd='nova show ' + genVMName + ' | grep "dpdk-network" | tr -s " " | cut -d" " -f 5' +genDPIP = subprocess.check_output(cmd , shell=True).strip() +cmd='nova show ' + genVMName + ' | grep "admin_internal_net" | tr -s " " | cut -d" " -f 6' +genAdminIP = subprocess.check_output(cmd , shell=True).strip() +cmd='nova show ' + sutVMName + ' | grep "dpdk-network" | tr -s " " | cut -d" " -f 5' +sutDPIP = subprocess.check_output(cmd , shell=True).strip() +cmd='nova show ' + sutVMName + ' | grep "admin_internal_net" | tr -s " " | cut -d" " -f 6' +sutAdminIP = subprocess.check_output(cmd , shell=True).strip() + +#======================================================================== +def connect_socket(client): + attempts = 1 + print("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: + raise Exception("Failed to connect to PROX on %s after %d attempts" + % (client.ip(), attempts)) + time.sleep(10) + print("Trying to connect to PROX (just launched) on %s, attempt: %d" + % (client.ip(), attempts)) + print("Connected to PROX on %s" % client.ip()) + return sock + +def connect_client(client): + attempts = 1 + print ("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: + raise Exception("Failed to connect to VM after %d attempts:\n%s" + % (attempts, ex)) + time.sleep(15) + print ("Trying to connect to VM which was just launched on %s, attempt: %d" + % (client.ip(), attempts)) + print("Connected to VM on %s" % client.ip()) + + +def run_testA(): + global genclient + global sutclient + 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.scp_put('./gen.cfg', '/root/gen.cfg') + sutclient.scp_put('./sut.cfg', '/root/sut.cfg') + genclient.scp_put('./parameters.lua', '/root/parameters.lua') + sutclient.scp_put('./parameters.lua', '/root/parameters.lua') + print("Config files copied") + cmd = '/root/prox/build/prox -e -t -o cli -f /root/gen.cfg' + genclient.fork_cmd(cmd, 'PROX GEN') + cmd = '/root/prox/build/prox -t -o cli -f /root/sut.cfg' + sutclient.fork_cmd(cmd, 'PROX SUT') + gensock = connect_socket(genclient) + sutsock = connect_socket(sutclient) + new_speed = 100 + attempts = 0 + cores = [1,2] + gencores = [1] + gensock.reset_stats() + sutsock.reset_stats() + gensock.start([2]) + print("+---------------------------------------------------------------------------------------------------------+") + print("| Generator is sending UDP (1 flow) packets (64 bytes) to SUT. SUT sends packets back |") + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + print("| Test | Speed requested | Req to Generate| Sent by Gen | Forward by SUT | Rec. by Gen | Result |") + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + while (new_speed > 0.1): + attempts += 1 + # Start generating packets at requested speed (in % of a 10Gb/s link) + gensock.speed(new_speed, gencores) + gensock.start(gencores) + time.sleep(1) + # Get statistics now that the generation is stable and NO ARP messages any more + 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(10) + # Get statistics after some execution time + new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(cores) + 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. + sut_rx = new_sut_rx - old_sut_rx + sut_tx = new_sut_tx - old_sut_tx + sut_tsc = new_sut_tsc - old_sut_tsc + if (tx == 0): + raise Exception("TX = 0") + drop_rate = round(((drop-rx) * 100.0)/(tx+drop-rx),1) + pps_req_tx = round((tx+drop-rx)*tsc_hz*1.0/(tsc*1000000),5) + pps_tx = round(tx*tsc_hz*1.0/(tsc*1000000),5) + pps_rx = round(rx*tsc_hz*1.0/(tsc*1000000),5) + pps_sut_tx = round(sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000),5) + if ((drop_rate) < 1): + # This will stop the test when number of dropped packets is below a certain percentage + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + print('|{:>5}'.format(str(attempts))+" | "+ '{:>14}'.format(str(new_speed)) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(str(pps_sut_tx)) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps | SUCCESS |") + print("+------+-----------------+----------------+----------------+----------------+----------------+------------+") + break + else: + print('|{:>5}'.format(str(attempts))+" | "+ '{:>14}'.format(str(new_speed)) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(str(pps_sut_tx)) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps | FAILED |") + # 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)) + new_speed = (int(new_speed*ratio*100)+0.5)/100 + gensock.quit() + sutsock.quit() + time.sleep(2) + print("") + +def run_testB(): + global genclient + global sutclient + 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.scp_put('./gen.cfg', '/root/gen.cfg') + sutclient.scp_put('./sut.cfg', '/root/sut.cfg') + genclient.scp_put('./parameters.lua', '/root/parameters.lua') + sutclient.scp_put('./parameters.lua', '/root/parameters.lua') + print("Config files copied") + cmd = '/root/prox/build/prox -e -t -o cli -f /root/gen.cfg' + genclient.fork_cmd(cmd, 'PROX GEN') + cmd = '/root/prox/build/prox -t -o cli -f /root/sut.cfg' + sutclient.fork_cmd(cmd, 'PROX SUT') + gensock = connect_socket(genclient) + sutsock = connect_socket(sutclient) + print("+----------------------------------------------------------------------------------------------+") + print("| UDP, 64 bytes, different number of flows by randomizing SRC & DST UDP port |") + print("+--------+-----------------+----------------+----------------+----------------+----------------+") + print("| Flows | Speed requested | Req to Generate| Sent by Gen | Forward by SUT | Rec. by Gen |") + print("+--------+-----------------+----------------+----------------+----------------+----------------+") + cores = [1,2] + gencores = [1] + gensock.start([2]) + new_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:['0000000000000XXX','000000000000XXXX'],1024:['00000000000XXXXX','00000000000XXXXX'],8192:['0000000000XXXXXX','000000000XXXXXXX'],65535:['00000000XXXXXXXX','00000000XXXXXXXX'],524280:['0000000XXXXXXXXX','000000XXXXXXXXXX']} + for flow_number in sorted(flows.iterkeys()): + #new_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() + 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 (new_speed > 0.1): + attempts += 1 + # Start generating packets at requested speed (in % of a 10Gb/s link) + gensock.speed(new_speed, gencores) + gensock.start(gencores) + time.sleep(1) + # Get statistics now that the generation is stable and NO ARP messages any more + 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(10) + # Get statistics after some execution time + new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(cores) + 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. + sut_rx = new_sut_rx - old_sut_rx + sut_tx = new_sut_tx - old_sut_tx + sut_tsc = new_sut_tsc - old_sut_tsc + if (tx == 0): + raise Exception("TX = 0") + drop_rate = round(((drop-rx) * 100.0)/(tx+drop-rx),1) + pps_req_tx = round((tx+drop-rx)*tsc_hz*1.0/(tsc*1000000),5) + pps_tx = round(tx*tsc_hz*1.0/(tsc*1000000),5) + pps_rx = round(rx*tsc_hz*1.0/(tsc*1000000),5) + pps_sut_tx = round(sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000),5) + if ((drop_rate) < 1): + # This will stop the test when number of dropped packets is below a certain percentage + print('|{:>7}'.format(str(flow_number))+" | "+ '{:>14}'.format(str(new_speed)) + '% | '+ '{:>9}'.format(str(pps_req_tx))+' Mpps | '+ '{:>9}'.format(str(pps_tx)) +' Mpps | ' + '{:>9}'.format(str(pps_sut_tx)) +' Mpps | '+ '{:>9}'.format(str(pps_rx))+" Mpps |") + print("+--------+-----------------+----------------+----------------+----------------+----------------+") + break + # 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)) + new_speed = (int(new_speed*ratio*100)+0.5)/100 + gensock.quit() + sutsock.quit() + time.sleep(2) + print("") + +#======================================================================== +genclient = prox_ctrl(genAdminIP, key+'.pem') +connect_client(genclient) +sutclient = prox_ctrl(sutAdminIP, key+'.pem') +connect_client(sutclient) +##################################################################################### +run_testA() +run_testB() +##################################################################################### +genclient.close() +sutclient.close() + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.yaml b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.yaml new file mode 100644 index 00000000..eab957f5 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/rapid.yaml @@ -0,0 +1,105 @@ +## +## 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. +## + +heat_template_version: 2016-04-08 +description: RAPID stack (Rapid Automated Performance Indication for Dataplane) +parameters: + image: + type: string + label: Image name or ID + description: Image to be used for compute instance + default: RapidVM + flavor: + type: string + label: Flavor + description: Type of instance (flavor) to be used + default: prox_flavor + key: + type: string + label: Key name + description: Name of key-pair to be used for compute instance + default: prox + dpdk_network: + type: string + label: Private network name or ID + description: Network to attach instance to. + default: dpdk-network + private_network: + type: string + label: Private network name or ID + description: Network to attach instance to. + default: admin_internal_net + availability_zone: + type: string + description: The Availability Zone to launch the instance. + default: nova + +resources: + sut: + type: OS::Nova::Server + properties: + availability_zone: { get_param: availability_zone } + user_data: + get_file: prox_sut_user_data.sh + key_name: { get_param: key } + image: { get_param: image } + flavor: { get_param: flavor } + networks: + - network: { get_param: private_network } + - network: { get_param: dpdk_network } + gen: + type: OS::Nova::Server + properties: + availability_zone: { get_param: availability_zone } + user_data: + get_file: prox_gen_user_data.sh + key_name: { get_param: key } + image: { get_param: image } + flavor: { get_param: flavor } + networks: + - network: { get_param: private_network } + - network: { get_param: dpdk_network } + + sut_floating_ip: + type: OS::Nova::FloatingIP + properties: + pool: admin_floating_net + + gen_floating_ip: + type: OS::Nova::FloatingIP + properties: + pool: admin_floating_net + + sut_association: + type: OS::Nova::FloatingIPAssociation + properties: + floating_ip: { get_resource: sut_floating_ip } + server_id: { get_resource: sut } + + gen_association: + type: OS::Nova::FloatingIPAssociation + properties: + floating_ip: { get_resource: gen_floating_ip } + server_id: { get_resource: gen } + +outputs: + sut_ip: + description: IP address of the instance + value: { get_attr: [sut, first_address] } + gen_ip: + description: IP address of the instance + value: { get_attr: [gen, first_address] } + diff --git a/VNFs/DPPD-PROX/helper-scripts/openstackrapid/sut.cfg b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/sut.cfg new file mode 100644 index 00000000..2937a749 --- /dev/null +++ b/VNFs/DPPD-PROX/helper-scripts/openstackrapid/sut.cfg @@ -0,0 +1,51 @@ +;; +;; 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. +;; + +[eal options] +-n=4 ; force number of memory channels +no-output=no ; disable DPDK debug output + +[lua] +dofile("parameters.lua") + +[port 0] +name=if0 +mac=hardware + +[defaults] +mempool size=2K + +[global] +name=NOP forwarding + +[core 0] +mode=master + +[core 1] +name=swap +task=0 +mode=arp +sub mode=local +rx port=if0 +tx port=if0 +tx cores=1t1 +local ipv4=${sut_ip} +task=1 +mode=swap +rx ring=yes +tx port=if0 +drop=no + |