import traceback
from attrdict import AttrDict
+from logging import FileHandler
import pbr.version
from pkg_resources import resource_string
try:
# recalc the running config based on the base config and options for this run
self._update_config(opts)
- if int(self.config.cache_size) < 0:
- self.config.cache_size = self.config.flow_count
+
# check that an empty openrc file (no OpenStack) is only allowed
# with EXT chain
if not self.config.openrc_file and self.config.service_chain != ChainType.EXT:
self.config.service_chain,
self.config.service_chain_count,
self.config.flow_count,
- self.config.frame_sizes)
+ self.config.frame_sizes,
+ self.config.user_id,
+ self.config.group_id)
def _update_config(self, opts):
"""Recalculate the running config based on the base config and opts.
Sanity check on the config is done here as well.
"""
self.config = AttrDict(dict(self.base_config))
+ # Update log file handler if needed after a config update (REST mode)
+ if 'log_file' in opts:
+ if opts['log_file']:
+ (path, _filename) = os.path.split(opts['log_file'])
+ if not os.path.exists(path):
+ LOG.warning(
+ 'Path %s does not exist. Please verify root path is shared with host. Path '
+ 'will be created.', path)
+ os.makedirs(path)
+ LOG.info('%s is created.', path)
+ for h in log.getLogger().handlers:
+ if isinstance(h, FileHandler) and h.baseFilename != opts['log_file']:
+ # clean log file handler
+ log.getLogger().removeHandler(h)
+ # add handler if not existing to avoid duplicates handlers
+ if len(log.getLogger().handlers) == 1:
+ log.add_file_logger(opts['log_file'])
+
self.config.update(opts)
config = self.config
config.service_chain = ChainType.EXT
config.no_arp = True
LOG.info('Running L2 loopback: using EXT chain/no ARP')
+
+ # traffic profile override options
+ if 'frame_sizes' in opts:
+ unidir = False
+ if 'unidir' in opts:
+ unidir = opts['unidir']
+ override_custom_traffic(config, opts['frame_sizes'], unidir)
+ LOG.info("Frame size has been set to %s for current configuration", opts['frame_sizes'])
+
config.flow_count = utils.parse_flow_count(config.flow_count)
required_flow_count = config.service_chain_count * 2
if config.flow_count < required_flow_count:
if config.flow_count % 2:
config.flow_count += 1
+ # Possibly adjust the cache size
+ if config.cache_size < 0:
+ config.cache_size = config.flow_count
+
+ # The size must be capped to 10000 (where does this limit come from?)
+ if config.cache_size > 10000:
+ config.cache_size = 10000
+
config.duration_sec = float(config.duration_sec)
config.interval_sec = float(config.interval_sec)
config.pause_sec = float(config.pause_sec)
if config.flavor.vcpus < 2:
raise Exception("Flavor vcpus must be >= 2")
-
config.ndr_run = (not config.no_traffic and
'ndr' in config.rate.strip().lower().split('_'))
config.pdr_run = (not config.no_traffic and
self.config_plugin.validate_config(config, self.specs.openstack)
+def bool_arg(x):
+ """Argument type to be used in parser.add_argument()
+ When a boolean like value is expected to be given
+ """
+ return (str(x).lower() != 'false') \
+ and (str(x).lower() != 'no') \
+ and (str(x).lower() != '0')
+
+
+def int_arg(x):
+ """Argument type to be used in parser.add_argument()
+ When an integer type value is expected to be given
+ (returns 0 if argument is invalid, hexa accepted)
+ """
+ return int(x, 0)
+
+
def _parse_opts_from_cli():
parser = argparse.ArgumentParser()
metavar='<vlan>',
help='Port to port or port to switch to port L2 loopback with VLAN id')
+ parser.add_argument('--user-info', dest='user_info',
+ action='store',
+ metavar='<data>',
+ help='Custom data to be included as is '
+ 'in the json report config branch - '
+ ' example, pay attention! no space: '
+ '--user-info=\'{"status":"explore","description":'
+ '{"target":"lab","ok":true,"version":2020}}\' - '
+ 'this option may be repeated; given data will be merged.')
+
+ parser.add_argument('--vlan-tagging', dest='vlan_tagging',
+ type=bool_arg,
+ metavar='<boolean>',
+ action='store',
+ default=None,
+ help='Override the NFVbench \'vlan_tagging\' parameter')
+
+ parser.add_argument('--intf-speed', dest='intf_speed',
+ metavar='<speed>',
+ action='store',
+ default=None,
+ help='Override the NFVbench \'intf_speed\' '
+ 'parameter (e.g. 10Gbps, auto, 16.72Gbps)')
+
+ parser.add_argument('--cores', dest='cores',
+ type=int_arg,
+ metavar='<number>',
+ action='store',
+ default=None,
+ help='Override the T-Rex \'cores\' parameter')
+
parser.add_argument('--cache-size', dest='cache_size',
+ type=int_arg,
+ metavar='<size>',
action='store',
default='0',
help='Specify the FE cache size (default: 0, flow-count if < 0)')
parser.add_argument('--service-mode', dest='service_mode',
action='store_true',
default=False,
- help='Enable T-Rex service mode for debugging only')
+ help='Enable T-Rex service mode (for debugging purpose)')
+
+ parser.add_argument('--no-e2e-check', dest='no_e2e_check',
+ action='store_true',
+ default=False,
+ help='Skip "end to end" connectivity check (on test purpose)')
parser.add_argument('--no-flow-stats', dest='no_flow_stats',
action='store_true',
default=False,
- help='Disable extra flow stats (on high load traffic)')
+ help='Disable additional flow stats (on high load traffic)')
parser.add_argument('--no-latency-stats', dest='no_latency_stats',
action='store_true',
default=False,
help='Disable latency measurements (no streams)')
+ parser.add_argument('--user-id', dest='user_id',
+ type=int_arg,
+ metavar='<uid>',
+ action='store',
+ default=None,
+ help='Change json/log files ownership with this user (int)')
+
+ parser.add_argument('--group-id', dest='group_id',
+ type=int_arg,
+ metavar='<gid>',
+ action='store',
+ default=None,
+ help='Change json/log files ownership with this group (int)')
+
+ parser.add_argument('--show-trex-log', dest='show_trex_log',
+ default=None,
+ action='store_true',
+ help='Show the current TRex local server log file contents'
+ ' => diagnostic/help in case of configuration problems')
+
+ parser.add_argument('--debug-mask', dest='debug_mask',
+ type=int_arg,
+ metavar='<mask>',
+ action='store',
+ default=None,
+ help='General purpose register (debugging flags), '
+ 'the hexadecimal notation (0x...) is accepted.'
+ 'Designed for development needs (default: 0).')
+
opts, unknown_opts = parser.parse_known_args()
return opts, unknown_opts
log.setup()
# load default config file
config, default_cfg = load_default_config()
+ # possibly override the default user_id & group_id values
+ if 'USER_ID' in os.environ:
+ config.user_id = int(os.environ['USER_ID'])
+ if 'GROUP_ID' in os.environ:
+ config.group_id = int(os.environ['GROUP_ID'])
+
# create factory for platform specific classes
try:
factory_module = importlib.import_module(config['factory_module'])
factory = getattr(factory_module, config['factory_class'])()
except AttributeError:
raise Exception("Requested factory module '{m}' or class '{c}' was not found."
- .format(m=config['factory_module'], c=config['factory_class']))
+ .format(m=config['factory_module'],
+ c=config['factory_class'])) from AttributeError
# create config plugin for this platform
config_plugin = factory.get_config_plugin_class()(config)
config = config_plugin.get_config()
print((default_cfg.decode("utf-8")))
sys.exit(0)
+ # dump the contents of the trex log file
+ if opts.show_trex_log:
+ try:
+ print(open('/tmp/trex.log').read(), end="")
+ except FileNotFoundError:
+ print("No TRex log file found!")
+ sys.exit(0)
+
config.name = ''
if opts.config:
# do not check extra_specs in flavor as it can contain any key/value pairs
- whitelist_keys = ['extra_specs']
+ # the same principle applies also to the optional user_info open property
+ whitelist_keys = ['extra_specs', 'user_info']
# override default config options with start config at path parsed from CLI
# check if it is an inline yaml/json config or a file name
if os.path.isfile(opts.config):
LOG.addHandler(fluent_logger)
break
+ # convert 'user_info' opt from json string to dictionnary
+ # and merge the result with the current config dictionnary
+ if opts.user_info:
+ opts.user_info = json.loads(opts.user_info)
+ if config.user_info:
+ config.user_info = config.user_info + opts.user_info
+ else:
+ config.user_info = opts.user_info
+ # hide the option to further _update_config()
+ opts.user_info = None
+
# traffic profile override options
override_custom_traffic(config, opts.frame_sizes, opts.unidir)
# add file log if requested
if config.log_file:
log.add_file_logger(config.log_file)
+ # possibly change file ownership
+ uid = config.user_id
+ gid = config.group_id
+ if gid is None:
+ gid = uid
+ if uid is not None:
+ os.chown(config.log_file, uid, gid)
openstack_spec = config_plugin.get_openstack_spec() if config.openrc_file \
else None