Skip to content
Snippets Groups Projects
Commit 3a5d204c authored by garciadeblas's avatar garciadeblas
Browse files

Feature 11021: Automatic generation of E2E Robot tests from templates and a reduced list of params


Change-Id: I161076669f3b1fb14179c82fc2207b9297ad0c85
Signed-off-by: default avatargarciadeblas <gerardo.garciadeblas@telefonica.com>
parent d0e69294
No related branches found
No related tags found
No related merge requests found
......@@ -253,6 +253,62 @@ rebot [-d <output_folder>] --merge output1.xml output2.xml ... outputN.xml
More information about post-processing Robot output files [here](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#post-processing-outputs)
## Autogeneration of tests
There is a tool `robot-systest/autogeneration/generate_osm_test.py` that allows generating a Robot test from a YAML configuration:
```bash
$ ./generate_osm_test.py -h
usage: generate_osm_test.py [-h] [-v] --config CONFIG [--template TEMPLATE] [--output OUTPUT]
Generate OSM tests from YAML configuration file.
options:
-h, --help show this help message and exit
-v, --verbose increase output verbosity
--config CONFIG yaml configuration file to create the test
--template TEMPLATE template file for rendering the test (default: test_template.j2)
--output OUTPUT output file (default: standard output)
```
A YAML configuration file provides the input parameters to generate the test, such as NF and NS packages, NS instances to be created and some operations to be executed once the NS instances are created. An example can be found in `robot-systest/autogeneration/test_config.yaml`:
```yaml
documentation: "[BASIC-40] Auto-generated test"
name: basic_40
tags:
- basic_40
- cluster_main
- daily
nfpkg:
- package: hackfest_basic_vnf
name: hackfest_basic-vnf
- package: hackfest_basic2_vnf
name: hackfest_basic2-vnf
nspkg:
- package: hackfest_basic_ns
name: hackfest_basic-ns
- package: hackfest_basic2_ns
name: hackfest_basic2-ns
ns:
- name: basic_40
nspkg: hackfest_basic-ns
config: "{vld: [ {name: mgmtnet, vim-network-name: %{VIM_MGMT_NET}} ] }"
vnf:
- ns: basic_40
vnf_member_index: vnf
tests:
- type: ping
- type: ssh
```
To generate the Robot, you can run:
```bash
cd robot-systest/autogeneration/
./generate_osm_test.py --config test_config.yaml > ../testsuite/mynewtest.robot
```
## Built With
* [Python](www.python.org/) - The language used
......
#!/usr/bin/python3
# Copyright ETSI Contributors and Others.
# All Rights Reserved.
#
# 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.
# generate_osm_test.py --config test_config.yaml
import argparse
import logging
import os
import yaml
# from jinja2 import Environment, FileSystemLoader, select_autoescape
from jinja2 import Template
####################################
# Global functions
####################################
def set_logger(verbose):
global logger
log_format_simple = "%(levelname)s %(message)s"
log_format_complete = "%(asctime)s %(levelname)s %(name)s %(filename)s:%(lineno)s %(funcName)s(): %(message)s"
log_formatter_simple = logging.Formatter(
log_format_simple, datefmt="%Y-%m-%dT%H:%M:%S"
)
handler = logging.StreamHandler()
handler.setFormatter(log_formatter_simple)
logger = logging.getLogger("generate_osm_test")
logger.setLevel(level=logging.WARNING)
logger.addHandler(handler)
if verbose == 1:
logger.setLevel(level=logging.INFO)
elif verbose > 1:
log_formatter = logging.Formatter(
log_format_complete, datefmt="%Y-%m-%dT%H:%M:%S"
)
handler.setFormatter(log_formatter)
logger.setLevel(level=logging.DEBUG)
def exit_with_error(message, code=1):
logger.error(message)
exit(code)
def render_template(template_file, test_data, output_file=None):
logger.info(f"Rendering {template_file} with test data")
# Load Jinja template
with open(template_file, "r") as template_stream:
template = Template(template_stream.read())
# Render template with test_data and store it in output_file
output = template.render(test_data)
if output_file:
if os.path.exists(output_file):
exit_with_error(
f"Output file '{output_file}' already exists. Use a different filename or remove it."
)
with open(output_file, "w") as output_stream:
output_stream.write(output)
else:
print(output)
def validate_data(test_data):
required_keys = {"nspkg", "ns"}
missing_keys = required_keys - test_data.keys()
if missing_keys:
logger.error(f"Missing required keys in YAML: {', '.join(missing_keys)}")
raise ValueError(f"Invalid test data structure: missing keys {missing_keys}")
def complete_data(test_data):
nspkg_indexes = {nspkg["name"]: i + 1 for i, nspkg in enumerate(test_data["nspkg"])}
ns_indexes = {ns["name"]: i + 1 for i, ns in enumerate(test_data["ns"])}
for ns in test_data["ns"]:
if ns["nspkg"] not in nspkg_indexes:
logger.error(
f"nspkg '{ns['nspkg']}' referenced in 'ns' not found in 'nspkg'"
)
raise ValueError(f"Invalid nspkg name: {ns['nspkg']}")
ns["nspkg_index"] = nspkg_indexes[ns["nspkg"]]
for vnf in test_data["vnf"]:
if vnf["ns"] not in ns_indexes:
logger.error(f"ns '{vnf['ns']}' referenced in 'vnf' not found in 'ns'")
raise ValueError(f"Invalid ns name: {vnf['ns']}")
vnf["ns_index"] = ns_indexes[vnf["ns"]]
####################################
# Main
####################################
if __name__ == "__main__":
# Argument parse
parser = argparse.ArgumentParser(
description="Generate OSM tests from YAML configuration file."
)
parser.add_argument(
"-v", "--verbose", action="count", default=0, help="increase output verbosity"
)
# parser.add_argument("--basedir", default=".", help="basedir for the test")
parser.add_argument(
"--config", required=True, help="yaml configuration file to create the test"
)
parser.add_argument(
"--template",
default="test_template.j2",
help="template file for rendering the test (default: test_template.j2)",
)
parser.add_argument(
"--output", default=None, help="output file (default: standard output)"
)
args = parser.parse_args()
# Calculate paths relative to the script directory
script_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.abspath(args.config)
template_path = os.path.join(script_dir, args.template)
# Initialize logger
set_logger(args.verbose)
# Load test_data
try:
with open(config_path, "r") as yaml_stream:
test_data = yaml.safe_load(yaml_stream)
except FileNotFoundError:
exit_with_error(f"Configuration file '{args.config}' not found.")
except yaml.YAMLError as e:
exit_with_error(f"Error parsing YAML file '{args.config}': {e}")
validate_data(test_data)
complete_data(test_data)
logger.debug(f"Test data:\n{yaml.safe_dump(test_data)}")
if not os.path.exists(template_path):
exit_with_error(f"Template file '{template_path}' not found.")
render_template(template_path, test_data, args.output)
# 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.
documentation: "[BASIC-40] Auto-generated test"
name: basic_40
tags:
- basic_40
- cluster_main
- daily
nfpkg:
- package: hackfest_basic_vnf
name: hackfest_basic-vnf
- package: hackfest_basic2_vnf
name: hackfest_basic2-vnf
nspkg:
- package: hackfest_basic_ns
name: hackfest_basic-ns
- package: hackfest_basic2_ns
name: hackfest_basic2-ns
ns:
- name: basic_40
nspkg: hackfest_basic-ns
config: "{vld: [ {name: mgmtnet, vim-network-name: %{VIM_MGMT_NET}} ] }"
vnf:
- ns: basic_40
vnf_member_index: vnf
tests:
- type: ping
- type: ssh
*** Comments ***
# 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.
*** Settings ***
Documentation {{ documentation }}
Library OperatingSystem
Library String
Library Collections
Library Process
Library SSHLibrary
Resource ../lib/vnfd_lib.resource
Resource ../lib/nsd_lib.resource
Resource ../lib/ns_lib.resource
Resource ../lib/connectivity_lib.resource
Resource ../lib/ssh_lib.resource
Test Tags {% for tag in tags %}{{ tag }}{% if not loop.last %} {% endif %}{% endfor %}
Suite Teardown Run Keyword And Ignore Error Suite Cleanup
*** Variables ***
# NF package folders and names
{% for item in nfpkg -%}
${NFPKG{{ loop.index }}_FOLDER} {{ item.package }}
${NFPKG{{ loop.index }}_NAME} {{ item.name }}
{% endfor %}
# NS package folders and names
{% for item in nspkg -%}
${NSPKG{{ loop.index }}_FOLDER} {{ item.package }}
${NSPKG{{ loop.index }}_NAME} {{ item.name }}
{% endfor %}
# NS instance name and configuration
{% for item in ns -%}
${NS{{ loop.index }}_NAME} {{ item.name }}
${NS{{ loop.index }}_CONFIG} {{ item.config }}
{% endfor %}
# SSH keys and username to be used
${PUBLICKEY} %{HOME}/.ssh/id_rsa.pub
${PRIVATEKEY} %{HOME}/.ssh/id_rsa
${USERNAME} ubuntu
${PASSWORD} ${EMPTY}
{% for item in ns -%}
${NS{{ loop.index }}_ID} ${EMPTY}
{%- endfor %}
{% for item in vnf -%}
${VNF{{ loop.index }}_IP_ADDR} ${EMPTY}
${VNF{{ loop.index }}_MEMBER_INDEX} {{ item.vnf_member_index }}
${VNF{{ loop.index }}_NS_NAME} {{ item.ns }}
{%- endfor %}
*** Test Cases ***
{%- for item in nfpkg %}
Add NF Package {{ item.name }}
[Documentation] Upload NF package {{ item.name }} for the testsuite.
Create VNFD '%{PACKAGES_FOLDER}/${NFPKG{{ loop.index }}_FOLDER}'
{% endfor %}
{%- for item in nspkg %}
Add NS Package {{ item.name }}
[Documentation] Upload NS package {{ item.name }} for the testsuite.
Create NSD '%{PACKAGES_FOLDER}/${NSPKG{{ loop.index }}_FOLDER}'
{% endfor %}
{%- for item in ns %}
Network Service Instance {{ item.name }}
[Documentation] Instantiate NS {{ item.name }} for the testsuite.
${id}= Create Network Service ${NSPKG{{ item.nspkg_index }}_NAME} %{VIM_TARGET} ${NS{{ loop.index }}_NAME} ${NS{{ loop.index }}_CONFIG} ${PUBLICKEY}
Set Suite Variable ${NS{{ loop.index }}_ID} ${id}
{% endfor %}
{%- for item in vnf %}
Get Vnf Ip Address
[Documentation] Get the mgmt IP address of the VNF {{ item.vnf_member_index }} of the NS {{ item.ns }}.
${ip_addr}= Get Vnf Management Ip Address ${NS{{ item.ns_index }}_ID} ${VNF{{ loop.index }}_MEMBER_INDEX}
Log ${ip_addr}
Set Suite Variable ${VNF{{ loop.index }}_IP_ADDR} ${ip_addr}
{% for item2 in item.tests %}
{#- Check if "ping" test exists for this VNF -#}
{% if item2.type == 'ping' %}
Test Ping
[Documentation] Test that the mgmt IP address of the VNF {{ item.vnf_member_index }} of the NS {{ item.ns }} is reachable with ping.
Test Connectivity ${VNF{{ loop.index }}_IP_ADDR}
{% endif %}
{#- Check if "ssh" test exists for this VNF -#}
{% if item2.type == 'ssh' %}
Test SSH Access
[Documentation] Check that the VNF {{ item.vnf_member_index }} of the NS {{ item.ns }} is accessible via SSH in its mgmt IP address.
Sleep 30s Waiting ssh daemon to be up
Test SSH Connection ${VNF{{ loop.index }}_IP_ADDR} ${USERNAME} ${PASSWORD} ${PRIVATEKEY}
{%- endif %}
{%- endfor %}
{%- endfor %}
{% for item in ns -%}
Delete NS Instance {{ item.name }}
[Documentation] Delete NS instance {{ item.name }}.
[Tags] cleanup
Delete NS ${NS{{ loop.index }}_NAME}
{% endfor %}
{%- for item in nspkg %}
Delete NS Package {{ item.name }}
[Documentation] Delete NS package {{ item.name }} from OSM.
[Tags] cleanup
Delete NSD ${NSPKG{{ loop.index }}_NAME}
{% endfor %}
{%- for item in nfpkg %}
Delete NF Package {{ item.name }}
[Documentation] Delete NF package {{ item.name }} from OSM.
[Tags] cleanup
Delete VNFD ${NFPKG{{ loop.index }}_NAME}
{% endfor %}
*** Keywords ***
Suite Cleanup
[Documentation] Test Suit Cleanup: Deleting packages and NS instances
{%- for item in ns %}
Run Keyword If Any Tests Failed Delete NS ${NS{{ loop.index }}_NAME}
{%- endfor -%}
{%- for item in nspkg %}
Run Keyword If Any Tests Failed Delete NSD ${NSPKG{{ loop.index }}_NAME}
{%- endfor -%}
{%- for item in nfpkg %}
Run Keyword If Any Tests Failed Delete VNFD ${NFPKG{{ loop.index }}_NAME}
{%- endfor %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment