test/automated-robot-suite/runner.py

375 lines
13 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Runner for StarlingX test suite"""
from __future__ import print_function
import argparse
import getpass
import os
from shutil import copy
from Config import config
import sys
import robot
import Utils.common as common
from Libraries.common import update_config_ini, get_controllers_ip
# Global variables
CURRENT_USER = getpass.getuser()
SUITE_DIR = os.path.dirname(os.path.abspath(__file__))
MAIN_SUITE = os.path.join(SUITE_DIR, 'Tests')
LOG_NAME = 'debug.log'
# Set PYHTHONPATH variable
os.environ["PYTHONPATH"] = SUITE_DIR
def update_general_config_file(configuration, config_type, env, config_file,
yaml_file):
"""Update general configuration file with selected options
Args:
- configuration: The configuration to be setup, the possible options
are:
1. for simplex configuration
2. for duplex configuration
3. for multinode controller storage configuration
4. for multinode dedicated storage configuration
- config_type: The type of configuration selected from the command
line
- env: The environment selected from the command line
- config_file: The stx-configuration.ini file to be setup in
the controller
"""
config_path = os.path.join(SUITE_DIR, 'Config', 'config.ini')
# Get Controller(s) IPs from the stx specific config file
stx_config_path = os.path.join(SUITE_DIR, 'Config', config_file)
if env == 'baremetal':
lab_yaml = ('{}.yaml').format(config_type)
lab_config = os.path.join(SUITE_DIR, 'baremetal', 'configs', lab_yaml)
else:
lab_config = os.path.join(SUITE_DIR, yaml_file)
unit_ips = get_controllers_ip(env, stx_config_path, config_type,
lab_config)
# Update configuration info
if env == 'baremetal':
update_config_ini(config_ini=config_path, KERNEL_OPTION=configuration,
CONFIGURATION_TYPE=config_type, ENVIRONMENT=env,
CONFIGURATION_FILE=config_file,
IP_UNIT_0_ADDRESS=unit_ips['IP_UNIT_0_ADDRESS'],
IP_UNIT_1_ADDRESS=unit_ips['IP_UNIT_1_ADDRESS'],
OAM=unit_ips['OAM_IF'],
MGMT=unit_ips['MGMT_IF'],
ENV_YAML_FILE=yaml_file)
else:
update_config_ini(config_ini=config_path, KERNEL_OPTION=configuration,
CONFIGURATION_TYPE=config_type, ENVIRONMENT=env,
CONFIGURATION_FILE=config_file,
IP_UNIT_0_ADDRESS=unit_ips['IP_UNIT_0_ADDRESS'],
IP_UNIT_1_ADDRESS=unit_ips['IP_UNIT_1_ADDRESS'],
ENV_YAML_FILE=yaml_file)
if ARGS.update:
print('''Suite Updated !!!
Following values are now set on Config/config.ini file
-----
KERNEL_OPTION={}
CONFIGURATION_TYPE={}
ENVIRONMENT={}
CONFIGURATION_FILE={}
IP_UNIT_0_ADDRESS={}
IP_UNIT_1_ADDRESS={}
ENV_YAML_FILE={}'''.format(configuration, config_type, env, config_file,
unit_ips['IP_UNIT_0_ADDRESS'],
unit_ips['IP_UNIT_1_ADDRESS'],
yaml_file))
if env == 'baremetal':
print('''
OAM={}
MGMT={}'''.format(unit_ips['OAM_IF'], unit_ips['MGMT_IF']))
# Only update configuration hence exit
sys.exit(0)
def update_yaml_file(config_opt, env):
"""Overwrite the yaml file for specific yaml file used
This function overwrite the current yaml file into environment folder
for a specific configuration file from environment/configs.
:param config_opt: the argument from the command line given by the user
:param env: environemnt argument from the command line given by the user
:return
- conf_type: the type of configuration selected from the
command line
- conf_file: the configuration to be use during config controller
command in the node
"""
conf_type = ''
conf_file = ''
if config_opt == '1':
conf_type = 'simplex'
conf_file = 'stx-simplex.yml'
elif config_opt == '2':
conf_type = 'duplex'
conf_file = 'stx-duplex.yml'
elif config_opt == '3':
conf_type = 'multinode_controller_storage'
conf_file = 'stx-multinode.yml'
elif config_opt == '4':
conf_type = 'multinode_dedicated_storage'
conf_file = 'stx-multinode.yml'
# Update yaml file of selected environment
if env == 'virtual':
env_dir = 'Qemu'
env_setup_file = 'qemu_setup.yaml'
else:
env_dir = 'baremetal'
env_setup_file = 'baremetal_setup.yaml'
origin = os.path.join(SUITE_DIR, '{}/configs/{}.yaml'
.format(env_dir, conf_type))
destination = os.path.join(SUITE_DIR, '{}/{}'
.format(env_dir, env_setup_file))
copy(origin, destination)
return {'ctype': conf_type, 'cfile': conf_file,
'eyaml': '{}/{}'.format(env_dir, env_setup_file,)}
def kernel_option(configuration):
"""Return the correct kernel option
This function return the kernel option to install the correct
configuration selected by the user
:param configuration: the argument from the command line given by the user
:return:
- kernel_opt: which is the kernel option for boot the controller-0
"""
kernel_opt = ''
if configuration == '1' or configuration == '2':
kernel_opt = '3'
elif configuration == '3' or configuration == '4':
kernel_opt = '1'
return kernel_opt
def get_args():
"""Define and handle arguments with options to run the script
Return:
parser.parse_args(): list arguments as objects assigned
as attributes of a namespace
"""
description = 'Script used to run sxt-test-suite'
parser = argparse.ArgumentParser(description=description)
# optional args
parser.add_argument(
'--list-suites', dest='list_suite_name',
nargs='?', const=os.path.basename(MAIN_SUITE),
help=(
'List the suite and sub-suites including test cases of the '
'specified suite, if no value is given the entire suites tree '
'is displayed.'))
# groups args
group = parser.add_argument_group(
'Execution Suite', 'One of this arguments is mandatory - Suite(s) to '
'be run')
group.add_argument('--run-all', dest='run_all',
action='store_true', help='Run all available suites')
group.add_argument('--run-suite', dest='run_suite_name',
help='Run the specified suite')
group_configuration = parser.add_argument_group(
'Execution Environment and Configuration',
'Environment and Configuration to be run in the host'
'- This option is only required if `--run-suite` is equal to `Setup`')
group_configuration.add_argument(
'--environment', dest='environment', choices=['virtual', 'baremetal'],
help=('The environment where the suite will run'))
group_configuration.add_argument(
'--configuration', dest='configuration', choices=['1', '2', '3', '4'],
help=(
'{}: will deploy configurations for the host. '
'1=simplex, 2=duplex, 3=multinode-controller-storage, 4='
'multinode-dedicated-storage')
.format(__file__))
group_configuration.add_argument(
'--update-only', dest='update', action='store_true',
help=('Update execution parameters on the suite.'))
group_extras = parser.add_argument_group(
'Execution Extras', 'Extra options to be used on the suite execution.')
group_extras.add_argument(
'--include', dest='tags',
help=(
'Executes only the test cases with specified tags.'
'Tags and patterns can also be combined together with `AND`, `OR`,'
'and `NOT` operators.'
'Examples: --include foo --include bar* --include foo AND bar*'))
group_extras.add_argument(
'--test', dest='tests', nargs='+', default='*',
help=(
'Select test cases to run by name. '
'Name is case and space insensitive. '
'Test cases should be separated by a blank space, '
'if the test case has spaces on the name send it beetwen "". '
'Examples: --test "TEST 1" TEST_2 "Test 3"'))
return parser.parse_args()
def list_suites_option(suite_to_list):
"""Display the suite tree including test cases
Args:
suite_to_list: name of the suite to display on stdout
"""
# Get suite details
suite = common.Suite(suite_to_list, MAIN_SUITE)
print(
'''
Suite is located at: {}
=== INFORMATION ====
[S] = Suite
(T) = Test Case
====================
=== SUITE TREE ====
'''.format(suite.path))
common.list_suites(suite.data, '')
def get_config_tag(configuration):
"""Associate to the configuration selected wit a tag
Args:
configuration: Configuration selected
Return:
tag: Tag ssociate to the configuration
"""
tags_dict = {
'simplex': 'Simplex',
'duplex': 'Duplex',
'multinode_controller_storage': 'MN-Local',
'multinode_dedicated_storage': 'MN-External'
}
return tags_dict.get(configuration)
def get_iso_name():
"""Check real name of the ISO used on the deployment
Return:
real_name: ISO real name
"""
name = config.get('general', 'STX_ISO_FILE')
# Check if synlink was used instead of updating config file
try:
real_name = os.readlink('{}/{}'.format(SUITE_DIR, name))
except OSError:
real_name = name
return real_name
def get_metadata():
"""Construct default metadata to be displayed on reports
Return:
metadata_list: List with names and values to be added as metadata
"""
metadata_list = []
system = ('System:{}'.format(config.get('general', 'CONFIGURATION_TYPE')))
iso = ('ISO:{}'.format(get_iso_name()))
metadata_list.extend([system, iso])
return metadata_list
def run_suite_option(suite_name):
"""Run Specified Test Suite and creates the results structure
Args:
- suite_name: name of the suite that will be executed
"""
# Get suite details
suite = common.Suite(suite_name, MAIN_SUITE)
# Create results directory if does not exist
results_dir = common.check_results_dir(SUITE_DIR)
# Create output directory to store execution results
output_dir = common.create_output_dir(results_dir, suite.name)
# Create a link pointing to the latest run
common.link_latest_run(SUITE_DIR, output_dir)
# Updating config.ini LOG_PATH variable with output_dir
config_path = os.path.join(SUITE_DIR, 'Config', 'config.ini')
update_config_ini(config_ini=config_path, LOG_PATH=output_dir)
# Get configuration and environent from general config file
config_type = config.get('general', 'CONFIGURATION_TYPE')
env = config.get('general', 'ENVIRONMENT')
env_yaml = config.get('general', 'ENV_YAML_FILE')
# Check configuration and add it as default to the tags
default_tag = get_config_tag(config_type)
# Select tags to be used, empty if not set to execute all
include_tags = ('{0}AND{1}'.format(default_tag, ARGS.tags)
if ARGS.tags else default_tag)
if ARGS.run_suite_name == 'Setup':
include_tags = ('{0}AND{1}'.format(include_tags, ARGS.environment))
metadata_list = get_metadata()
# Run sxt-test-suite using robot framework
return robot.run(suite.path, outputdir=output_dir, debugfile=LOG_NAME,
test=ARGS.tests,
variable=['CONFIGURATION_TYPE :{}'.format(default_tag),
'ENVIRONMENT :{}'.format(env),
'ENV_YAML :{}'.format(env_yaml)],
include=include_tags, tagstatinclude=include_tags,
metadata=metadata_list)
if __name__ == '__main__':
if CURRENT_USER == 'root':
raise RuntimeError('DO NOT RUN AS ROOT')
# Validate if script is called with at least one argument
# Get args variables
ARGS = get_args()
if not (ARGS.run_suite_name or ARGS.run_all or ARGS.list_suite_name):
sys.exit('Execution Suite could not be empty')
env_configuration = (
False if not (ARGS.environment and ARGS.configuration) else True)
if ARGS.run_suite_name == 'Setup':
if not env_configuration:
sys.exit('Execution Environment arguments are required')
else:
config_dict = update_yaml_file(ARGS.configuration,
ARGS.environment)
configuration_type = config_dict['ctype']
configuration_file = config_dict['cfile']
environment_yaml = config_dict['eyaml']
# Update configuration file with values selected from command line
update_general_config_file(kernel_option(ARGS.configuration),
configuration_type, ARGS.environment,
configuration_file, environment_yaml)
# Check options selected
if ARGS.list_suite_name:
list_suites_option(ARGS.list_suite_name)
elif ARGS.run_all:
sys.exit(run_suite_option(ARGS.run_suite_name))
elif ARGS.run_suite_name:
sys.exit(run_suite_option(ARGS.run_suite_name))