From c517a273b59b874ef22dc93c84407f5a04e2918a Mon Sep 17 00:00:00 2001 From: "Sridhar K. N. Rao" Date: Tue, 11 Jun 2019 13:28:09 +0530 Subject: CONFIG: VSPERF Config-file Generation Wizard. This patch adds a tool create config file. When run, the wizard asks user a set of questions. A config file, named vsperf.conf, will be created. Fix some pylint Issues. JIRA: VSPERF-603 Change-Id: I1b800d8384cb3c0883b7f3859a9df77bb874b7b8 Signed-off-by: Sridhar K. N. Rao --- tools/confgenwizard/vsperfwiz.py | 736 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 736 insertions(+) create mode 100644 tools/confgenwizard/vsperfwiz.py (limited to 'tools/confgenwizard/vsperfwiz.py') diff --git a/tools/confgenwizard/vsperfwiz.py b/tools/confgenwizard/vsperfwiz.py new file mode 100644 index 00000000..48a2d504 --- /dev/null +++ b/tools/confgenwizard/vsperfwiz.py @@ -0,0 +1,736 @@ +# Copyright 2019-2020 Spirent Communications. +# +# 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. + +""" +Tool to create configuration file for VSPERF +""" + +from __future__ import print_function +import signal +import sys +from pypsi import wizard as wiz +from pypsi.shell import Shell +import nicinfo + + +#pylint: disable=too-many-instance-attributes +class VsperfWizard(object): + """ + Class to create wizards + """ + + def __init__(self): + """ + Perform Initialization. + """ + self.shell = Shell() + self.vpp_values = {} + self.dut_values = {} + self.main_values = {} + self.guest_values = {} + self.ovs_values = {} + self.ixnet_values = {} + self.stc_values = {} + self.trex_values = {} + self.traffic_values = {} + self.vpp_values = {} + self.wiz_dut = None + self.wiz_ixnet = None + self.wiz_stc = None + self.wiz_ovs = None + self.wiz_traffic = None + self.wiz_main = None + self.wiz_guest = None + self.wiz_trex = None + self.wiz_vpp = None + self.rhi = None + self.devices = '' + self.devs = {} + + + +######## Support Functions ############################ + def get_nicpcis(self): + """ + Get NIC information from Remote Host + """ + self.rhi = nicinfo.RemoteInfo(self.dut_values['dutip'], + self.dut_values['dutuname'], + self.dut_values['dutpwd']) + dev_list = self.rhi.get_nic_details() + index = 0 + for dev in dev_list: + self.devices += str("(" + str(index) + ")" + " " + + str(dev["Slot"]) + ', ') + self.devs[str(index)] = str(dev["Slot"]) + index = index + 1 + + def get_nics_string(self): + """ + Create string that's acceptable to configuration + """ + indexes = self.main_values['nics'].split(',') + wlns = '' + for index in indexes: + wlns += "'" + self.devs[index] + "' ," + print(wlns) + return wlns.rstrip(',') + + +############# All the Wizards ################################## + + def dut_wizard(self): + """ + Wizard to collect DUT information + """ + self.wiz_dut = wiz.PromptWizard( + name="VSPERF DUT Info Collection", + description="This collects DUT info", + steps=( + # The list of input prompts to ask the user. + wiz.WizardStep( + # ID where the value will be stored + id="dutip", + # Display name + name="Enter the IP address of the DUT [local]", + # Help message + help="IP address of the DUT host", + # List of validators to run on the input + validators=(wiz.required_validator) + ), + wiz.WizardStep( + # ID where the value will be stored + id="dutuname", + # Display name + name="Enter the username to connect to DUT", + # Help message + help="Username for DUT host", + # List of validators to run on the input + validators=(wiz.required_validator) + ), + wiz.WizardStep( + # ID where the value will be stored + id="dutpwd", + # Display name + name="Enter the Password to connect to DUT", + # Help message + help="Password for the DUT host", + # List of validators to run on the input + validators=(wiz.required_validator) + ), + ) + ) + + def main_wizard(self): + """ + The Main Wizard + """ + # First get the nics. + self.get_nicpcis() + self.wiz_main = wiz.PromptWizard( + name="VSPERF Common Configuration", + description="This configuration covers Basic inputs", + steps=( + # The list of input prompts to ask the user. + wiz.WizardStep( + # ID where the value will be stored + id="vswitch", + # Display name + name="VSwitch to use? - OVS or VPP?", + # Help message + help=" Enter the vswitch to use - either OVS or VPP", + # List of validators to run on the input + default='OVS' + ), + wiz.WizardStep( + id='nics', + name="NICs to Whitelist: " + self.devices, + help="Enter the list (separated by comma) of PCI-IDs", + validators=(wiz.required_validator), + ), + wiz.WizardStep( + id='tgen', + name=("What trafficgen to use: [TestCenter" + + " IxNet, Moongen, Trex]?"), + help=("Enter the trafficgen to use -" + + " TestCenter, IxNet, Moongen, Trex"), + validators=(wiz.required_validator), + default="Trex" + ), + wiz.WizardStep( + id='guest', + name=("Is Scenario either PVP or PVVP?"), + help=("This is ti capture guest Configuration"), + validators=(wiz.required_validator), + default="YES" + ) + ) + ) + + def traffic_wizard(self): + """ + Wizard to collectd Traffic Info. + """ + self.wiz_traffic = wiz.PromptWizard( + name="Traffic Configuration", + description="This configuration covers Traffic specifc inputs", + steps=( + wiz.WizardStep( + id='pktsizes', + name='Enter the Packet Sizes - comma separated', + help="Allowed values: (64,128,256,512,1024,1280,1518)", + validators=(wiz.required_validator) + ), + wiz.WizardStep( + id='duration', + name='Enter the Duration (in secs) for the traffic', + help="Enter for how long each iteration should be", + default='60', + ), + # wiz.WizardStep( + # id='multistream', + # name='Multistream preferred?', + # help="Multistream preference - Yes or No", + # default='No', + # validators=(wiz.required_validator) + #), + wiz.WizardStep( + id='count', + name='Number of flows?', + help="Enter the number of flows - 2 - 1,000,000", + default='2', + # validators=(wiz.required_validator) + ), + ) + ) + + def ovs_wizard(self): + """ + Wizard to collect OVS Information + """ + self.wiz_ovs = wiz.PromptWizard( + name="Vswitch Configuration", + description="Specific configurations of the virtual-Switch", + steps=( + wiz.WizardStep( + id='type', + name='OVS Type? [Vanilla or DPDK]', + help='Enter either Vanilla or DPDK', + default='Vanilla', + ), + wiz.WizardStep( + id='mask', + name='Enter the CPU Mask for OVS to use', + help='Mask for OVS PMDs', + default='30', + ), + ) + ) + + def vpp_wizard(self): + """ + Wizard to collect VPP configuration + """ + self.wiz_vpp = wiz.PromptWizard( + name="Vswitch Configuration", + description="Specific configurations of the virtual-Switch", + steps=( + wiz.WizardStep( + id='mode', + name='L2 Connection mode xconnect|bridge|l2patch to use?', + help='Select the l2 connection mode', + default='xconnect', + ), + ) + ) + + def trex_wizard(self): + """ + Wizard to collect Trex configuration + """ + self.wiz_trex = wiz.PromptWizard( + name="Trex Traffic Generator Configuration", + description="Specific configurations of Trex TGen", + steps=( + wiz.WizardStep( + id='hostip', + name='What is IP address of the T-Rex Host?', + help='Enter the IP address of host where Trex is running', + validators=(wiz.required_validator) + ), + wiz.WizardStep( + id='user', + name='What is Usernameof the T-Rex Host?', + help='Enter the Username of host where Trex is running', + default='root', + ), + wiz.WizardStep( + id='bdir', + name='What is Dir where the T-Rex Binary resides?', + help='Enter the Location where Trex Binary is', + default='/root/trex_2.37/scripts/', + ), + wiz.WizardStep( + id='pci1', + name='What is PCI address of the port-1?', + help='Enter the PCI address of Data port 1', + validators=(wiz.required_validator) + ), + wiz.WizardStep( + id='pci2', + name='What is PCI address of the port-2?', + help='Enter the PCI address of Data port 2', + validators=(wiz.required_validator) + ), + wiz.WizardStep( + id='rate', + name='What is Line rate (in Gbps) of the ports?', + help='Enter the linerate of the ports', + default='10', + ), + wiz.WizardStep( + id='prom', + name='T-Rex Promiscuous enabled?', + help='Do you want to enable the Promiscuous mode?', + default='False', + ), + wiz.WizardStep( + id='lat', + name='Whats the Trex Latency PPS?', + help='Enter the Latency value in PPS', + default='1000', + ), + wiz.WizardStep( + id='bslv', + name='Do you want Binary Loss Verification Enabled?', + help='Enter True if you want it to be enabled.', + default='True', + ), + wiz.WizardStep( + id='maxrep', + name='If Loss Verification, what the max rep?', + help='If BSLV is enabled, whats the max repetition value?', + default='2', + ), + ) + ) + + def stc_wizard(self): + """ + Wizard to collect STC configuration + """ + self.wiz_stc = wiz.PromptWizard( + name="Spirent STC Traffic Generator Configuration", + description="Specific configurations of Spirent-STC TGen", + steps=( + wiz.WizardStep( + id='lab', + name='Lab Server IP?', + help='Enter the IP of Lab Server', + default='10.10.120.244', + ), + wiz.WizardStep( + id='lisc', + name='License Server IP?', + help='Enter the IP of the License Server', + default='10.10.120.246', + ), + wiz.WizardStep( + id='eaddr', + name='East Port Chassis Address?', + help='IP address of the East-Port', + default='10.10.120.245', + ), + wiz.WizardStep( + id='eslot', + name='East Port Slot Number', + help='Slot Number of the East Port', + default='1', + ), + wiz.WizardStep( + id='eport', + name='Port Number of the East-Port', + help='Port Number for the East Port', + default='1', + ), + wiz.WizardStep( + id='eint', + name='East port Interface Address', + help='IP to use for East Port?', + default='192.85.1.3', + ), + wiz.WizardStep( + id='egw', + name='Gateway Address for East Port', + help='IP of the East-Port Gateway', + default='192.85.1.103', + ), + wiz.WizardStep( + id='waddr', + name='West Port Chassis Address?', + help='IP address of the West-Port', + default='10.10.120.245', + ), + wiz.WizardStep( + id='wslot', + name='West Port Slot Number', + help='Slot Number of the West Port', + default='1', + ), + wiz.WizardStep( + id='wport', + name='Port Number of the West-Port', + help='Port Number for the West Port', + default='2', + ), + wiz.WizardStep( + id='wint', + name='West port Interface Address', + help='IP to use for West Port?', + default='192.85.1.103', + ), + wiz.WizardStep( + id='wgw', + name='Gateway Address for West Port', + help='IP of the West-Port Gateway', + default='192.85.1.3', + ), + wiz.WizardStep( + id='script', + name='Name of the Script to use for RFC2544 Tests?', + help='Script Name to use for RFC 2544 Tests.', + default='testcenter-rfc2544-rest.py', + ), + ) + ) + + def ixnet_wizard(self): + """ + Wizard to collect ixnet configuration + """ + self.wiz_ixnet = wiz.PromptWizard( + name="Ixia IxNet Traffic Generator Configuration", + description="Specific configurations of Ixia-Ixnet TGen", + steps=( + wiz.WizardStep( + id='card', + name='Card Number?', + help='Chassis Card Number', + default='1', + ), + wiz.WizardStep( + id='port1', + name='Port-1 Number?', + help='Chassis Port-1 Number', + default='5', + ), + wiz.WizardStep( + id='port2', + name='Port-2 Number?', + help='Chassis Port-2 Number', + default='6', + ), + wiz.WizardStep( + id='libp1', + name='IXIA Library path?', + help='Library path of Ixia', + default='/opt/ixnet/ixos-api/8.01.0.2/lib/ixTcl1.0', + ), + wiz.WizardStep( + id='libp2', + name='IXNET Library Path', + help='Library Path for the IXNET', + default='/opt/ixnet/ixnetwork/8.01.1029.6/lib/IxTclNetwork', + ), + wiz.WizardStep( + id='host', + name='IP of the CHassis?', + help='Chassis IP', + default='10.10.50.6', + ), + wiz.WizardStep( + id='machine', + name='IP of the API Server?', + help='API Server IP ', + default='10.10.120.6', + ), + wiz.WizardStep( + id='port', + name='Port of the API Server?', + help='API Server Port', + default='9127', + ), + wiz.WizardStep( + id='user', + name='Username for the API server?', + help='Username to use to connect to API Server', + default='vsperf_sandbox', + ), + wiz.WizardStep( + id='tdir', + name='Path for Results Directory on API Server', + help='Results Path on API Server', + default='c:/ixia_results/vsperf_sandbox', + ), + wiz.WizardStep( + id='rdir', + name='Path for Results directory on DUT', + help='DUT Results Path', + default='/mnt/ixia_results/vsperf_sandbox', + ), + ) + ) + + def guest_wizard(self): + """ + Wizard to collect guest configuration + """ + self.wiz_guest = wiz.PromptWizard( + name="Guest Configuration for PVP and PVVP Scenarios", + description="Guest configurations", + steps=( + wiz.WizardStep( + id='image', + name='Enter the Path for the iamge', + help='Complete path where image resides', + default='/home/opnfv/vloop-vnf-ubuntu-14.04_20160823.qcow2', + ), + wiz.WizardStep( + id='mode', + name='Enter the forwarding mode to use', + help='one of io|mac|mac_retry|macswap|flowgen|rxonly|....', + default='io', + ), + wiz.WizardStep( + id='smp', + name='Number of SMP to use?', + help='While Spawning the guest, how many SMPs to use?', + default='2', + ), + wiz.WizardStep( + id='cores', + name="Guest Core binding. For 2 cores a & b: ['a', 'b']", + help='Enter the cores to use in the specified format', + default="['8', '9']", + ), + ) + ) + +############### All the Run Operations ###################### + + def run_dutwiz(self): + """ + Run the DUT wizard + """ + self.dut_wizard() + self.dut_values = self.wiz_dut.run(self.shell) + + def run_mainwiz(self): + """ + Run the Main wizard + """ + self.main_wizard() + self.main_values = self.wiz_main.run(self.shell) + print(self.main_values['nics']) + + def run_vswitchwiz(self): + """ + Run the vSwitch wizard + """ + if self.main_values['vswitch'] == "OVS": + self.ovs_wizard() + self.ovs_values = self.wiz_ovs.run(self.shell) + elif self.main_values['vswitch'] == 'VPP': + self.vpp_wizard() + self.vpp_values = self.wiz_vpp.run(self.shell) + + def run_trafficwiz(self): + """ + Run the Traffic wizard + """ + self.traffic_wizard() + self.traffic_values = self.wiz_traffic.run(self.shell) + + def run_tgenwiz(self): + """ + Run the Tgen wizard + """ + if self.main_values['tgen'] == "Trex": + self.trex_wizard() + self.trex_values = self.wiz_trex.run(self.shell) + elif self.main_values['tgen'] == "TestCenter": + self.stc_wizard() + self.stc_values = self.wiz_stc.run(self.shell) + elif self.main_values['tgen'] == 'IxNet': + self.ixnet_wizard() + self.ixnet_values = self.wiz_ixnet.run(self.shell) + + def run_guestwiz(self): + """ + Run the Guest wizard + """ + if self.main_values['guest'] == 'YES': + self.guest_wizard() + self.guest_values = self.wiz_guest.run(self.shell) + +################ Prepare Configuration File ################## + #pylint: disable=too-many-statements + def prepare_conffile(self): + """ + Create the Configuration file that can be used with VSPERF + """ + with open("./vsperf.conf", 'w+') as ofile: + ofile.write("#### This file is Automatically Created ####\n\n") + if self.main_values['vswitch'] == "OVS": + if self.ovs_values['type'] == "Vanilla": + ofile.write("VSWITCH = 'OvsVanilla'\n") + else: + ofile.write("VSWITCH = 'OvsDpdkVhost'\n") + ofile.write("VSWITCH_PMD_CPU_MASK = '" + + self.ovs_values['mask'] + "'\n") + else: + ofile.write("VSWITCH = 'VppDpdkVhost'\n") + ofile.write("VSWITCH_VPP_L2_CONNECT_MODE = '" + + self.vpp_values['mode'] + "'\n") + nics = self.get_nics_string() + wln = "WHITELIST_NICS = [" + nics + "]" + "\n" + ofile.write(wln) + ofile.write("RTE_TARGET = 'x86_64-native-linuxapp-gcc'") + ofile.write("\n") + ofile.write("TRAFFICGEN = " + "'" + self.main_values['tgen'] + "'") + ofile.write("\n") + ofile.write("VSWITCH_BRIDGE_NAME = 'vsperf-br0'") + ofile.write("\n") + ofile.write("TRAFFICGEN_DURATION = " + + self.traffic_values['duration'] + "\n") + ofile.write("TRAFFICGEN_LOSSRATE = 0" + "\n") + ofile.write("TRAFFICGEN_PKT_SIZES = (" + + self.traffic_values['pktsizes'] + + ")" + "\n") + if self.main_values['tgen'] == "Trex": + ofile.write("TRAFFICGEN_TREX_HOST_IP_ADDR = '" + + self.trex_values['hostip'] + "'" + "\n") + ofile.write("TRAFFICGEN_TREX_USER = '" + + self.trex_values['user'] + "'" + "\n") + ofile.write("TRAFFICGEN_TREX_BASE_DIR = '" + + self.trex_values['bdir'] + "'" + "\n") + ofile.write("TRAFFICGEN_TREX_LINE_SPEED_GBPS = '" + + self.trex_values['rate'] + "'" + "\n") + ofile.write("TRAFFICGEN_TREX_PORT1 = '" + + self.trex_values['pci1'] + "'" + "\n") + ofile.write("TRAFFICGEN_TREX_PORT2 = '" + + self.trex_values['pci2'] + "'" + "\n") + ofile.write("TRAFFICGEN_TREX_PROMISCUOUS = " + + self.trex_values['prom'] + "\n") + ofile.write("TRAFFICGEN_TREX_LATENCY_PPS = " + + self.trex_values['lat'] + "\n") + ofile.write("TRAFFICGEN_TREX_RFC2544_BINARY_SEARCH_LOSS_VERIFICATION = " + + self.trex_values['bslv']) + ofile.write("TRAFFICGEN_TREX_MAX_REPEAT = " + + self.trex_values['maxrep'] + "\n") + elif self.main_values['tgen'] == "TestCenter": + ofile.write("TRAFFICGEN_STC_LAB_SERVER_ADDR = '" + + self.stc_values['lab'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_LICENSE_SERVER_ADDR = '" + + self.stc_values['lisc'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_EAST_CHASSIS_ADDR = '" + + self.stc_values['eaddr'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_EAST_SLOT_NUM = '" + + self.stc_values['eslot'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_EAST_PORT_NUM = '" + + self.stc_values['eport'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_EAST_INTF_ADDR = '" + + self.stc_values['eint'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_EAST_INTF_GATEWAY_ADDR = '" + + self.stc_values['egw'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_WEST_CHASSIS_ADDR = '" + + self.stc_values['waddr'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_WEST_SLOT_NUM = '" + + self.stc_values['wslot'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_WEST_PORT_NUM = '" + + self.stc_values['wport'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_WEST_INTF_ADDR = '" + + self.stc_values['wint'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_WEST_INTF_GATEWAY_ADDR = '" + + self.stc_values['wgw'] + "'" + "\n") + ofile.write("TRAFFICGEN_STC_RFC2544_TPUT_TEST_FILE_NAME = '" + + self.stc_values['script'] + "'" + "\n") + elif self.main_values['tgen'] == 'IxNet': + print("IXIA Trafficgen") + # Ixia/IxNet configuration + ofile.write("TRAFFICGEN_IXIA_CARD = '" + + self.ixnet_values['card'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXIA_PORT1 = '" + + self.ixnet_values['port1'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXIA_PORT2 = '" + + self.ixnet_values['port2'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXIA_LIB_PATH = '" + + self.ixnet_values['libp1'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXNET_LIB_PATH = '" + + self.ixnet_values['libp2'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXIA_HOST = '" + + self.ixnet_values['host'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXNET_MACHINE = '" + + self.ixnet_values['machine'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXNET_PORT = '" + + self.ixnet_values['port'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXNET_USER = '" + + self.ixnet_values['user'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXNET_TESTER_RESULT_DIR = '" + + self.ixnet_values['tdir'] + "'" + "\n") + ofile.write("TRAFFICGEN_IXNET_DUT_RESULT_DIR = '" + + self.ixnet_values['rdir'] + "'" + "\n") + if self.main_values['guest'] == 'YES': + ofile.write("GUEST_IMAGE = ['" + + self.guest_values['image'] + "']" + "\n") + ofile.write("GUEST_TESTPMD_FWD_MODE = ['" + + self.guest_values['mode'] + "']" + "\n") + ofile.write("GUEST_SMP = ['" + + self.guest_values['smp'] + "']" + "\n") + ofile.write("GUEST_CORE_BINDING = [" + + self.guest_values['cores'] + ",]" + "\n") + + +def signal_handler(signum, frame): + """ + Signal Handler + """ + print("\n You interrupted, No File will be generated!") + print(signum, frame) + sys.exit(0) + + +def main(): + """ + The Main Function + """ + try: + vwiz = VsperfWizard() + vwiz.run_dutwiz() + vwiz.run_mainwiz() + vwiz.run_vswitchwiz() + vwiz.run_trafficwiz() + vwiz.run_tgenwiz() + vwiz.run_guestwiz() + vwiz.prepare_conffile() + except (KeyboardInterrupt, MemoryError): + print("Some Error Occured, No file will be generated!") + + print("Thanks for using the VSPERF-WIZARD, Please look for vsperf.conf " + + "file in the current folder") + + +if __name__ == "__main__": + signal.signal(signal.SIGINT, signal_handler) + main() -- cgit 1.2.3-korg