b11c288f11
Change-Id: I30b1cdb2930560d7c40c1bde098fd21f16a17683 Signed-off-by: Paul Vinciguerra <pvinci@vinciconsulting.com>
2025 lines
71 KiB
Python
2025 lines
71 KiB
Python
# Copyright (c) 2016 Cisco and/or its affiliates.
|
|
# 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.
|
|
|
|
"""Library that supports Auto Configuration."""
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
import logging
|
|
import os
|
|
import re
|
|
from ipaddress import ip_address
|
|
|
|
import yaml
|
|
|
|
from vpplib.VPPUtil import VPPUtil
|
|
from vpplib.VppPCIUtil import VppPCIUtil
|
|
from vpplib.VppHugePageUtil import VppHugePageUtil
|
|
from vpplib.CpuUtils import CpuUtils
|
|
from vpplib.VppGrubUtil import VppGrubUtil
|
|
from vpplib.QemuUtils import QemuUtils
|
|
|
|
# Python2/3 compatible
|
|
try:
|
|
input = raw_input # noqa
|
|
except NameError:
|
|
pass
|
|
|
|
__all__ = ["AutoConfig"]
|
|
|
|
# Constants
|
|
MIN_SYSTEM_CPUS = 2
|
|
MIN_TOTAL_HUGE_PAGES = 1024
|
|
MAX_PERCENT_FOR_HUGE_PAGES = 70
|
|
|
|
IPERFVM_XML = 'configs/iperf-vm.xml'
|
|
IPERFVM_IMAGE = 'images/xenial-mod.img'
|
|
IPERFVM_ISO = 'configs/cloud-config.iso'
|
|
|
|
|
|
class AutoConfig(object):
|
|
"""Auto Configuration Tools"""
|
|
|
|
def __init__(self, rootdir, filename, clean=False):
|
|
"""
|
|
The Auto Configure class.
|
|
|
|
:param rootdir: The root directory for all the auto configuration files
|
|
:param filename: The autoconfiguration file
|
|
:param clean: When set initialize the nodes from the auto-config file
|
|
:type rootdir: str
|
|
:type filename: str
|
|
:type clean: bool
|
|
"""
|
|
self._autoconfig_filename = rootdir + filename
|
|
self._rootdir = rootdir
|
|
self._metadata = {}
|
|
self._nodes = {}
|
|
self._vpp_devices_node = {}
|
|
self._hugepage_config = ""
|
|
self._clean = clean
|
|
self._loadconfig()
|
|
self._sockfilename = ""
|
|
|
|
def get_nodes(self):
|
|
"""
|
|
Returns the nodes dictionary.
|
|
|
|
:returns: The nodes
|
|
:rtype: dictionary
|
|
"""
|
|
|
|
return self._nodes
|
|
|
|
@staticmethod
|
|
def _autoconfig_backup_file(filename):
|
|
"""
|
|
Create a backup file.
|
|
|
|
:param filename: The file to backup
|
|
:type filename: str
|
|
"""
|
|
|
|
# Does a copy of the file exist, if not create one
|
|
ofile = filename + '.orig'
|
|
(ret, stdout, stderr) = VPPUtil.exec_command('ls {}'.format(ofile))
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
if stdout.strip('\n') != ofile:
|
|
cmd = 'sudo cp {} {}'.format(filename, ofile)
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
|
|
# noinspection PyBroadException
|
|
@staticmethod
|
|
def _ask_user_ipv4():
|
|
"""
|
|
Asks the user for a number within a range.
|
|
default is returned if return is entered.
|
|
|
|
:returns: IP address with cidr
|
|
:rtype: str
|
|
"""
|
|
|
|
while True:
|
|
answer = input("Please enter the IPv4 Address [n.n.n.n/n]: ")
|
|
try:
|
|
ipinput = answer.split('/')
|
|
ipaddr = ip_address(ipinput[0])
|
|
if len(ipinput) > 1:
|
|
plen = answer.split('/')[1]
|
|
else:
|
|
answer = input("Please enter the netmask [n.n.n.n]: ")
|
|
plen = ip_address(answer).netmask_bits()
|
|
return '{}/{}'.format(ipaddr, plen)
|
|
except None:
|
|
print("Please enter a valid IPv4 address.")
|
|
|
|
@staticmethod
|
|
def _ask_user_range(question, first, last, default):
|
|
"""
|
|
Asks the user for a number within a range.
|
|
default is returned if return is entered.
|
|
|
|
:param question: Text of a question.
|
|
:param first: First number in the range
|
|
:param last: Last number in the range
|
|
:param default: The value returned when return is entered
|
|
:type question: string
|
|
:type first: int
|
|
:type last: int
|
|
:type default: int
|
|
:returns: The answer to the question
|
|
:rtype: int
|
|
"""
|
|
|
|
while True:
|
|
answer = input(question)
|
|
if answer == '':
|
|
answer = default
|
|
break
|
|
if re.findall(r'[0-9+]', answer):
|
|
if int(answer) in range(first, last + 1):
|
|
break
|
|
else:
|
|
print("Please a value between {} and {} or Return.".
|
|
format(first, last))
|
|
else:
|
|
print("Please a number between {} and {} or Return.".
|
|
format(first, last))
|
|
|
|
return int(answer)
|
|
|
|
@staticmethod
|
|
def _ask_user_yn(question, default):
|
|
"""
|
|
Asks the user for a yes or no question.
|
|
|
|
:param question: Text of a question.
|
|
:param default: The value returned when return is entered
|
|
:type question: string
|
|
:type default: string
|
|
:returns: The answer to the question
|
|
:rtype: string
|
|
"""
|
|
|
|
input_valid = False
|
|
default = default.lower()
|
|
answer = ''
|
|
while not input_valid:
|
|
answer = input(question)
|
|
if answer == '':
|
|
answer = default
|
|
if re.findall(r'[YyNn]', answer):
|
|
input_valid = True
|
|
answer = answer[0].lower()
|
|
else:
|
|
print("Please answer Y, N or Return.")
|
|
|
|
return answer
|
|
|
|
def _loadconfig(self):
|
|
"""
|
|
Load the testbed configuration, given the auto configuration file.
|
|
|
|
"""
|
|
|
|
# Get the Topology, from the topology layout file
|
|
topo = {}
|
|
with open(self._autoconfig_filename, 'r') as stream:
|
|
try:
|
|
topo = yaml.load(stream)
|
|
if 'metadata' in topo:
|
|
self._metadata = topo['metadata']
|
|
except yaml.YAMLError as exc:
|
|
raise RuntimeError(
|
|
"Couldn't read the Auto config file {}.".format(
|
|
self._autoconfig_filename, exc))
|
|
|
|
systemfile = self._rootdir + self._metadata['system_config_file']
|
|
if self._clean is False and os.path.isfile(systemfile):
|
|
with open(systemfile, 'r') as sysstream:
|
|
try:
|
|
systopo = yaml.load(sysstream)
|
|
if 'nodes' in systopo:
|
|
self._nodes = systopo['nodes']
|
|
except yaml.YAMLError as sysexc:
|
|
raise RuntimeError(
|
|
"Couldn't read the System config file {}.".format(
|
|
systemfile, sysexc))
|
|
else:
|
|
# Get the nodes from Auto Config
|
|
if 'nodes' in topo:
|
|
self._nodes = topo['nodes']
|
|
|
|
# Set the root directory in all the nodes
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
node['rootdir'] = self._rootdir
|
|
|
|
def updateconfig(self):
|
|
"""
|
|
Update the testbed configuration, given the auto configuration file.
|
|
We will write the system configuration file with the current node
|
|
information
|
|
|
|
"""
|
|
|
|
# Initialize the yaml data
|
|
ydata = {'metadata': self._metadata, 'nodes': self._nodes}
|
|
|
|
# Write the system config file
|
|
filename = self._rootdir + self._metadata['system_config_file']
|
|
with open(filename, 'w') as yamlfile:
|
|
yaml.dump(ydata, yamlfile)
|
|
|
|
def _update_auto_config(self):
|
|
"""
|
|
Write the auto configuration file with the new configuration data,
|
|
input from the user.
|
|
|
|
"""
|
|
|
|
# Initialize the yaml data
|
|
nodes = {}
|
|
with open(self._autoconfig_filename, 'r') as stream:
|
|
try:
|
|
ydata = yaml.load(stream)
|
|
if 'nodes' in ydata:
|
|
nodes = ydata['nodes']
|
|
except yaml.YAMLError as exc:
|
|
print(exc)
|
|
return
|
|
|
|
for i in nodes.items():
|
|
key = i[0]
|
|
node = i[1]
|
|
|
|
# Interfaces
|
|
node['interfaces'] = {}
|
|
for item in self._nodes[key]['interfaces'].items():
|
|
port = item[0]
|
|
interface = item[1]
|
|
|
|
node['interfaces'][port] = {}
|
|
addr = '{}'.format(interface['pci_address'])
|
|
node['interfaces'][port]['pci_address'] = addr
|
|
if 'mac_address' in interface:
|
|
node['interfaces'][port]['mac_address'] = \
|
|
interface['mac_address']
|
|
|
|
if 'total_other_cpus' in self._nodes[key]['cpu']:
|
|
node['cpu']['total_other_cpus'] = \
|
|
self._nodes[key]['cpu']['total_other_cpus']
|
|
if 'total_vpp_cpus' in self._nodes[key]['cpu']:
|
|
node['cpu']['total_vpp_cpus'] = \
|
|
self._nodes[key]['cpu']['total_vpp_cpus']
|
|
if 'reserve_vpp_main_core' in self._nodes[key]['cpu']:
|
|
node['cpu']['reserve_vpp_main_core'] = \
|
|
self._nodes[key]['cpu']['reserve_vpp_main_core']
|
|
|
|
# TCP
|
|
if 'active_open_sessions' in self._nodes[key]['tcp']:
|
|
node['tcp']['active_open_sessions'] = \
|
|
self._nodes[key]['tcp']['active_open_sessions']
|
|
if 'passive_open_sessions' in self._nodes[key]['tcp']:
|
|
node['tcp']['passive_open_sessions'] = \
|
|
self._nodes[key]['tcp']['passive_open_sessions']
|
|
|
|
# Huge pages
|
|
node['hugepages']['total'] = self._nodes[key]['hugepages']['total']
|
|
|
|
# Write the auto config config file
|
|
with open(self._autoconfig_filename, 'w') as yamlfile:
|
|
yaml.dump(ydata, yamlfile)
|
|
|
|
def apply_huge_pages(self):
|
|
"""
|
|
Apply the huge page config
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
hpg = VppHugePageUtil(node)
|
|
hpg.hugepages_dryrun_apply()
|
|
|
|
@staticmethod
|
|
def _apply_vpp_unix(node):
|
|
"""
|
|
Apply the VPP Unix config
|
|
|
|
:param node: Node dictionary with cpuinfo.
|
|
:type node: dict
|
|
"""
|
|
|
|
unix = ' nodaemon\n'
|
|
if 'unix' not in node['vpp']:
|
|
return ''
|
|
|
|
unixv = node['vpp']['unix']
|
|
if 'interactive' in unixv:
|
|
interactive = unixv['interactive']
|
|
if interactive is True:
|
|
unix = ' interactive\n'
|
|
|
|
return unix.rstrip('\n')
|
|
|
|
@staticmethod
|
|
def _apply_vpp_cpu(node):
|
|
"""
|
|
Apply the VPP cpu config
|
|
|
|
:param node: Node dictionary with cpuinfo.
|
|
:type node: dict
|
|
"""
|
|
|
|
# Get main core
|
|
cpu = '\n'
|
|
if 'vpp_main_core' in node['cpu']:
|
|
vpp_main_core = node['cpu']['vpp_main_core']
|
|
else:
|
|
vpp_main_core = 0
|
|
if vpp_main_core != 0:
|
|
cpu += ' main-core {}\n'.format(vpp_main_core)
|
|
|
|
# Get workers
|
|
vpp_workers = node['cpu']['vpp_workers']
|
|
vpp_worker_len = len(vpp_workers)
|
|
if vpp_worker_len > 0:
|
|
vpp_worker_str = ''
|
|
for i, worker in enumerate(vpp_workers):
|
|
if i > 0:
|
|
vpp_worker_str += ','
|
|
if worker[0] == worker[1]:
|
|
vpp_worker_str += "{}".format(worker[0])
|
|
else:
|
|
vpp_worker_str += "{}-{}".format(worker[0], worker[1])
|
|
|
|
cpu += ' corelist-workers {}\n'.format(vpp_worker_str)
|
|
|
|
return cpu
|
|
|
|
@staticmethod
|
|
def _apply_vpp_devices(node):
|
|
"""
|
|
Apply VPP PCI Device configuration to vpp startup.
|
|
|
|
:param node: Node dictionary with cpuinfo.
|
|
:type node: dict
|
|
"""
|
|
|
|
devices = ''
|
|
ports_per_numa = node['cpu']['ports_per_numa']
|
|
total_mbufs = node['cpu']['total_mbufs']
|
|
|
|
for item in ports_per_numa.items():
|
|
value = item[1]
|
|
interfaces = value['interfaces']
|
|
|
|
# if 0 was specified for the number of vpp workers, use 1 queue
|
|
num_rx_queues = None
|
|
num_tx_queues = None
|
|
if 'rx_queues' in value:
|
|
num_rx_queues = value['rx_queues']
|
|
if 'tx_queues' in value:
|
|
num_tx_queues = value['tx_queues']
|
|
|
|
num_rx_desc = None
|
|
num_tx_desc = None
|
|
|
|
# Create the devices string
|
|
for interface in interfaces:
|
|
pci_address = interface['pci_address']
|
|
pci_address = pci_address.lstrip("'").rstrip("'")
|
|
devices += '\n'
|
|
devices += ' dev {} {{ \n'.format(pci_address)
|
|
if num_rx_queues:
|
|
devices += ' num-rx-queues {}\n'.format(num_rx_queues)
|
|
else:
|
|
devices += ' num-rx-queues {}\n'.format(1)
|
|
if num_tx_queues:
|
|
devices += ' num-tx-queues {}\n'.format(num_tx_queues)
|
|
if num_rx_desc:
|
|
devices += ' num-rx-desc {}\n'.format(num_rx_desc)
|
|
if num_tx_desc:
|
|
devices += ' num-tx-desc {}\n'.format(num_tx_desc)
|
|
devices += ' }'
|
|
|
|
# If the total mbufs is not 0 or less than the default, set num-bufs
|
|
logging.debug("Total mbufs: {}".format(total_mbufs))
|
|
if total_mbufs != 0 and total_mbufs > 16384:
|
|
devices += '\n num-mbufs {}'.format(total_mbufs)
|
|
|
|
return devices
|
|
|
|
@staticmethod
|
|
def _calc_vpp_workers(node, vpp_workers, numa_node, other_cpus_end,
|
|
total_vpp_workers,
|
|
reserve_vpp_main_core):
|
|
"""
|
|
Calculate the VPP worker information
|
|
|
|
:param node: Node dictionary
|
|
:param vpp_workers: List of VPP workers
|
|
:param numa_node: Numa node
|
|
:param other_cpus_end: The end of the cpus allocated for cores
|
|
other than vpp
|
|
:param total_vpp_workers: The number of vpp workers needed
|
|
:param reserve_vpp_main_core: Is there a core needed for
|
|
the vpp main core
|
|
:type node: dict
|
|
:type numa_node: int
|
|
:type other_cpus_end: int
|
|
:type total_vpp_workers: int
|
|
:type reserve_vpp_main_core: bool
|
|
:returns: Is a core still needed for the vpp main core
|
|
:rtype: bool
|
|
"""
|
|
|
|
# Can we fit the workers in one of these slices
|
|
cpus = node['cpu']['cpus_per_node'][numa_node]
|
|
for cpu in cpus:
|
|
start = cpu[0]
|
|
end = cpu[1]
|
|
if start <= other_cpus_end:
|
|
start = other_cpus_end + 1
|
|
|
|
if reserve_vpp_main_core:
|
|
start += 1
|
|
|
|
workers_end = start + total_vpp_workers - 1
|
|
|
|
if workers_end <= end:
|
|
if reserve_vpp_main_core:
|
|
node['cpu']['vpp_main_core'] = start - 1
|
|
reserve_vpp_main_core = False
|
|
if total_vpp_workers:
|
|
vpp_workers.append((start, workers_end))
|
|
break
|
|
|
|
# We still need to reserve the main core
|
|
if reserve_vpp_main_core:
|
|
node['cpu']['vpp_main_core'] = other_cpus_end + 1
|
|
|
|
return reserve_vpp_main_core
|
|
|
|
@staticmethod
|
|
def _calc_desc_and_queues(total_numa_nodes,
|
|
total_ports_per_numa,
|
|
total_rx_queues,
|
|
ports_per_numa_value):
|
|
"""
|
|
Calculate the number of descriptors and queues
|
|
|
|
:param total_numa_nodes: The total number of numa nodes
|
|
:param total_ports_per_numa: The total number of ports for this
|
|
numa node
|
|
:param total_rx_queues: The total number of rx queues / port
|
|
:param ports_per_numa_value: The value from the ports_per_numa
|
|
dictionary
|
|
:type total_numa_nodes: int
|
|
:type total_ports_per_numa: int
|
|
:type total_rx_queues: int
|
|
:type ports_per_numa_value: dict
|
|
:returns The total number of message buffers
|
|
:rtype: int
|
|
"""
|
|
|
|
# Get the number of rx queues
|
|
rx_queues = max(1, total_rx_queues)
|
|
tx_queues = rx_queues * total_numa_nodes + 1
|
|
|
|
# Get the descriptor entries
|
|
desc_entries = 1024
|
|
ports_per_numa_value['rx_queues'] = rx_queues
|
|
total_mbufs = (((rx_queues * desc_entries) +
|
|
(tx_queues * desc_entries)) *
|
|
total_ports_per_numa)
|
|
total_mbufs = total_mbufs
|
|
|
|
return total_mbufs
|
|
|
|
@staticmethod
|
|
def _create_ports_per_numa(node, interfaces):
|
|
"""
|
|
Create a dictionary or ports per numa node
|
|
:param node: Node dictionary
|
|
:param interfaces: All the interfaces to be used by vpp
|
|
:type node: dict
|
|
:type interfaces: dict
|
|
:returns: The ports per numa dictionary
|
|
:rtype: dict
|
|
"""
|
|
|
|
# Make a list of ports by numa node
|
|
ports_per_numa = {}
|
|
for item in interfaces.items():
|
|
i = item[1]
|
|
if i['numa_node'] not in ports_per_numa:
|
|
ports_per_numa[i['numa_node']] = {'interfaces': []}
|
|
ports_per_numa[i['numa_node']]['interfaces'].append(i)
|
|
else:
|
|
ports_per_numa[i['numa_node']]['interfaces'].append(i)
|
|
node['cpu']['ports_per_numa'] = ports_per_numa
|
|
|
|
return ports_per_numa
|
|
|
|
def calculate_cpu_parameters(self):
|
|
"""
|
|
Calculate the cpu configuration.
|
|
|
|
"""
|
|
|
|
# Calculate the cpu parameters, needed for the
|
|
# vpp_startup and grub configuration
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
# get total number of nic ports
|
|
interfaces = node['interfaces']
|
|
|
|
# Make a list of ports by numa node
|
|
ports_per_numa = self._create_ports_per_numa(node, interfaces)
|
|
|
|
# Get the number of cpus to skip, we never use the first cpu
|
|
other_cpus_start = 1
|
|
other_cpus_end = other_cpus_start + \
|
|
node['cpu']['total_other_cpus'] - 1
|
|
other_workers = None
|
|
if other_cpus_end != 0:
|
|
other_workers = (other_cpus_start, other_cpus_end)
|
|
node['cpu']['other_workers'] = other_workers
|
|
|
|
# Allocate the VPP main core and workers
|
|
vpp_workers = []
|
|
reserve_vpp_main_core = node['cpu']['reserve_vpp_main_core']
|
|
total_vpp_cpus = node['cpu']['total_vpp_cpus']
|
|
total_rx_queues = node['cpu']['total_rx_queues']
|
|
|
|
# If total_vpp_cpus is 0 or is less than the numa nodes with ports
|
|
# then we shouldn't get workers
|
|
total_workers_node = 0
|
|
if len(ports_per_numa):
|
|
total_workers_node = total_vpp_cpus // len(ports_per_numa)
|
|
total_main = 0
|
|
if reserve_vpp_main_core:
|
|
total_main = 1
|
|
total_mbufs = 0
|
|
if total_main + total_workers_node != 0:
|
|
for item in ports_per_numa.items():
|
|
numa_node = item[0]
|
|
value = item[1]
|
|
|
|
# Get the number of descriptors and queues
|
|
mbufs = self._calc_desc_and_queues(
|
|
len(ports_per_numa),
|
|
len(value['interfaces']), total_rx_queues, value)
|
|
total_mbufs += mbufs
|
|
|
|
# Get the VPP workers
|
|
reserve_vpp_main_core = self._calc_vpp_workers(
|
|
node, vpp_workers, numa_node,
|
|
other_cpus_end, total_workers_node,
|
|
reserve_vpp_main_core)
|
|
|
|
total_mbufs *= 2.5
|
|
total_mbufs = int(total_mbufs)
|
|
else:
|
|
total_mbufs = 0
|
|
|
|
# Save the info
|
|
node['cpu']['vpp_workers'] = vpp_workers
|
|
node['cpu']['total_mbufs'] = total_mbufs
|
|
|
|
# Write the config
|
|
self.updateconfig()
|
|
|
|
@staticmethod
|
|
def _apply_vpp_tcp(node):
|
|
"""
|
|
Apply the VPP Unix config
|
|
|
|
:param node: Node dictionary with cpuinfo.
|
|
:type node: dict
|
|
"""
|
|
|
|
active_open_sessions = node['tcp']['active_open_sessions']
|
|
aos = int(active_open_sessions)
|
|
|
|
passive_open_sessions = node['tcp']['passive_open_sessions']
|
|
pos = int(passive_open_sessions)
|
|
|
|
# Generate the api-segment gid vpp sheit in any case
|
|
if (aos + pos) == 0:
|
|
tcp = '\n'.join([
|
|
"api-segment {",
|
|
" gid vpp",
|
|
"}"
|
|
])
|
|
return tcp.rstrip('\n')
|
|
|
|
tcp = '\n'.join([
|
|
"# TCP stack-related configuration parameters",
|
|
"# expecting {:d} client sessions, {:d} server sessions\n".format(
|
|
aos, pos),
|
|
"heapsize 4g\n",
|
|
"api-segment {",
|
|
" global-size 2000M",
|
|
" api-size 1G",
|
|
"}\n",
|
|
|
|
"session {",
|
|
" event-queue-length {:d}".format(aos + pos),
|
|
" preallocated-sessions {:d}".format(aos + pos),
|
|
" v4-session-table-buckets {:d}".format((aos + pos) // 4),
|
|
" v4-session-table-memory 3g\n"
|
|
])
|
|
if aos > 0:
|
|
tcp = tcp + " v4-halfopen-table-buckets {:d}".format(
|
|
(aos + pos) // 4) + "\n"
|
|
tcp = tcp + " v4-halfopen-table-memory 3g\n"
|
|
tcp = tcp + " local-endpoints-table-buckets {:d}".format(
|
|
(aos + pos) // 4) + "\n"
|
|
tcp = tcp + " local-endpoints-table-memory 3g\n"
|
|
tcp = tcp + "}\n\n"
|
|
|
|
tcp = tcp + "tcp {\n"
|
|
tcp = tcp + " preallocated-connections {:d}".format(aos + pos) + "\n"
|
|
if aos > 0:
|
|
tcp = tcp + " preallocated-half-open-connections {:d}".format(
|
|
aos) + "\n"
|
|
tcp = tcp + "}\n\n"
|
|
|
|
return tcp.rstrip('\n')
|
|
|
|
def apply_vpp_startup(self):
|
|
"""
|
|
Apply the vpp startup configration
|
|
|
|
"""
|
|
|
|
# Apply the VPP startup configruation
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
# Get the startup file
|
|
rootdir = node['rootdir']
|
|
sfile = rootdir + node['vpp']['startup_config_file']
|
|
|
|
# Get the devices
|
|
devices = self._apply_vpp_devices(node)
|
|
|
|
# Get the CPU config
|
|
cpu = self._apply_vpp_cpu(node)
|
|
|
|
# Get the unix config
|
|
unix = self._apply_vpp_unix(node)
|
|
|
|
# Get the TCP configuration, if any
|
|
tcp = self._apply_vpp_tcp(node)
|
|
|
|
# Make a backup if needed
|
|
self._autoconfig_backup_file(sfile)
|
|
|
|
# Get the template
|
|
tfile = sfile + '.template'
|
|
(ret, stdout, stderr) = \
|
|
VPPUtil.exec_command('cat {}'.format(tfile))
|
|
if ret != 0:
|
|
raise RuntimeError('Executing cat command failed to node {}'.
|
|
format(node['host']))
|
|
startup = stdout.format(unix=unix,
|
|
cpu=cpu,
|
|
devices=devices,
|
|
tcp=tcp)
|
|
|
|
(ret, stdout, stderr) = \
|
|
VPPUtil.exec_command('rm {}'.format(sfile))
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
|
|
cmd = "sudo cat > {0} << EOF\n{1}\n".format(sfile, startup)
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
raise RuntimeError('Writing config failed node {}'.
|
|
format(node['host']))
|
|
|
|
def apply_grub_cmdline(self):
|
|
"""
|
|
Apply the grub cmdline
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
# Get the isolated CPUs
|
|
other_workers = node['cpu']['other_workers']
|
|
vpp_workers = node['cpu']['vpp_workers']
|
|
if 'vpp_main_core' in node['cpu']:
|
|
vpp_main_core = node['cpu']['vpp_main_core']
|
|
else:
|
|
vpp_main_core = 0
|
|
all_workers = []
|
|
if other_workers is not None:
|
|
all_workers = [other_workers]
|
|
if vpp_main_core != 0:
|
|
all_workers += [(vpp_main_core, vpp_main_core)]
|
|
all_workers += vpp_workers
|
|
isolated_cpus = ''
|
|
for idx, worker in enumerate(all_workers):
|
|
if worker is None:
|
|
continue
|
|
if idx > 0:
|
|
isolated_cpus += ','
|
|
if worker[0] == worker[1]:
|
|
isolated_cpus += "{}".format(worker[0])
|
|
else:
|
|
isolated_cpus += "{}-{}".format(worker[0], worker[1])
|
|
|
|
vppgrb = VppGrubUtil(node)
|
|
current_cmdline = vppgrb.get_current_cmdline()
|
|
if 'grub' not in node:
|
|
node['grub'] = {}
|
|
node['grub']['current_cmdline'] = current_cmdline
|
|
node['grub']['default_cmdline'] = \
|
|
vppgrb.apply_cmdline(node, isolated_cpus)
|
|
|
|
self.updateconfig()
|
|
|
|
def get_hugepages(self):
|
|
"""
|
|
Get the hugepage configuration
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
hpg = VppHugePageUtil(node)
|
|
max_map_count, shmmax = hpg.get_huge_page_config()
|
|
node['hugepages']['max_map_count'] = max_map_count
|
|
node['hugepages']['shmax'] = shmmax
|
|
total, free, size, memtotal, memfree = hpg.get_actual_huge_pages()
|
|
node['hugepages']['actual_total'] = total
|
|
node['hugepages']['free'] = free
|
|
node['hugepages']['size'] = size
|
|
node['hugepages']['memtotal'] = memtotal
|
|
node['hugepages']['memfree'] = memfree
|
|
|
|
self.updateconfig()
|
|
|
|
def get_grub(self):
|
|
"""
|
|
Get the grub configuration
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
vppgrb = VppGrubUtil(node)
|
|
current_cmdline = vppgrb.get_current_cmdline()
|
|
default_cmdline = vppgrb.get_default_cmdline()
|
|
|
|
# Get the total number of isolated CPUs
|
|
current_iso_cpus = 0
|
|
iso_cpur = re.findall(r'isolcpus=[\w+\-,]+', current_cmdline)
|
|
iso_cpurl = len(iso_cpur)
|
|
if iso_cpurl > 0:
|
|
iso_cpu_str = iso_cpur[0]
|
|
iso_cpu_str = iso_cpu_str.split('=')[1]
|
|
iso_cpul = iso_cpu_str.split(',')
|
|
for iso_cpu in iso_cpul:
|
|
isocpuspl = iso_cpu.split('-')
|
|
if len(isocpuspl) == 1:
|
|
current_iso_cpus += 1
|
|
else:
|
|
first = int(isocpuspl[0])
|
|
second = int(isocpuspl[1])
|
|
if first == second:
|
|
current_iso_cpus += 1
|
|
else:
|
|
current_iso_cpus += second - first
|
|
|
|
if 'grub' not in node:
|
|
node['grub'] = {}
|
|
node['grub']['current_cmdline'] = current_cmdline
|
|
node['grub']['default_cmdline'] = default_cmdline
|
|
node['grub']['current_iso_cpus'] = current_iso_cpus
|
|
|
|
self.updateconfig()
|
|
|
|
@staticmethod
|
|
def _get_device(node):
|
|
"""
|
|
Get the device configuration for a single node
|
|
|
|
:param node: Node dictionary with cpuinfo.
|
|
:type node: dict
|
|
|
|
"""
|
|
|
|
vpp = VppPCIUtil(node)
|
|
vpp.get_all_devices()
|
|
|
|
# Save the device information
|
|
node['devices'] = {}
|
|
node['devices']['dpdk_devices'] = vpp.get_dpdk_devices()
|
|
node['devices']['kernel_devices'] = vpp.get_kernel_devices()
|
|
node['devices']['other_devices'] = vpp.get_other_devices()
|
|
node['devices']['linkup_devices'] = vpp.get_link_up_devices()
|
|
|
|
def get_devices_per_node(self):
|
|
"""
|
|
Get the device configuration for all the nodes
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
# Update the interface data
|
|
|
|
self._get_device(node)
|
|
|
|
self.updateconfig()
|
|
|
|
@staticmethod
|
|
def get_cpu_layout(node):
|
|
"""
|
|
Get the cpu layout
|
|
|
|
using lscpu -p get the cpu layout.
|
|
Returns a list with each item representing a single cpu.
|
|
|
|
:param node: Node dictionary.
|
|
:type node: dict
|
|
:returns: The cpu layout
|
|
:rtype: list
|
|
"""
|
|
|
|
cmd = 'lscpu -p'
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
raise RuntimeError('{} failed on node {} {}'.
|
|
format(cmd, node['host'], stderr))
|
|
|
|
pcpus = []
|
|
lines = stdout.split('\n')
|
|
for line in lines:
|
|
if line == '' or line[0] == '#':
|
|
continue
|
|
linesplit = line.split(',')
|
|
layout = {'cpu': linesplit[0], 'core': linesplit[1],
|
|
'socket': linesplit[2], 'node': linesplit[3]}
|
|
|
|
# cpu, core, socket, node
|
|
pcpus.append(layout)
|
|
|
|
return pcpus
|
|
|
|
def get_cpu(self):
|
|
"""
|
|
Get the cpu configuration
|
|
|
|
"""
|
|
|
|
# Get the CPU layout
|
|
CpuUtils.get_cpu_layout_from_all_nodes(self._nodes)
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
# Get the cpu layout
|
|
layout = self.get_cpu_layout(node)
|
|
node['cpu']['layout'] = layout
|
|
|
|
cpuinfo = node['cpuinfo']
|
|
smt_enabled = CpuUtils.is_smt_enabled(cpuinfo)
|
|
node['cpu']['smt_enabled'] = smt_enabled
|
|
|
|
# We don't want to write the cpuinfo
|
|
node['cpuinfo'] = ""
|
|
|
|
# Write the config
|
|
self.updateconfig()
|
|
|
|
def discover(self):
|
|
"""
|
|
Get the current system configuration.
|
|
|
|
"""
|
|
|
|
# Get the Huge Page configuration
|
|
self.get_hugepages()
|
|
|
|
# Get the device configuration
|
|
self.get_devices_per_node()
|
|
|
|
# Get the CPU configuration
|
|
self.get_cpu()
|
|
|
|
# Get the current grub cmdline
|
|
self.get_grub()
|
|
|
|
def _modify_cpu_questions(self, node, total_cpus, numa_nodes):
|
|
"""
|
|
Ask the user questions related to the cpu configuration.
|
|
|
|
:param node: Node dictionary
|
|
:param total_cpus: The total number of cpus in the system
|
|
:param numa_nodes: The list of numa nodes in the system
|
|
:type node: dict
|
|
:type total_cpus: int
|
|
:type numa_nodes: list
|
|
"""
|
|
|
|
print("\nYour system has {} core(s) and {} Numa Nodes.".
|
|
format(total_cpus, len(numa_nodes)))
|
|
print("To begin, we suggest not reserving any cores for "
|
|
"VPP or other processes.")
|
|
print("Then to improve performance start reserving cores and "
|
|
"adding queues as needed.")
|
|
|
|
# Leave 1 for the general system
|
|
total_cpus -= 1
|
|
max_vpp_cpus = min(total_cpus, 4)
|
|
total_vpp_cpus = 0
|
|
if max_vpp_cpus > 0:
|
|
question = "\nHow many core(s) shall we reserve for " \
|
|
"VPP [0-{}][0]? ".format(max_vpp_cpus)
|
|
total_vpp_cpus = self._ask_user_range(question, 0, max_vpp_cpus, 0)
|
|
node['cpu']['total_vpp_cpus'] = total_vpp_cpus
|
|
|
|
total_other_cpus = 0
|
|
max_other_cores = total_cpus - total_vpp_cpus
|
|
if max_other_cores > 0:
|
|
question = 'How many core(s) do you want to reserve for ' \
|
|
'processes other than VPP? [0-{}][0]? '. \
|
|
format(str(max_other_cores))
|
|
total_other_cpus = self._ask_user_range(question, 0, max_other_cores, 0)
|
|
node['cpu']['total_other_cpus'] = total_other_cpus
|
|
|
|
max_main_cpus = total_cpus - total_vpp_cpus - total_other_cpus
|
|
reserve_vpp_main_core = False
|
|
if max_main_cpus > 0:
|
|
question = "Should we reserve 1 core for the VPP Main thread? "
|
|
question += "[y/N]? "
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
reserve_vpp_main_core = True
|
|
node['cpu']['reserve_vpp_main_core'] = reserve_vpp_main_core
|
|
node['cpu']['vpp_main_core'] = 0
|
|
|
|
question = "How many RX queues per port shall we use for " \
|
|
"VPP [1-4][1]? ".format(max_vpp_cpus)
|
|
total_rx_queues = self._ask_user_range(question, 1, 4, 1)
|
|
node['cpu']['total_rx_queues'] = total_rx_queues
|
|
|
|
def modify_cpu(self, ask_questions=True):
|
|
"""
|
|
Modify the cpu configuration, asking for the user for the values.
|
|
|
|
:param ask_questions: When true ask the user for config parameters
|
|
|
|
"""
|
|
|
|
# Get the CPU layout
|
|
CpuUtils.get_cpu_layout_from_all_nodes(self._nodes)
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
total_cpus = 0
|
|
total_cpus_per_slice = 0
|
|
cpus_per_node = {}
|
|
numa_nodes = []
|
|
cores = []
|
|
cpu_layout = self.get_cpu_layout(node)
|
|
|
|
# Assume the number of cpus per slice is always the same as the
|
|
# first slice
|
|
first_node = '0'
|
|
for cpu in cpu_layout:
|
|
if cpu['node'] != first_node:
|
|
break
|
|
total_cpus_per_slice += 1
|
|
|
|
# Get the total number of cpus, cores, and numa nodes from the
|
|
# cpu layout
|
|
for cpul in cpu_layout:
|
|
numa_node = cpul['node']
|
|
core = cpul['core']
|
|
cpu = cpul['cpu']
|
|
total_cpus += 1
|
|
|
|
if numa_node not in cpus_per_node:
|
|
cpus_per_node[numa_node] = []
|
|
cpuperslice = int(cpu) % total_cpus_per_slice
|
|
if cpuperslice == 0:
|
|
cpus_per_node[numa_node].append((int(cpu), int(cpu) +
|
|
total_cpus_per_slice - 1))
|
|
if numa_node not in numa_nodes:
|
|
numa_nodes.append(numa_node)
|
|
if core not in cores:
|
|
cores.append(core)
|
|
node['cpu']['cpus_per_node'] = cpus_per_node
|
|
|
|
# Ask the user some questions
|
|
if ask_questions and total_cpus >= 4:
|
|
self._modify_cpu_questions(node, total_cpus, numa_nodes)
|
|
|
|
# Populate the interfaces with the numa node
|
|
if 'interfaces' in node:
|
|
ikeys = node['interfaces'].keys()
|
|
VPPUtil.get_interfaces_numa_node(node, *tuple(ikeys))
|
|
|
|
# We don't want to write the cpuinfo
|
|
node['cpuinfo'] = ""
|
|
|
|
# Write the configs
|
|
self._update_auto_config()
|
|
self.updateconfig()
|
|
|
|
def _modify_other_devices(self, node,
|
|
other_devices, kernel_devices, dpdk_devices):
|
|
"""
|
|
Modify the devices configuration, asking for the user for the values.
|
|
|
|
"""
|
|
|
|
odevices_len = len(other_devices)
|
|
if odevices_len > 0:
|
|
print("\nThese device(s) are currently NOT being used "
|
|
"by VPP or the OS.\n")
|
|
VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
|
|
question = "\nWould you like to give any of these devices"
|
|
question += " back to the OS [Y/n]? "
|
|
answer = self._ask_user_yn(question, 'Y')
|
|
if answer == 'y':
|
|
vppd = {}
|
|
for dit in other_devices.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
question = "Would you like to use device {} for". \
|
|
format(dvid)
|
|
question += " the OS [y/N]? "
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
if 'unused' in device and len(
|
|
device['unused']) != 0 and \
|
|
device['unused'][0] != '':
|
|
driver = device['unused'][0]
|
|
ret = VppPCIUtil.bind_vpp_device(
|
|
node, driver, dvid)
|
|
if ret:
|
|
logging.debug(
|
|
'Could not bind device {}'.format(dvid))
|
|
else:
|
|
vppd[dvid] = device
|
|
for dit in vppd.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
kernel_devices[dvid] = device
|
|
del other_devices[dvid]
|
|
|
|
odevices_len = len(other_devices)
|
|
if odevices_len > 0:
|
|
print("\nThese device(s) are still NOT being used "
|
|
"by VPP or the OS.\n")
|
|
VppPCIUtil.show_vpp_devices(other_devices, show_interfaces=False)
|
|
question = "\nWould you like use any of these for VPP [y/N]? "
|
|
answer = self._ask_user_yn(question, 'N')
|
|
if answer == 'y':
|
|
vppd = {}
|
|
for dit in other_devices.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
question = "Would you like to use device {} ".format(dvid)
|
|
question += "for VPP [y/N]? "
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
vppd[dvid] = device
|
|
for dit in vppd.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
if 'unused' in device and len(device['unused']) != 0 and \
|
|
device['unused'][0] != '':
|
|
driver = device['unused'][0]
|
|
logging.debug(
|
|
'Binding device {} to driver {}'.format(dvid,
|
|
driver))
|
|
ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
|
|
if ret:
|
|
logging.debug(
|
|
'Could not bind device {}'.format(dvid))
|
|
else:
|
|
dpdk_devices[dvid] = device
|
|
del other_devices[dvid]
|
|
|
|
def update_interfaces_config(self):
|
|
"""
|
|
Modify the interfaces directly from the config file.
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
devices = node['devices']
|
|
all_devices = devices['other_devices']
|
|
all_devices.update(devices['dpdk_devices'])
|
|
all_devices.update(devices['kernel_devices'])
|
|
|
|
current_ifcs = {}
|
|
interfaces = {}
|
|
if 'interfaces' in node:
|
|
current_ifcs = node['interfaces']
|
|
if current_ifcs:
|
|
for ifc in current_ifcs.values():
|
|
dvid = ifc['pci_address']
|
|
if dvid in all_devices:
|
|
VppPCIUtil.vpp_create_interface(interfaces, dvid,
|
|
all_devices[dvid])
|
|
node['interfaces'] = interfaces
|
|
|
|
self.updateconfig()
|
|
|
|
def modify_devices(self):
|
|
"""
|
|
Modify the devices configuration, asking for the user for the values.
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
devices = node['devices']
|
|
other_devices = devices['other_devices']
|
|
kernel_devices = devices['kernel_devices']
|
|
dpdk_devices = devices['dpdk_devices']
|
|
|
|
if other_devices:
|
|
self._modify_other_devices(node, other_devices,
|
|
kernel_devices, dpdk_devices)
|
|
|
|
# Get the devices again for this node
|
|
self._get_device(node)
|
|
devices = node['devices']
|
|
kernel_devices = devices['kernel_devices']
|
|
dpdk_devices = devices['dpdk_devices']
|
|
|
|
klen = len(kernel_devices)
|
|
if klen > 0:
|
|
print("\nThese devices are safe to be used with VPP.\n")
|
|
VppPCIUtil.show_vpp_devices(kernel_devices)
|
|
question = "\nWould you like to use any of these " \
|
|
"device(s) for VPP [y/N]? "
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
vppd = {}
|
|
for dit in kernel_devices.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
question = "Would you like to use device {} ".format(dvid)
|
|
question += "for VPP [y/N]? "
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
vppd[dvid] = device
|
|
for dit in vppd.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
if 'unused' in device and len(
|
|
device['unused']) != 0 and device['unused'][
|
|
0] != '':
|
|
driver = device['unused'][0]
|
|
question = "Would you like to bind the driver {} for {} [y/N]? ".format(driver, dvid)
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
logging.debug('Binding device {} to driver {}'.format(dvid, driver))
|
|
ret = VppPCIUtil.bind_vpp_device(node, driver, dvid)
|
|
if ret:
|
|
logging.debug('Could not bind device {}'.format(dvid))
|
|
dpdk_devices[dvid] = device
|
|
del kernel_devices[dvid]
|
|
|
|
dlen = len(dpdk_devices)
|
|
if dlen > 0:
|
|
print("\nThese device(s) are already using DPDK.\n")
|
|
VppPCIUtil.show_vpp_devices(dpdk_devices,
|
|
show_interfaces=False)
|
|
question = "\nWould you like to remove any of "
|
|
question += "these device(s) [y/N]? "
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
vppdl = {}
|
|
for dit in dpdk_devices.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
question = "Would you like to remove {} [y/N]? ". \
|
|
format(dvid)
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
vppdl[dvid] = device
|
|
for dit in vppdl.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
if 'unused' in device and len(
|
|
device['unused']) != 0 and device['unused'][
|
|
0] != '':
|
|
driver = device['unused'][0]
|
|
logging.debug(
|
|
'Binding device {} to driver {}'.format(
|
|
dvid, driver))
|
|
ret = VppPCIUtil.bind_vpp_device(node, driver,
|
|
dvid)
|
|
if ret:
|
|
logging.debug(
|
|
'Could not bind device {}'.format(dvid))
|
|
else:
|
|
kernel_devices[dvid] = device
|
|
del dpdk_devices[dvid]
|
|
|
|
interfaces = {}
|
|
for dit in dpdk_devices.items():
|
|
dvid = dit[0]
|
|
device = dit[1]
|
|
VppPCIUtil.vpp_create_interface(interfaces, dvid, device)
|
|
node['interfaces'] = interfaces
|
|
|
|
self._update_auto_config()
|
|
self.updateconfig()
|
|
|
|
def modify_huge_pages(self):
|
|
"""
|
|
Modify the huge page configuration, asking for the user for the values.
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
total = node['hugepages']['actual_total']
|
|
free = node['hugepages']['free']
|
|
size = node['hugepages']['size']
|
|
memfree = node['hugepages']['memfree'].split(' ')[0]
|
|
hugesize = int(size.split(' ')[0])
|
|
# The max number of huge pages should be no more than
|
|
# 70% of total free memory
|
|
maxpages = (int(memfree) * MAX_PERCENT_FOR_HUGE_PAGES // 100) // \
|
|
hugesize
|
|
print("\nThere currently {} {} huge pages free.".format(
|
|
free, size))
|
|
question = "Do you want to reconfigure the number of " \
|
|
"huge pages [y/N]? "
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'n':
|
|
node['hugepages']['total'] = total
|
|
continue
|
|
|
|
print("\nThere currently a total of {} huge pages.".
|
|
format(total))
|
|
question = "How many huge pages do you want [{} - {}][{}]? ". \
|
|
format(MIN_TOTAL_HUGE_PAGES, maxpages, MIN_TOTAL_HUGE_PAGES)
|
|
answer = self._ask_user_range(question, 1024, maxpages, 1024)
|
|
node['hugepages']['total'] = str(answer)
|
|
|
|
# Update auto-config.yaml
|
|
self._update_auto_config()
|
|
|
|
# Rediscover just the hugepages
|
|
self.get_hugepages()
|
|
|
|
def get_tcp_params(self):
|
|
"""
|
|
Get the tcp configuration
|
|
|
|
"""
|
|
# maybe nothing to do here?
|
|
self.updateconfig()
|
|
|
|
def acquire_tcp_params(self):
|
|
"""
|
|
Ask the user for TCP stack configuration parameters
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
question = "\nHow many active-open / tcp client sessions are " \
|
|
"expected [0-10000000][0]? "
|
|
answer = self._ask_user_range(question, 0, 10000000, 0)
|
|
# Less than 10K is equivalent to 0
|
|
if int(answer) < 10000:
|
|
answer = 0
|
|
node['tcp']['active_open_sessions'] = answer
|
|
|
|
question = "How many passive-open / tcp server sessions are " \
|
|
"expected [0-10000000][0]? "
|
|
answer = self._ask_user_range(question, 0, 10000000, 0)
|
|
# Less than 10K is equivalent to 0
|
|
if int(answer) < 10000:
|
|
answer = 0
|
|
node['tcp']['passive_open_sessions'] = answer
|
|
|
|
# Update auto-config.yaml
|
|
self._update_auto_config()
|
|
|
|
# Rediscover tcp parameters
|
|
self.get_tcp_params()
|
|
|
|
@staticmethod
|
|
def patch_qemu(node):
|
|
"""
|
|
Patch qemu with the correct patches.
|
|
|
|
:param node: Node dictionary
|
|
:type node: dict
|
|
"""
|
|
|
|
print('\nWe are patching the node "{}":\n'.format(node['host']))
|
|
QemuUtils.build_qemu(node, force_install=True, apply_patch=True)
|
|
|
|
@staticmethod
|
|
def cpu_info(node):
|
|
"""
|
|
print the CPU information
|
|
|
|
"""
|
|
|
|
cpu = CpuUtils.get_cpu_info_per_node(node)
|
|
|
|
item = 'Model name'
|
|
if item in cpu:
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
item = 'CPU(s)'
|
|
if item in cpu:
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
item = 'Thread(s) per core'
|
|
if item in cpu:
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
item = 'Core(s) per socket'
|
|
if item in cpu:
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
item = 'Socket(s)'
|
|
if item in cpu:
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
item = 'NUMA node(s)'
|
|
numa_nodes = 0
|
|
if item in cpu:
|
|
numa_nodes = int(cpu[item])
|
|
for i in range(0, numa_nodes):
|
|
item = "NUMA node{} CPU(s)".format(i)
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
item = 'CPU max MHz'
|
|
if item in cpu:
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
item = 'CPU min MHz'
|
|
if item in cpu:
|
|
print("{:>20}: {}".format(item, cpu[item]))
|
|
|
|
if node['cpu']['smt_enabled']:
|
|
smt = 'Enabled'
|
|
else:
|
|
smt = 'Disabled'
|
|
print("{:>20}: {}".format('SMT', smt))
|
|
|
|
# VPP Threads
|
|
print("\nVPP Threads: (Name: Cpu Number)")
|
|
vpp_processes = cpu['vpp_processes']
|
|
for i in vpp_processes.items():
|
|
print(" {:10}: {:4}".format(i[0], i[1]))
|
|
|
|
@staticmethod
|
|
def device_info(node):
|
|
"""
|
|
Show the device information.
|
|
|
|
"""
|
|
|
|
if 'cpu' in node and 'total_mbufs' in node['cpu']:
|
|
total_mbufs = node['cpu']['total_mbufs']
|
|
if total_mbufs != 0:
|
|
print("Total Number of Buffers: {}".format(total_mbufs))
|
|
|
|
vpp = VppPCIUtil(node)
|
|
vpp.get_all_devices()
|
|
linkup_devs = vpp.get_link_up_devices()
|
|
if len(linkup_devs):
|
|
print("\nDevices with link up (can not be used with VPP):")
|
|
vpp.show_vpp_devices(linkup_devs, show_header=False)
|
|
# for dev in linkup_devs:
|
|
# print (" " + dev)
|
|
kernel_devs = vpp.get_kernel_devices()
|
|
if len(kernel_devs):
|
|
print("\nDevices bound to kernel drivers:")
|
|
vpp.show_vpp_devices(kernel_devs, show_header=False)
|
|
else:
|
|
print("\nNo devices bound to kernel drivers")
|
|
|
|
dpdk_devs = vpp.get_dpdk_devices()
|
|
if len(dpdk_devs):
|
|
print("\nDevices bound to DPDK drivers:")
|
|
vpp.show_vpp_devices(dpdk_devs, show_interfaces=True,
|
|
show_header=False)
|
|
else:
|
|
print("\nNo devices bound to DPDK drivers")
|
|
|
|
other_devs = vpp.get_other_devices()
|
|
if len(other_devs):
|
|
print("\nDevices not bound to Kernel or DPDK drivers:")
|
|
vpp.show_vpp_devices(other_devs, show_interfaces=True,
|
|
show_header=False)
|
|
else:
|
|
print("\nNo devices not bound to Kernel or DPDK drivers")
|
|
|
|
vpputl = VPPUtil()
|
|
interfaces = vpputl.get_hardware(node)
|
|
if interfaces == {}:
|
|
return
|
|
|
|
print("\nDevices in use by VPP:")
|
|
|
|
if len(interfaces.items()) < 2:
|
|
print("None")
|
|
return
|
|
|
|
print("{:30} {:4} {:4} {:7} {:4} {:7}".
|
|
format('Name', 'Numa', 'RXQs',
|
|
'RXDescs', 'TXQs', 'TXDescs'))
|
|
for intf in sorted(interfaces.items()):
|
|
name = intf[0]
|
|
value = intf[1]
|
|
if name == 'local0':
|
|
continue
|
|
numa = rx_qs = rx_ds = tx_qs = tx_ds = ''
|
|
if 'numa' in value:
|
|
numa = int(value['numa'])
|
|
if 'rx queues' in value:
|
|
rx_qs = int(value['rx queues'])
|
|
if 'rx descs' in value:
|
|
rx_ds = int(value['rx descs'])
|
|
if 'tx queues' in value:
|
|
tx_qs = int(value['tx queues'])
|
|
if 'tx descs' in value:
|
|
tx_ds = int(value['tx descs'])
|
|
|
|
print("{:30} {:>4} {:>4} {:>7} {:>4} {:>7}".
|
|
format(name, numa, rx_qs, rx_ds, tx_qs, tx_ds))
|
|
|
|
@staticmethod
|
|
def hugepage_info(node):
|
|
"""
|
|
Show the huge page information.
|
|
|
|
"""
|
|
|
|
hpg = VppHugePageUtil(node)
|
|
hpg.show_huge_pages()
|
|
|
|
@staticmethod
|
|
def has_interfaces(node):
|
|
"""
|
|
Check for interfaces, return tru if there is at least one
|
|
|
|
:returns: boolean
|
|
"""
|
|
if 'interfaces' in node and len(node['interfaces']):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
@staticmethod
|
|
def min_system_resources(node):
|
|
"""
|
|
Check the system for basic minimum resources, return true if
|
|
there is enough.
|
|
|
|
:returns: boolean
|
|
"""
|
|
|
|
min_sys_res = True
|
|
|
|
# CPUs
|
|
if 'layout' in node['cpu']:
|
|
total_cpus = len(node['cpu']['layout'])
|
|
if total_cpus < 2:
|
|
print("\nThere is only {} CPU(s) available on this system. "
|
|
"This is not enough to run VPP.".format(total_cpus))
|
|
min_sys_res = False
|
|
|
|
# System Memory
|
|
if 'free' in node['hugepages'] and \
|
|
'memfree' in node['hugepages'] and \
|
|
'size' in node['hugepages']:
|
|
free = node['hugepages']['free']
|
|
memfree = float(node['hugepages']['memfree'].split(' ')[0])
|
|
hugesize = float(node['hugepages']['size'].split(' ')[0])
|
|
|
|
memhugepages = MIN_TOTAL_HUGE_PAGES * hugesize
|
|
percentmemhugepages = (memhugepages / memfree) * 100
|
|
if free is '0' and \
|
|
percentmemhugepages > MAX_PERCENT_FOR_HUGE_PAGES:
|
|
print(
|
|
"\nThe System has only {} of free memory. You will not "
|
|
"be able to allocate enough Huge Pages for VPP.".format(
|
|
int(
|
|
memfree))
|
|
)
|
|
min_sys_res = False
|
|
|
|
return min_sys_res
|
|
|
|
def sys_info(self):
|
|
"""
|
|
Print the system information
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
print("\n==============================")
|
|
name = i[0]
|
|
node = i[1]
|
|
|
|
print("NODE: {}\n".format(name))
|
|
|
|
# CPU
|
|
print("CPU:")
|
|
self.cpu_info(node)
|
|
|
|
# Grub
|
|
print("\nGrub Command Line:")
|
|
if 'grub' in node:
|
|
print(" Current: {}".format(
|
|
node['grub']['current_cmdline']))
|
|
print(" Configured: {}".format(
|
|
node['grub']['default_cmdline']))
|
|
|
|
# Huge Pages
|
|
print("\nHuge Pages:")
|
|
self.hugepage_info(node)
|
|
|
|
# Devices
|
|
print("\nDevices:")
|
|
self.device_info(node)
|
|
|
|
# Status
|
|
print("\nVPP Service Status:")
|
|
state, errors = VPPUtil.status(node)
|
|
print(" {}".format(state))
|
|
for e in errors:
|
|
print(" {}".format(e))
|
|
|
|
# Minimum system resources
|
|
self.min_system_resources(node)
|
|
|
|
print("\n==============================")
|
|
|
|
def _ipv4_interface_setup_questions(self, node):
|
|
"""
|
|
Ask the user some questions and get a list of interfaces
|
|
and IPv4 addresses associated with those interfaces
|
|
|
|
:param node: Node dictionary.
|
|
:type node: dict
|
|
:returns: A list or interfaces with ip addresses
|
|
:rtype: dict
|
|
"""
|
|
|
|
vpputl = VPPUtil()
|
|
interfaces = vpputl.get_hardware(node)
|
|
if interfaces == {}:
|
|
return
|
|
|
|
interfaces_with_ip = []
|
|
for intf in sorted(interfaces.items()):
|
|
name = intf[0]
|
|
if name == 'local0':
|
|
continue
|
|
|
|
question = "Would you like add address to " \
|
|
"interface {} [Y/n]? ".format(name)
|
|
answer = self._ask_user_yn(question, 'y')
|
|
if answer == 'y':
|
|
address = {}
|
|
addr = self._ask_user_ipv4()
|
|
address['name'] = name
|
|
address['addr'] = addr
|
|
interfaces_with_ip.append(address)
|
|
|
|
return interfaces_with_ip
|
|
|
|
def ipv4_interface_setup(self):
|
|
"""
|
|
After asking the user some questions, get a list of interfaces
|
|
and IPv4 addresses associated with those interfaces
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
# Show the current interfaces with IP addresses
|
|
current_ints = VPPUtil.get_int_ip(node)
|
|
if current_ints != {}:
|
|
print("\nThese are the current interfaces with IP addresses:")
|
|
for items in sorted(current_ints.items()):
|
|
name = items[0]
|
|
value = items[1]
|
|
if 'address' not in value:
|
|
address = 'Not Set'
|
|
else:
|
|
address = value['address']
|
|
print("{:30} {:20} {:10}".format(name, address,
|
|
value['state']))
|
|
question = "\nWould you like to keep this configuration " \
|
|
"[Y/n]? "
|
|
answer = self._ask_user_yn(question, 'y')
|
|
if answer == 'y':
|
|
continue
|
|
else:
|
|
print("\nThere are currently no interfaces with IP "
|
|
"addresses.")
|
|
|
|
# Create a script that add the ip addresses to the interfaces
|
|
# and brings the interfaces up
|
|
ints_with_addrs = self._ipv4_interface_setup_questions(node)
|
|
content = ''
|
|
for ints in ints_with_addrs:
|
|
name = ints['name']
|
|
addr = ints['addr']
|
|
setipstr = 'set int ip address {} {}\n'.format(name, addr)
|
|
setintupstr = 'set int state {} up\n'.format(name)
|
|
content += setipstr + setintupstr
|
|
|
|
# Write the content to the script
|
|
rootdir = node['rootdir']
|
|
filename = rootdir + '/vpp/vpp-config/scripts/set_int_ipv4_and_up'
|
|
with open(filename, 'w+') as sfile:
|
|
sfile.write(content)
|
|
|
|
# Execute the script
|
|
cmd = 'vppctl exec {}'.format(filename)
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
|
|
print("\nA script as been created at {}".format(filename))
|
|
print("This script can be run using the following:")
|
|
print("vppctl exec {}\n".format(filename))
|
|
|
|
def _create_vints_questions(self, node):
|
|
"""
|
|
Ask the user some questions and get a list of interfaces
|
|
and IPv4 addresses associated with those interfaces
|
|
|
|
:param node: Node dictionary.
|
|
:type node: dict
|
|
:returns: A list or interfaces with ip addresses
|
|
:rtype: list
|
|
"""
|
|
|
|
vpputl = VPPUtil()
|
|
interfaces = vpputl.get_hardware(node)
|
|
if interfaces == {}:
|
|
return []
|
|
|
|
# First delete all the Virtual interfaces
|
|
for intf in sorted(interfaces.items()):
|
|
name = intf[0]
|
|
if name[:7] == 'Virtual':
|
|
cmd = 'vppctl delete vhost-user {}'.format(name)
|
|
(ret, stdout, stderr) = vpputl.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug('{} failed on node {} {}'.format(
|
|
cmd, node['host'], stderr))
|
|
|
|
# Create a virtual interface, for each interface the user wants to use
|
|
interfaces = vpputl.get_hardware(node)
|
|
if interfaces == {}:
|
|
return []
|
|
interfaces_with_virtual_interfaces = []
|
|
inum = 1
|
|
for intf in sorted(interfaces.items()):
|
|
name = intf[0]
|
|
if name == 'local0':
|
|
continue
|
|
|
|
question = "Would you like connect this interface {} to " \
|
|
"the VM [Y/n]? ".format(name)
|
|
answer = self._ask_user_yn(question, 'y')
|
|
if answer == 'y':
|
|
sockfilename = '/var/run/vpp/{}.sock'.format(
|
|
name.replace('/', '_'))
|
|
if os.path.exists(sockfilename):
|
|
os.remove(sockfilename)
|
|
cmd = 'vppctl create vhost-user socket {} server'.format(
|
|
sockfilename)
|
|
(ret, stdout, stderr) = vpputl.exec_command(cmd)
|
|
if ret != 0:
|
|
raise RuntimeError(
|
|
"Couldn't execute the command {}, {}.".format(cmd,
|
|
stderr))
|
|
vintname = stdout.rstrip('\r\n')
|
|
|
|
cmd = 'chmod 777 {}'.format(sockfilename)
|
|
(ret, stdout, stderr) = vpputl.exec_command(cmd)
|
|
if ret != 0:
|
|
raise RuntimeError(
|
|
"Couldn't execute the command {}, {}.".format(cmd,
|
|
stderr))
|
|
|
|
interface = {'name': name,
|
|
'virtualinterface': '{}'.format(vintname),
|
|
'bridge': '{}'.format(inum)}
|
|
inum += 1
|
|
interfaces_with_virtual_interfaces.append(interface)
|
|
|
|
return interfaces_with_virtual_interfaces
|
|
|
|
def create_and_bridge_virtual_interfaces(self):
|
|
"""
|
|
After asking the user some questions, create a VM and connect
|
|
the interfaces to VPP interfaces
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
# Show the current bridge and interface configuration
|
|
print("\nThis the current bridge configuration:")
|
|
VPPUtil.show_bridge(node)
|
|
question = "\nWould you like to keep this configuration [Y/n]? "
|
|
answer = self._ask_user_yn(question, 'y')
|
|
if answer == 'y':
|
|
continue
|
|
|
|
# Create a script that builds a bridge configuration with
|
|
# physical interfaces and virtual interfaces
|
|
ints_with_vints = self._create_vints_questions(node)
|
|
content = ''
|
|
for intf in ints_with_vints:
|
|
vhoststr = '\n'.join([
|
|
'comment { The following command creates the socket }',
|
|
'comment { and returns a virtual interface }',
|
|
'comment {{ create vhost-user socket '
|
|
'/var/run/vpp/sock{}.sock server }}\n'.format(
|
|
intf['bridge'])
|
|
])
|
|
|
|
setintdnstr = 'set interface state {} down\n'.format(
|
|
intf['name'])
|
|
|
|
setintbrstr = 'set interface l2 bridge {} {}\n'.format(
|
|
intf['name'], intf['bridge'])
|
|
setvintbrstr = 'set interface l2 bridge {} {}\n'.format(
|
|
intf['virtualinterface'], intf['bridge'])
|
|
|
|
# set interface state VirtualEthernet/0/0/0 up
|
|
setintvststr = 'set interface state {} up\n'.format(
|
|
intf['virtualinterface'])
|
|
|
|
# set interface state VirtualEthernet/0/0/0 down
|
|
setintupstr = 'set interface state {} up\n'.format(
|
|
intf['name'])
|
|
|
|
content += vhoststr + setintdnstr + setintbrstr + \
|
|
setvintbrstr + setintvststr + setintupstr
|
|
|
|
# Write the content to the script
|
|
rootdir = node['rootdir']
|
|
filename = rootdir + \
|
|
'/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp'
|
|
with open(filename, 'w+') as sfile:
|
|
sfile.write(content)
|
|
|
|
# Execute the script
|
|
cmd = 'vppctl exec {}'.format(filename)
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
|
|
print("\nA script as been created at {}".format(filename))
|
|
print("This script can be run using the following:")
|
|
print("vppctl exec {}\n".format(filename))
|
|
|
|
def _iperf_vm_questions(self, node):
|
|
"""
|
|
Ask the user some questions and get a list of interfaces
|
|
and IPv4 addresses associated with those interfaces
|
|
|
|
:param node: Node dictionary.
|
|
:type node: dict
|
|
:returns: A list or interfaces with ip addresses
|
|
:rtype: list
|
|
"""
|
|
|
|
vpputl = VPPUtil()
|
|
interfaces = vpputl.get_hardware(node)
|
|
if interfaces == {}:
|
|
return []
|
|
|
|
# First delete all the Virtual interfaces
|
|
for intf in sorted(interfaces.items()):
|
|
name = intf[0]
|
|
if name[:7] == 'Virtual':
|
|
cmd = 'vppctl delete vhost-user {}'.format(name)
|
|
(ret, stdout, stderr) = vpputl.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug('{} failed on node {} {}'.format(
|
|
cmd, node['host'], stderr))
|
|
|
|
# Create a virtual interface, for each interface the user wants to use
|
|
interfaces = vpputl.get_hardware(node)
|
|
if interfaces == {}:
|
|
return []
|
|
interfaces_with_virtual_interfaces = []
|
|
inum = 1
|
|
|
|
while True:
|
|
print('\nPlease pick one interface to connect to the iperf VM.')
|
|
for intf in sorted(interfaces.items()):
|
|
name = intf[0]
|
|
if name == 'local0':
|
|
continue
|
|
|
|
question = "Would you like connect this interface {} to " \
|
|
"the VM [y/N]? ".format(name)
|
|
answer = self._ask_user_yn(question, 'n')
|
|
if answer == 'y':
|
|
self._sockfilename = '/var/run/vpp/{}.sock'.format(
|
|
name.replace('/', '_'))
|
|
if os.path.exists(self._sockfilename):
|
|
os.remove(self._sockfilename)
|
|
cmd = 'vppctl create vhost-user socket {} server'.format(
|
|
self._sockfilename)
|
|
(ret, stdout, stderr) = vpputl.exec_command(cmd)
|
|
if ret != 0:
|
|
raise RuntimeError(
|
|
"Couldn't execute the command {}, {}.".format(
|
|
cmd, stderr))
|
|
vintname = stdout.rstrip('\r\n')
|
|
|
|
cmd = 'chmod 777 {}'.format(self._sockfilename)
|
|
(ret, stdout, stderr) = vpputl.exec_command(cmd)
|
|
if ret != 0:
|
|
raise RuntimeError(
|
|
"Couldn't execute the command {}, {}.".format(
|
|
cmd, stderr))
|
|
|
|
interface = {'name': name,
|
|
'virtualinterface': '{}'.format(vintname),
|
|
'bridge': '{}'.format(inum)}
|
|
inum += 1
|
|
interfaces_with_virtual_interfaces.append(interface)
|
|
return interfaces_with_virtual_interfaces
|
|
|
|
def create_and_bridge_iperf_virtual_interface(self):
|
|
"""
|
|
After asking the user some questions, and create and bridge a
|
|
virtual interface to be used with iperf VM
|
|
|
|
"""
|
|
|
|
for i in self._nodes.items():
|
|
node = i[1]
|
|
|
|
# Show the current bridge and interface configuration
|
|
print("\nThis the current bridge configuration:")
|
|
ifaces = VPPUtil.show_bridge(node)
|
|
question = "\nWould you like to keep this configuration [Y/n]? "
|
|
answer = self._ask_user_yn(question, 'y')
|
|
if answer == 'y':
|
|
self._sockfilename = '/var/run/vpp/{}.sock'.format(
|
|
ifaces[0]['name'].replace('/', '_'))
|
|
if os.path.exists(self._sockfilename):
|
|
continue
|
|
|
|
# Create a script that builds a bridge configuration with
|
|
# physical interfaces and virtual interfaces
|
|
ints_with_vints = self._iperf_vm_questions(node)
|
|
content = ''
|
|
for intf in ints_with_vints:
|
|
vhoststr = '\n'.join([
|
|
'comment { The following command creates the socket }',
|
|
'comment { and returns a virtual interface }',
|
|
'comment {{ create vhost-user socket '
|
|
'/var/run/vpp/sock{}.sock server }}\n'.format(
|
|
intf['bridge'])
|
|
])
|
|
|
|
setintdnstr = 'set interface state {} down\n'.format(
|
|
intf['name'])
|
|
|
|
setintbrstr = 'set interface l2 bridge {} {}\n'.format(
|
|
intf['name'], intf['bridge'])
|
|
setvintbrstr = 'set interface l2 bridge {} {}\n'.format(
|
|
intf['virtualinterface'], intf['bridge'])
|
|
|
|
# set interface state VirtualEthernet/0/0/0 up
|
|
setintvststr = 'set interface state {} up\n'.format(
|
|
intf['virtualinterface'])
|
|
|
|
# set interface state VirtualEthernet/0/0/0 down
|
|
setintupstr = 'set interface state {} up\n'.format(
|
|
intf['name'])
|
|
|
|
content += vhoststr + setintdnstr + setintbrstr + \
|
|
setvintbrstr + setintvststr + setintupstr
|
|
|
|
# Write the content to the script
|
|
rootdir = node['rootdir']
|
|
filename = rootdir + '/vpp/vpp-config/scripts/create_iperf_vm'
|
|
with open(filename, 'w+') as sfile:
|
|
sfile.write(content)
|
|
|
|
# Execute the script
|
|
cmd = 'vppctl exec {}'.format(filename)
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
|
|
print("\nA script as been created at {}".format(filename))
|
|
print("This script can be run using the following:")
|
|
print("vppctl exec {}\n".format(filename))
|
|
|
|
@staticmethod
|
|
def destroy_iperf_vm(name):
|
|
"""
|
|
After asking the user some questions, create a VM and connect
|
|
the interfaces to VPP interfaces
|
|
|
|
:param name: The name of the VM to be be destroyed
|
|
:type name: str
|
|
"""
|
|
|
|
cmd = 'virsh list'
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
raise RuntimeError(
|
|
"Couldn't execute the command {} : {}".format(cmd, stderr))
|
|
|
|
if re.findall(name, stdout):
|
|
cmd = 'virsh destroy {}'.format(name)
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
raise RuntimeError(
|
|
"Couldn't execute the command {} : {}".format(
|
|
cmd, stderr))
|
|
|
|
def create_iperf_vm(self, vmname):
|
|
"""
|
|
After asking the user some questions, create a VM and connect
|
|
the interfaces to VPP interfaces
|
|
|
|
"""
|
|
|
|
# Read the iperf VM template file
|
|
distro = VPPUtil.get_linux_distro()
|
|
if distro[0] == 'Ubuntu':
|
|
tfilename = \
|
|
'{}/vpp/vpp-config/configs/iperf-ubuntu.xml.template'.format(
|
|
self._rootdir)
|
|
else:
|
|
tfilename = \
|
|
'{}/vpp/vpp-config/configs/iperf-centos.xml.template'.format(
|
|
self._rootdir)
|
|
|
|
with open(tfilename, 'r') as tfile:
|
|
tcontents = tfile.read()
|
|
tfile.close()
|
|
|
|
# Add the variables
|
|
imagename = '{}/vpp/vpp-config/{}'.format(
|
|
self._rootdir, IPERFVM_IMAGE)
|
|
isoname = '{}/vpp/vpp-config/{}'.format(self._rootdir, IPERFVM_ISO)
|
|
tcontents = tcontents.format(vmname=vmname, imagename=imagename,
|
|
isoname=isoname,
|
|
vhostsocketname=self._sockfilename)
|
|
|
|
# Write the xml
|
|
ifilename = '{}/vpp/vpp-config/{}'.format(self._rootdir, IPERFVM_XML)
|
|
with open(ifilename, 'w+') as ifile:
|
|
ifile.write(tcontents)
|
|
ifile.close()
|
|
|
|
cmd = 'virsh create {}'.format(ifilename)
|
|
(ret, stdout, stderr) = VPPUtil.exec_command(cmd)
|
|
if ret != 0:
|
|
logging.debug(stderr)
|
|
raise RuntimeError(
|
|
"Couldn't execute the command {} : {}".format(cmd, stderr))
|