# 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()