From b761beb3ea2bafd786473775b5ba0afcb3fa2fd3 Mon Sep 17 00:00:00 2001 From: Bartek Grzybowski Date: Tue, 15 Dec 2020 12:33:31 +0100 Subject: Move Masspnf simulator code from Integration repository to it's own Change-Id: Id48ca08ec0d042d93e204b49ae9fe1bc86efd67b Issue-ID: INT-1810 Signed-off-by: Bartek Grzybowski --- .gitignore | 6 + MassPnfSim.py | 501 ++++ README.md | 90 + clean.sh | 7 + conftest.py | 57 + deployment/heat/README.rst | 58 + deployment/heat/cloud-config.yaml | 30 + deployment/heat/heat.env | 10 + deployment/heat/heat.yaml | 103 + deployment/heat/mass-pnf-sim_run.sh | 11 + diagnostic.sh | 30 + mass-pnf-sim.py | 24 + pnf-sim-lightweight/README.md | 50 + pnf-sim-lightweight/ROP_file_creator.sh | 59 + pnf-sim-lightweight/config/config.json | 15 + pnf-sim-lightweight/config/config.yml | 8 + pnf-sim-lightweight/config/netconf.env | 10 + .../config/vsftpd_ssl-TEMPLATE.conf | 59 + .../deployment/PnP_PNF_sim_heat_template.yml | 118 + .../PnP_PNF_sim_heat_template_Ubuntu_16_04.yml | 164 ++ pnf-sim-lightweight/docker-compose-template.yml | 64 + pnf-sim-lightweight/fix-sftp-perms-template.sh | 2 + .../json_schema/input_validator.json | 108 + .../output_validator_ves_schema_30.0.1.json | 2744 ++++++++++++++++++++ pnf-sim-lightweight/netconf/pnf-simulator.data.xml | 4 + pnf-sim-lightweight/netconf/pnf-simulator.yang | 9 + pnf-sim-lightweight/pom.xml | 345 +++ pnf-sim-lightweight/simulator.sh | 262 ++ pnf-sim-lightweight/src/assembly/resources.xml | 75 + .../onap/pnfsimulator/ConfigurationProvider.java | 28 + .../java/org/onap/pnfsimulator/FileProvider.java | 41 + .../src/main/java/org/onap/pnfsimulator/Main.java | 53 + .../java/org/onap/pnfsimulator/PnfSimConfig.java | 57 + .../main/java/org/onap/pnfsimulator/logback.xml | 50 + .../onap/pnfsimulator/logging/MDCVariables.java | 36 + .../pnfsimulator/message/JSONObjectFactory.java | 142 + .../pnfsimulator/message/MessageConstants.java | 95 + .../onap/pnfsimulator/message/MessageProvider.java | 88 + .../NetconfConfigurationCheckingTask.java | 62 + .../netconfmonitor/NetconfMonitorService.java | 71 + .../NetconfMonitorServiceConfiguration.java | 114 + .../netconf/NetconfConfigurationCache.java | 34 + .../netconf/NetconfConfigurationReader.java | 42 + .../netconf/NetconfConfigurationWriter.java | 51 + .../netconf/NetconfConnectionParams.java | 45 + .../pnfsimulator/rest/SimulatorController.java | 235 ++ .../org/onap/pnfsimulator/rest/util/DateUtil.java | 35 + .../pnfsimulator/rest/util/ResponseBuilder.java | 62 + .../org/onap/pnfsimulator/simulator/Simulator.java | 213 ++ .../pnfsimulator/simulator/SimulatorFactory.java | 54 + .../simulator/client/RestTemplateAdapter.java | 26 + .../simulator/client/RestTemplateAdapterImpl.java | 155 ++ .../simulator/validation/JSONValidator.java | 66 + .../simulator/validation/NoRopFilesException.java | 28 + .../simulator/validation/ValidationException.java | 28 + .../src/main/resources/application.properties | 6 + .../message/JSONObjectFactoryTest.java | 95 + .../pnfsimulator/message/MessageProviderTest.java | 122 + .../NetconfConfigurationCheckingTaskTest.java | 95 + .../NetconfMonitorServiceConfigurationTest.java | 72 + .../netconfmonitor/NetconfMonitorServiceTest.java | 73 + .../netconf/NetconfConfigurationCacheTest.java | 38 + .../netconf/NetconfConfigurationReaderTest.java | 70 + .../netconf/NetconfConfigurationWriterTest.java | 67 + .../pnfsimulator/rest/SimulatorControllerTest.java | 226 ++ .../onap/pnfsimulator/rest/util/DateUtilTest.java | 38 + .../rest/util/ResponseBuilderTest.java | 65 + .../simulator/SimulatorFactoryTest.java | 66 + .../onap/pnfsimulator/simulator/SimulatorTest.java | 204 ++ .../onap/pnfsimulator/simulator/TestMessages.java | 116 + .../client/RestTemplateAdapterImplTest.java | 134 + .../simulator/validation/JSONValidatorTest.java | 123 + .../src/test/resources/invalid-test-schema.json | 19 + .../src/test/resources/logback-test.xml | 49 + .../simulator/validCommonEventHeaderParams.json | 5 + .../simulator/validNotificationParams.json | 4 + .../simulator/validPnfRegistrationParams.json | 10 + .../simulator/validSimulatorParams.json | 4 + .../src/test/resources/valid-test-schema.json | 19 + pnf-sim-lightweight/templates/.gitattributes | 1 + .../templates/file_template_new.xml.gz | Bin 0 -> 188547 bytes pnf-sim-lightweight/tls/README.md | 4 + pnf-sim-lightweight/tls/dfc.crt | 19 + pnf-sim-lightweight/tls/ftp.crt | 19 + pnf-sim-lightweight/tls/ftp.key | 27 + setup.py | 38 + setup.sh | 10 + test_cli.py | 75 + test_lifecycle.py | 201 ++ test_settings.py | 9 + tox.ini | 13 +- 91 files changed, 9069 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100755 MassPnfSim.py create mode 100644 README.md create mode 100755 clean.sh create mode 100644 conftest.py create mode 100644 deployment/heat/README.rst create mode 100644 deployment/heat/cloud-config.yaml create mode 100644 deployment/heat/heat.env create mode 100644 deployment/heat/heat.yaml create mode 100644 deployment/heat/mass-pnf-sim_run.sh create mode 100755 diagnostic.sh create mode 100755 mass-pnf-sim.py create mode 100644 pnf-sim-lightweight/README.md create mode 100755 pnf-sim-lightweight/ROP_file_creator.sh create mode 100644 pnf-sim-lightweight/config/config.json create mode 100644 pnf-sim-lightweight/config/config.yml create mode 100644 pnf-sim-lightweight/config/netconf.env create mode 100644 pnf-sim-lightweight/config/vsftpd_ssl-TEMPLATE.conf create mode 100644 pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template.yml create mode 100644 pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template_Ubuntu_16_04.yml create mode 100644 pnf-sim-lightweight/docker-compose-template.yml create mode 100755 pnf-sim-lightweight/fix-sftp-perms-template.sh create mode 100644 pnf-sim-lightweight/json_schema/input_validator.json create mode 100644 pnf-sim-lightweight/json_schema/output_validator_ves_schema_30.0.1.json create mode 100644 pnf-sim-lightweight/netconf/pnf-simulator.data.xml create mode 100644 pnf-sim-lightweight/netconf/pnf-simulator.yang create mode 100644 pnf-sim-lightweight/pom.xml create mode 100755 pnf-sim-lightweight/simulator.sh create mode 100644 pnf-sim-lightweight/src/assembly/resources.xml create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/ConfigurationProvider.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/FileProvider.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/Main.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/PnfSimConfig.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logback.xml create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/JSONObjectFactory.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageConstants.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageProvider.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTask.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorService.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfiguration.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCache.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReader.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriter.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConnectionParams.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/Simulator.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/SimulatorFactory.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapter.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImpl.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/JSONValidator.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/NoRopFilesException.java create mode 100644 pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/ValidationException.java create mode 100644 pnf-sim-lightweight/src/main/resources/application.properties create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/JSONObjectFactoryTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/MessageProviderTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTaskTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfigurationTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCacheTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReaderTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriterTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorFactoryTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/TestMessages.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImplTest.java create mode 100644 pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/validation/JSONValidatorTest.java create mode 100644 pnf-sim-lightweight/src/test/resources/invalid-test-schema.json create mode 100644 pnf-sim-lightweight/src/test/resources/logback-test.xml create mode 100644 pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validCommonEventHeaderParams.json create mode 100644 pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validNotificationParams.json create mode 100644 pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validPnfRegistrationParams.json create mode 100644 pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validSimulatorParams.json create mode 100644 pnf-sim-lightweight/src/test/resources/valid-test-schema.json create mode 100644 pnf-sim-lightweight/templates/.gitattributes create mode 100644 pnf-sim-lightweight/templates/file_template_new.xml.gz create mode 100644 pnf-sim-lightweight/tls/README.md create mode 100644 pnf-sim-lightweight/tls/dfc.crt create mode 100644 pnf-sim-lightweight/tls/ftp.crt create mode 100644 pnf-sim-lightweight/tls/ftp.key create mode 100755 setup.py create mode 100755 setup.sh create mode 100644 test_cli.py create mode 100644 test_lifecycle.py create mode 100644 test_settings.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2294027 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +pnf-sim-lw-* +.env/ +pnf-sim-lightweight/logs/* +files/onap/* +__pycache__ +MassPnfSim.egg-info/ diff --git a/MassPnfSim.py b/MassPnfSim.py new file mode 100755 index 0000000..50e4b6e --- /dev/null +++ b/MassPnfSim.py @@ -0,0 +1,501 @@ +#!/usr/bin/env python3 +import logging +import base64 +from subprocess import run, CalledProcessError +import argparse +import ipaddress +from sys import exit +from os import chdir, getcwd, path, popen, kill, getuid, stat, mkdir, chmod +from shutil import copytree, rmtree, move +from json import loads, dumps +from yaml import load, SafeLoader, dump +from glob import glob +from time import strftime, tzname, daylight +from docker import from_env +from requests import get, codes, post +from requests.exceptions import MissingSchema, InvalidSchema, InvalidURL, ConnectionError, ConnectTimeout + +def validate_url(url): + '''Helper function to perform --urlves input param validation''' + logger = logging.getLogger("urllib3") + logger.setLevel(logging.WARNING) + try: + get(url, timeout=0.001) + except (MissingSchema, InvalidSchema, InvalidURL): + raise argparse.ArgumentTypeError(f'{url} is not a valid URL') + except (ConnectionError, ConnectTimeout): + pass + return url + +def merge_dictionaries(origin, custom): + '''Combine 2 dictionaries based on common keys.''' + return { + key: dict( + origin.get(key, {}), + **custom.get(key, {})) + for key in origin.keys() | custom.keys() + } + +def validate_ip(ip): + '''Helper function to validate input param is a vaild IP address''' + try: + ip_valid = ipaddress.ip_address(ip) + except ValueError: + raise argparse.ArgumentTypeError(f'{ip} is not a valid IP address') + else: + return ip_valid + +def get_auth_token_base64(plain): + '''Converts user:password to Base64.''' + basic_auth_plain = plain + basic_auth_bytes = basic_auth_plain.encode('ascii') + basic_auth_base64_bytes = base64.b64encode(basic_auth_bytes) + basic_auth_base64 = basic_auth_base64_bytes.decode('ascii') + return basic_auth_base64 + +def get_parser(): + '''Process input arguments''' + + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(title='Subcommands', dest='subcommand') + # Build command parser + subparsers.add_parser('build', help='Build simulator image') + # Bootstrap command parser + parser_bootstrap = subparsers.add_parser('bootstrap', help='Bootstrap the system') + parser_bootstrap.add_argument('--count', help='Instance count to bootstrap', type=int, metavar='INT', default=1) + parser_bootstrap.add_argument('--urlves', help='URL of the VES collector', type=validate_url, metavar='URL', required=True) + parser_bootstrap.add_argument('--ipfileserver', help='Visible IP of the file server (SFTP/FTPS) to be included in the VES event', + type=validate_ip, metavar='IP', required=True) + parser_bootstrap.add_argument('--typefileserver', help='Type of the file server (SFTP/FTPS) to be included in the VES event', + type=str, choices=['sftp', 'ftps'], required=True) + parser_bootstrap.add_argument('--user', help='File server username', type=str, metavar='USERNAME', required=True) + parser_bootstrap.add_argument('--password', help='File server password', type=str, metavar='PASSWORD', required=True) + parser_bootstrap.add_argument('--ipstart', help='IP address range beginning', type=validate_ip, metavar='IP', required=True) + # Start command parser + parser_start = subparsers.add_parser('start', help='Start instances') + parser_start.add_argument('--count', help='Instance count to start', type=int, metavar='INT', default=0) + # Stop command parser + parser_stop = subparsers.add_parser('stop', help='Stop instances') + parser_stop.add_argument('--count', help='Instance count to stop', type=int, metavar='INT', default=0) + # Trigger command parser + parser_trigger = subparsers.add_parser('trigger', help='Trigger one single VES event from each simulator') + parser_trigger.add_argument('--count', help='Instance count to trigger', type=int, metavar='INT', default=0) + parser_trigger.add_argument('--user', help='VES auth username', type=str, metavar='USERNAME') + parser_trigger.add_argument('--password', help='VES auth password', type=str, metavar='PASSWORD') + # Stop-simulator command parser + parser_stopsimulator = subparsers.add_parser('stop_simulator', help='Stop sending PNF registration messages') + parser_stopsimulator.add_argument('--count', help='Instance count to stop', type=int, metavar='INT', default=0) + # Trigger-custom command parser + parser_triggerstart = subparsers.add_parser('trigger_custom', help='Trigger one single VES event from specific simulators') + parser_triggerstart.add_argument('--triggerstart', help='First simulator id to trigger', type=int, + metavar='INT', required=True) + parser_triggerstart.add_argument('--triggerend', help='Last simulator id to trigger', type=int, + metavar='INT', required=True) + parser_triggerstart.add_argument('--user', help='VES auth username', type=str, metavar='USERNAME') + parser_triggerstart.add_argument('--password', help='VES auth password', type=str, metavar='PASSWORD') + parser_triggerstart.add_argument('--data', help='Custom data to override default values', type=dict, metavar='DATA') + # Status command parser + parser_status = subparsers.add_parser('status', help='Status') + parser_status.add_argument('--count', help='Instance count to show status for', type=int, metavar='INT', default=0) + # Clean command parser + subparsers.add_parser('clean', help='Clean work-dirs') + # General options parser + parser.add_argument('--verbose', help='Verbosity level', choices=['info', 'debug'], + type=str, default='info') + return parser + +class MassPnfSim: + + # MassPnfSim class actions decorator + class _MassPnfSim_Decorators: + @staticmethod + def validate_subcommand(method): + def wrapper(self, args): # pylint: disable=W0613 + # Validate 'trigger_custom' subcommand options + if self.args.subcommand == 'trigger_custom': + if (self.args.triggerend + 1) > self._enum_sim_instances(): + self.logger.error('--triggerend value greater than existing instance count.') + exit(1) + + # Validate --count option for subcommands that support it + if self.args.subcommand in ['start', 'stop', 'trigger', 'status', 'stop_simulator']: + if self.args.count > self._enum_sim_instances(): + self.logger.error('--count value greater that existing instance count') + exit(1) + if not self._enum_sim_instances(): + self.logger.error('No bootstrapped instance found') + exit(1) + + # Validate 'bootstrap' subcommand + if (self.args.subcommand == 'bootstrap') and self._enum_sim_instances(): + self.logger.error('Bootstrapped instances detected, not overwiriting, clean first') + exit(1) + method(self, args) + return wrapper + + @staticmethod + def substitute_instance_args(method): + def wrapper(self, args): + self.args = args + method(self, args) + return wrapper + + log_lvl = logging.INFO + sim_compose_template = 'docker-compose-template.yml' + sim_vsftpd_template = 'config/vsftpd_ssl-TEMPLATE.conf' + sim_vsftpd_config = 'config/vsftpd_ssl.conf' + sim_sftp_script = 'fix-sftp-perms.sh' + sim_sftp_script_template = 'fix-sftp-perms-template.sh' + sim_config = 'config/config.yml' + sim_msg_config = 'config/config.json' + sim_port = 5000 + sim_base_url = 'http://{}:' + str(sim_port) + '/simulator' + sim_start_url = sim_base_url + '/start' + sim_status_url = sim_base_url + '/status' + sim_stop_url = sim_base_url + '/stop' + sim_container_name = 'pnf-simulator' + rop_script_name = 'ROP_file_creator.sh' + + def __init__(self): + self.logger = logging.getLogger(__name__) + self.logger.setLevel(self.log_lvl) + self.sim_dirname_pattern = "pnf-sim-lw-" + self.mvn_build_cmd = 'mvn clean package docker:build -Dcheckstyle.skip' + self.docker_compose_status_cmd = 'docker-compose ps' + + def _run_cmd(self, cmd, dir_context='.'): + old_pwd = getcwd() + try: + chdir(dir_context) + self.logger.debug(f'_run_cmd: Current direcotry: {getcwd()}') + self.logger.debug(f'_run_cmd: Command string: {cmd}') + run(cmd, check=True, shell=True) + chdir(old_pwd) + except FileNotFoundError: + self.logger.error(f"Directory {dir_context} not found") + except CalledProcessError as e: + exit(e.returncode) + + def _enum_sim_instances(self): + '''Helper method that returns bootstraped simulator instances count''' + return len(glob(f"{self.sim_dirname_pattern}[0-9]*")) + + def _get_sim_instance_data(self, instance_id): + '''Helper method that returns specific instance data''' + oldpwd = getcwd() + chdir(f"{self.sim_dirname_pattern}{instance_id}") + with open(self.sim_config) as cfg: + yml = load(cfg, Loader=SafeLoader) + chdir(oldpwd) + return yml['ippnfsim'] + + def _get_docker_containers(self): + '''Returns a list containing 'name' attribute of running docker containers''' + dc = from_env() + containers = [] + for container in dc.containers.list(): + containers.append(container.attrs['Name'][1:]) + return containers + + def _get_iter_range(self): + '''Helper routine to get the iteration range + for the lifecycle commands''' + if hasattr(self.args, 'count'): + if not self.args.count: + return [self._enum_sim_instances()] + else: + return [self.args.count] + elif hasattr(self.args, 'triggerstart'): + return [self.args.triggerstart, self.args.triggerend + 1] + else: + return [self._enum_sim_instances()] + + def _archive_logs(self, sim_dir): + '''Helper function to archive simulator logs or create the log dir''' + old_pwd = getcwd() + try: + chdir(sim_dir) + if path.isdir('logs'): + arch_dir = f"logs/archive_{strftime('%Y-%m-%d_%T')}" + mkdir(arch_dir) + self.logger.debug(f'Created {arch_dir}') + # Collect file list to move + self.logger.debug('Archiving log files') + for fpattern in ['*.log', '*.xml']: + for f in glob('logs/' + fpattern): + # Move files from list to arch dir + move(f, arch_dir) + self.logger.debug(f'Moving {f} to {arch_dir}') + else: + mkdir('logs') + self.logger.debug("Logs dir didn't exist, created") + chdir(old_pwd) + except FileNotFoundError: + self.logger.error(f"Directory {sim_dir} not found") + + def _generate_pnf_sim_config(self, i, port_sftp, port_ftps, pnf_sim_ip): + '''Writes a yaml formatted configuration file for Java simulator app''' + yml = {} + yml['urlves'] = self.args.urlves + yml['urlsftp'] = f'sftp://{self.args.user}:{self.args.password}@{self.args.ipfileserver}:{port_sftp}' + yml['urlftps'] = f'ftps://{self.args.user}:{self.args.password}@{self.args.ipfileserver}:{port_ftps}' + yml['ippnfsim'] = pnf_sim_ip + yml['typefileserver'] = self.args.typefileserver + self.logger.debug(f'Generated simulator config:\n{dump(yml)}') + with open(f'{self.sim_dirname_pattern}{i}/{self.sim_config}', 'w') as fout: + fout.write(dump(yml)) + + def _generate_config_file(self, source, dest, **kwargs): + '''Helper private method to generate a file based on a template''' + old_pwd = getcwd() + chdir(self.sim_dirname_pattern + str(kwargs['I'])) + # Read the template file + with open(source, 'r') as f: + template = f.read() + # Replace all occurences of env like variable with it's + # relevant value from a corresponding key form kwargs + for (k,v) in kwargs.items(): + template = template.replace('${' + k + '}', str(v)) + with open(dest, 'w') as f: + f.write(template) + chdir(old_pwd) + + @_MassPnfSim_Decorators.substitute_instance_args + @_MassPnfSim_Decorators.validate_subcommand + def bootstrap(self, args): # pylint: disable=W0613 + self.logger.info("Bootstrapping PNF instances") + + start_port = 2000 + ftps_pasv_port_start = 8000 + ftps_pasv_port_num_of_ports = 10 + + ftps_pasv_port_end = ftps_pasv_port_start + ftps_pasv_port_num_of_ports + + for i in range(self.args.count): + self.logger.info(f"PNF simulator instance: {i}") + + # The IP ranges are in distance of 16 compared to each other. + # This is matching the /28 subnet mask used in the dockerfile inside. + instance_ip_offset = i * 16 + ip_properties = [ + 'subnet', + 'gw', + 'PnfSim', + 'ftps', + 'sftp' + ] + + ip_offset = 0 + ip = {} + for prop in ip_properties: + ip.update({prop: str(self.args.ipstart + ip_offset + instance_ip_offset)}) + ip_offset += 1 + + self.logger.debug(f'Instance #{i} properties:\n {dumps(ip, indent=4)}') + + PortSftp = start_port + 1 + PortFtps = start_port + 2 + start_port += 2 + + self.logger.info(f'\tCreating {self.sim_dirname_pattern}{i}') + copytree('pnf-sim-lightweight', f'{self.sim_dirname_pattern}{i}') + self.logger.info(f"\tCreating instance #{i} configuration ") + self._generate_pnf_sim_config(i, PortSftp, PortFtps, ip['PnfSim']) + # generate docker-compose for the simulator instance + self._generate_config_file(self.sim_compose_template, 'docker-compose.yml', + IPGW = ip['gw'], IPSUBNET = ip['subnet'], + I = i, IPPNFSIM = ip['PnfSim'], + PORTSFTP = str(PortSftp), + PORTFTPS = str(PortFtps), + IPFTPS = ip['ftps'], IPSFTP = ip['sftp'], + FTPS_PASV_MIN = str(ftps_pasv_port_start), + FTPS_PASV_MAX = str(ftps_pasv_port_end), + TIMEZONE = tzname[daylight], + FILESERV_USER = self.args.user, + FILESERV_PASS = self.args.password) + # generate vsftpd config file for the simulator instance + self._generate_config_file(self.sim_vsftpd_template, self.sim_vsftpd_config, + I = i, + FTPS_PASV_MIN = str(ftps_pasv_port_start), + FTPS_PASV_MAX = str(ftps_pasv_port_end), + IPFILESERVER = str(self.args.ipfileserver)) + # generate sftp permission fix script + self._generate_config_file(self.sim_sftp_script_template, self.sim_sftp_script, + I = i, FILESERV_USER = self.args.user) + chmod(f'{self.sim_dirname_pattern}{i}/{self.sim_sftp_script}', 0o755) + + ftps_pasv_port_start += ftps_pasv_port_num_of_ports + 1 + ftps_pasv_port_end += ftps_pasv_port_num_of_ports + 1 + + # ugly hack to chown vsftpd config file to root + if getuid(): + self._run_cmd(f'sudo chown root {self.sim_vsftpd_config}', f'{self.sim_dirname_pattern}{i}') + self.logger.debug(f"vsftpd config file owner UID: {stat(self.sim_dirname_pattern + str(i) + '/' + self.sim_vsftpd_config).st_uid}") + + self.logger.info(f'Done setting up instance #{i}') + + @_MassPnfSim_Decorators.substitute_instance_args + def build(self, args): # pylint: disable=W0613 + self.logger.info("Building simulator image") + if path.isfile('pnf-sim-lightweight/pom.xml'): + self._run_cmd(self.mvn_build_cmd, 'pnf-sim-lightweight') + else: + self.logger.error('POM file was not found, Maven cannot run') + exit(1) + + @_MassPnfSim_Decorators.substitute_instance_args + def clean(self, args): # pylint: disable=W0613 + self.logger.info('Cleaning simulators workdirs') + for sim_id in range(self._enum_sim_instances()): + rmtree(f"{self.sim_dirname_pattern}{sim_id}") + + @_MassPnfSim_Decorators.substitute_instance_args + @_MassPnfSim_Decorators.validate_subcommand + def start(self, args): # pylint: disable=W0613 + for i in range(*self._get_iter_range()): + # Start measurements file generator if not running + rop_running = False + for ps_line in iter(popen(f'ps --no-headers -C {self.rop_script_name} -o pid,cmd').readline, ''): + # try getting ROP script pid + try: + ps_line_arr = ps_line.split() + assert self.rop_script_name in ps_line_arr[2] + assert ps_line_arr[3] == str(i) + except AssertionError: + pass + else: + self.logger.warning(f'3GPP measurements file generator for instance {i} is already running') + rop_running = True + if not rop_running: + self._run_cmd(f'./ROP_file_creator.sh {i} &', f"{self.sim_dirname_pattern}{i}") + self.logger.info(f'ROP_file_creator.sh {i} successfully started') + # If container is not running + if f"{self.sim_container_name}-{i}" not in self._get_docker_containers(): + self.logger.info(f'Starting {self.sim_dirname_pattern}{i} instance:') + self.logger.info(f' PNF-Sim IP: {self._get_sim_instance_data(i)}') + #Move logs to archive + self._archive_logs(self.sim_dirname_pattern + str(i)) + self.logger.info(' Starting simulator containers using netconf model specified in config/netconf.env') + self._run_cmd('docker-compose up -d', self.sim_dirname_pattern + str(i)) + else: + self.logger.warning(f'Instance {self.sim_dirname_pattern}{i} containers are already up') + + @_MassPnfSim_Decorators.substitute_instance_args + @_MassPnfSim_Decorators.validate_subcommand + def status(self, args): # pylint: disable=W0613 + for i in range(*self._get_iter_range()): + self.logger.info(f'Getting {self.sim_dirname_pattern}{i} instance status:') + if f"{self.sim_container_name}-{i}" in self._get_docker_containers(): + try: + sim_ip = self._get_sim_instance_data(i) + self.logger.info(f' PNF-Sim IP: {sim_ip}') + self._run_cmd(self.docker_compose_status_cmd, f"{self.sim_dirname_pattern}{i}") + sim_response = get('{}'.format(self.sim_status_url).format(sim_ip)) + if sim_response.status_code == codes.ok: + self.logger.info(sim_response.text) + else: + self.logger.error(f'Simulator request returned http code {sim_response.status_code}') + except KeyError: + self.logger.error(f'Unable to get sim instance IP from {self.sim_config}') + else: + self.logger.info(' Simulator containers are down') + + @_MassPnfSim_Decorators.substitute_instance_args + @_MassPnfSim_Decorators.validate_subcommand + def stop(self, args): # pylint: disable=W0613 + for i in range(*self._get_iter_range()): + self.logger.info(f'Stopping {self.sim_dirname_pattern}{i} instance:') + self.logger.info(f' PNF-Sim IP: {self._get_sim_instance_data(i)}') + # attempt killing ROP script + rop_pid = [] + for ps_line in iter(popen(f'ps --no-headers -C {self.rop_script_name} -o pid,cmd').readline, ''): + # try getting ROP script pid + try: + ps_line_arr = ps_line.split() + assert self.rop_script_name in ps_line_arr[2] + assert ps_line_arr[3] == str(i) + rop_pid = ps_line_arr[0] + except AssertionError: + pass + else: + # get rop script childs, kill ROP script and all childs + childs = popen(f'pgrep -P {rop_pid}').read().split() + for pid in [rop_pid] + childs: + kill(int(pid), 15) + self.logger.info(f' ROP_file_creator.sh {i} successfully killed') + if not rop_pid: + # no process found + self.logger.warning(f' ROP_file_creator.sh {i} already not running') + # try tearing down docker-compose application + if f"{self.sim_container_name}-{i}" in self._get_docker_containers(): + self._run_cmd('docker-compose down', self.sim_dirname_pattern + str(i)) + self._run_cmd('docker-compose rm', self.sim_dirname_pattern + str(i)) + else: + self.logger.warning(" Simulator containers are already down") + + @_MassPnfSim_Decorators.substitute_instance_args + @_MassPnfSim_Decorators.validate_subcommand + def trigger(self, args): # pylint: disable=W0613 + self.logger.info("Triggering VES sending:") + + for i in range(*self._get_iter_range()): + + sim_ip = self._get_sim_instance_data(i) + self.logger.info(f'Triggering {self.sim_dirname_pattern}{i} instance:') + self.logger.info(f' PNF-Sim IP: {sim_ip}') + + # create a Basic auth token + plaintext_auth = f"{args.user}:{args.password}" + basic_auth_base64 = get_auth_token_base64(plaintext_auth) + basic_auth_token = f"Basic {basic_auth_base64}" + self.logger.info((basic_auth_base64)) + + # setup req headers + req_headers = { + "Content-Type": "application/json", + "X-ONAP-RequestID": "123", + "X-InvocationID": "456", + "Authorization": basic_auth_token + } + self.logger.debug(f' Request headers: {req_headers}') + + try: + + # get payload for the request + with open(f'{self.sim_dirname_pattern}{i}/{self.sim_msg_config}') as data: + + json_data = loads(data.read()) + try: + json_data = merge_dictionaries(json_data, args.data) + except AttributeError: + self.logger.debug('The request will be sent without customization.') + + self.logger.debug(f' JSON payload for the simulator:\n{json_data}') + + # make a http request to the simulator + sim_response = post('{}'.format(self.sim_start_url).format(sim_ip), headers=req_headers, json=json_data) + + if sim_response.status_code == codes.ok: + self.logger.info(' Simulator response: ' + sim_response.text) + else: + self.logger.warning(' Simulator response ' + sim_response.text) + + except TypeError: + self.logger.error(f' Could not load JSON data from {self.sim_dirname_pattern}{i}/{self.sim_msg_config}') + + # Make the 'trigger_custom' an alias to the 'trigger' method + trigger_custom = trigger + + @_MassPnfSim_Decorators.substitute_instance_args + @_MassPnfSim_Decorators.validate_subcommand + def stop_simulator(self, args): # pylint: disable=W0613 + self.logger.info("Stopping sending PNF registration messages:") + for i in range(*self._get_iter_range()): + sim_ip = self._get_sim_instance_data(i) + self.logger.info(f'Stopping {self.sim_dirname_pattern}{i} instance:') + self.logger.info(f' PNF-Sim IP: {sim_ip}') + sim_response = post('{}'.format(self.sim_stop_url).format(sim_ip)) + if sim_response.status_code == codes.ok: + self.logger.info(' Simulator response: ' + sim_response.text) + else: + self.logger.warning(' Simulator response ' + sim_response.text) diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb11b2b --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +### Mass PNF simulator + +The purpose of this simulator is to mimic the PNF for benchmark purposes. +This variant is based on the PNF simulator and use several components. +The modification are focusing on the following areas: + +- add a script configuring and governing multiple instances of PNF simualtor +- removing parts which are not required for benchmark purposes. +- add functionality which creates and maintains the ROP files +- add functionality to query the actual ROP files and construct VES events based on them + +### Pre-configuration + +The ipstart should align to a /28 Ip address range start (e.g. 10.11.0.16, 10.11.0.32) + +For debug purposes, you can use your own IP address as VES collector, use "ip" command to determine it. + +Run ./setup.sh to create pre-set Python virtualenv with all required dependencies for the scripts. + +### Build simulator image + +``` +./mass-pnf-sim.py build +``` + +### Bootstrap simulator instances + +``` +./mass-pnf-sim.py bootstrap --count 2 --urlves http://10.148.95.??:10000/eventListener/v7 --ipfileserver 10.148.95.??? --typefileserver sftp --ipstart 10.11.0.16 +``` + +Note that the file creator is started at a time of the bootstrapping. +Stop/start will not re-launch it. + +### Replacing VES for test purposes + +``` +sudo nc -vv -l -k -p 10000 +``` + +### Start all bootstrapped instances + +``` +./mass-pnf-sim.py start +``` + +### Trigger + +``` +./mass-pnf-sim.py trigger +``` + +### Trigger only a subset of the simulators + +The following command will trigger 0,1,2,3: + +``` +./mass-pnf-sim.py trigger-custom --triggerstart 0 --triggerend 3 +``` + +The following command will trigger 4 and 5: + +``` +./mass-pnf-sim.py trigger-custom --triggerstart 4 --triggerend 5 +``` + +### Stop sending PNF registration messages from simulators + +``` +./mass-pnf-sim.py stop_simulator +``` + +### Stop docker containers and clean bootstrapped simulators + +``` +./mass-pnf-sim.py stop +./mass-pnf-sim.py clean +``` + +### Verbose printout from Python + +``` +python3 -m trace --trace --count -C . ./mass-pnf-sim.py ..... +``` + +### Cleaning and recovery after incorrect configuration + +``` +./clean.sh +``` diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..c557d3e --- /dev/null +++ b/clean.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +killall ROP_file_creator.sh sleep + +docker stop $(docker ps -aq); docker rm $(docker ps -aq) + +./mass-pnf-sim.py clean diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..3d9dbcc --- /dev/null +++ b/conftest.py @@ -0,0 +1,57 @@ +import MassPnfSim +import pytest +from test_settings import * # pylint: disable=W0614 +from docker import from_env + +@pytest.fixture(scope="module") +def parser(): + return MassPnfSim.get_parser() + +@pytest.fixture(scope="module") +def args_bootstrap(parser): + return parser.parse_args(['bootstrap', '--count', str(SIM_INSTANCES), + '--urlves', URLVES, '--ipfileserver', IPFILESERVER, + '--typefileserver', TYPEFILESERVER, '--ipstart', + IPSTART, '--user', USER, '--password', + PASSWORD]) + +@pytest.fixture(scope="module") +def args_start(parser): + return parser.parse_args(['start']) + +@pytest.fixture(scope="module") +def args_stop(parser): + return parser.parse_args(['stop']) + +@pytest.fixture(scope="module") +def args_status(parser): + return parser.parse_args(['status']) + +@pytest.fixture(scope="module") +def args_trigger(parser): + return parser.parse_args(['trigger', '--user', USER, '--password', PASSWORD]) + +@pytest.fixture(scope="module") +def args_trigger_custom(parser): + return parser.parse_args([ + 'trigger_custom', + '--triggerstart', '0', + '--triggerend', str(SIM_INSTANCES-1), + '--user', USER, + '--password', PASSWORD]) + +@pytest.fixture(scope="module") +def args_stop_simulator(parser): + return parser.parse_args(['stop_simulator']) + +@pytest.fixture +def args_clean(parser): + return parser.parse_args(['clean']) + +@pytest.fixture +def docker_containers(): + docker_client = from_env() + container_list = [] + for container in docker_client.containers.list(): + container_list.append(container.attrs['Name'][1:]) + return container_list diff --git a/deployment/heat/README.rst b/deployment/heat/README.rst new file mode 100644 index 0000000..60473fa --- /dev/null +++ b/deployment/heat/README.rst @@ -0,0 +1,58 @@ +---------------------------------------------- +Heat template for deploying Mass PNF Simulator +---------------------------------------------- + +About +===== + +This directory contains the setup that allows the deploy of Mass PNF Simulator to an OpenStack cloud instance. After successful VM run a predefined number of PNF instances will be spawned on it and VES event sending will be triggered. + +Prerequisites +============= + +1) In order to instantiate the VM with the simulator a running OpenStack infrastructure is needed with HOT (Heat Orchestration Template) support and a preconfigured floating ip network for accessing the instance. + +2) An Ubuntu 18.04 image is required to boot the instance. It may also work on 16.04 or newer images albeit the setup was only validated on Ubuntu Bionic. + +3) A running instance of ONAP with VES Collector is required only if it's desired to send the PM events from simulator to a real collector instance. In other case any arbitrary mockup service e.g. *netcat* can be used to listen to the events. In either case a valid http link is required. + +Configuration +============= + +Prior running the Heat template *heat.env* needs to be filled with appropriate parameters: + +**image_name: ubuntu-18.04-server-cloudimg-amd64** + Name of the image for the instance. See remarks above concerning validated setup. + +**flavor_name:** + Flavor name should depend on the number of simulator instances required. Tests show that a setup with 50 PNF simulator instances require approximately 16GB of RAM + +**key_name:** + Name of the existing key for passwordless login + +**instance_net_id:** + Id of the tenant network for instance + +**float_net_id:** + Id of the public network with floating IPs + +**simulator_instances:** + Requested number of PNF simulator instances to run on the VM + +**ves_url:** + A http link to the VES Collector's event listener. Can be any arbitrary mock service that will merely receive the events for debugging purposes. + +**ftp_user:** + A username for the ftp service exposed by the running setup that serves the PM files + +**ftp_password:** + A password for the ftp service + +Running +======= + +To instantiate the Heat template run from *openstack* CLI: + +:: + + stack create -t integration/test/mocks/masspnfsim/deployment/heat/heat.yaml -e integration/test/mocks/masspnfsim/deployment/heat/heat.env diff --git a/deployment/heat/cloud-config.yaml b/deployment/heat/cloud-config.yaml new file mode 100644 index 0000000..8203a84 --- /dev/null +++ b/deployment/heat/cloud-config.yaml @@ -0,0 +1,30 @@ +#cloud-config +package_upgrade: true +packages: + - apt-transport-https + - ca-certificates + - gnupg-agent + - software-properties-common + - docker-ce + - docker-ce-cli + - containerd.io + - docker-compose + - python3-virtualenv + - python3-pip + - maven + - openjdk-8-jdk-headless +# Docker's apt key needs to be injected early in the boot as 'apt' cloud-init +# module doesn't support configuring key from file +bootcmd: + - [curl, "https://download.docker.com/linux/ubuntu/gpg", -o, /run/docker.key] + - [apt-key, add, /run/docker.key] +apt: + sources: + docker: + source: "deb [arch=amd64] https://download.docker.com/linux/ubuntu $RELEASE stable" +runcmd: + - [pip3, install, tox] + - [mkdir, /root/.m2] + - [curl, "https://git.onap.org/oparent/plain/settings.xml", -o, /root/.m2/settings.xml] + - [update-java-alternatives, -s, java-1.8.0-openjdk-amd64] + - [bash, /root/mass-pnf-sim_run.sh] diff --git a/deployment/heat/heat.env b/deployment/heat/heat.env new file mode 100644 index 0000000..3e402fe --- /dev/null +++ b/deployment/heat/heat.env @@ -0,0 +1,10 @@ +parameters: + image_name: ubuntu-18.04-server-cloudimg-amd64 + flavor_name: m1.medium + key_name: + instance_net_id: + float_net_id: + simulator_instances: + ves_url: + ftp_user: + ftp_password: diff --git a/deployment/heat/heat.yaml b/deployment/heat/heat.yaml new file mode 100644 index 0000000..9718c5c --- /dev/null +++ b/deployment/heat/heat.yaml @@ -0,0 +1,103 @@ +description: Heat template for deploying Mass PNF Simulator +heat_template_version: '2017-02-24' +parameters: + flavor_name: + description: Flavor name of the simulator instance. This should depend on the requested amount of simulator instances + type: string + image_name: + description: Name or id of the image (Debian family only) + type: string + key_name: + label: Key pair name for passwordless login to instance + type: string + float_net_id: + description: ID of the network with a pool of floating IPs + label: Floating IP network + type: string + instance_net_id: + type: string + description: Id of a private network for instance interface + simulator_instances: + type: number + description: Number of simulator instances to run on cloud instance + ves_url: + type: string + description: VES Collector url + ftp_user: + type: string + description: Username for the ftp server + ftp_password: + type: string + description: Password for the ftp server +resources: + config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: cloud-config.yaml + sim_script: + type: OS::Heat::SoftwareConfig + properties: + config: + str_replace: + template: + get_file: mass-pnf-sim_run.sh + params: + $SIMULATOR_INSTANCES: { get_param: simulator_instances } + $VES_URL: { get_param: ves_url } + $FILE_SERVER: { get_attr: [simulator_floating_ip, floating_ip_address] } + $FTP_USER: { get_param: ftp_user } + $FTP_PASSWORD: { get_param: ftp_password } + sim_script_file: + type: OS::Heat::CloudConfig + properties: + cloud_config: + write_files: + - path: /root/mass-pnf-sim_run.sh + content: + get_attr: [sim_script, config] + simulator_port: + type: OS::Neutron::Port + properties: + network: + get_param: instance_net_id + simulator_floating_ip: + type: OS::Neutron::FloatingIP + properties: + floating_network: + get_param: float_net_id + port_id: + get_resource: simulator_port + cloud_config: + type: OS::Heat::MultipartMime + properties: + parts: + - config: + get_resource: config + - config: + get_resource: sim_script_file + simulator: + type: OS::Nova::Server + properties: + name: mass-pnf-simulator + key_name: + get_param: key_name + image: + get_param: image_name + flavor: + get_param: flavor_name + networks: + - port: + get_resource: simulator_port + user_data_format: RAW + user_data: + get_resource: cloud_config +outputs: + simulator_instance_id: + description: ID of the simulator instance + value: + get_resource: simulator + simulator_instance_fip: + description: Floating IP address of the simulator instance + value: + get_attr: [simulator_floating_ip, floating_ip_address] diff --git a/deployment/heat/mass-pnf-sim_run.sh b/deployment/heat/mass-pnf-sim_run.sh new file mode 100644 index 0000000..184684b --- /dev/null +++ b/deployment/heat/mass-pnf-sim_run.sh @@ -0,0 +1,11 @@ +#!/bin/bash +pushd /root +git clone https://git.onap.org/integration +pushd integration/test/mocks/masspnfsim +./setup.sh +source .tox/MassPnfSim-runtime/bin/activate +./mass-pnf-sim.py build +./mass-pnf-sim.py bootstrap --count $SIMULATOR_INSTANCES --urlves $VES_URL --ipfileserver $FILE_SERVER --typefileserver ftps --ipstart 10.11.0.16 --user $FTP_USER --password $FTP_PASSWORD +./mass-pnf-sim.py start +sleep 30 +./mass-pnf-sim.py trigger diff --git a/diagnostic.sh b/diagnostic.sh new file mode 100755 index 0000000..99e35cd --- /dev/null +++ b/diagnostic.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +echo "======= docker ps" +docker ps + +echo "======= Docker image cache" +docker images nexus3.onap.org:10003/onap/masspnf-simulator + +export NUM_OF_SIMS=`find pnf-sim-lw* -maxdepth 0 | wc -l` +echo $NUM_OF_SIMS + +if [ "$NUM_OF_SIMS" -gt 0 ]; +then + echo "======= docker-compose, first instance" + cat pnf-sim-lw-0/docker-compose.yml + + echo "======= Java config.yml, first instance" + cat pnf-sim-lw-0/config/config.yml +fi + +if (("$NUM_OF_SIMS" > 2)); +then + echo "======= docker-compose, last instance" + cat pnf-sim-lw-$(($NUM_OF_SIMS-1))/docker-compose.yml + + echo "======= Java config.yml, last instance" + cat pnf-sim-lw-$(($NUM_OF_SIMS-1))/config/config.yml +fi + + diff --git a/mass-pnf-sim.py b/mass-pnf-sim.py new file mode 100755 index 0000000..ce8cd73 --- /dev/null +++ b/mass-pnf-sim.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import sys +import logging +from MassPnfSim import MassPnfSim, get_parser + +if __name__ == '__main__': + parser = get_parser() + args = parser.parse_args() + log_lvl = getattr(logging, args.verbose.upper()) + + if sys.stdout.isatty(): + logging.basicConfig(level=logging.INFO, format='\033[92m[%(levelname)s]\033[0m %(message)s') + else: + logging.basicConfig(level=logging.INFO, format='[%(levelname)s] %(message)s') + + logger = logging.getLogger(__name__) + logger.setLevel(log_lvl) + MassPnfSim.log_lvl = log_lvl + + if args.subcommand is not None: + sim_routine = getattr(MassPnfSim(), args.subcommand) + sim_routine(args) + else: + parser.print_usage() diff --git a/pnf-sim-lightweight/README.md b/pnf-sim-lightweight/README.md new file mode 100644 index 0000000..2b902d0 --- /dev/null +++ b/pnf-sim-lightweight/README.md @@ -0,0 +1,50 @@ +## Local development shortcuts: + +To start listening on port 10000 for test purposes: + +``` +nc -l -k -p 10000 +``` + +Test the command above: + +``` +echo "Hello World" | nc localhost 10000 +``` + +Trigger the pnf simulator locally: + +``` +~/dev/git/integration/test/mocks/masspnfsim/pnf-sim-lightweight$ curl -s -X POST -H "Content-Type: application/json" -H "X-ONAP-RequestID: 123" -H "X-InvocationID: 456" -d @config/config.json +http://localhost:5000/simulator/start +``` + +## VES event sending + +the default action is to send a VES Message every 15 minutes and the total duration of the VES FileReady Message sending is 1 day (these values can be changed in config/config.json) + +Message from the stdout of nc: + +``` +POST / HTTP/1.1 +Content-Type: application/json +X-ONAP-RequestID: 123 +X-InvocationID: 3a256e95-2594-4b11-b25c-68c4baeb5c20 +Content-Length: 734 +Host: localhost:10000 +Connection: Keep-Alive +User-Agent: Apache-HttpClient/4.5.5 (Java/1.8.0_162) +Accept-Encoding: gzip,deflate +``` + +```i +javascript +{"event":{"commonEventHeader":{"startEpochMicrosec":"1551865758690","sourceId":"val13","eventId":"registration_51865758", +"nfcNamingCode":"oam","internalHeaderFields":{},"priority":"Normal","version":"4.0.1","reportingEntityName":"NOK6061ZW3", +"sequence":"0","domain":"notification","lastEpochMicrosec":"1551865758690","eventName":"pnfRegistration_Nokia_5gDu", +"vesEventListenerVersion":"7.0.1","sourceName":"NOK6061ZW3","nfNamingCode":"gNB"}, +"notificationFields":{"notificationFieldsVersion":"2.0","changeType":"FileReady","changeIdentifier":"PM_MEAS_FILES", +"arrayOfNamedHashMap":[{"name":"10MB.tar.gz","hashMap":{ +"location":"ftpes://10.11.0.68/10MB.tar.gz","fileFormatType":"org.3GPP.32.435#measCollec", +"fileFormatVersion":"V10","compression":"gzip"}}]}}} +``` diff --git a/pnf-sim-lightweight/ROP_file_creator.sh b/pnf-sim-lightweight/ROP_file_creator.sh new file mode 100755 index 0000000..6ea6ffd --- /dev/null +++ b/pnf-sim-lightweight/ROP_file_creator.sh @@ -0,0 +1,59 @@ +#!/bin/bash +MAIN_DIRECTORY=${PWD##*/} +FULL_DIRECTORY=${PWD} +FILE_DIRECTORY=$FULL_DIRECTORY/files/onap +FILE_TEMPLATE=$FULL_DIRECTORY/templates/file_template_new.xml.gz +UPDATE_MINS=15 +NUM_FILES=96 + +rm -rf $FILE_DIRECTORY +mkdir -p "$FILE_DIRECTORY" + +for ((n=0;n<$NUM_FILES;n++)) +do + if [[ "$OSTYPE" == "linux-gnu" ]]; then # Linux OS + DATE=$(date -d $(($UPDATE_MINS*($n+1)-1))" minutes ago" +"%Y%m%d") + TIME_START=$(date -d $(($UPDATE_MINS*($n+1)-1))" minutes ago" +"%H%M%z") + TIME_END=$(date -d $(($UPDATE_MINS*$n))" minutes ago" +"%H%M%z") + elif [[ "$OSTYPE" == "darwin"* ]]; then # Mac OS + DATE=$(date -v "-"$(($UPDATE_MINS*($n+1)-1))"M" +"%Y%m%d") + TIME_START=$(date -v "-"$(($UPDATE_MINS*($n+1)-1))"M" +"%H%M%z") + TIME_END=$(date -v "-"$(($UPDATE_MINS*$n))"M" +"%H%M%z") + else + echo "ERROR: OS not supported" + exit 1 + fi + + FILE_NAME_TIMESTAMP="A"$DATE"."$TIME_START"-"$TIME_END + FILE_NAME=$FILE_NAME_TIMESTAMP"_"$HOSTNAME"-"$MAIN_DIRECTORY".xml.gz" + cp $FILE_TEMPLATE $FILE_DIRECTORY/$FILE_NAME + + TIMESTAMP_ARRAY[$n]=$FILE_NAME_TIMESTAMP +done + +while true +do + sleep $(($UPDATE_MINS*60)) + OLD_TIMESTAMP=${TIMESTAMP_ARRAY[$NUM_FILES-1]} + unset TIMESTAMP_ARRAY[$NUM_FILES-1] + + TIME_END=$(date +"%H%M%z") + if [[ "$OSTYPE" == "linux-gnu" ]]; then # Linux OS + DATE=$(date -d $(($UPDATE_MINS-1))" minutes ago" +"%Y%m%d") + TIME_START=$(date -d $(($UPDATE_MINS-1))" minutes ago" +"%H%M%z") + elif [[ "$OSTYPE" == "darwin"* ]]; then # Mac OS + DATE=$(date -v "-"$(($UPDATE_MINS-1))"M" +"%Y%m%d") + TIME_START=$(date -v "-"$(($UPDATE_MINS-1))"M" +"%H%M%z") + else + echo "ERROR: OS not supported" + exit 1 + fi + + NEW_TIMESTAMP="A"$DATE"."$TIME_START"-"$TIME_END + OLD_FILE_NAME=$OLD_TIMESTAMP"_"$HOSTNAME"-"$MAIN_DIRECTORY".xml.gz" + NEW_FILE_NAME=$NEW_TIMESTAMP"_"$HOSTNAME"-"$MAIN_DIRECTORY".xml.gz" + mv $FILE_DIRECTORY/$OLD_FILE_NAME $FILE_DIRECTORY/$NEW_FILE_NAME + #echo "Renamed OLD file: "$OLD_FILE_NAME" to NEW file: "$NEW_FILE_NAME # uncomment for debugging + + TIMESTAMP_ARRAY=("$NEW_TIMESTAMP" "${TIMESTAMP_ARRAY[@]}") +done diff --git a/pnf-sim-lightweight/config/config.json b/pnf-sim-lightweight/config/config.json new file mode 100644 index 0000000..9d2ba7e --- /dev/null +++ b/pnf-sim-lightweight/config/config.json @@ -0,0 +1,15 @@ +{ + "simulatorParams": { + "testDuration": "86400", + "messageInterval": "900" + }, + "commonEventHeaderParams": { + "eventName": "Noti_RnNode-Ericsson_FileReady", + "nfNamingCode": "gNB", + "nfcNamingCode": "oam" + }, + "notificationParams": { + "changeIdentifier": "PM_MEAS_FILES", + "changeType": "FileReady" + } +} diff --git a/pnf-sim-lightweight/config/config.yml b/pnf-sim-lightweight/config/config.yml new file mode 100644 index 0000000..f21329e --- /dev/null +++ b/pnf-sim-lightweight/config/config.yml @@ -0,0 +1,8 @@ +--- +urlves: http://localhost:10000/eventListener/v7 +urlftps: ftps://onap:pano@10.11.0.67 +urlsftp: sftp://onap:pano@10.11.0.68 +#when run in simulator, it does not have own IP +ippnfsim: localhost +typefileserver: sftp +... diff --git a/pnf-sim-lightweight/config/netconf.env b/pnf-sim-lightweight/config/netconf.env new file mode 100644 index 0000000..1230cdc --- /dev/null +++ b/pnf-sim-lightweight/config/netconf.env @@ -0,0 +1,10 @@ + +# to run in simulator +# NETCONF_ADDRESS=netopeer + +# to run in docker +NETCONF_ADDRESS=localhost + +NETCONF_PORT=830 +NETCONF_MODEL=pnf-simulator +NETCONF_MAIN_CONTAINER=config diff --git a/pnf-sim-lightweight/config/vsftpd_ssl-TEMPLATE.conf b/pnf-sim-lightweight/config/vsftpd_ssl-TEMPLATE.conf new file mode 100644 index 0000000..75bb974 --- /dev/null +++ b/pnf-sim-lightweight/config/vsftpd_ssl-TEMPLATE.conf @@ -0,0 +1,59 @@ +# Server Config +anonymous_enable=NO +local_enable=YES +write_enable=YES +local_umask=022 +dirmessage_enable=YES + +# Security and User auth +chroot_local_user=YES +pam_service_name=vsftpd_virtual +virtual_use_local_privs=YES +chmod_enable=NO +user_config_dir=/etc/vsftpd/user_conf +user_sub_token=$USER +#local_root=/srv/$USER +local_root=/srv/ +userlist_enable=NO +allow_writeable_chroot=YES + +# Logging +log_ftp_protocol=YES +xferlog_enable=YES +xferlog_std_format=YES +#xferlog_file=/dev/stdout +syslog_enable=NO +dual_log_enable=YES + +# Remap all login users to this username +guest_enable=YES +guest_username=ftp +hide_ids=YES + +# Networking +connect_from_port_20=NO +listen=YES +tcp_wrappers=YES +pasv_min_port=${FTPS_PASV_MIN} +pasv_max_port=${FTPS_PASV_MAX} + +# SSL +ssl_enable=Yes +require_ssl_reuse=NO +force_local_data_ssl=YES +force_local_logins_ssl=YES +ssl_ciphers=HIGH +allow_anon_ssl=NO + +ssl_tlsv1=YES +ssl_sslv2=YES +ssl_sslv3=YES +rsa_cert_file=/etc/ssl/private/ftp.crt +rsa_private_key_file=/etc/ssl/private/ftp.key + +require_cert=YES +ssl_request_cert=YES +ca_certs_file=/etc/ssl/private/dfc.crt + +write_enable=YES +pasv_address=${IPFILESERVER} diff --git a/pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template.yml b/pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template.yml new file mode 100644 index 0000000..6f9b5f0 --- /dev/null +++ b/pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template.yml @@ -0,0 +1,118 @@ +description: Heat template that deploys PnP PNF simulator +heat_template_version: '2013-05-23' +parameters: + flavor_name: {description: Type of instance (flavor) to be used, label: Flavor, + type: string} + image_name: {description: Image to be used for compute instance, label: Image name + or ID, type: string} + key_name: {description: Public/Private key pair name, label: Key pair name, type: string} + public_net_id: {description: Public network that enables remote connection to VNF, + label: Public network name or ID, type: string} + private_net_id: {type: string, description: Private network id, label: Private network name or ID} + private_subnet_id: {type: string, description: Private subnet id, label: Private subnetwork name or ID} + proxy: {type: string, description: Proxy, label: Proxy, default: ""} +resources: + PNF_PnP_simualtor: + type: OS::Nova::Server + properties: + key_name: { get_param: key_name } + image: { get_param: image_name } + flavor: { get_param: flavor_name } + networks: + - port: { get_resource: PNF_PnP_simualtor_port0 } + user_data_format: RAW + user_data: + str_replace: + template: | + #!/bin/bash + + set_versions () { + DOCKER_COMPOSE_VERSION=1.22.0 + } + + + enable_root_ssh () { + sed -i 's/PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config + sed -i 's/PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config + service sshd restart + echo -e "arthur\narthur" | passwd root + } + + update_os () { + dnf -y install fedora-upgrade + } + + docker_remove () { + dnf -y remove docker \ + docker-client \ + docker-client-latest \ + docker-common \ + docker-latest \ + docker-latest-logrotate \ + docker-logrotate \ + docker-selinux \ + docker-engine-selinux \ + docker-engine + } + + docker_install_and_configure () { + dnf -y install dnf-plugins-core + dnf config-manager \ + --add-repo \ + https://download.docker.com/linux/fedora/docker-ce.repo + dnf -y install docker-ce + systemctl start docker + mkdir -p /etc/systemd/system/docker.service.d/ + cat > /etc/systemd/system/docker.service.d/override.conf<< EOF + [Service] + Environment="HTTP_PROXY=$proxy" + Environment="HTTPS_PROXY=$proxy" + EOF + systemctl daemon-reload + systemctl restart docker + } + docker_compose_install () { + curl -L https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_VERSION/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + } + pnf_sim_file_checkout () { + cd /root; git clone https://gerrit.onap.org/r/integration + printf "{\n "simulatorParams": {\n "vesServerUrl": "http://VES-HOST:VES-PORT/eventListener/v7",\n "testDuration": "10",\n "messageInterval": "1"\n },\n "commonEventHeaderParams": {\n "eventName": "pnfRegistration_Nokia_5gDu",\n "nfNamingCode": "gNB",\n "nfcNamingCode": "oam",\n "sourceName": "NOK6061ZW3",\n "sourceId": "val13",\n "reportingEntityName": "NOK6061ZW3"\n },\n "pnfRegistrationParams": {\n "serialNumber": "6061ZW3",\n "vendorName": "Nokia",\n "oamV4IpAddress": "val3",\n "oamV6IpAddress": "val4",\n "unitFamily": "BBU",\n "modelNumber": "val6",\n "softwareVersion": "val7",\n "unitType": "val8"\n }\n}" > integration/test/mocks/pnfsimulator/config/config.json + } + + start_simulator () { + docker login -u docker -p docker nexus3.onap.org:10003 + cd ~/integration/test/mocks/pnfsimulator + ./simulator.sh start + } + + set_versions + enable_root_ssh + update_os + docker_remove + docker_install_and_configure + docker_compose_install + pnf_sim_file_checkout + start_simulator + params: + $proxy: { get_param: proxy } + PNF_PnP_simualtor_port0: + type: OS::Neutron::Port + properties: + network_id: { get_param: private_net_id } + security_groups: + - default + fixed_ips: + - subnet_id: { get_param: private_subnet_id } + PNF_PnP_simualtor_public: + type: OS::Neutron::FloatingIP + properties: + floating_network_id: { get_param: public_net_id } + port_id: { get_resource: PNF_PnP_simualtor_port0 } +outputs: + PNF_PnP_simualtor_private_ip: + description: IP address of PNF_PnP_simualtor in private network + value: { get_attr: [ PNF_PnP_simualtor, first_address ] } + PNF_PnP_simualtor_public_ip: + description: Floating IP address of PNF_PnP_simualtor in public network + value: { get_attr: [ PNF_PnP_simualtor_public, floating_ip_address ] } diff --git a/pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template_Ubuntu_16_04.yml b/pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template_Ubuntu_16_04.yml new file mode 100644 index 0000000..cef9d42 --- /dev/null +++ b/pnf-sim-lightweight/deployment/PnP_PNF_sim_heat_template_Ubuntu_16_04.yml @@ -0,0 +1,164 @@ +description: Heat template that deploys PnP PNF simulator +heat_template_version: '2013-05-23' +outputs: + PNF_PnP_simualtor_private_ip: + description: IP address of PNF_PnP_simualtor in private network + value: + get_attr: [PNF_PnP_simualtor, first_address] + PNF_PnP_simualtor_public_ip: + description: Floating IP address of PNF_PnP_simualtor in public network + value: + get_attr: [PNF_PnP_simualtor_public, floating_ip_address] +parameters: + flavor_name: + description: Type of instance (flavor) to be used + label: Flavor + type: string + image_name: + description: Ubuntu 16.04 image to be used + label: Image name or ID + type: string + key_name: + description: Public/Private key pair name + label: Key pair name + type: string + private_net_id: + description: Private network id + label: Private network name or ID + type: string + private_subnet_id: + description: Private subnet id + label: Private subnetwork name or ID + type: string + public_net_id: + description: Public network that enables remote connection to VNF + label: Public network name or ID + type: string + security_group: + default: default + description: Security group to be used + label: Security Groups + type: string + proxy: + default: '' + description: Proxy + label: Proxy + type: string + correlation_id: + default: 'someId' + description: Correlation ID + label: Correlation ID + type: string + VES-HOST: + default: 'VES-HOST' + description: VES collector host ip + label: VES ip + type: string + VES-PORT: + default: 'VES-PORT' + description: VES collector host port + label: VES port + type: string +resources: + PNF_PnP_simualtor: + properties: + flavor: + get_param: flavor_name + image: + get_param: image_name + key_name: + get_param: key_name + networks: + - port: + get_resource: PNF_PnP_simualtor_port0 + user_data: + str_replace: + params: + $proxy: + get_param: proxy + $VES-PORT: + get_param: VES-PORT + $VES-HOST: + get_param: VES-HOST + $correlation_id: + get_param: correlation_id + template: | + #!/bin/bash + + set_versions () { + DOCKER_VERSION=17.03 + DOCKER_COMPOSE_VERSION=1.22.0 + PROTOBUF_VERSION=3.6.1 + } + + enable_root_ssh () { + sed -i 's/PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config + sed -i 's/PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config + service sshd restart + echo -e "onap\nonap" | passwd root + } + + update_os () { + rm -rf /var/lib/apt/lists/* + apt-get clean + apt-get update + } + + docker_install_and_configure () { + curl "https://releases.rancher.com/install-docker/$DOCKER_VERSION.sh" | sh + mkdir -p /etc/systemd/system/docker.service.d/ + cat > /etc/systemd/system/docker.service.d/docker.conf << EOF + [Service] + ExecStart= + ExecStart=/usr/bin/dockerd -H fd:// --insecure-registry=nexus3.onap.org:10003 + Environment="HTTP_PROXY=$proxy" + Environment="HTTPS_PROXY=$proxy" + EOF + systemctl daemon-reload + systemctl restart docker + apt-mark hold docker-ce + docker login -u docker -p docker nexus3.onap.org:10003 + } + + docker_compose_install () { + curl -L "https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_VERSION/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + } + + pnf_sim_file_checkout () { + cd /root; git clone https://gerrit.onap.org/r/integration + printf "{\n \"simulatorParams\": {\n \"vesServerUrl\": \"http://$VES-HOST:$VES-PORT/eventListener/v7\",\n \"testDuration\": \"10\",\n \"messageInterval\": \"1\"\n },\n \"commonEventHeaderParams\": {\n \"eventName\": \"pnfRegistration_Nokia_5gDu\",\n \"nfNamingCode\": \"gNB\",\n \"nfcNamingCode\": \"oam\",\n \"sourceName\": \"$correlation_id\",\n \"sourceId\": \"val13\",\n \"reportingEntityName\": \"NOK6061ZW3\"\n },\n \"pnfRegistrationParams\": {\n \"serialNumber\": \"6061ZW3\",\n \"vendorName\": \"Nokia\",\n \"oamV4IpAddress\": \"val3\",\n \"oamV6IpAddress\": \"val4\",\n \"unitFamily\": \"BBU\",\n \"modelNumber\": \"val6\",\n \"softwareVersion\": \"val7\",\n \"unitType\": \"val8\"\n }\n}" > integration/test/mocks/pnfsimulator/config/config.json + } + + start_simulator () { + docker login -u docker -p docker nexus3.onap.org:10003 + cd ~/integration/test/mocks/pnfsimulator + ./simulator.sh start + } + + set_versions + enable_root_ssh + update_os + docker_install_and_configure + docker_compose_install + pnf_sim_file_checkout + start_simulator + + type: OS::Nova::Server + PNF_PnP_simualtor_port0: + properties: + fixed_ips: + - subnet_id: + get_param: private_subnet_id + network_id: + get_param: private_net_id + security_groups: + - get_param: security_group + type: OS::Neutron::Port + PNF_PnP_simualtor_public: + properties: + floating_network_id: + get_param: public_net_id + port_id: + get_resource: PNF_PnP_simualtor_port0 + type: OS::Neutron::FloatingIP diff --git a/pnf-sim-lightweight/docker-compose-template.yml b/pnf-sim-lightweight/docker-compose-template.yml new file mode 100644 index 0000000..8a59a11 --- /dev/null +++ b/pnf-sim-lightweight/docker-compose-template.yml @@ -0,0 +1,64 @@ +version: '2' +networks: + front: + driver: bridge + ipam: + config: + - gateway: "${IPGW}" + subnet: "${IPSUBNET}/28" + driver: default +services: + pnf-simulator-${I}: + container_name: pnf-simulator-${I} + image: nexus3.onap.org:10003/onap/masspnf-simulator:1.0.0-SNAPSHOT + networks: + front: + ipv4_address: "${IPPNFSIM}" + volumes: + - ./logs:/var/log + - ./json_schema:/json_schema + - ./config/config.yml:/config/config.yml:rw + - ./files/:/files/:rw + environment: + TZ: "${TIMEZONE}" + env_file: + - ./config/netconf.env + restart: on-failure + + sftp-server-${I}: + container_name: sftp-server-${I} + image: atmoz/sftp:alpine + networks: + front: + ipv4_address: "${IPSFTP}" + ports: + - "${PORTSFTP}:22" + volumes: + - ./files/onap/:/home/${FILESERV_USER}/ + - ./fix-sftp-perms.sh:/etc/sftp.d/fix-sftp-perms.sh + restart: on-failure + command: ${FILESERV_USER}:${FILESERV_PASS}:1001 + + ftpes-server-vsftpd-${I}: + container_name: ftpes-server-vsftpd-${I} + image: docker.io/panubo/vsftpd + networks: + front: + ipv4_address: "${IPFTPS}" + ports: + - "${PORTFTPS}:21" + - "${FTPS_PASV_MIN}-${FTPS_PASV_MAX}:${FTPS_PASV_MIN}-${FTPS_PASV_MAX}" + environment: + FTP_USER: ${FILESERV_USER} + FTP_PASSWORD: ${FILESERV_PASS} + PASV_ADDRESS: localhost + PASV_MIN_PORT: ${FTPS_PASV_MIN} + PASV_MAX_PORT: ${FTPS_PASV_MAX} + volumes: + - ./tls/ftp.crt:/etc/ssl/private/ftp.crt:ro + - ./tls/ftp.key:/etc/ssl/private/ftp.key:ro + - ./tls/dfc.crt:/etc/ssl/private/dfc.crt:ro + - ./config/vsftpd_ssl.conf:/etc/vsftpd_ssl.conf:ro + - ./files/onap/:/srv/ + restart: on-failure + command: vsftpd /etc/vsftpd_ssl.conf diff --git a/pnf-sim-lightweight/fix-sftp-perms-template.sh b/pnf-sim-lightweight/fix-sftp-perms-template.sh new file mode 100755 index 0000000..52e69b7 --- /dev/null +++ b/pnf-sim-lightweight/fix-sftp-perms-template.sh @@ -0,0 +1,2 @@ +#!/bin/bash +chmod o+w /home/${FILESERV_USER} diff --git a/pnf-sim-lightweight/json_schema/input_validator.json b/pnf-sim-lightweight/json_schema/input_validator.json new file mode 100644 index 0000000..9f56e8d --- /dev/null +++ b/pnf-sim-lightweight/json_schema/input_validator.json @@ -0,0 +1,108 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "simulatorParams": { + "type": "object", + "properties": { + "testDuration": { + "type": "string" + }, + "messageInterval": { + "type": "string" + } + }, + "required": [ + "testDuration", + "messageInterval" + ] + }, + "commonEventHeaderParams": { + "type": "object", + "properties": { + "eventName": { + "type": "string" + }, + "nfNamingCode": { + "type": "string" + }, + "nfcNamingCode": { + "type": "string" + } + }, + "required": [ + "eventName" + ] + }, + "pnfRegistrationParams": { + "type": "object", + "properties": { + "serialNumber": { + "type": "string" + }, + "vendorName": { + "type": "string" + }, + "oamV4IpAddress": { + "type": "string" + }, + "oamV6IpAddress": { + "type": "string" + }, + "unitFamily": { + "type": "string" + }, + "modelNumber": { + "type": "string" + }, + "softwareVersion": { + "type": "string" + }, + "unitType": { + "type": "string" + } + }, + "required": [ + "serialNumber", + "vendorName", + "oamV4IpAddress", + "oamV6IpAddress", + "unitFamily", + "modelNumber", + "softwareVersion", + "unitType" + ] + }, + "notificationParams": { + "type": "object", + "properties": { + "changeIdentifier": { + "type": "string" + }, + "changeType": { + "type": "string" + } + }, + "required": [ + "changeIdentifier", + "changeType" + ] + } + }, + "oneOf": [ + { + "required": [ + "simulatorParams", + "commonEventHeaderParams", + "pnfRegistrationParams" + ] + }, + { + "required": [ + "simulatorParams", + "commonEventHeaderParams", + "notificationParams" + ] + } + ] +} diff --git a/pnf-sim-lightweight/json_schema/output_validator_ves_schema_30.0.1.json b/pnf-sim-lightweight/json_schema/output_validator_ves_schema_30.0.1.json new file mode 100644 index 0000000..f65fb6d --- /dev/null +++ b/pnf-sim-lightweight/json_schema/output_validator_ves_schema_30.0.1.json @@ -0,0 +1,2744 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "VES Event Listener Common Event Format", + "type": "object", + "properties": { + "event": { + "$ref": "#/definitions/event" + }, + "eventList": { + "$ref": "#/definitions/eventList" + } + }, + "definitions": { + "schemaHeaderBlock": { + "description": "schema date, version, author and associated API", + "type": "object", + "properties": { + "associatedApi": { + "description": "VES Event Listener", + "type": "string" + }, + "lastUpdatedBy": { + "description": "re2947", + "type": "string" + }, + "schemaDate": { + "description": "July 31, 2018", + "type": "string" + }, + "schemaVersion": { + "description": "30.0.1", + "type": "number" + } + } + }, + "schemaLicenseAndCopyrightNotice": { + "description": "Copyright (c) 2018, AT&T Intellectual Property. All rights reserved", + "type": "object", + "properties": { + "apacheLicense2.0": { + "description": "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:", + "type": "string" + }, + "licenseUrl": { + "description": "http://www.apache.org/licenses/LICENSE-2.0", + "type": "string" + }, + "asIsClause": { + "description": "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.", + "type": "string" + }, + "permissionsAndLimitations": { + "description": "See the License for the specific language governing permissions and limitations under the License.", + "type": "string" + } + } + }, + "arrayOfJsonObject": { + "description": "array of json objects described by name, schema and other meta-information", + "type": "array", + "items": { + "$ref": "#/definitions/jsonObject" + } + }, + "arrayOfNamedHashMap": { + "description": "array of named hashMaps", + "type": "array", + "items": { + "$ref": "#/definitions/namedHashMap" + } + }, + "codecsInUse": { + "description": "number of times an identified codec was used over the measurementInterval", + "type": "object", + "properties": { + "codecIdentifier": { + "type": "string" + }, + "numberInUse": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "codecIdentifier", + "numberInUse" + ] + }, + "commonEventHeader": { + "description": "fields common to all events", + "type": "object", + "properties": { + "domain": { + "description": "the eventing domain associated with the event", + "type": "string", + "enum": [ + "fault", + "heartbeat", + "measurement", + "mobileFlow", + "notification", + "other", + "pnfRegistration", + "sipSignaling", + "stateChange", + "syslog", + "thresholdCrossingAlert", + "voiceQuality" + ] + }, + "eventId": { + "description": "event key that is unique to the event source", + "type": "string" + }, + "eventName": { + "description": "unique event name", + "type": "string" + }, + "eventType": { + "description": "for example - applicationNf, guestOS, hostOS, platform", + "type": "string" + }, + "internalHeaderFields": { + "$ref": "#/definitions/internalHeaderFields" + }, + "lastEpochMicrosec": { + "description": "the latest unix time aka epoch time associated with the event from any component--as microseconds elapsed since 1 Jan 1970 not including leap seconds", + "type": "number" + }, + "nfcNamingCode": { + "description": "3 character network function component type, aligned with vfc naming standards", + "type": "string" + }, + "nfNamingCode": { + "description": "4 character network function type, aligned with nf naming standards", + "type": "string" + }, + "nfVendorName": { + "description": "network function vendor name", + "type": "string" + }, + "priority": { + "description": "processing priority", + "type": "string", + "enum": [ + "High", + "Medium", + "Normal", + "Low" + ] + }, + "reportingEntityId": { + "description": "UUID identifying the entity reporting the event, for example an OAM VM; must be populated by the ATT enrichment process", + "type": "string" + }, + "reportingEntityName": { + "description": "name of the entity reporting the event, for example, an EMS name; may be the same as sourceName", + "type": "string" + }, + "sequence": { + "description": "ordering of events communicated by an event source instance or 0 if not needed", + "type": "integer" + }, + "sourceId": { + "description": "UUID identifying the entity experiencing the event issue; must be populated by the ATT enrichment process", + "type": "string" + }, + "sourceName": { + "description": "name of the entity experiencing the event issue", + "type": "string" + }, + "startEpochMicrosec": { + "description": "the earliest unix time aka epoch time associated with the event from any component--as microseconds elapsed since 1 Jan 1970 not including leap seconds", + "type": "number" + }, + "timeZoneOffset": { + "description": "UTC offset for the local time zone of the device as UTC+/-hh.mm", + "type": "string" + }, + "version": { + "description": "version of the event header", + "type": "string", + "enum": [ + "4.0.1" + ] + }, + "vesEventListenerVersion": { + "description": "version of the VES Event Listener API", + "type": "string", + "enum": [ + "7.0.1" + ] + } + }, + "additionalProperties": false, + "required": [ + "domain", + "eventId", + "eventName", + "lastEpochMicrosec", + "priority", + "reportingEntityName", + "sequence", + "sourceName", + "startEpochMicrosec", + "version", + "vesEventListenerVersion" + ] + }, + "counter": { + "description": "performance counter", + "type": "object", + "properties": { + "criticality": { + "type": "string", + "enum": [ + "CRIT", + "MAJ" + ] + }, + "hashMap": { + "$ref": "#/definitions/hashMap" + }, + "thresholdCrossed": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "criticality", + "hashMap", + "thresholdCrossed" + ] + }, + "cpuUsage": { + "description": "usage of an identified CPU", + "type": "object", + "properties": { + "cpuCapacityContention": { + "description": "the amount of time the CPU cannot run due to contention, in milliseconds over the measurementInterval", + "type": "number" + }, + "cpuDemandAvg": { + "description": "the total CPU time that the NF/NFC/VM could use if there was no contention, in milliseconds over the measurementInterval", + "type": "number" + }, + "cpuDemandMhz": { + "description": "CPU demand in megahertz", + "type": "number" + }, + "cpuDemandPct": { + "description": "CPU demand as a percentage of the provisioned capacity", + "type": "number" + }, + "cpuIdentifier": { + "description": "cpu identifer", + "type": "string" + }, + "cpuIdle": { + "description": "percentage of CPU time spent in the idle task", + "type": "number" + }, + "cpuLatencyAvg": { + "description": "percentage of time the VM is unable to run because it is contending for access to the physical CPUs", + "type": "number" + }, + "cpuOverheadAvg": { + "description": "the overhead demand above available allocations and reservations, in milliseconds over the measurementInterval", + "type": "number" + }, + "cpuSwapWaitTime": { + "description": "swap wait time. in milliseconds over the measurementInterval", + "type": "number" + }, + "cpuUsageInterrupt": { + "description": "percentage of time spent servicing interrupts", + "type": "number" + }, + "cpuUsageNice": { + "description": "percentage of time spent running user space processes that have been niced", + "type": "number" + }, + "cpuUsageSoftIrq": { + "description": "percentage of time spent handling soft irq interrupts", + "type": "number" + }, + "cpuUsageSteal": { + "description": "percentage of time spent in involuntary wait which is neither user, system or idle time and is effectively time that went missing", + "type": "number" + }, + "cpuUsageSystem": { + "description": "percentage of time spent on system tasks running the kernel", + "type": "number" + }, + "cpuUsageUser": { + "description": "percentage of time spent running un-niced user space processes", + "type": "number" + }, + "cpuWait": { + "description": "percentage of CPU time spent waiting for I/O operations to complete", + "type": "number" + }, + "percentUsage": { + "description": "aggregate cpu usage of the virtual machine on which the xNFC reporting the event is running", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "cpuIdentifier", + "percentUsage" + ] + }, + "diskUsage": { + "description": "usage of an identified disk", + "type": "object", + "properties": { + "diskBusResets": { + "description": "number of bus resets over the measurementInterval", + "type": "number" + }, + "diskCommandsAborted": { + "description": "number of disk commands aborted over the measurementInterval", + "type": "number" + }, + "diskCommandsAvg": { + "description": "average number of commands per second over the measurementInterval", + "type": "number" + }, + "diskFlushRequests": { + "description": "total flush requests of the disk cache over the measurementInterval", + "type": "number" + }, + "diskFlushTime": { + "description": "milliseconds spent on disk cache flushing over the measurementInterval", + "type": "number" + }, + "diskIdentifier": { + "description": "disk identifier", + "type": "string" + }, + "diskIoTimeAvg": { + "description": "milliseconds spent doing input/output operations over 1 sec; treat this metric as a device load percentage where 1000ms matches 100% load; provide the average over the measurement interval", + "type": "number" + }, + "diskIoTimeLast": { + "description": "milliseconds spent doing input/output operations over 1 sec; treat this metric as a device load percentage where 1000ms matches 100% load; provide the last value measurement within the measurement interval", + "type": "number" + }, + "diskIoTimeMax": { + "description": "milliseconds spent doing input/output operations over 1 sec; treat this metric as a device load percentage where 1000ms matches 100% load; provide the maximum value measurement within the measurement interval", + "type": "number" + }, + "diskIoTimeMin": { + "description": "milliseconds spent doing input/output operations over 1 sec; treat this metric as a device load percentage where 1000ms matches 100% load; provide the minimum value measurement within the measurement interval", + "type": "number" + }, + "diskMergedReadAvg": { + "description": "number of logical read operations that were merged into physical read operations, e.g., two logical reads were served by one physical disk access; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskMergedReadLast": { + "description": "number of logical read operations that were merged into physical read operations, e.g., two logical reads were served by one physical disk access; provide the last value measurement within the measurement interval", + "type": "number" + }, + "diskMergedReadMax": { + "description": "number of logical read operations that were merged into physical read operations, e.g., two logical reads were served by one physical disk access; provide the maximum value measurement within the measurement interval", + "type": "number" + }, + "diskMergedReadMin": { + "description": "number of logical read operations that were merged into physical read operations, e.g., two logical reads were served by one physical disk access; provide the minimum value measurement within the measurement interval", + "type": "number" + }, + "diskMergedWriteAvg": { + "description": "number of logical write operations that were merged into physical write operations, e.g., two logical writes were served by one physical disk access; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskMergedWriteLast": { + "description": "number of logical write operations that were merged into physical write operations, e.g., two logical writes were served by one physical disk access; provide the last value measurement within the measurement interval", + "type": "number" + }, + "diskMergedWriteMax": { + "description": "number of logical write operations that were merged into physical write operations, e.g., two logical writes were served by one physical disk access; provide the maximum value measurement within the measurement interval", + "type": "number" + }, + "diskMergedWriteMin": { + "description": "number of logical write operations that were merged into physical write operations, e.g., two logical writes were served by one physical disk access; provide the minimum value measurement within the measurement interval", + "type": "number" + }, + "diskOctetsReadAvg": { + "description": "number of octets per second read from a disk or partition; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskOctetsReadLast": { + "description": "number of octets per second read from a disk or partition; provide the last measurement within the measurement interval", + "type": "number" + }, + "diskOctetsReadMax": { + "description": "number of octets per second read from a disk or partition; provide the maximum measurement within the measurement interval", + "type": "number" + }, + "diskOctetsReadMin": { + "description": "number of octets per second read from a disk or partition; provide the minimum measurement within the measurement interval", + "type": "number" + }, + "diskOctetsWriteAvg": { + "description": "number of octets per second written to a disk or partition; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskOctetsWriteLast": { + "description": "number of octets per second written to a disk or partition; provide the last measurement within the measurement interval", + "type": "number" + }, + "diskOctetsWriteMax": { + "description": "number of octets per second written to a disk or partition; provide the maximum measurement within the measurement interval", + "type": "number" + }, + "diskOctetsWriteMin": { + "description": "number of octets per second written to a disk or partition; provide the minimum measurement within the measurement interval", + "type": "number" + }, + "diskOpsReadAvg": { + "description": "number of read operations per second issued to the disk; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskOpsReadLast": { + "description": "number of read operations per second issued to the disk; provide the last measurement within the measurement interval", + "type": "number" + }, + "diskOpsReadMax": { + "description": "number of read operations per second issued to the disk; provide the maximum measurement within the measurement interval", + "type": "number" + }, + "diskOpsReadMin": { + "description": "number of read operations per second issued to the disk; provide the minimum measurement within the measurement interval", + "type": "number" + }, + "diskOpsWriteAvg": { + "description": "number of write operations per second issued to the disk; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskOpsWriteLast": { + "description": "number of write operations per second issued to the disk; provide the last measurement within the measurement interval", + "type": "number" + }, + "diskOpsWriteMax": { + "description": "number of write operations per second issued to the disk; provide the maximum measurement within the measurement interval", + "type": "number" + }, + "diskOpsWriteMin": { + "description": "number of write operations per second issued to the disk; provide the minimum measurement within the measurement interval", + "type": "number" + }, + "diskPendingOperationsAvg": { + "description": "queue size of pending I/O operations per second; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskPendingOperationsLast": { + "description": "queue size of pending I/O operations per second; provide the last measurement within the measurement interval", + "type": "number" + }, + "diskPendingOperationsMax": { + "description": "queue size of pending I/O operations per second; provide the maximum measurement within the measurement interval", + "type": "number" + }, + "diskPendingOperationsMin": { + "description": "queue size of pending I/O operations per second; provide the minimum measurement within the measurement interval", + "type": "number" + }, + "diskReadCommandsAvg": { + "description": "average number of read commands issued per second to the disk over the measurementInterval", + "type": "number" + }, + "diskTime": { + "description": "nanoseconds spent on disk cache reads/writes within the measurement interval", + "type": "number" + }, + "diskTimeReadAvg": { + "description": "milliseconds a read operation took to complete; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskTimeReadLast": { + "description": "milliseconds a read operation took to complete; provide the last measurement within the measurement interval", + "type": "number" + }, + "diskTimeReadMax": { + "description": "milliseconds a read operation took to complete; provide the maximum measurement within the measurement interval", + "type": "number" + }, + "diskTimeReadMin": { + "description": "milliseconds a read operation took to complete; provide the minimum measurement within the measurement interval", + "type": "number" + }, + "diskTimeWriteAvg": { + "description": "milliseconds a write operation took to complete; provide the average measurement within the measurement interval", + "type": "number" + }, + "diskTimeWriteLast": { + "description": "milliseconds a write operation took to complete; provide the last measurement within the measurement interval", + "type": "number" + }, + "diskTimeWriteMax": { + "description": "milliseconds a write operation took to complete; provide the maximum measurement within the measurement interval", + "type": "number" + }, + "diskTimeWriteMin": { + "description": "milliseconds a write operation took to complete; provide the minimum measurement within the measurement interval", + "type": "number" + }, + "diskTotalReadLatencyAvg": { + "description": "average read time from the perspective of a Guest OS: sum of the Kernel Read Latency and Physical Device Read Latency in milliseconds over the measurement interval", + "type": "number" + }, + "diskTotalWriteLatencyAvg": { + "description": "average write time from the perspective of a Guest OS: sum of the Kernel Write Latency and Physical Device Write Latency in milliseconds over the measurement interval", + "type": "number" + }, + "diskWeightedIoTimeAvg": { + "description": "measure in ms over 1 sec of both I/O completion time and the backlog that may be accumulating; value is the average within the collection interval", + "type": "number" + }, + "diskWeightedIoTimeLast": { + "description": "measure in ms over 1 sec of both I/O completion time and the backlog that may be accumulating; value is the last within the collection interval", + "type": "number" + }, + "diskWeightedIoTimeMax": { + "description": "measure in ms over 1 sec of both I/O completion time and the backlog that may be accumulating; value is the maximum within the collection interval", + "type": "number" + }, + "diskWeightedIoTimeMin": { + "description": "measure in ms over 1 sec of both I/O completion time and the backlog that may be accumulating; value is the minimum within the collection interval", + "type": "number" + }, + "diskWriteCommandsAvg": { + "description": "average number of write commands issued per second to the disk over the measurementInterval", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "diskIdentifier" + ] + }, + "endOfCallVqmSummaries": { + "description": "provides end of call voice quality metrics", + "type": "object", + "properties": { + "adjacencyName": { + "description": " adjacency name", + "type": "string" + }, + "endpointAverageJitter": { + "description": "endpoint average jitter", + "type": "number" + }, + "endpointDescription": { + "description": "either Caller or Callee", + "type": "string", + "enum": [ + "Caller", + "Callee" + ] + }, + "endpointMaxJitter": { + "description": "endpoint maximum jitter", + "type": "number" + }, + "endpointRtpOctetsDiscarded": { + "description": "", + "type": "number" + }, + "endpointRtpOctetsLost": { + "description": "endpoint RTP octets lost", + "type": "number" + }, + "endpointRtpOctetsReceived": { + "description": "", + "type": "number" + }, + "endpointRtpOctetsSent": { + "description": "", + "type": "number" + }, + "endpointRtpPacketsDiscarded": { + "description": "", + "type": "number" + }, + "endpointRtpPacketsLost": { + "description": "endpoint RTP packets lost", + "type": "number" + }, + "endpointRtpPacketsReceived": { + "description": "", + "type": "number" + }, + "endpointRtpPacketsSent": { + "description": "", + "type": "number" + }, + "localAverageJitter": { + "description": "Local average jitter", + "type": "number" + }, + "localAverageJitterBufferDelay": { + "description": "Local average jitter delay", + "type": "number" + }, + "localMaxJitter": { + "description": "Local maximum jitter", + "type": "number" + }, + "localMaxJitterBufferDelay": { + "description": "Local maximum jitter delay", + "type": "number" + }, + "localRtpOctetsDiscarded": { + "description": "", + "type": "number" + }, + "localRtpOctetsLost": { + "description": "Local RTP octets lost", + "type": "number" + }, + "localRtpOctetsReceived": { + "description": "", + "type": "number" + }, + "localRtpOctetsSent": { + "description": "", + "type": "number" + }, + "localRtpPacketsDiscarded": { + "description": "", + "type": "number" + }, + "localRtpPacketsLost": { + "description": "Local RTP packets lost", + "type": "number" + }, + "localRtpPacketsReceived": { + "description": "", + "type": "number" + }, + "localRtpPacketsSent": { + "description": "", + "type": "number" + }, + "mosCqe": { + "description": "1-5 1dp", + "type": "number" + }, + "oneWayDelay": { + "description": "one-way path delay in milliseconds", + "type": "number" + }, + "packetLossPercent": { + "description": "Calculated percentage packet loss based on Endpoint RTP packets lost (as reported in RTCP) and Local RTP packets sent. Direction is based on Endpoint description (Caller, Callee). Decimal (2 dp)", + "type": "number" + }, + "rFactor": { + "description": "0-100", + "type": "number" + }, + "roundTripDelay": { + "description": "millisecs", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "adjacencyName", + "endpointDescription" + ] + }, + "event": { + "description": "the root level of the common event format", + "type": "object", + "properties": { + "commonEventHeader": { + "$ref": "#/definitions/commonEventHeader" + }, + "faultFields": { + "$ref": "#/definitions/faultFields" + }, + "heartbeatFields": { + "$ref": "#/definitions/heartbeatFields" + }, + "measurementFields": { + "$ref": "#/definitions/measurementFields" + }, + "mobileFlowFields": { + "$ref": "#/definitions/mobileFlowFields" + }, + "notificationFields": { + "$ref": "#/definitions/notificationFields" + }, + "otherFields": { + "$ref": "#/definitions/otherFields" + }, + "pnfRegistrationFields": { + "$ref": "#/definitions/pnfRegistrationFields" + }, + "sipSignalingFields": { + "$ref": "#/definitions/sipSignalingFields" + }, + "stateChangeFields": { + "$ref": "#/definitions/stateChangeFields" + }, + "syslogFields": { + "$ref": "#/definitions/syslogFields" + }, + "thresholdCrossingAlertFields": { + "$ref": "#/definitions/thresholdCrossingAlertFields" + }, + "voiceQualityFields": { + "$ref": "#/definitions/voiceQualityFields" + } + }, + "additionalProperties": false, + "required": [ + "commonEventHeader" + ] + }, + "eventList": { + "description": "array of events", + "type": "array", + "items": { + "$ref": "#/definitions/event" + } + }, + "faultFields": { + "description": "fields specific to fault events", + "type": "object", + "properties": { + "alarmAdditionalInformation": { + "$ref": "#/definitions/hashMap" + }, + "alarmCondition": { + "description": "alarm condition reported by the device", + "type": "string" + }, + "alarmInterfaceA": { + "description": "card, port, channel or interface name of the device generating the alarm", + "type": "string" + }, + "eventCategory": { + "description": "Event category, for example: license, link, routing, security, signaling", + "type": "string" + }, + "eventSeverity": { + "description": "event severity", + "type": "string", + "enum": [ + "CRITICAL", + "MAJOR", + "MINOR", + "WARNING", + "NORMAL" + ] + }, + "eventSourceType": { + "description": "type of event source; examples: card, host, other, port, portThreshold, router, slotThreshold, switch, virtualMachine, virtualNetworkFunction", + "type": "string" + }, + "faultFieldsVersion": { + "description": "version of the faultFields block", + "type": "string", + "enum": [ + "4.0" + ] + }, + "specificProblem": { + "description": "short description of the alarm or problem", + "type": "string" + }, + "vfStatus": { + "description": "virtual function status enumeration", + "type": "string", + "enum": [ + "Active", + "Idle", + "Preparing to terminate", + "Ready to terminate", + "Requesting termination" + ] + } + }, + "additionalProperties": false, + "required": [ + "alarmCondition", + "eventSeverity", + "eventSourceType", + "faultFieldsVersion", + "specificProblem", + "vfStatus" + ] + }, + "filesystemUsage": { + "description": "disk usage of an identified virtual machine in gigabytes and/or gigabytes per second", + "type": "object", + "properties": { + "blockConfigured": { + "type": "number" + }, + "blockIops": { + "type": "number" + }, + "blockUsed": { + "type": "number" + }, + "ephemeralConfigured": { + "type": "number" + }, + "ephemeralIops": { + "type": "number" + }, + "ephemeralUsed": { + "type": "number" + }, + "filesystemName": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "blockConfigured", + "blockIops", + "blockUsed", + "ephemeralConfigured", + "ephemeralIops", + "ephemeralUsed", + "filesystemName" + ] + }, + "gtpPerFlowMetrics": { + "description": "Mobility GTP Protocol per flow metrics", + "type": "object", + "properties": { + "avgBitErrorRate": { + "description": "average bit error rate", + "type": "number" + }, + "avgPacketDelayVariation": { + "description": "Average packet delay variation or jitter in milliseconds for received packets: Average difference between the packet timestamp and time received for all pairs of consecutive packets", + "type": "number" + }, + "avgPacketLatency": { + "description": "average delivery latency", + "type": "number" + }, + "avgReceiveThroughput": { + "description": "average receive throughput", + "type": "number" + }, + "avgTransmitThroughput": { + "description": "average transmit throughput", + "type": "number" + }, + "durConnectionFailedStatus": { + "description": "duration of failed state in milliseconds, computed as the cumulative time between a failed echo request and the next following successful error request, over this reporting interval", + "type": "number" + }, + "durTunnelFailedStatus": { + "description": "Duration of errored state, computed as the cumulative time between a tunnel error indicator and the next following non-errored indicator, over this reporting interval", + "type": "number" + }, + "flowActivatedBy": { + "description": "Endpoint activating the flow", + "type": "string" + }, + "flowActivationEpoch": { + "description": "Time the connection is activated in the flow (connection) being reported on, or transmission time of the first packet if activation time is not available", + "type": "number" + }, + "flowActivationMicrosec": { + "description": "Integer microseconds for the start of the flow connection", + "type": "number" + }, + "flowActivationTime": { + "description": "time the connection is activated in the flow being reported on, or transmission time of the first packet if activation time is not available; with RFC 2822 compliant format: Sat, 13 Mar 2010 11:29:05 -0800", + "type": "string" + }, + "flowDeactivatedBy": { + "description": "Endpoint deactivating the flow", + "type": "string" + }, + "flowDeactivationEpoch": { + "description": "Time for the start of the flow connection, in integer UTC epoch time aka UNIX time", + "type": "number" + }, + "flowDeactivationMicrosec": { + "description": "Integer microseconds for the start of the flow connection", + "type": "number" + }, + "flowDeactivationTime": { + "description": "Transmission time of the first packet in the flow connection being reported on; with RFC 2822 compliant format: Sat, 13 Mar 2010 11:29:05 -0800", + "type": "string" + }, + "flowStatus": { + "description": "connection status at reporting time as a working / inactive / failed indicator value", + "type": "string" + }, + "gtpConnectionStatus": { + "description": "Current connection state at reporting time", + "type": "string" + }, + "gtpTunnelStatus": { + "description": "Current tunnel state at reporting time", + "type": "string" + }, + "ipTosCountList": { + "$ref": "#/definitions/hashMap" + }, + "ipTosList": { + "description": "Array of unique IP Type-of-Service values observed in the flow where values range from '0' to '255'", + "type": "array", + "items": { + "type": "string" + } + }, + "largePacketRtt": { + "description": "large packet round trip time", + "type": "number" + }, + "largePacketThreshold": { + "description": "large packet threshold being applied", + "type": "number" + }, + "maxPacketDelayVariation": { + "description": "Maximum packet delay variation or jitter in milliseconds for received packets: Maximum of the difference between the packet timestamp and time received for all pairs of consecutive packets", + "type": "number" + }, + "maxReceiveBitRate": { + "description": "maximum receive bit rate", + "type": "number" + }, + "maxTransmitBitRate": { + "description": "maximum transmit bit rate", + "type": "number" + }, + "mobileQciCosCountList": { + "$ref": "#/definitions/hashMap" + }, + "mobileQciCosList": { + "description": "Array of unique LTE QCI or UMTS class-of-service values observed in the flow", + "type": "array", + "items": { + "type": "string" + } + }, + "numActivationFailures": { + "description": "Number of failed activation requests, as observed by the reporting node", + "type": "number" + }, + "numBitErrors": { + "description": "number of errored bits", + "type": "number" + }, + "numBytesReceived": { + "description": "number of bytes received, including retransmissions", + "type": "number" + }, + "numBytesTransmitted": { + "description": "number of bytes transmitted, including retransmissions", + "type": "number" + }, + "numDroppedPackets": { + "description": "number of received packets dropped due to errors per virtual interface", + "type": "number" + }, + "numGtpEchoFailures": { + "description": "Number of Echo request path failures where failed paths are defined in 3GPP TS 29.281 sec 7.2.1 and 3GPP TS 29.060 sec. 11.2", + "type": "number" + }, + "numGtpTunnelErrors": { + "description": "Number of tunnel error indications where errors are defined in 3GPP TS 29.281 sec 7.3.1 and 3GPP TS 29.060 sec. 11.1", + "type": "number" + }, + "numHttpErrors": { + "description": "Http error count", + "type": "number" + }, + "numL7BytesReceived": { + "description": "number of tunneled layer 7 bytes received, including retransmissions", + "type": "number" + }, + "numL7BytesTransmitted": { + "description": "number of tunneled layer 7 bytes transmitted, excluding retransmissions", + "type": "number" + }, + "numLostPackets": { + "description": "number of lost packets", + "type": "number" + }, + "numOutOfOrderPackets": { + "description": "number of out-of-order packets", + "type": "number" + }, + "numPacketErrors": { + "description": "number of errored packets", + "type": "number" + }, + "numPacketsReceivedExclRetrans": { + "description": "number of packets received, excluding retransmission", + "type": "number" + }, + "numPacketsReceivedInclRetrans": { + "description": "number of packets received, including retransmission", + "type": "number" + }, + "numPacketsTransmittedInclRetrans": { + "description": "number of packets transmitted, including retransmissions", + "type": "number" + }, + "numRetries": { + "description": "number of packet retries", + "type": "number" + }, + "numTimeouts": { + "description": "number of packet timeouts", + "type": "number" + }, + "numTunneledL7BytesReceived": { + "description": "number of tunneled layer 7 bytes received, excluding retransmissions", + "type": "number" + }, + "roundTripTime": { + "description": "round trip time", + "type": "number" + }, + "tcpFlagCountList": { + "$ref": "#/definitions/hashMap" + }, + "tcpFlagList": { + "description": "Array of unique TCP Flags observed in the flow", + "type": "array", + "items": { + "type": "string" + } + }, + "timeToFirstByte": { + "description": "Time in milliseconds between the connection activation and first byte received", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "avgBitErrorRate", + "avgPacketDelayVariation", + "avgPacketLatency", + "avgReceiveThroughput", + "avgTransmitThroughput", + "flowActivationEpoch", + "flowActivationMicrosec", + "flowDeactivationEpoch", + "flowDeactivationMicrosec", + "flowDeactivationTime", + "flowStatus", + "maxPacketDelayVariation", + "numActivationFailures", + "numBitErrors", + "numBytesReceived", + "numBytesTransmitted", + "numDroppedPackets", + "numL7BytesReceived", + "numL7BytesTransmitted", + "numLostPackets", + "numOutOfOrderPackets", + "numPacketErrors", + "numPacketsReceivedExclRetrans", + "numPacketsReceivedInclRetrans", + "numPacketsTransmittedInclRetrans", + "numRetries", + "numTimeouts", + "numTunneledL7BytesReceived", + "roundTripTime", + "timeToFirstByte" + ] + }, + "hashMap": { + "description": "an associative array which is an array of key:value pairs", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": {} + }, + "heartbeatFields": { + "description": "optional field block for fields specific to heartbeat events", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "heartbeatFieldsVersion": { + "description": "version of the heartbeatFields block", + "type": "string", + "enum": [ + "3.0" + ] + }, + "heartbeatInterval": { + "description": "current heartbeat interval in seconds", + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "heartbeatFieldsVersion", + "heartbeatInterval" + ] + }, + "hugePages": { + "description": "metrics on system hugepages", + "type": "object", + "properties": { + "bytesFree": { + "description": "number of free hugepages in bytes", + "type": "number" + }, + "bytesUsed": { + "description": "number of used hugepages in bytes", + "type": "number" + }, + "hugePagesIdentifier": { + "description": "hugePages identifier", + "type": "number" + }, + "percentFree": { + "description": "number of free hugepages in percent", + "type": "number" + }, + "percentUsed": { + "description": "number of free hugepages in percent", + "type": "number" + }, + "vmPageNumberFree": { + "description": "number of free vmPages in numbers", + "type": "number" + }, + "vmPageNumberUsed": { + "description": "number of used vmPages in numbers", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "hugePagesIdentifier" + ] + }, + "internalHeaderFields": { + "description": "enrichment fields for internal VES Event Listener service use only, not supplied by event sources", + "type": "object" + }, + "ipmi": { + "description": "intelligent platform management interface metrics", + "type": "object", + "properties": { + "exitAirTemperature": { + "description": "system fan exit air flow temperature in celsius", + "type": "number" + }, + "frontPanelTemperature": { + "description": "front panel temperature in celsius", + "type": "number" + }, + "ioModuleTemperature": { + "description": "io module temperature in celsius", + "type": "number" + }, + "ipmiBaseboardTemperatureArray": { + "description": "array of ipmiBaseboardTemperature objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiBaseboardTemperature" + } + }, + "ipmiBaseboardVoltageRegulatorArray": { + "description": "array of ipmiBaseboardVoltageRegulator objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiBaseboardVoltageRegulator" + } + }, + "ipmiBatteryArray": { + "description": "array of ipmiBattery objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiBattery" + } + }, + "ipmiFanArray": { + "description": "array of ipmiFan objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiFan" + } + }, + "ipmiHsbpArray": { + "description": "array of ipmiHsbp objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiHsbp" + } + }, + "ipmiGlobalAggregateTemperatureMarginArray": { + "description": "array of ipmiGlobalAggregateTemperatureMargin objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiGlobalAggregateTemperatureMargin" + } + }, + "ipmiNicArray": { + "description": "array of ipmiNic objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiNic" + } + }, + "ipmiPowerSupplyArray": { + "description": "array of ipmiPowerSupply objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiPowerSupply" + } + }, + "ipmiProcessorArray": { + "description": "array of ipmiProcessor objects", + "type": "array", + "items": { + "$ref": "#/definitions/ipmiProcessor" + } + }, + "systemAirflow": { + "description": "airfflow in cubic feet per minute (cfm)", + "type": "number" + } + }, + "additionalProperties": false + }, + "ipmiBaseboardTemperature": { + "description": "intelligent platform management interface (ipmi) baseboard temperature metrics", + "type": "object", + "properties": { + "baseboardTemperatureIdentifier": { + "description": "identifier for the location where the temperature is taken", + "type": "string" + }, + "baseboardTemperature": { + "description": "baseboard temperature in celsius", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "baseboardTemperatureIdentifier" + ] + }, + "ipmiBaseboardVoltageRegulator": { + "description": "intelligent platform management interface (ipmi) baseboard voltage regulator metrics", + "type": "object", + "properties": { + "baseboardVoltageRegulatorIdentifier": { + "description": "identifier for the baseboard voltage regulator", + "type": "string" + }, + "voltageRegulatorTemperature": { + "description": "voltage regulator temperature in celsius", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "baseboardVoltageRegulatorIdentifier" + ] + }, + "ipmiBattery": { + "description": "intelligent platform management interface (ipmi) battery metrics", + "type": "object", + "properties": { + "batteryIdentifier": { + "description": "identifier for the battery", + "type": "string" + }, + "batteryType": { + "description": "type of battery", + "type": "string" + }, + "batteryVoltageLevel": { + "description": "battery voltage level", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "batteryIdentifier" + ] + }, + "ipmiFan": { + "description": "intelligent platform management interface (ipmi) fan metrics", + "type": "object", + "properties": { + "fanIdentifier": { + "description": "identifier for the fan", + "type": "string" + }, + "fanSpeed": { + "description": "fan speed in revolutions per minute (rpm)", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "fanIdentifier" + ] + }, + "ipmiGlobalAggregateTemperatureMargin": { + "description": "intelligent platform management interface (ipmi) global aggregate temperature margin", + "type": "object", + "properties": { + "ipmiGlobalAggregateTemperatureMarginIdentifier": { + "description": "identifier for the ipmi global aggregate temperature margin metrics", + "type": "string" + }, + "globalAggregateTemperatureMargin": { + "description": "the difference between the current global aggregate temperature, in celsius, and the global aggregate throttling thermal trip point", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "ipmiGlobalAggregateTemperatureMarginIdentifier", + "globalAggregateTemperatureMargin" + ] + }, + "ipmiHsbp": { + "description": "intelligent platform management interface (ipmi) hot swap backplane power metrics", + "type": "object", + "properties": { + "hsbpIdentifier": { + "description": "identifier for the hot swap backplane power unit", + "type": "string" + }, + "hsbpTemperature": { + "description": "hot swap backplane power temperature in celsius", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "hsbpIdentifier" + ] + }, + "ipmiNic": { + "description": "intelligent platform management interface (ipmi) network interface control card (nic) metrics", + "type": "object", + "properties": { + "nicIdentifier": { + "description": "identifier for the network interface control card", + "type": "string" + }, + "nicTemperature": { + "description": "nic temperature in celsius", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "nicIdentifier" + ] + }, + "ipmiPowerSupply": { + "description": "intelligent platform management interface (ipmi) power supply metrics", + "type": "object", + "properties": { + "powerSupplyIdentifier": { + "description": "identifier for the power supply", + "type": "string" + }, + "powerSupplyInputPower": { + "description": "input power in watts", + "type": "number" + }, + "powerSupplyCurrentOutputPercent": { + "description": "current output voltage as a percentage of the design specified level", + "type": "number" + }, + "powerSupplyTemperature": { + "description": "power supply temperature in celsius", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "powerSupplyIdentifier" + ] + }, + "ipmiProcessor": { + "description": "intelligent platform management interface processor metrics", + "type": "object", + "properties": { + "processorIdentifier": { + "description": "identifier for an ipmi processor", + "type": "string" + }, + "processorThermalControlPercent": { + "description": "io module temperature in celsius", + "type": "number" + }, + "processorDtsThermalMargin": { + "description": "front panel temperature in celsius", + "type": "number" + }, + "processorDimmAggregateThermalMarginArray": { + "description": "array of processorDimmAggregateThermalMargin objects", + "type": "array", + "items": { + "$ref": "#/definitions/processorDimmAggregateThermalMargin" + } + } + }, + "additionalProperties": false, + "required": [ + "processorIdentifier" + ] + }, + "jsonObject": { + "description": "json object schema, name and other meta-information along with one or more object instances", + "type": "object", + "properties": { + "objectInstances": { + "description": "one or more instances of the jsonObject", + "type": "array", + "items": { + "$ref": "#/definitions/jsonObjectInstance" + } + }, + "objectName": { + "description": "name of the JSON Object", + "type": "string" + }, + "objectSchema": { + "description": "json schema for the object", + "type": "string" + }, + "objectSchemaUrl": { + "description": "Url to the json schema for the object", + "type": "string" + }, + "nfSubscribedObjectName": { + "description": "name of the object associated with the nfSubscriptonId", + "type": "string" + }, + "nfSubscriptionId": { + "description": "identifies an openConfig telemetry subscription on a network function, which configures the network function to send complex object data associated with the jsonObject", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "objectInstances", + "objectName" + ] + }, + "jsonObjectInstance": { + "description": "meta-information about an instance of a jsonObject along with the actual object instance", + "type": "object", + "properties": { + "jsonObject": { + "$ref": "#/definitions/jsonObject" + }, + "objectInstance": { + "description": "an instance conforming to the jsonObject objectSchema", + "type": "object" + }, + "objectInstanceEpochMicrosec": { + "description": "the unix time aka epoch time associated with this objectInstance--as microseconds elapsed since 1 Jan 1970 not including leap seconds", + "type": "number" + }, + "objectKeys": { + "description": "an ordered set of keys that identifies this particular instance of jsonObject", + "type": "array", + "items": { + "$ref": "#/definitions/key" + } + } + }, + "additionalProperties": false + }, + "key": { + "description": "tuple which provides the name of a key along with its value and relative order", + "type": "object", + "properties": { + "keyName": { + "description": "name of the key", + "type": "string" + }, + "keyOrder": { + "description": "relative sequence or order of the key with respect to other keys", + "type": "integer" + }, + "keyValue": { + "description": "value of the key", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "keyName" + ] + }, + "latencyBucketMeasure": { + "description": "number of counts falling within a defined latency bucket", + "type": "object", + "properties": { + "countsInTheBucket": { + "type": "number" + }, + "highEndOfLatencyBucket": { + "type": "number" + }, + "lowEndOfLatencyBucket": { + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "countsInTheBucket" + ] + }, + "load": { + "description": "/proc/loadavg cpu utilization and io utilization metrics", + "type": "object", + "properties": { + "longTerm": { + "description": "number of jobs in the run queue (state R, cpu utilization) or waiting for disk I/O (state D, io utilization) averaged over 15 minutes using /proc/loadavg", + "type": "number" + }, + "midTerm": { + "description": "number of jobs in the run queue (state R, cpu utilization) or waiting for disk I/O (state D, io utilization) averaged over 5 minutes using /proc/loadavg", + "type": "number" + }, + "shortTerm": { + "description": "number of jobs in the run queue (state R, cpu utilization) or waiting for disk I/O (state D, io utilization) averaged over 1 minute using /proc/loadavg", + "type": "number" + } + }, + "additionalProperties": false + }, + "machineCheckException": { + "description": "metrics on vm machine check exceptions", + "type": "object", + "properties": { + "correctedMemoryErrors": { + "description": "total hardware errors that were corrected by the hardware (e.g. data corruption corrected via \u00a0ECC) over the measurementInterval", + "type": "number" + }, + "correctedMemoryErrorsIn1Hr": { + "description": "total hardware errors that were corrected by the hardware over the last one hour", + "type": "number" + }, + "uncorrectedMemoryErrors": { + "description": "total uncorrected hardware errors that were detected by the hardware (e.g., causing data corruption) over the measurementInterval", + "type": "number" + }, + "uncorrectedMemoryErrorsIn1Hr": { + "description": "total uncorrected hardware errors that were detected by the hardware over the last one hour", + "type": "number" + }, + "vmIdentifier": { + "description": "virtual machine identifier associated with the machine check exception", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "vmIdentifier" + ] + }, + "measurementFields": { + "description": "measurement fields", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "additionalMeasurements": { + "$ref": "#/definitions/arrayOfNamedHashMap" + }, + "additionalObjects": { + "$ref": "#/definitions/arrayOfJsonObject" + }, + "codecUsageArray": { + "description": "array of codecs in use", + "type": "array", + "items": { + "$ref": "#/definitions/codecsInUse" + } + }, + "concurrentSessions": { + "description": "peak concurrent sessions for the VM or xNF over the measurementInterval", + "type": "integer" + }, + "configuredEntities": { + "description": "over the measurementInterval, peak total number of: users, subscribers, devices, adjacencies, etc., for the VM, or subscribers, devices, etc., for the xNF", + "type": "integer" + }, + "cpuUsageArray": { + "description": "usage of an array of CPUs", + "type": "array", + "items": { + "$ref": "#/definitions/cpuUsage" + } + }, + "diskUsageArray": { + "description": "usage of an array of disks", + "type": "array", + "items": { + "$ref": "#/definitions/diskUsage" + } + }, + "featureUsageArray": { + "$ref": "#/definitions/hashMap" + }, + "filesystemUsageArray": { + "description": "filesystem usage of the VM on which the xNFC reporting the event is running", + "type": "array", + "items": { + "$ref": "#/definitions/filesystemUsage" + } + }, + "hugePagesArray": { + "description": "array of metrics on hugepPages", + "type": "array", + "items": { + "$ref": "#/definitions/hugePages" + } + }, + "ipmi": { + "$ref": "#/definitions/ipmi" + }, + "latencyDistribution": { + "description": "array of integers representing counts of requests whose latency in milliseconds falls within per-xNF configured ranges", + "type": "array", + "items": { + "$ref": "#/definitions/latencyBucketMeasure" + } + }, + "loadArray": { + "description": "array of system load metrics", + "type": "array", + "items": { + "$ref": "#/definitions/load" + } + }, + "machineCheckExceptionArray": { + "description": "array of machine check exceptions", + "type": "array", + "items": { + "$ref": "#/definitions/machineCheckException" + } + }, + "meanRequestLatency": { + "description": "mean seconds required to respond to each request for the VM on which the xNFC reporting the event is running", + "type": "number" + }, + "measurementInterval": { + "description": "interval over which measurements are being reported in seconds", + "type": "number" + }, + "measurementFieldsVersion": { + "description": "version of the measurementFields block", + "type": "string", + "enum": [ + "4.0" + ] + }, + "memoryUsageArray": { + "description": "memory usage of an array of VMs", + "type": "array", + "items": { + "$ref": "#/definitions/memoryUsage" + } + }, + "numberOfMediaPortsInUse": { + "description": "number of media ports in use", + "type": "integer" + }, + "requestRate": { + "description": "peak rate of service requests per second to the xNF over the measurementInterval", + "type": "number" + }, + "nfcScalingMetric": { + "description": "represents busy-ness of the network function from 0 to 100 as reported by the xNFC", + "type": "integer" + }, + "nicPerformanceArray": { + "description": "usage of an array of network interface cards", + "type": "array", + "items": { + "$ref": "#/definitions/nicPerformance" + } + }, + "processStatsArray": { + "description": "array of metrics on system processes", + "type": "array", + "items": { + "$ref": "#/definitions/processStats" + } + } + }, + "additionalProperties": false, + "required": [ + "measurementInterval", + "measurementFieldsVersion" + ] + }, + "memoryUsage": { + "description": "memory usage of an identified virtual machine", + "type": "object", + "properties": { + "memoryBuffered": { + "description": "kibibytes of temporary storage for raw disk blocks", + "type": "number" + }, + "memoryCached": { + "description": "kibibytes of memory used for cache", + "type": "number" + }, + "memoryConfigured": { + "description": "kibibytes of memory configured in the virtual machine on which the xNFC reporting the event is running", + "type": "number" + }, + "memoryDemand": { + "description": "host demand in kibibytes", + "type": "number" + }, + "memoryFree": { + "description": "kibibytes of physical RAM left unused by the system", + "type": "number" + }, + "memoryLatencyAvg": { + "description": "Percentage of time the VM is waiting to access swapped or compressed memory", + "type": "number" + }, + "memorySharedAvg": { + "description": "shared memory in kilobytes", + "type": "number" + }, + "memorySlabRecl": { + "description": "the part of the slab that can be reclaimed such as caches measured in kibibytes", + "type": "number" + }, + "memorySlabUnrecl": { + "description": "the part of the slab that cannot be reclaimed even when lacking memory measured in kibibytes", + "type": "number" + }, + "memorySwapInAvg": { + "description": "Amount of memory swapped-in from host cache in kibibytes", + "type": "number" + }, + "memorySwapInRateAvg": { + "description": "rate at which memory is swapped from disk into active memory during the interval in kilobytes per second", + "type": "number" + }, + "memorySwapOutAvg": { + "description": "Amount of memory swapped-out to host cache in kibibytes", + "type": "number" + }, + "memorySwapOutRateAvg": { + "description": "rate at which memory is being swapped from active memory to disk during the current interval in kilobytes per second", + "type": "number" + }, + "memorySwapUsedAvg": { + "description": "space used for caching swapped pages in the host cache in kibibytes", + "type": "number" + }, + "memoryUsed": { + "description": "total memory minus the sum of free, buffered, cached and slab memory measured in kibibytes", + "type": "number" + }, + "percentMemoryUsage": { + "description": "Percentage of memory usage; value = (memoryUsed / (memoryUsed + memoryFree) x 100 if denomintor is nonzero, or 0, if otherwise", + "type": "number" + }, + "vmIdentifier": { + "description": "virtual machine identifier associated with the memory metrics", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "memoryFree", + "memoryUsed", + "vmIdentifier" + ] + }, + "mobileFlowFields": { + "description": "mobileFlow fields", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "applicationType": { + "description": "Application type inferred", + "type": "string" + }, + "appProtocolType": { + "description": "application protocol", + "type": "string" + }, + "appProtocolVersion": { + "description": "application protocol version", + "type": "string" + }, + "cid": { + "description": "cell id", + "type": "string" + }, + "connectionType": { + "description": "Abbreviation referencing a 3GPP reference point e.g., S1-U, S11, etc", + "type": "string" + }, + "ecgi": { + "description": "Evolved Cell Global Id", + "type": "string" + }, + "flowDirection": { + "description": "Flow direction, indicating if the reporting node is the source of the flow or destination for the flow", + "type": "string" + }, + "gtpPerFlowMetrics": { + "$ref": "#/definitions/gtpPerFlowMetrics" + }, + "gtpProtocolType": { + "description": "GTP protocol", + "type": "string" + }, + "gtpVersion": { + "description": "GTP protocol version", + "type": "string" + }, + "httpHeader": { + "description": "HTTP request header, if the flow connects to a node referenced by HTTP", + "type": "string" + }, + "imei": { + "description": "IMEI for the subscriber UE used in this flow, if the flow connects to a mobile device", + "type": "string" + }, + "imsi": { + "description": "IMSI for the subscriber UE used in this flow, if the flow connects to a mobile device", + "type": "string" + }, + "ipProtocolType": { + "description": "IP protocol type e.g., TCP, UDP, RTP...", + "type": "string" + }, + "ipVersion": { + "description": "IP protocol version e.g., IPv4, IPv6", + "type": "string" + }, + "lac": { + "description": "location area code", + "type": "string" + }, + "mcc": { + "description": "mobile country code", + "type": "string" + }, + "mnc": { + "description": "mobile network code", + "type": "string" + }, + "mobileFlowFieldsVersion": { + "description": "version of the mobileFlowFields block", + "type": "string", + "enum": [ + "4.0" + ] + }, + "msisdn": { + "description": "MSISDN for the subscriber UE used in this flow, as an integer, if the flow connects to a mobile device", + "type": "string" + }, + "otherEndpointIpAddress": { + "description": "IP address for the other endpoint, as used for the flow being reported on", + "type": "string" + }, + "otherEndpointPort": { + "description": "IP Port for the reporting entity, as used for the flow being reported on", + "type": "integer" + }, + "otherFunctionalRole": { + "description": "Functional role of the other endpoint for the flow being reported on e.g., MME, S-GW, P-GW, PCRF...", + "type": "string" + }, + "rac": { + "description": "routing area code", + "type": "string" + }, + "radioAccessTechnology": { + "description": "Radio Access Technology e.g., 2G, 3G, LTE", + "type": "string" + }, + "reportingEndpointIpAddr": { + "description": "IP address for the reporting entity, as used for the flow being reported on", + "type": "string" + }, + "reportingEndpointPort": { + "description": "IP port for the reporting entity, as used for the flow being reported on", + "type": "integer" + }, + "sac": { + "description": "service area code", + "type": "string" + }, + "samplingAlgorithm": { + "description": "Integer identifier for the sampling algorithm or rule being applied in calculating the flow metrics if metrics are calculated based on a sample of packets, or 0 if no sampling is applied", + "type": "integer" + }, + "tac": { + "description": "transport area code", + "type": "string" + }, + "tunnelId": { + "description": "tunnel identifier", + "type": "string" + }, + "vlanId": { + "description": "VLAN identifier used by this flow", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "flowDirection", + "gtpPerFlowMetrics", + "ipProtocolType", + "ipVersion", + "mobileFlowFieldsVersion", + "otherEndpointIpAddress", + "otherEndpointPort", + "reportingEndpointIpAddr", + "reportingEndpointPort" + ] + }, + "namedHashMap": { + "description": "a hashMap which is associated with and described by a name", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "hashMap": { + "$ref": "#/definitions/hashMap" + } + }, + "additionalProperties": false, + "required": [ + "name", + "hashMap" + ] + }, + "nicPerformance": { + "description": "describes the performance and errors of an identified network interface card", + "type": "object", + "properties": { + "administrativeState": { + "description": "administrative state", + "type": "string", + "enum": [ + "inService", + "outOfService" + ] + }, + "nicIdentifier": { + "description": "nic identification", + "type": "string" + }, + "operationalState": { + "description": "operational state", + "type": "string", + "enum": [ + "inService", + "outOfService" + ] + }, + "receivedBroadcastPacketsAccumulated": { + "description": "Cumulative count of broadcast packets received as read at the end of the measurement interval", + "type": "number" + }, + "receivedBroadcastPacketsDelta": { + "description": "Count of broadcast packets received within the measurement interval", + "type": "number" + }, + "receivedDiscardedPacketsAccumulated": { + "description": "Cumulative count of discarded packets received as read at the end of the measurement interval", + "type": "number" + }, + "receivedDiscardedPacketsDelta": { + "description": "Count of discarded packets received within the measurement interval", + "type": "number" + }, + "receivedErrorPacketsAccumulated": { + "description": "Cumulative count of error packets received as read at the end of the measurement interval", + "type": "number" + }, + "receivedErrorPacketsDelta": { + "description": "Count of error packets received within the measurement interval", + "type": "number" + }, + "receivedMulticastPacketsAccumulated": { + "description": "Cumulative count of multicast packets received as read at the end of the measurement interval", + "type": "number" + }, + "receivedMulticastPacketsDelta": { + "description": "Count of multicast packets received within the measurement interval", + "type": "number" + }, + "receivedOctetsAccumulated": { + "description": "Cumulative count of octets received as read at the end of the measurement interval", + "type": "number" + }, + "receivedOctetsDelta": { + "description": "Count of octets received within the measurement interval", + "type": "number" + }, + "receivedTotalPacketsAccumulated": { + "description": "Cumulative count of all packets received as read at the end of the measurement interval", + "type": "number" + }, + "receivedPercentDiscard": { + "description": "Percentage of discarded packets received; value = (receivedDiscardedPacketsDelta / receivedTotalPacketsDelta) x 100, if denominator is nonzero, or 0, if otherwise", + "type": "number" + }, + "receivedPercentError": { + "description": "Percentage of error packets received; value = (receivedErrorPacketsDelta / receivedTotalPacketsDelta) x 100, if denominator is nonzero, or 0, if otherwise.", + "type": "number" + }, + "receivedTotalPacketsDelta": { + "description": "Count of all packets received within the measurement interval", + "type": "number" + }, + "receivedUnicastPacketsAccumulated": { + "description": "Cumulative count of unicast packets received as read at the end of the measurement interval", + "type": "number" + }, + "receivedUnicastPacketsDelta": { + "description": "Count of unicast packets received within the measurement interval", + "type": "number" + }, + "receivedUtilization": { + "description": "Percentage of utilization received; value = (receivedOctetsDelta / (speed x (lastEpochMicrosec - startEpochMicrosec))) x 100, if denominator is nonzero, or 0, if otherwise", + "type": "number" + }, + "speed": { + "description": "Speed configured in mbps", + "type": "number" + }, + "transmittedBroadcastPacketsAccumulated": { + "description": "Cumulative count of broadcast packets transmitted as read at the end of the measurement interval", + "type": "number" + }, + "transmittedBroadcastPacketsDelta": { + "description": "Count of broadcast packets transmitted within the measurement interval", + "type": "number" + }, + "transmittedDiscardedPacketsAccumulated": { + "description": "Cumulative count of discarded packets transmitted as read at the end of the measurement interval", + "type": "number" + }, + "transmittedDiscardedPacketsDelta": { + "description": "Count of discarded packets transmitted within the measurement interval", + "type": "number" + }, + "transmittedErrorPacketsAccumulated": { + "description": "Cumulative count of error packets transmitted as read at the end of the measurement interval", + "type": "number" + }, + "transmittedErrorPacketsDelta": { + "description": "Count of error packets transmitted within the measurement interval", + "type": "number" + }, + "transmittedMulticastPacketsAccumulated": { + "description": "Cumulative count of multicast packets transmitted as read at the end of the measurement interval", + "type": "number" + }, + "transmittedMulticastPacketsDelta": { + "description": "Count of multicast packets transmitted within the measurement interval", + "type": "number" + }, + "transmittedOctetsAccumulated": { + "description": "Cumulative count of octets transmitted as read at the end of the measurement interval", + "type": "number" + }, + "transmittedOctetsDelta": { + "description": "Count of octets transmitted within the measurement interval", + "type": "number" + }, + "transmittedTotalPacketsAccumulated": { + "description": "Cumulative count of all packets transmitted as read at the end of the measurement interval", + "type": "number" + }, + "transmittedTotalPacketsDelta": { + "description": "Count of all packets transmitted within the measurement interval", + "type": "number" + }, + "transmittedUnicastPacketsAccumulated": { + "description": "Cumulative count of unicast packets transmitted as read at the end of the measurement interval", + "type": "number" + }, + "transmittedUnicastPacketsDelta": { + "description": "Count of unicast packets transmitted within the measurement interval", + "type": "number" + }, + "transmittedPercentDiscard": { + "description": "Percentage of discarded packets transmitted; value = (transmittedDiscardedPacketsDelta / transmittedTotalPacketsDelta) x 100, if denominator is nonzero, or 0, if otherwise", + "type": "number" + }, + "transmittedPercentError": { + "description": "Percentage of error packets received; value = (transmittedErrorPacketsDelta / transmittedTotalPacketsDelta) x 100, if denominator is nonzero, or 0, if otherwise", + "type": "number" + }, + "transmittedUtilization": { + "description": "Percentage of utilization transmitted; value = (transmittedOctetsDelta / (speed x (lastEpochMicrosec - startEpochMicrosec))) x 100, if denominator is nonzero, or 0, if otherwise.", + "type": "number" + }, + "valuesAreSuspect": { + "description": "Indicates whether vNicPerformance values are likely inaccurate due to counter overflow or other condtions", + "type": "string", + "enum": [ + "true", + "false" + ] + } + }, + "additionalProperties": false, + "required": [ + "nicIdentifier", + "valuesAreSuspect" + ] + }, + "notificationFields": { + "description": "notification fields", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "arrayOfNamedHashMap": { + "$ref": "#/definitions/arrayOfNamedHashMap" + }, + "changeContact": { + "description": "identifier for a contact related to the change", + "type": "string" + }, + "changeIdentifier": { + "description": "system or session identifier associated with the change", + "type": "string" + }, + "changeType": { + "description": "describes what has changed for the entity", + "type": "string" + }, + "newState": { + "description": "new state of the entity", + "type": "string" + }, + "oldState": { + "description": "previous state of the entity", + "type": "string" + }, + "notificationFieldsVersion": { + "description": "version of the notificationFields block", + "type": "string", + "enum": [ + "2.0" + ] + }, + "stateInterface": { + "description": "card or port name of the entity that changed state", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "changeIdentifier", + "changeType", + "notificationFieldsVersion" + ] + }, + "otherFields": { + "description": "fields for events belonging to the 'other' domain of the commonEventHeader domain enumeration", + "type": "object", + "properties": { + "arrayOfNamedHashMap": { + "$ref": "#/definitions/arrayOfNamedHashMap" + }, + "hashMap": { + "$ref": "#/definitions/hashMap" + }, + "jsonObjects": { + "$ref": "#/definitions/arrayOfJsonObject" + }, + "otherFieldsVersion": { + "description": "version of the otherFields block", + "type": "string", + "enum": [ + "3.0" + ] + } + }, + "additionalProperties": false, + "required": [ + "otherFieldsVersion" + ] + }, + "pnfRegistrationFields": { + "description": "hardware device registration fields", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "lastServiceDate": { + "description": "TS 32.692 dateOfLastService = date of last service; e.g. 15022017", + "type": "string" + }, + "macAddress": { + "description": "MAC address of OAM interface of the unit", + "type": "string" + }, + "manufactureDate": { + "description": "TS 32.692 dateOfManufacture = manufacture date of the unit; 24032016", + "type": "string" + }, + "modelNumber": { + "description": "TS 32.692 versionNumber = version of the unit from vendor; e.g. AJ02. Maps to AAI equip-model", + "type": "string" + }, + "oamV4IpAddress": { + "description": "IPv4 m-plane IP address to be used by the manager to contact the PNF", + "type": "string" + }, + "oamV6IpAddress": { + "description": "IPv6 m-plane IP address to be used by the manager to contact the PNF", + "type": "string" + }, + "pnfRegistrationFieldsVersion": { + "description": "version of the pnfRegistrationFields block", + "type": "string", + "enum": [ + "2.0" + ] + }, + "serialNumber": { + "description": "TS 32.692 serialNumber = serial number of the unit; e.g. 6061ZW3", + "type": "string" + }, + "softwareVersion": { + "description": "TS 32.692 swName = active SW running on the unit; e.g. 5gDUv18.05.201", + "type": "string" + }, + "unitFamily": { + "description": "TS 32.692 vendorUnitFamilyType = general type of HW unit; e.g. BBU", + "type": "string" + }, + "unitType": { + "description": "TS 32.692 vendorUnitTypeNumber = vendor name for the unit; e.g. Airscale", + "type": "string" + }, + "vendorName": { + "description": "TS 32.692 vendorName = name of manufacturer; e.g. Nokia. Maps to AAI equip-vendor", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "pnfRegistrationFieldsVersion" + ] + }, + "processorDimmAggregateThermalMargin": { + "description": "intelligent platform management interface (ipmi) processor dual inline memory module aggregate thermal margin metrics", + "type": "object", + "properties": { + "processorDimmAggregateThermalMarginIdentifier": { + "description": "identifier for the aggregate thermal margin metrics from the processor dual inline memory module", + "type": "string" + }, + "thermalMargin": { + "description": "the difference between the DIMM's current temperature, in celsius, and the DIMM's throttling thermal trip point", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "processorDimmAggregateThermalMarginIdentifier", + "thermalMargin" + ] + }, + "processStats": { + "description": "metrics on system processes", + "type": "object", + "properties": { + "forkRate": { + "description": "the number of threads created since the last reboot", + "type": "number" + }, + "processIdentifier": { + "description": "processIdentifier", + "type": "string" + }, + "psStateBlocked": { + "description": "the number of processes in a blocked state", + "type": "number" + }, + "psStatePaging": { + "description": "the number of processes in a paging state", + "type": "number" + }, + "psStateRunning": { + "description": "the number of processes in a running state", + "type": "number" + }, + "psStateSleeping": { + "description": "the number of processes in a sleeping state", + "type": "number" + }, + "psStateStopped": { + "description": "the number of processes in a stopped state", + "type": "number" + }, + "psStateZombie": { + "description": "the number of processes in a zombie state", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "processIdentifier" + ] + }, + "requestError": { + "description": "standard request error data structure", + "type": "object", + "properties": { + "messageId": { + "description": "Unique message identifier of the format ABCnnnn where ABC is either SVC for Service Exceptions or POL for Policy Exception", + "type": "string" + }, + "text": { + "description": "Message text, with replacement variables marked with %n, where n is an index into the list of elements, starting at 1", + "type": "string" + }, + "url": { + "description": "Hyperlink to a detailed error resource e.g., an HTML page for browser user agents", + "type": "string" + }, + "variables": { + "description": "List of zero or more strings that represent the contents of the variables used by the message text", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "messageId", + "text" + ] + }, + "sipSignalingFields": { + "description": "sip signaling fields", + "type": "object", + "properties": { + "additionalInformation": { + "$ref": "#/definitions/hashMap" + }, + "compressedSip": { + "description": "the full SIP request/response including headers and bodies", + "type": "string" + }, + "correlator": { + "description": "this is the same for all events on this call", + "type": "string" + }, + "localIpAddress": { + "description": "IP address on xNF", + "type": "string" + }, + "localPort": { + "description": "port on xNF", + "type": "string" + }, + "remoteIpAddress": { + "description": "IP address of peer endpoint", + "type": "string" + }, + "remotePort": { + "description": "port of peer endpoint", + "type": "string" + }, + "sipSignalingFieldsVersion": { + "description": "version of the sipSignalingFields block", + "type": "string", + "enum": [ + "3.0" + ] + }, + "summarySip": { + "description": "the SIP Method or Response ('INVITE', '200 OK', 'BYE', etc)", + "type": "string" + }, + "vendorNfNameFields": { + "$ref": "#/definitions/vendorNfNameFields" + } + }, + "additionalProperties": false, + "required": [ + "correlator", + "localIpAddress", + "localPort", + "remoteIpAddress", + "remotePort", + "sipSignalingFieldsVersion", + "vendorNfNameFields" + ] + }, + "stateChangeFields": { + "description": "stateChange fields", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "newState": { + "description": "new state of the entity", + "type": "string", + "enum": [ + "inService", + "maintenance", + "outOfService" + ] + }, + "oldState": { + "description": "previous state of the entity", + "type": "string", + "enum": [ + "inService", + "maintenance", + "outOfService" + ] + }, + "stateChangeFieldsVersion": { + "description": "version of the stateChangeFields block", + "type": "string", + "enum": [ + "4.0" + ] + }, + "stateInterface": { + "description": "card or port name of the entity that changed state", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "newState", + "oldState", + "stateChangeFieldsVersion", + "stateInterface" + ] + }, + "syslogFields": { + "description": "sysLog fields", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "eventSourceHost": { + "description": "hostname of the device", + "type": "string" + }, + "eventSourceType": { + "description": "type of event source; examples: other, router, switch, host, card, port, slotThreshold, portThreshold, virtualMachine, virtualNetworkFunction", + "type": "string" + }, + "syslogFacility": { + "description": "numeric code from 0 to 23 for facility--see table in documentation", + "type": "integer" + }, + "syslogFieldsVersion": { + "description": "version of the syslogFields block", + "type": "string", + "enum": [ + "4.0" + ] + }, + "syslogMsg": { + "description": "syslog message", + "type": "string" + }, + "syslogMsgHost": { + "description": "hostname parsed from non-VES syslog message", + "type": "string" + }, + "syslogPri": { + "description": "0-192 combined severity and facility", + "type": "integer" + }, + "syslogProc": { + "description": "identifies the application that originated the message", + "type": "string" + }, + "syslogProcId": { + "description": "a change in the value of this field indicates a discontinuity in syslog reporting", + "type": "number" + }, + "syslogSData": { + "description": "syslog structured data consisting of a structured data Id followed by a set of key value pairs", + "type": "string" + }, + "syslogSdId": { + "description": "0-32 char in format name@number for example ourSDID@32473", + "type": "string" + }, + "syslogSev": { + "description": "numerical Code for severity derived from syslogPri as remaider of syslogPri / 8", + "type": "string", + "enum": [ + "Alert", + "Critical", + "Debug", + "Emergency", + "Error", + "Info", + "Notice", + "Warning" + ] + }, + "syslogTag": { + "description": "msgId indicating the type of message such as TCPOUT or TCPIN; NILVALUE should be used when no other value can be provided", + "type": "string" + }, + "syslogTs": { + "description": "timestamp parsed from non-VES syslog message", + "type": "string" + }, + "syslogVer": { + "description": "IANA assigned version of the syslog protocol specification - typically 1", + "type": "number" + } + }, + "additionalProperties": false, + "required": [ + "eventSourceType", + "syslogFieldsVersion", + "syslogMsg", + "syslogTag" + ] + }, + "thresholdCrossingAlertFields": { + "description": "fields specific to threshold crossing alert events", + "type": "object", + "properties": { + "additionalFields": { + "$ref": "#/definitions/hashMap" + }, + "additionalParameters": { + "description": "performance counters", + "type": "array", + "items": { + "$ref": "#/definitions/counter" + } + }, + "alertAction": { + "description": "Event action", + "type": "string", + "enum": [ + "CLEAR", + "CONT", + "SET" + ] + }, + "alertDescription": { + "description": "Unique short alert description such as IF-SHUB-ERRDROP", + "type": "string" + }, + "alertType": { + "description": "Event type", + "type": "string", + "enum": [ + "CARD-ANOMALY", + "ELEMENT-ANOMALY", + "INTERFACE-ANOMALY", + "SERVICE-ANOMALY" + ] + }, + "alertValue": { + "description": "Calculated API value (if applicable)", + "type": "string" + }, + "associatedAlertIdList": { + "description": "List of eventIds associated with the event being reported", + "type": "array", + "items": { + "type": "string" + } + }, + "collectionTimestamp": { + "description": "Time when the performance collector picked up the data; with RFC 2822 compliant format: Sat, 13 Mar 2010 11:29:05 -0800", + "type": "string" + }, + "dataCollector": { + "description": "Specific performance collector instance used", + "type": "string" + }, + "elementType": { + "description": "type of network element - internal ATT field", + "type": "string" + }, + "eventSeverity": { + "description": "event severity or priority", + "type": "string", + "enum": [ + "CRITICAL", + "MAJOR", + "MINOR", + "WARNING", + "NORMAL" + ] + }, + "eventStartTimestamp": { + "description": "Time closest to when the measurement was made; with RFC 2822 compliant format: Sat, 13 Mar 2010 11:29:05 -0800", + "type": "string" + }, + "interfaceName": { + "description": "Physical or logical port or card (if applicable)", + "type": "string" + }, + "networkService": { + "description": "network name - internal ATT field", + "type": "string" + }, + "possibleRootCause": { + "description": "Reserved for future use", + "type": "string" + }, + "thresholdCrossingFieldsVersion": { + "description": "version of the thresholdCrossingAlertFields block", + "type": "string", + "enum": [ + "4.0" + ] + } + }, + "additionalProperties": false, + "required": [ + "additionalParameters", + "alertAction", + "alertDescription", + "alertType", + "collectionTimestamp", + "eventSeverity", + "eventStartTimestamp", + "thresholdCrossingFieldsVersion" + ] + }, + "vendorNfNameFields": { + "description": "provides vendor, nf and nfModule identifying information", + "type": "object", + "properties": { + "vendorName": { + "description": "network function vendor name", + "type": "string" + }, + "nfModuleName": { + "description": "name of the nfModule generating the event", + "type": "string" + }, + "nfName": { + "description": "name of the network function generating the event", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "vendorName" + ] + }, + "voiceQualityFields": { + "description": "provides statistics related to customer facing voice products", + "type": "object", + "properties": { + "additionalInformation": { + "$ref": "#/definitions/hashMap" + }, + "calleeSideCodec": { + "description": "callee codec for the call", + "type": "string" + }, + "callerSideCodec": { + "description": "caller codec for the call", + "type": "string" + }, + "correlator": { + "description": "this is the same for all events on this call", + "type": "string" + }, + "endOfCallVqmSummaries": { + "$ref": "#/definitions/endOfCallVqmSummaries" + }, + "phoneNumber": { + "description": "phone number associated with the correlator", + "type": "string" + }, + "midCallRtcp": { + "description": "Base64 encoding of the binary RTCP data excluding Eth/IP/UDP headers", + "type": "string" + }, + "vendorNfNameFields": { + "$ref": "#/definitions/vendorNfNameFields" + }, + "voiceQualityFieldsVersion": { + "description": "version of the voiceQualityFields block", + "type": "string", + "enum": [ + "4.0" + ] + } + }, + "additionalProperties": false, + "required": [ + "calleeSideCodec", + "callerSideCodec", + "correlator", + "midCallRtcp", + "vendorNfNameFields", + "voiceQualityFieldsVersion" + ] + } + } +} diff --git a/pnf-sim-lightweight/netconf/pnf-simulator.data.xml b/pnf-sim-lightweight/netconf/pnf-simulator.data.xml new file mode 100644 index 0000000..9d648bb --- /dev/null +++ b/pnf-sim-lightweight/netconf/pnf-simulator.data.xml @@ -0,0 +1,4 @@ + + 42 + 35 + diff --git a/pnf-sim-lightweight/netconf/pnf-simulator.yang b/pnf-sim-lightweight/netconf/pnf-simulator.yang new file mode 100644 index 0000000..d7fc2f2 --- /dev/null +++ b/pnf-sim-lightweight/netconf/pnf-simulator.yang @@ -0,0 +1,9 @@ +module pnf-simulator { + namespace "http://nokia.com/pnf-simulator"; + prefix config; + container config { + config true; + leaf itemValue1 {type uint32;} + leaf itemValue2 {type uint32;} + } +} diff --git a/pnf-sim-lightweight/pom.xml b/pnf-sim-lightweight/pom.xml new file mode 100644 index 0000000..eab5af2 --- /dev/null +++ b/pnf-sim-lightweight/pom.xml @@ -0,0 +1,345 @@ + + + + 4.0.0 + + + org.onap.oparent + oparent + 1.2.1 + + + org.onap.masspnfsimulator + masspnf-simulator + 1.0.0-SNAPSHOT + + pnf-simulator-sandbox + + + UTF-8 + 1.8 + 1.8 + yyyyMMdd'T'HHmmss + + org.onap.pnfsimulator.Main + latest + 5.1.0 + 5.1.0 + onap/${project.artifactId} + + libs + ${project.build.directory}/${dependency.directory.name} + + + nexus3.onap.org:10003 + + + + + org.springframework.boot + spring-boot-starter + 2.0.2.RELEASE + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-web + 2.0.2.RELEASE + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.9.8 + + + + ch.qos.logback + logback-classic + 1.2.3 + + + ch.qos.logback + logback-core + 1.2.3 + + + org.slf4j + slf4j-api + 1.7.25 + + + commons-io + commons-io + 2.6 + + + org.json + json + 20180130 + + + org.everit.json + org.everit.json.schema + 1.3.0 + + + com.github.fge + json-schema-validator + 2.2.6 + + + com.google.code.gson + gson + 2.8.2 + + + org.apache.httpcomponents + httpclient + 4.5.5 + + + com.google.guava + guava + 21.0 + + + commons-cli + commons-cli + 1.4 + + + org.apache.commons + commons-lang3 + 3.7 + + + org.onosproject + jnc + 1.0 + + + + org.junit.jupiter + junit-jupiter-engine + 5.1.1 + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + ${junit.jupiter.version} + test + + + org.assertj + assertj-core + 3.9.1 + test + + + org.apache.sshd + sshd-core + 0.9.0 + test + + + org.mockito + mockito-core + 2.18.3 + test + + + + org.springframework + spring-test + 5.0.4.RELEASE + test + + + + org.springframework.boot + spring-boot-starter-test + 2.0.1.RELEASE + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + src/assembly/resources.xml + ${project.artifactId}-${project.version} + + + + package + + single + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${maven.compiler.source} + ${maven.compiler.target} + true + true + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + + ${simulator.main.class} + ${maven.build.timestamp} + + + + + + pl.project13.maven + git-commit-id-plugin + 2.2.4 + + + get-commit-info + + revision + + + + + ${project.basedir}/.git + true + git.commit.id.abbrev + + + + maven-surefire-plugin + 2.19 + + + org.junit.platform + junit-platform-surefire-provider + 1.1.1 + + + + true + true + + + + org.apache.maven.plugins + maven-dependency-plugin + + ${dependency.directory.location} + runtime + true + + + + copy-external-dependencies + package + + copy-dependencies + + + + + + com.spotify + docker-maven-plugin + 1.0.0 + + ${onap.nexus.dockerregistry.daily} + ${onap.nexus.dockerregistry.daily}/${docker.image.name} + true + + ${project.version} + ${project.version}-${maven.build.timestamp} + + openjdk:8-jre-alpine + java -cp ${dependency.directory.name}/*:${project.build.finalName}.jar ${simulator.main.class} + + + ${dependency.directory.name} + ${dependency.directory.location} + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + true + + + + org.jacoco + jacoco-maven-plugin + 0.8.1 + + + org/onap/pnfsimulator/Main.class + + + + + default-prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + + check + + check + + + + + CLASS + + + COVEREDRATIO + 0.70 + + + BRANCH + COVEREDRATIO + 0.75 + + + + + + + + + + + diff --git a/pnf-sim-lightweight/simulator.sh b/pnf-sim-lightweight/simulator.sh new file mode 100755 index 0000000..69bfc86 --- /dev/null +++ b/pnf-sim-lightweight/simulator.sh @@ -0,0 +1,262 @@ +#!/usr/bin/env bash + +set -euo pipefail + +COMPOSE_FILE_NAME=docker-compose.yml +NETOPEER_CONTAINER_NAME=netopeer +SIMULATOR_CONTAINER_NAME=pnf-simulator +SIMULATOR_PORT=5000 + +SIMULATOR_BASE=http://localhost:$SIMULATOR_PORT/simulator/ +SIMULATOR_START_URL=$SIMULATOR_BASE/start +SIMULATOR_STOP_URL=$SIMULATOR_BASE/stop +SIMULATOR_STATUS_URL=$SIMULATOR_BASE/status + +RUNNING_COMPOSE_CONFIG=$COMPOSE_FILE_NAME + +function main(){ + + COMMAND=${1:-"help"} + + case $COMMAND in + "compose") + compose $2 $3 $4 $5 $6 $7 $8 $9 "${10}" "${11}" "${12}" "${13}" "${14}" ;; + #IPGW, #IPSUBNET, #I, #URLVES, #IPPNFSIM, #IPFILESERVER, #TYPEFILESERVER, #PORTSFTP, #PORTFTPS, #IPFTPS, #IPSFTP, #FTPS_PASV_MIN, #FTPS_PAST_MAX + "build") + build_image;; + "start") + start $COMPOSE_FILE_NAME;; + "stop") + if [[ -z ${2+x} ]] + then + echo "Error: action 'stop' requires the instance identifier" + exit 1 + fi + stop $2;; + "trigger-simulator") + trigger_simulator;; + "stop-simulator") + stop_simulator;; + "status") + get_status;; + "clear-logs") + clear_logs;; + *) + print_help;; + esac +} + + +function get_pnfsim_ip() { + + export IPPNFSIM=$(cat ./config/config.yml | grep ippnfsim | awk -F'[ ]' '{print $2}') + echo "PNF-Sim IP: " $IPPNFSIM + + export SIMULATOR_BASE=http://$IPPNFSIM:$SIMULATOR_PORT/simulator/ + export SIMULATOR_START_URL=$SIMULATOR_BASE/start + export SIMULATOR_STOP_URL=$SIMULATOR_BASE/stop + export SIMULATOR_STATUS_URL=$SIMULATOR_BASE/status +} + +function compose(){ + #creating custom docker-compose based on IP arguments + #creting config.yml by injecting the same IP + + export IPGW=$1 + export IPSUBNET=$2 + export I=$3 + export URLVES=$4 + export IPPNFSIM=$5 + export IPFILESERVER=$6 + export TYPEFILESERVER=$7 + export PORTSFTP=$8 + export PORTFTPS=$9 + export IPFTPS=${10} + export IPSFTP=${11} + export FTPS_PASV_MIN=${12} + export FTPS_PASV_MAX=${13} + LOCALTIME=$(ls -l /etc/localtime) + export TIMEZONE=${LOCALTIME//*zoneinfo\/} + + #will insert $I to distinguish containers, networks properly + #docker compose cannot substitute these, as they are keys, not values. + envsubst < docker-compose-template.yml > docker-compose-temporary.yml + #variable substitution + docker-compose -f docker-compose-temporary.yml config > docker-compose.yml + rm docker-compose-temporary.yml + + ./ROP_file_creator.sh $I & + + write_config $URLVES $IPFILESERVER $TYPEFILESERVER $PORTSFTP $PORTFTPS $IPPNFSIM + + pushd config + envsubst < vsftpd_ssl-TEMPLATE.conf > vsftpd_ssl.conf + popd +} + +function build_image(){ + if [ -f pom.xml ]; then + mvn clean package docker:build -Dcheckstyle.skip + else + echo "pom.xml file not found" + exit 1 + fi +} + +function set_vsftpd_file_owner() { + # This is to avoid "500 OOPS: cannot open config file" + # on vsftpd daemon start + sudo chown root ./config/vsftpd_ssl.conf +} + + +function write_config(){ + #building a YML file for usage in Java + echo "urlves: $1" > config/config.yml + echo "urlsftp: sftp://onap:pano@$2:$4" >> config/config.yml + echo "urlftps: ftps://onap:pano@$2:$5" >> config/config.yml + echo "ippnfsim: $6" >> config/config.yml + echo "typefileserver: $3" >> config/config.yml +} + +function start(){ + get_pnfsim_ip + if [[ $(running_containers) ]]; then + echo "Simulator containers are already up" + else + echo "Starting simulator containers using netconf model specified in config/netconf.env" + set_vsftpd_file_owner + archive_logs + docker-compose -f $1 up -d + fi +} + +function running_containers(){ + docker-compose -f $COMPOSE_FILE_NAME ps -q +} + +function stop(){ + get_pnfsim_ip + + set +e # override global script setting + declare -a pids_to_kill + # get ROP_file_creator.sh instance pid + pids_to_kill[0]=$(pgrep -f "ROP_file_creator.sh ${1}$") + if [[ ! -z ${pids_to_kill[0]} ]]; + then + # get ROP_file_creator.sh childs pids + pids_to_kill=(${pids_to_kill[@]} $(pgrep -P ${pids_to_kill[0]})) + kill ${pids_to_kill[@]} + else + echo "ROP_file_creator.sh already not running" + fi + + if [[ $(running_containers) ]]; then + docker-compose -f $RUNNING_COMPOSE_CONFIG down + docker-compose -f $RUNNING_COMPOSE_CONFIG rm + else + echo "Simulator containers are already down" + fi +} + +function trigger_simulator(){ + get_pnfsim_ip + cat << EndOfMessage +Simulator response: +$(curl -s -X POST -H "Content-Type: application/json" -H "X-ONAP-RequestID: 123" -H "X-InvocationID: 456" -d @config/config.json $SIMULATOR_START_URL) +EndOfMessage +} + +function stop_simulator(){ + get_pnfsim_ip + cat << EndOfMessage +Simulator response: +$(curl -s -X POST $SIMULATOR_STOP_URL) +EndOfMessage +} + +function get_status(){ + if [[ $(running_containers) ]]; then + print_status + else + echo "Simulator containers are down" + fi +} + +function print_status(){ + get_pnfsim_ip + cat << EndOfMessage +$(docker-compose -f $RUNNING_COMPOSE_CONFIG ps) + +Simulator response: +$(curl -s -X GET $SIMULATOR_STATUS_URL) +EndOfMessage +} + +function print_help(){ +cat << EndOfMessage +Available options: +build - locally builds simulator image from existing code +start - starts simulator and netopeer2 containers using remote simulator image and specified model name +compose - customize the docker-compose and configuration based on arguments +trigger-simulator - starts sending PNF registration messages with parameters specified in config.json +stop-simulator - stop sending PNF registration messages +stop - stops both containers +status - prints simulator status +clear-logs - deletes log folder + +Starting simulation: +- Setup the instance of this simulator by: + - ./simulator.sh compose IPGW IPSUBNET I URLVES IPPNFSIM IPFILESERVER TYPEFILESERVER PORTSFTP PORTFTPS IPFTPS IPSFTP + where IPGW and IPSUBNET will be used for docker network + where I is the integer suffix to differentiate instances + where URLVES is the URL of the VES collector + where IPPNFSIM, IPFILESERVER, IPFTPS, IPSFTP are the IP addresses for containers + where TYPEFILESERVER is the type of fileserver, i.e., FTPS or SFTP + where PORTSFTP, PORTFTPS are the SFTP and FTPS ports + e.g. ./simulator.sh compose 10.11.0.65 10.11.0.64 3 http://10.11.0.69:10000/eventListener/v7 10.11.0.2 10.11.0.66 ftps 2001 2002 10.11.0.67 10.11.0.68 + +- Setup environment with "./simulator.sh start". It will download required docker images from the internet and run them on docker machine +- To start the simulation use "./simulator.sh trigger-simulator", which will start sending PNF registration messages with parameters specified in config.json + +To stop simulation use "./simulator.sh stop-simulator" command. To check simulator's status use "./simulator.sh status". +If you want to change message parameters simply edit config.json, then trigger the simulation with "./simulator.sh trigger-simulator" again +Logs are written to logs/pnf-simulator.log. + +If you change the source code you have to rebuild image with "./simulator.sh build" and run "./simulator.sh start" again +EndOfMessage +} + +function archive_logs(){ + + if [ -d logs ]; then + echo "Moving log file to archive" + DIR_PATH=logs/archive/simulator[$(timestamp)] + mkdir -p $DIR_PATH + if [ -f logs/pnfsimulator.log ]; then + mv logs/pnfsimulator.log $DIR_PATH + fi + + if [ -f logs/*.xml ]; then + mv logs/*.xml $DIR_PATH + fi + + else + mkdir logs + fi +} + +function clear_logs(){ + + if [[ $(running_containers) ]]; then + echo "Cannot delete logs when simulator is running" + else + rm -rf logs + fi +} + +function timestamp(){ + date "+%Y-%m-%d_%T" +} + +main $@ diff --git a/pnf-sim-lightweight/src/assembly/resources.xml b/pnf-sim-lightweight/src/assembly/resources.xml new file mode 100644 index 0000000..063c710 --- /dev/null +++ b/pnf-sim-lightweight/src/assembly/resources.xml @@ -0,0 +1,75 @@ + + resources + + zip + + + + + + simulator.sh + + unix + 0755 + + + + docker-compose.yml + + unix + 0644 + + + config + config + + **/* + + + + deployment + deployment + + **/* + + + + ftpes + ftpes + + **/* + + 0644 + + + + json_schema + json_schema + + **/* + + + + netconf + netconf + + **/* + + + + sftp + sftp + + **/* + + + + ssh + ssh + + **/* + + + + + \ No newline at end of file diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/ConfigurationProvider.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/ConfigurationProvider.java new file mode 100644 index 0000000..15c687e --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/ConfigurationProvider.java @@ -0,0 +1,28 @@ +package org.onap.pnfsimulator; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import java.io.File; + +public class ConfigurationProvider { + static PnfSimConfig conf = null; + + public static PnfSimConfig getConfigInstance() { + + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + try { + File file = new File("./config/config.yml"); + + conf = mapper.readValue(file, PnfSimConfig.class); + System.out.println("Ves URL: " + conf.getUrlves()); + System.out.println("SFTP URL: " + conf.getUrlsftp()); + System.out.println("FTPS URL: " + conf.getUrlftps()); + System.out.println("PNF sim IP: " + conf.getIppnfsim()); + + } catch (Exception e) { + e.printStackTrace(); + } + return conf; + } + +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/FileProvider.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/FileProvider.java new file mode 100644 index 0000000..beb564d --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/FileProvider.java @@ -0,0 +1,41 @@ +package org.onap.pnfsimulator; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.onap.pnfsimulator.simulator.validation.NoRopFilesException; + +public class FileProvider { + + public List getFiles() throws NoRopFilesException { + + List files = queryFiles(); + + files.sort(Collections.reverseOrder()); + + List fileListSorted = new ArrayList<>(); + for (String f : files) { + fileListSorted.add(f); + } + return fileListSorted; + } + + private static List queryFiles() throws NoRopFilesException { + + File folder = new File("./files/onap/"); + File[] listOfFiles = folder.listFiles(); + if (listOfFiles == null || listOfFiles.length == 0) { + throw new NoRopFilesException("No ROP files found in specified directory"); + } + + List results = new ArrayList<>(); + for (int i = 0; i < listOfFiles.length; i++) { + if (listOfFiles[i].isFile()) { + results.add(listOfFiles[i].getName()); + } + } + + return results; + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/Main.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/Main.java new file mode 100644 index 0000000..a66bedb --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/Main.java @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ Copyright (C) + * 2018 NOKIA Intellectual Property. 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. ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator; + +import java.util.concurrent.TimeUnit; +import org.onap.pnfsimulator.message.MessageProvider; +import org.onap.pnfsimulator.simulator.validation.JSONValidator; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.scheduling.annotation.EnableAsync; + +@SpringBootApplication +@EnableAsync +public class Main { + + public static void main(String[] args) throws InterruptedException { + SpringApplication.run(Main.class, args); + + TimeUnit.SECONDS.sleep(5); + System.out.println("Start sending VES events"); + + + } + + @Bean + public MessageProvider messageProvider() { + return new MessageProvider(); + } + + @Bean + public JSONValidator jsonValidator() { + return new JSONValidator(); + } + +} + + diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/PnfSimConfig.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/PnfSimConfig.java new file mode 100644 index 0000000..3dd4aba --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/PnfSimConfig.java @@ -0,0 +1,57 @@ +package org.onap.pnfsimulator; + +public class PnfSimConfig { + private String urlves; + private String urlftps; + private String urlsftp; + private String ippnfsim; + private String typefileserver; + + public String getTypefileserver() { + return typefileserver; + } + + public void setTypefileserver(String typefileserver) { + this.typefileserver = typefileserver; + } + + + public String getUrlves() { + return urlves; + } + + public void setUrlves(String urlves) { + this.urlves = urlves; + } + + public String getUrlftps() { + return urlftps; + } + + public void setUrlftps(String urlftps) { + this.urlftps = urlftps; + } + + public String getUrlsftp() { + return urlsftp; + } + + public void setUrlsftp(String urlsftp) { + this.urlsftp = urlsftp; + } + + public void setIppnfsim(String ippnfsim) { + this.ippnfsim = ippnfsim; + } + + public String getIppnfsim() { + return ippnfsim; + } + + @Override + public String toString() { + return "PnfSimConfig [vesip=" + urlves + ", urlftps=" + urlftps + ", urlsftp=" + urlsftp + ", ippnfsim=" + + ippnfsim + ", typefileserver=" + typefileserver + "]"; + } + +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logback.xml b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logback.xml new file mode 100644 index 0000000..0f6d9de --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logback.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + %nopexception%logger + |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC} + |%level + |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'} + |%thread + |%n + + + + + + %nopexception%logger + |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC} + |%level + |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'} + |%thread + |%n + + ${log-path}/${outputFilename}.log + + ${archive}/${outputFilename}.%d{yyyy-MM-dd}.%i.log.zip + ${maxFileSize} + ${maxHistory} + ${totalSizeCap} + + + + + + + + diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java new file mode 100644 index 0000000..e479b4a --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/logging/MDCVariables.java @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.logging; + +public final class MDCVariables { + + public static final String X_ONAP_REQUEST_ID = "X-ONAP-RequestID"; + public static final String X_INVOCATION_ID = "X-InvocationID"; + public static final String REQUEST_ID = "RequestID"; + public static final String INVOCATION_ID = "InvocationID"; + public static final String INSTANCE_UUID = "InstanceUUID"; + public static final String RESPONSE_CODE = "ResponseCode"; + public static final String SERVICE_NAME = "ServiceName"; + public static final String AUTHORIZATION = "Authorization"; + + private MDCVariables() { + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/JSONObjectFactory.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/JSONObjectFactory.java new file mode 100644 index 0000000..7c12983 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/JSONObjectFactory.java @@ -0,0 +1,142 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.message; + +import static org.onap.pnfsimulator.message.MessageConstants.COMPRESSION; +import static org.onap.pnfsimulator.message.MessageConstants.COMPRESSION_VALUE; +import static org.onap.pnfsimulator.message.MessageConstants.EVENT_ID; +import static org.onap.pnfsimulator.message.MessageConstants.FILE_FORMAT_TYPE; +import static org.onap.pnfsimulator.message.MessageConstants.FILE_FORMAT_TYPE_VALUE; +import static org.onap.pnfsimulator.message.MessageConstants.FILE_FORMAT_VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.FILE_FORMAT_VERSION_VALUE; +import static org.onap.pnfsimulator.message.MessageConstants.HASH_MAP; +import static org.onap.pnfsimulator.message.MessageConstants.INTERNAL_HEADER_FIELDS; +import static org.onap.pnfsimulator.message.MessageConstants.LAST_EPOCH_MICROSEC; +import static org.onap.pnfsimulator.message.MessageConstants.LOCATION; +import static org.onap.pnfsimulator.message.MessageConstants.NAME; +import static org.onap.pnfsimulator.message.MessageConstants.NOTIFICATION_FIELDS_VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.NOTIFICATION_FIELDS_VERSION_VALUE; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_LAST_SERVICE_DATE; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_MANUFACTURE_DATE; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_REGISTRATION_FIELDS_VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_REGISTRATION_FIELDS_VERSION_VALUE; +import static org.onap.pnfsimulator.message.MessageConstants.PRIORITY; +import static org.onap.pnfsimulator.message.MessageConstants.PRIORITY_NORMAL; +import static org.onap.pnfsimulator.message.MessageConstants.REPORTING_ENTITY_NAME; +import static org.onap.pnfsimulator.message.MessageConstants.SEQUENCE; +import static org.onap.pnfsimulator.message.MessageConstants.SEQUENCE_NUMBER; +import static org.onap.pnfsimulator.message.MessageConstants.SOURCE_NAME; +import static org.onap.pnfsimulator.message.MessageConstants.START_EPOCH_MICROSEC; +import static org.onap.pnfsimulator.message.MessageConstants.TIME_ZONE_OFFSET; +import static org.onap.pnfsimulator.message.MessageConstants.VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.VERSION_NUMBER; +import static org.onap.pnfsimulator.message.MessageConstants.VES_EVENT_LISTENER_VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.VES_EVENT_LISTENER_VERSION_NUMBER; +import java.util.Calendar; +import java.io.File; +import java.util.List; +import java.util.TimeZone; +import org.json.JSONArray; +import org.json.JSONObject; + +final class JSONObjectFactory { + + static JSONObject generateConstantCommonEventHeader() { + JSONObject commonEventHeader = new JSONObject(); + long timestamp = System.currentTimeMillis(); + String nodeName = generateDefaultName(); + commonEventHeader.put(EVENT_ID, generateEventId()); + commonEventHeader.put(TIME_ZONE_OFFSET, generateTimeZone(timestamp)); + commonEventHeader.put(LAST_EPOCH_MICROSEC, timestamp); + commonEventHeader.put(PRIORITY, PRIORITY_NORMAL); + commonEventHeader.put(SEQUENCE, SEQUENCE_NUMBER); + commonEventHeader.put(START_EPOCH_MICROSEC, timestamp); + commonEventHeader.put(INTERNAL_HEADER_FIELDS, new JSONObject()); + commonEventHeader.put(VERSION, VERSION_NUMBER); + commonEventHeader.put(VES_EVENT_LISTENER_VERSION, VES_EVENT_LISTENER_VERSION_NUMBER); + commonEventHeader.put(SOURCE_NAME, nodeName); + commonEventHeader.put(REPORTING_ENTITY_NAME, nodeName); + return commonEventHeader; + } + + static JSONObject generatePnfRegistrationFields() { + JSONObject pnfRegistrationFields = new JSONObject(); + pnfRegistrationFields.put(PNF_REGISTRATION_FIELDS_VERSION, PNF_REGISTRATION_FIELDS_VERSION_VALUE); + pnfRegistrationFields.put(PNF_LAST_SERVICE_DATE, String.valueOf(System.currentTimeMillis())); + pnfRegistrationFields.put(PNF_MANUFACTURE_DATE, String.valueOf(System.currentTimeMillis())); + return pnfRegistrationFields; + } + + static JSONObject generateNotificationFields() { + JSONObject notificationFields = new JSONObject(); + notificationFields.put(NOTIFICATION_FIELDS_VERSION, NOTIFICATION_FIELDS_VERSION_VALUE); + return notificationFields; + } + + static JSONArray generateArrayOfNamedHashMap(List fileList, String xnfUrl) { + JSONArray arrayOfNamedHashMap = new JSONArray(); + + for (String fileName : fileList) { + JSONObject namedHashMap = new JSONObject(); + namedHashMap.put(NAME, fileName); + + JSONObject hashMap = new JSONObject(); + hashMap.put(FILE_FORMAT_TYPE, FILE_FORMAT_TYPE_VALUE); + hashMap.put(LOCATION, xnfUrl.concat(fileName)); + hashMap.put(FILE_FORMAT_VERSION, FILE_FORMAT_VERSION_VALUE); + hashMap.put(COMPRESSION, COMPRESSION_VALUE); + namedHashMap.put(HASH_MAP, hashMap); + + arrayOfNamedHashMap.put(namedHashMap); + } + + + return arrayOfNamedHashMap; + } + + + static String generateEventId() { + String timeAsString = String.valueOf(System.currentTimeMillis()); + return String.format("FileReady_%s", timeAsString); + } + + static String generateDefaultName() { + String defaultName = "default"; + int year = Calendar.getInstance().get(Calendar.YEAR); + int month = Calendar.getInstance().get(Calendar.MONTH); + int day = Calendar.getInstance().get(Calendar.DAY_OF_MONTH); + + return defaultName + "-" + year + month + day; + } + + static String generateTimeZone(long timestamp) { + TimeZone timeZone = TimeZone.getDefault(); + int offsetInMillis = timeZone.getOffset(timestamp); + String offsetHHMM = String.format("%02d:%02d", Math.abs(offsetInMillis / 3600000), + Math.abs((offsetInMillis / 60000) % 60)); + return ("UTC" + (offsetInMillis >= 0 ? "+" : "-") + offsetHHMM); + } + + private JSONObjectFactory() { + + } + +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageConstants.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageConstants.java new file mode 100644 index 0000000..6ff6e5d --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageConstants.java @@ -0,0 +1,95 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.message; + +public final class MessageConstants { + + public static final String SIMULATOR_PARAMS = "simulatorParams"; + public static final String COMMON_EVENT_HEADER_PARAMS = "commonEventHeaderParams"; + public static final String PNF_REGISTRATION_PARAMS = "pnfRegistrationParams"; + public static final String NOTIFICATION_PARAMS = "notificationParams"; + + static final String COMMON_EVENT_HEADER = "commonEventHeader"; + static final String PNF_REGISTRATION_FIELDS = "pnfRegistrationFields"; + static final String NOTIFICATION_FIELDS = "notificationFields"; + static final String EVENT = "event"; + + //============================================================================================= + //Simulation parameters + public static final String VES_SERVER_URL = "vesServerUrl"; + public static final String TEST_DURATION = "testDuration"; + public static final String MESSAGE_INTERVAL = "messageInterval"; + + //============================================================================================= + //commonEventHeader + //parameters + static final String DOMAIN = "domain"; + static final String EVENT_ID = "eventId"; + static final String TIME_ZONE_OFFSET = "timeZoneOffset"; + static final String EVENT_TYPE = "eventType"; + static final String LAST_EPOCH_MICROSEC = "lastEpochMicrosec"; + static final String PRIORITY = "priority"; + static final String SEQUENCE = "sequence"; + static final String START_EPOCH_MICROSEC = "startEpochMicrosec"; + static final String INTERNAL_HEADER_FIELDS = "internalHeaderFields"; + static final String VERSION = "version"; + static final String VES_EVENT_LISTENER_VERSION = "vesEventListenerVersion"; + static final String SOURCE_NAME = "sourceName"; + static final String REPORTING_ENTITY_NAME = "reportingEntityName"; + //constant values + static final int SEQUENCE_NUMBER = 0; + static final String VERSION_NUMBER = "4.0.1"; + static final String VES_EVENT_LISTENER_VERSION_NUMBER = "7.0.1"; + static final String PRIORITY_NORMAL = "Normal"; + + //============================================================================================= + //PNF registration + //parameters + static final String PNF_REGISTRATION_FIELDS_VERSION = "pnfRegistrationFieldsVersion"; + static final String PNF_LAST_SERVICE_DATE = "lastServiceDate"; + static final String PNF_MANUFACTURE_DATE = "manufactureDate"; + //constant values + static final String PNF_REGISTRATION_FIELDS_VERSION_VALUE = "2.0"; + static final String DOMAIN_PNF_REGISTRATION ="pnfRegistration"; + + //============================================================================================= + // Notifications + //parameters + static final String NOTIFICATION_FIELDS_VERSION = "notificationFieldsVersion"; + static final String ARRAY_OF_NAMED_HASH_MAP = "arrayOfNamedHashMap"; + static final String NAME = "name"; + static final String HASH_MAP = "hashMap"; + static final String FILE_FORMAT_TYPE = "fileFormatType"; + static final String LOCATION = "location"; + static final String FILE_FORMAT_VERSION = "fileFormatVersion"; + static final String COMPRESSION = "compression"; + + //constant values + static final String NOTIFICATION_FIELDS_VERSION_VALUE = "2.0"; + static final String DOMAIN_NOTIFICATION ="notification"; + static final String FILE_FORMAT_TYPE_VALUE = "org.3GPP.32.435#measCollec"; + static final String FILE_FORMAT_VERSION_VALUE = "V10"; + static final String COMPRESSION_VALUE = "gzip"; + + private MessageConstants() { + } + +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageProvider.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageProvider.java new file mode 100644 index 0000000..c863625 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/message/MessageProvider.java @@ -0,0 +1,88 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ Copyright (C) + * 2018 NOKIA Intellectual Property. 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. ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.message; + +import static org.onap.pnfsimulator.message.MessageConstants.ARRAY_OF_NAMED_HASH_MAP; +import static org.onap.pnfsimulator.message.MessageConstants.COMMON_EVENT_HEADER; +import static org.onap.pnfsimulator.message.MessageConstants.DOMAIN; +import static org.onap.pnfsimulator.message.MessageConstants.DOMAIN_NOTIFICATION; +import static org.onap.pnfsimulator.message.MessageConstants.DOMAIN_PNF_REGISTRATION; +import static org.onap.pnfsimulator.message.MessageConstants.EVENT; +import static org.onap.pnfsimulator.message.MessageConstants.EVENT_TYPE; +import static org.onap.pnfsimulator.message.MessageConstants.NOTIFICATION_FIELDS; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_REGISTRATION_FIELDS; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.json.JSONArray; +import org.json.JSONObject; + +public class MessageProvider { + + public JSONObject createMessage(JSONObject commonEventHeaderParams, Optional pnfRegistrationParams, + Optional notificationParams) { + List emptyList = new ArrayList<>(); + String emptyString = ""; + return createMessage(commonEventHeaderParams, pnfRegistrationParams, notificationParams, emptyList, emptyString); + } + + public JSONObject createMessage(JSONObject commonEventHeaderParams, Optional pnfRegistrationParams, + Optional notificationParams, List fileList, String xnfUrl) { + + if (!pnfRegistrationParams.isPresent() && !notificationParams.isPresent()) { + throw new IllegalArgumentException( + "Both PNF registration and notification parameters objects are not present"); + } + JSONObject event = new JSONObject(); + + JSONObject commonEventHeader = JSONObjectFactory.generateConstantCommonEventHeader(); + Map commonEventHeaderFields = commonEventHeaderParams.toMap(); + commonEventHeaderFields.forEach((key, value) -> { + commonEventHeader.put(key, value); + }); + + JSONObject pnfRegistrationFields = JSONObjectFactory.generatePnfRegistrationFields(); + pnfRegistrationParams.ifPresent(jsonObject -> { + copyParametersToFields(jsonObject.toMap(), pnfRegistrationFields); + commonEventHeader.put(DOMAIN, DOMAIN_PNF_REGISTRATION); + commonEventHeader.put(EVENT_TYPE, DOMAIN_PNF_REGISTRATION); + event.put(PNF_REGISTRATION_FIELDS, pnfRegistrationFields); + }); + + JSONObject notificationFields = JSONObjectFactory.generateNotificationFields(); + notificationParams.ifPresent(jsonObject -> { + copyParametersToFields(jsonObject.toMap(), notificationFields); + JSONArray arrayOfNamedHashMap = JSONObjectFactory.generateArrayOfNamedHashMap(fileList, xnfUrl); + notificationFields.put(ARRAY_OF_NAMED_HASH_MAP, arrayOfNamedHashMap); + commonEventHeader.put(DOMAIN, DOMAIN_NOTIFICATION); + event.put(NOTIFICATION_FIELDS, notificationFields); + }); + + event.put(COMMON_EVENT_HEADER, commonEventHeader); + JSONObject root = new JSONObject(); + root.put(EVENT, event); + return root; + } + + private void copyParametersToFields(Map paramersMap, JSONObject fieldsJsonObject) { + paramersMap.forEach((key, value) -> { + fieldsJsonObject.put(key, value); + }); + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTask.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTask.java new file mode 100644 index 0000000..bb173ae --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTask.java @@ -0,0 +1,62 @@ +/// * +// * ============LICENSE_START======================================================= +// * PNF-REGISTRATION-HANDLER +// * ================================================================================ +// * Copyright (C) 2018 NOKIA Intellectual Property. 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. +// * ============LICENSE_END========================================================= +// */ +// +// package org.onap.pnfsimulator.netconfmonitor; +// +// import com.tailf.jnc.JNCException; +// import java.io.IOException; +// import java.util.TimerTask; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationCache; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationReader; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationWriter; +// import org.slf4j.Logger; +// import org.slf4j.LoggerFactory; +// +// public class NetconfConfigurationCheckingTask extends TimerTask { +// +// private static final Logger LOGGER = +/// LoggerFactory.getLogger(NetconfConfigurationCheckingTask.class); +// +// private final NetconfConfigurationReader reader; +// private final NetconfConfigurationWriter writer; +// private final NetconfConfigurationCache cache; +// +// public NetconfConfigurationCheckingTask(NetconfConfigurationReader reader, +// NetconfConfigurationWriter writer, +// NetconfConfigurationCache cache) { +// this.reader = reader; +// this.writer = writer; +// this.cache = cache; +// } +// +// @Override +// public void run() { +// try { +// String currentConfiguration = reader.read(); +// if (!currentConfiguration.equals(cache.getConfiguration())) { +// LOGGER.info("Configuration has changed, new configuration:\n\n{}", currentConfiguration); +// writer.writeToFile(currentConfiguration); +// cache.update(currentConfiguration); +// } +// } catch (IOException | JNCException e) { +// LOGGER.warn("Error during configuration reading: {}", e.getMessage()); +// } +// } +// } diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorService.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorService.java new file mode 100644 index 0000000..4e484b9 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorService.java @@ -0,0 +1,71 @@ +/// * +// * ============LICENSE_START======================================================= +// * PNF-REGISTRATION-HANDLER +// * ================================================================================ Copyright (C) +// * 2018 NOKIA Intellectual Property. 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. ============LICENSE_END========================================================= +// */ +// +// package org.onap.pnfsimulator.netconfmonitor; +// +// import com.tailf.jnc.JNCException; +// import java.io.IOException; +// import java.util.Timer; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationCache; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationReader; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationWriter; +// import org.slf4j.Logger; +// import org.slf4j.LoggerFactory; +// import org.springframework.beans.factory.annotation.Autowired; +// +//// @Service +// public class NetconfMonitorService { +// private static final Logger LOGGER = LoggerFactory.getLogger(NetconfMonitorService.class); +// private static final long timePeriod = 1000L; +// private static final long startDelay = 0; +// +// private Timer timer; +// private NetconfConfigurationReader reader; +// private NetconfConfigurationWriter writer; +// private NetconfConfigurationCache cache; +// +// @Autowired +// public NetconfMonitorService(Timer timer, NetconfConfigurationReader reader, +/// NetconfConfigurationWriter writer, +// NetconfConfigurationCache cache) { +// this.timer = timer; +// this.reader = reader; +// this.writer = writer; +// this.cache = cache; +// } +// +// // @PostConstruct +// public void start() { +// setStartConfiguration(); +// NetconfConfigurationCheckingTask task = new NetconfConfigurationCheckingTask(reader, writer, +/// cache); +// timer.scheduleAtFixedRate(task, startDelay, timePeriod); +// } +// +// private void setStartConfiguration() { +// try { +// String configuration = reader.read(); +// writer.writeToFile(configuration); +// cache.update(configuration); +// } catch (IOException | JNCException e) { +// LOGGER.warn("Error during configuration reading: {}", e.getMessage()); +// } +// } +// } diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfiguration.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfiguration.java new file mode 100644 index 0000000..d97315b --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfiguration.java @@ -0,0 +1,114 @@ +/// * +// * ============LICENSE_START======================================================= +// * PNF-REGISTRATION-HANDLER +// * ================================================================================ +// * Copyright (C) 2018 NOKIA Intellectual Property. 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. +// * ============LICENSE_END========================================================= +// */ +// +// package org.onap.pnfsimulator.netconfmonitor; +// +// import com.tailf.jnc.JNCException; +// import com.tailf.jnc.NetconfSession; +// import com.tailf.jnc.SSHConnection; +// import com.tailf.jnc.SSHSession; +// import java.io.IOException; +// import java.util.Map; +// import java.util.Timer; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationCache; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationReader; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationWriter; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConnectionParams; +// import org.slf4j.Logger; +// import org.slf4j.LoggerFactory; +// import org.springframework.context.annotation.Bean; +// import org.springframework.context.annotation.Configuration; +// +// @Configuration +// public class NetconfMonitorServiceConfiguration { +// +// private static final Logger LOGGER = +/// LoggerFactory.getLogger(NetconfMonitorServiceConfiguration.class); +// private static final Map enviroment = System.getenv(); +// +// private static final String LOG_PATH = "/var/log"; +// +// private static final String NETCONF_ADDRESS = "NETCONF_ADDRESS"; +// private static final String NETCONF_PORT = "NETCONF_PORT"; +// private static final String NETCONF_MODEL = "NETCONF_MODEL"; +// private static final String NETCONF_MAIN_CONTAINER = "NETCONF_MAIN_CONTAINER"; +// +// private static final String DEFAULT_NETCONF_ADDRESS = "localhost"; +// private static final int DEFAULT_NETCONF_PORT = 830; +// private static final String DEFAULT_NETCONF_MODEL = "pnf-simulator"; +// private static final String DEFAULT_NETCONF_MAIN_CONTAINER = "config"; +// +// private static final String DEFAULT_NETCONF_USER = "netconf"; +// private static final String DEFAULT_NETCONF_PASSWORD = "netconf"; +// +// @Bean +// public Timer timer() { +// return new Timer("NetconfMonitorServiceTimer"); +// } +// +// @Bean +// public NetconfConfigurationCache configurationCache() { +// return new NetconfConfigurationCache(); +// } +// +// @Bean +// public NetconfConfigurationReader configurationReader() throws IOException, JNCException { +// NetconfConnectionParams params = resolveConnectionParams(); +// LOGGER.info("Configuration params are : {}", params); +// NetconfSession session = createNetconfSession(params); +// return new NetconfConfigurationReader(session, buildModelPath()); +// } +// +// NetconfSession createNetconfSession(NetconfConnectionParams params) throws IOException, +/// JNCException { +// SSHConnection sshConnection = new SSHConnection(params.address, params.port); +// sshConnection.authenticateWithPassword(params.user, params.password); +// return new NetconfSession( new SSHSession(sshConnection)); +// } +// +// @Bean +// public NetconfConfigurationWriter netconfConfigurationWriter() { +// return new NetconfConfigurationWriter(LOG_PATH); +// } +// +// private String buildModelPath() { +// return String.format("/%s:%s", +// enviroment.getOrDefault(NETCONF_MODEL, DEFAULT_NETCONF_MODEL), +// enviroment.getOrDefault(NETCONF_MAIN_CONTAINER, DEFAULT_NETCONF_MAIN_CONTAINER)); +// } +// +// NetconfConnectionParams resolveConnectionParams() { +// return new NetconfConnectionParams( +// enviroment.getOrDefault(NETCONF_ADDRESS, DEFAULT_NETCONF_ADDRESS), +// resolveNetconfPort(), +// DEFAULT_NETCONF_USER, +// DEFAULT_NETCONF_PASSWORD); +// } +// +// private int resolveNetconfPort() { +// try { +// return Integer.parseInt(enviroment.get(NETCONF_PORT)); +// } catch (NumberFormatException e) { +// LOGGER.warn("Invalid netconf port: {}. Default netconf port {} is set.", e.getMessage(), +// DEFAULT_NETCONF_PORT); +// return DEFAULT_NETCONF_PORT; +// } +// } +// } diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCache.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCache.java new file mode 100644 index 0000000..3972184 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCache.java @@ -0,0 +1,34 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.netconfmonitor.netconf; + +public class NetconfConfigurationCache { + + private String configuration = ""; + + public String getConfiguration() { + return configuration; + } + + public void update(String configuration) { + this.configuration = configuration; + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReader.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReader.java new file mode 100644 index 0000000..e41e58f --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReader.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ Copyright (C) + * 2018 NOKIA Intellectual Property. 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. ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.netconfmonitor.netconf; + +import com.tailf.jnc.JNCException; +import com.tailf.jnc.NetconfSession; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class NetconfConfigurationReader { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetconfConfigurationReader.class); + private final NetconfSession session; + private final String netconfModelPath; + + public NetconfConfigurationReader(NetconfSession session, String netconfModelPath) { + LOGGER.warn("netconfModelPath: {}", netconfModelPath); + this.session = session; + this.netconfModelPath = netconfModelPath; + } + + public String read() throws IOException, JNCException { + return session.getConfig(netconfModelPath).first().toXMLString(); + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriter.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriter.java new file mode 100644 index 0000000..4003079 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriter.java @@ -0,0 +1,51 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.netconfmonitor.netconf; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import org.onap.pnfsimulator.rest.util.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfConfigurationWriter { + + private static final Logger LOGGER = LoggerFactory.getLogger(NetconfConfigurationWriter.class); + private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); + private String pathToLog; + + public NetconfConfigurationWriter(String pathToLog) { + this.pathToLog = pathToLog; + } + + public void writeToFile(String configuration) { + String fileName = String.format("%s/config[%s].xml", pathToLog, DateUtil.getTimestamp(dateFormat)); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) { + writer.write(configuration); + LOGGER.info("Configuration wrote to file {}/{} ", pathToLog, fileName); + } catch (IOException e) { + LOGGER.warn("Failed to write configuration to file: {}", e.getMessage()); + } + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConnectionParams.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConnectionParams.java new file mode 100644 index 0000000..1d6eb89 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConnectionParams.java @@ -0,0 +1,45 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.netconfmonitor.netconf; + +public class NetconfConnectionParams { + + public final String address; + public final int port; + public final String user; + public final String password; + + public NetconfConnectionParams(String address, int port, String user, String password) { + this.address = address; + this.port = port; + this.user = user; + this.password = password; + } + + @Override + public String toString() { + return String.format("NetconfConnectionParams{address=%s, port=%d, user=%s, password=%s}", + address, + port, + user, + password); + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java new file mode 100644 index 0000000..7bb646c --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java @@ -0,0 +1,235 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ Copyright (C) + * 2018 NOKIA Intellectual Property. 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. ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.rest; + +import static org.onap.pnfsimulator.logging.MDCVariables.INSTANCE_UUID; +import static org.onap.pnfsimulator.logging.MDCVariables.INVOCATION_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.REQUEST_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.RESPONSE_CODE; +import static org.onap.pnfsimulator.logging.MDCVariables.SERVICE_NAME; +import static org.onap.pnfsimulator.logging.MDCVariables.X_INVOCATION_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.AUTHORIZATION; +import static org.onap.pnfsimulator.logging.MDCVariables.X_ONAP_REQUEST_ID; +import static org.onap.pnfsimulator.message.MessageConstants.COMMON_EVENT_HEADER_PARAMS; +import static org.onap.pnfsimulator.message.MessageConstants.SIMULATOR_PARAMS; +import static org.onap.pnfsimulator.rest.util.ResponseBuilder.MESSAGE; +import static org.onap.pnfsimulator.rest.util.ResponseBuilder.REMAINING_TIME; +import static org.onap.pnfsimulator.rest.util.ResponseBuilder.SIMULATOR_STATUS; +import static org.onap.pnfsimulator.rest.util.ResponseBuilder.TIMESTAMP; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.OK; +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Optional; +import java.util.UUID; +import org.json.JSONException; +import org.json.JSONObject; +import org.onap.pnfsimulator.message.MessageConstants; +import org.onap.pnfsimulator.rest.util.DateUtil; +import org.onap.pnfsimulator.rest.util.ResponseBuilder; +import org.onap.pnfsimulator.simulator.Simulator; +import org.onap.pnfsimulator.simulator.SimulatorFactory; +import org.onap.pnfsimulator.simulator.validation.JSONValidator; +import org.onap.pnfsimulator.simulator.validation.ValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/simulator") +public class SimulatorController { + + private static final Logger LOGGER = LoggerFactory.getLogger(Simulator.class); + private static final DateFormat RESPONSE_DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss,SSS"); + private final Marker ENTRY = MarkerFactory.getMarker("ENTRY"); + private Simulator simulator; + private JSONValidator validator; + private SimulatorFactory factory; + + @Autowired + public SimulatorController(JSONValidator validator, SimulatorFactory factory) { + this.validator = validator; + this.factory = factory; + } + + @PostMapping("start") + public ResponseEntity start(@RequestHeader HttpHeaders headers, @RequestBody String message) { + MDC.put(REQUEST_ID, headers.getFirst(X_ONAP_REQUEST_ID)); + MDC.put(INVOCATION_ID, headers.getFirst(X_INVOCATION_ID)); + MDC.put(AUTHORIZATION, headers.getFirst(AUTHORIZATION)); + MDC.put(INSTANCE_UUID, UUID.randomUUID().toString()); + MDC.put(SERVICE_NAME, "/simulator/start"); + LOGGER.info(ENTRY, "Simulator starting"); + + if (isSimulatorRunning()) { + MDC.put(RESPONSE_CODE, BAD_REQUEST.toString()); + return ResponseBuilder.status(BAD_REQUEST).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Cannot start simulator since it's already running").build(); + } + + try { + validator.validate(message, "json_schema/input_validator.json"); + JSONObject root = new JSONObject(message); + JSONObject simulatorParams = root.getJSONObject(SIMULATOR_PARAMS); + JSONObject commonEventHeaderParams = root.getJSONObject(COMMON_EVENT_HEADER_PARAMS); + Optional pnfRegistrationFields = root.has(MessageConstants.PNF_REGISTRATION_PARAMS) + ? Optional.of(root.getJSONObject(MessageConstants.PNF_REGISTRATION_PARAMS)) + : Optional.empty(); + Optional notificationFields = root.has(MessageConstants.NOTIFICATION_PARAMS) + ? Optional.of(root.getJSONObject(MessageConstants.NOTIFICATION_PARAMS)) + : Optional.empty(); + simulator = + factory.create(simulatorParams, commonEventHeaderParams, pnfRegistrationFields, notificationFields); + simulator.start(); + + MDC.put(RESPONSE_CODE, OK.toString()); + return ResponseBuilder.status(OK).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Simulator started").build(); + + } catch (JSONException e) { + MDC.put(RESPONSE_CODE, BAD_REQUEST.toString()); + LOGGER.warn("Cannot start simulator, invalid json format: {}", e.getMessage()); + LOGGER.debug("Received json has invalid format", e); + return ResponseBuilder.status(BAD_REQUEST).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Cannot start simulator, invalid json format").build(); + + } catch (ProcessingException | ValidationException | IOException e) { + MDC.put(RESPONSE_CODE, BAD_REQUEST.toString()); + LOGGER.warn("Json validation failed: {}", e.getMessage()); + return ResponseBuilder.status(BAD_REQUEST).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Cannot start simulator - Json format is not compatible with schema definitions") + .build(); + + } catch (Exception e) { + MDC.put(RESPONSE_CODE, INTERNAL_SERVER_ERROR.toString()); + LOGGER.error("Cannot start simulator - unexpected exception", e); + return ResponseBuilder.status(INTERNAL_SERVER_ERROR) + .put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Unexpected exception: " + e.getMessage()).build(); + } finally { + MDC.clear(); + } + } + + @PostMapping("startmassmode") + public ResponseEntity startmassmode(@RequestHeader HttpHeaders headers, @RequestBody String message) { + MDC.put(REQUEST_ID, headers.getFirst(X_ONAP_REQUEST_ID)); + MDC.put(INVOCATION_ID, headers.getFirst(X_INVOCATION_ID)); + MDC.put(AUTHORIZATION, headers.getFirst(AUTHORIZATION)); + MDC.put(INSTANCE_UUID, UUID.randomUUID().toString()); + MDC.put(SERVICE_NAME, "/simulator/start"); + LOGGER.info(ENTRY, "Simulator starting"); + + if (isSimulatorRunning()) { + MDC.put(RESPONSE_CODE, BAD_REQUEST.toString()); + return ResponseBuilder.status(BAD_REQUEST).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Cannot start simulator since it's already running").build(); + } + + try { + validator.validate(message, "json_schema/input_validator.json"); + JSONObject root = new JSONObject(message); + JSONObject simulatorParams = root.getJSONObject(SIMULATOR_PARAMS); + JSONObject commonEventHeaderParams = root.getJSONObject(COMMON_EVENT_HEADER_PARAMS); + Optional pnfRegistrationFields = root.has(MessageConstants.PNF_REGISTRATION_PARAMS) + ? Optional.of(root.getJSONObject(MessageConstants.PNF_REGISTRATION_PARAMS)) + : Optional.empty(); + Optional notificationFields = root.has(MessageConstants.NOTIFICATION_PARAMS) + ? Optional.of(root.getJSONObject(MessageConstants.NOTIFICATION_PARAMS)) + : Optional.empty(); + simulator = + factory.create(simulatorParams, commonEventHeaderParams, pnfRegistrationFields, notificationFields); + simulator.start(); + + MDC.put(RESPONSE_CODE, OK.toString()); + return ResponseBuilder.status(OK).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Simulator started").build(); + + } catch (JSONException e) { + MDC.put(RESPONSE_CODE, BAD_REQUEST.toString()); + LOGGER.warn("Cannot start simulator, invalid json format: {}", e.getMessage()); + LOGGER.debug("Received json has invalid format", e); + return ResponseBuilder.status(BAD_REQUEST).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Cannot start simulator, invalid json format").build(); + + } catch (ProcessingException | ValidationException | IOException e) { + MDC.put(RESPONSE_CODE, BAD_REQUEST.toString()); + LOGGER.warn("Json validation failed: {}", e.getMessage()); + return ResponseBuilder.status(BAD_REQUEST).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Cannot start simulator - Json format is not compatible with schema definitions") + .build(); + + } catch (Exception e) { + MDC.put(RESPONSE_CODE, INTERNAL_SERVER_ERROR.toString()); + LOGGER.error("Cannot start simulator - unexpected exception", e); + return ResponseBuilder.status(INTERNAL_SERVER_ERROR) + .put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Unexpected exception: " + e.getMessage()).build(); + } finally { + MDC.clear(); + } + } + + + + @GetMapping("status") + public ResponseEntity status() { + if (isSimulatorRunning()) { + ResponseBuilder responseBuilder = ResponseBuilder.status(OK) + .put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)).put(SIMULATOR_STATUS, "RUNNING"); + + return !simulator.isEndless() ? responseBuilder.put(REMAINING_TIME, simulator.getRemainingTime()).build() + : responseBuilder.build(); + } else { + return ResponseBuilder.status(OK).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(SIMULATOR_STATUS, "NOT RUNNING").build(); + } + } + + @PostMapping("stop") + public ResponseEntity stop() { + if (isSimulatorRunning()) { + simulator.interrupt(); + + return ResponseBuilder.status(OK).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Simulator successfully stopped").build(); + } else { + return ResponseBuilder.status(BAD_REQUEST).put(TIMESTAMP, DateUtil.getTimestamp(RESPONSE_DATE_FORMAT)) + .put(MESSAGE, "Cannot stop simulator, because it's not running").build(); + } + } + + private boolean isSimulatorRunning() { + return simulator != null && simulator.isAlive(); + } +} + diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java new file mode 100644 index 0000000..284d589 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/DateUtil.java @@ -0,0 +1,35 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.rest.util; + +import java.text.DateFormat; +import java.util.Date; + +public final class DateUtil { + + private DateUtil() { + } + + public static String getTimestamp(DateFormat dateFormat) { + + return dateFormat.format(new Date()); + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java new file mode 100644 index 0000000..98f4588 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/rest/util/ResponseBuilder.java @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.rest.util; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class ResponseBuilder { + + public static final String TIMESTAMP = "timestamp"; + public static final String MESSAGE = "message"; + public static final String SIMULATOR_STATUS = "simulatorStatus"; + public static final String REMAINING_TIME = "remainingTime"; + + private HttpStatus httpStatus; + private Map body = new LinkedHashMap<>(); + + private ResponseBuilder(HttpStatus httpStatus) { + this.httpStatus = httpStatus; + } + + public static ResponseBuilder status(HttpStatus httpStatus) { + + return new ResponseBuilder(httpStatus); + } + + public ResponseBuilder put(String key, Object value) { + + body.put(key, value); + return this; + } + + public ResponseEntity build() { + + if (body.isEmpty()) { + return ResponseEntity.status(httpStatus).build(); + } + + return ResponseEntity.status(httpStatus).body(body); + } + +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/Simulator.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/Simulator.java new file mode 100644 index 0000000..725fe43 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/Simulator.java @@ -0,0 +1,213 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ Copyright (C) + * 2018 NOKIA Intellectual Property. 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. ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator; + +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.json.JSONObject; +import org.onap.pnfsimulator.FileProvider; +import org.onap.pnfsimulator.message.MessageProvider; +import org.onap.pnfsimulator.simulator.client.RestTemplateAdapter; +import org.onap.pnfsimulator.simulator.client.RestTemplateAdapterImpl; +import org.onap.pnfsimulator.simulator.validation.JSONValidator; +import org.onap.pnfsimulator.simulator.validation.NoRopFilesException; +import org.onap.pnfsimulator.simulator.validation.ValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +public class Simulator extends Thread { + + private static final Logger LOGGER = LoggerFactory.getLogger(Simulator.class); + private final Marker EXIT = MarkerFactory.getMarker("EXIT"); + private Map contextMap = MDC.getCopyOfContextMap(); + private boolean isEndless; + private String vesUrl; + private RestTemplateAdapter httpClient; + private JSONObject messageBody; + private Duration duration; + private Duration interval; + private Instant endTime; + private JSONObject commonEventHeaderParams; + private Optional pnfRegistrationParams; + private Optional notificationParams; + private String xnfUrl; + private static final String DEFAULT_OUTPUT_SCHEMA_PATH = "json_schema/output_validator_ves_schema_30.0.1.json"; + private FileProvider fileProvider; + private Exception thrownException = null; + + private Simulator() {} + + public static Builder builder() { + return new Builder(); + } + + @Override + public void run() { + setMdcContextMap(contextMap); + LOGGER.info("Simulation started - duration: {}, interval: {}s", getDuration(), interval.getSeconds()); + endTime = Instant.now().plus(duration); + while (isEndless || runningTimeNotExceeded()) { + try { + + List fileList = fileProvider.getFiles(); + MessageProvider messageProvider = new MessageProvider(); + JSONValidator validator = new JSONValidator(); + messageBody = messageProvider.createMessage(this.commonEventHeaderParams, this.pnfRegistrationParams, + this.notificationParams, fileList, this.xnfUrl); + validator.validate(messageBody.toString(), DEFAULT_OUTPUT_SCHEMA_PATH); + + LOGGER.info("Message to be sent:\n" + getMessage()); + httpClient.send(messageBody.toString(), vesUrl); + Thread.sleep(interval.toMillis()); + } catch (InterruptedException | ValidationException | ProcessingException | IOException | NoRopFilesException e) { + LOGGER.info("Simulation stopped due to an exception: " + e); + thrownException = e; + return; + } + } + LOGGER.info(EXIT, "Simulation finished"); + MDC.clear(); + } + + private void setMdcContextMap(Map mdcContextMap) { + if (mdcContextMap != null) + MDC.setContextMap(mdcContextMap); + } + + private String getMessage() { + return messageBody.toString(4); + } + + private String getDuration() { + return isEndless() ? "infinity" : duration.getSeconds() + "s"; + } + + private boolean runningTimeNotExceeded() { + return Instant.now().isBefore(endTime); + } + + public boolean isEndless() { + return isEndless; + } + + public Exception getThrownException() { + return thrownException; + } + + public long getRemainingTime() { + return Duration.between(Instant.now(), endTime).getSeconds(); + } + + public static class Builder { + + private String vesUrl; + private RestTemplateAdapter httpClient; + //private JSONObject messageBody; + private Duration duration; + private Duration interval; + private Optional notificationParams; + private Optional pnfRegistrationParams; + private JSONObject commonEventHeaderParams; + private String xnfUrl; + private FileProvider fileProvider; + + private Builder() { + this.vesUrl = ""; + this.httpClient = new RestTemplateAdapterImpl(); + //this.messageBody = new JSONObject(); + this.duration = Duration.ZERO; + this.interval = Duration.ZERO; + this.commonEventHeaderParams = new JSONObject(); + } + + public Builder withVesUrl(String vesUrl) { + this.vesUrl = vesUrl; + return this; + } + + public Builder withCustomRestTemplateAdapter(RestTemplateAdapter httpClient) { + this.httpClient = httpClient; + return this; + } + + /*public Builder withMessageBody(JSONObject messageBody) { + this.messageBody = messageBody; + return this; + }*/ + + public Builder withDuration(Duration duration) { + this.duration = duration; + return this; + } + + + public Builder withInterval(Duration interval) { + this.interval = interval; + return this; + } + + public Builder withCommonEventHeaderParams(JSONObject commonEventHeaderParams) { + this.commonEventHeaderParams = commonEventHeaderParams; + return this; + } + + public Builder withNotificationParams(Optional notificationParams) { + this.notificationParams = notificationParams; + return this; + } + + public Builder withPnfRegistrationParams(Optional pnfRegistrationParams) { + this.pnfRegistrationParams = pnfRegistrationParams; + return this; + } + + public Builder withXnfUrl(String xnfUrl) { + this.xnfUrl = xnfUrl; + return this; + } + + public Builder withFileProvider(FileProvider fileProvider) { + this.fileProvider = fileProvider; + return this; + } + + public Simulator build() { + Simulator simulator = new Simulator(); + simulator.vesUrl = this.vesUrl; + simulator.httpClient = this.httpClient; + //simulator.messageBody = this.messageBody; + simulator.duration = this.duration; + simulator.interval = this.interval; + simulator.xnfUrl = this.xnfUrl; + simulator.fileProvider = this.fileProvider; + simulator.commonEventHeaderParams = this.commonEventHeaderParams; + simulator.pnfRegistrationParams = this.pnfRegistrationParams; + simulator.notificationParams = this.notificationParams; + simulator.isEndless = duration.equals(Duration.ZERO); + return simulator; + } + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/SimulatorFactory.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/SimulatorFactory.java new file mode 100644 index 0000000..851e6ad --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/SimulatorFactory.java @@ -0,0 +1,54 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ Copyright (C) + * 2018 NOKIA Intellectual Property. 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. ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator; + +import static java.lang.Integer.parseInt; +import static org.onap.pnfsimulator.message.MessageConstants.MESSAGE_INTERVAL; +import static org.onap.pnfsimulator.message.MessageConstants.TEST_DURATION; +import java.time.Duration; +import java.util.Optional; +import org.json.JSONObject; +import org.onap.pnfsimulator.ConfigurationProvider; +import org.onap.pnfsimulator.FileProvider; +import org.onap.pnfsimulator.PnfSimConfig; +import org.springframework.stereotype.Service; + +@Service +public class SimulatorFactory { + + public Simulator create(JSONObject simulatorParams, JSONObject commonEventHeaderParams, + Optional pnfRegistrationParams, Optional notificationParams) { + PnfSimConfig configuration = ConfigurationProvider.getConfigInstance(); + + String xnfUrl = null; + if (configuration.getTypefileserver().equals("sftp")) { + xnfUrl = configuration.getUrlsftp() + "/"; + } else if (configuration.getTypefileserver().equals("ftps")) { + xnfUrl = configuration.getUrlftps() + "/"; + } + + String urlVes = configuration.getUrlves(); + Duration duration = Duration.ofSeconds(parseInt(simulatorParams.getString(TEST_DURATION))); + Duration interval = Duration.ofSeconds(parseInt(simulatorParams.getString(MESSAGE_INTERVAL))); + + return Simulator.builder().withVesUrl(urlVes).withXnfUrl(xnfUrl).withDuration(duration) + .withFileProvider(new FileProvider()).withCommonEventHeaderParams(commonEventHeaderParams) + .withNotificationParams(notificationParams).withPnfRegistrationParams(pnfRegistrationParams) + .withInterval(interval).build(); + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapter.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapter.java new file mode 100644 index 0000000..92e66b4 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapter.java @@ -0,0 +1,26 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator.client; + +public interface RestTemplateAdapter { + + void send(String content, String url); +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImpl.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImpl.java new file mode 100644 index 0000000..e082637 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImpl.java @@ -0,0 +1,155 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator.client; + +import static org.onap.pnfsimulator.logging.MDCVariables.REQUEST_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.X_INVOCATION_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.X_ONAP_REQUEST_ID; +import static org.onap.pnfsimulator.logging.MDCVariables.AUTHORIZATION; + +import org.springframework.web.client.ResourceAccessException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import org.springframework.http.HttpEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.http.HttpHeaders; + +import java.util.UUID; +import org.springframework.web.client.HttpClientErrorException; +import org.apache.http.client.config.RequestConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +public class RestTemplateAdapterImpl implements RestTemplateAdapter { + + private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateAdapterImpl.class); + private static final String CONTENT_TYPE = "Content-Type"; + private static final String APPLICATION_JSON = "application/json"; + private final Marker INVOKE = MarkerFactory.getMarker("INVOKE"); + private static final RequestConfig CONFIG = RequestConfig.custom() + .setConnectTimeout(1000) + .setConnectionRequestTimeout(1000) + .setSocketTimeout(1000) + .build(); + + private RestTemplate restTemplate; + + public RestTemplateAdapterImpl() { + try { + this.restTemplate = createRestTemplate(); + } catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException ex) { + LOGGER.warn("Error while creating a RestTemplate object: {}", ex.getMessage()); + } + } + + RestTemplateAdapterImpl(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + @Override + public void send(String content, String url) { + try { + HttpEntity entity = createPostEntity(content); + ResponseEntity response = restTemplate.postForEntity(url, entity, String.class); + LOGGER.info(INVOKE, "Message sent, ves response code: {}", response.getStatusCode()); + } catch (HttpClientErrorException codeEx) { + LOGGER.warn("Response body: ", codeEx.getResponseBodyAsString()); + LOGGER.warn("Error sending message to ves: {}", codeEx.getMessage()); + LOGGER.warn("URL: {}", url); + } catch (ResourceAccessException ioEx) { + LOGGER.warn("The URL cannot be reached: {}", ioEx.getMessage()); + LOGGER.warn("URL: {}", url); + } + } + + private CloseableHttpClient createClient() + throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { + + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, + String authType) {} + + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, + String authType) {} + } + }; + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init( + null, + trustAllCerts, + new java.security.SecureRandom() + ); + + CloseableHttpClient httpClient = HttpClients + .custom() + .setSSLContext(sslContext) + .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) + .build(); + + return httpClient; + } + + private RestTemplate createRestTemplate() + throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { + + CloseableHttpClient client = createClient(); + HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); + requestFactory.setHttpClient(client); + + return new RestTemplate(requestFactory); + + } + + private HttpEntity createPostEntity(String content) { + + HttpHeaders headers = new HttpHeaders(); + headers.set(CONTENT_TYPE, APPLICATION_JSON); + headers.set(AUTHORIZATION, MDC.get(AUTHORIZATION)); + headers.set(X_ONAP_REQUEST_ID, MDC.get(REQUEST_ID)); + headers.set(X_INVOCATION_ID, UUID.randomUUID().toString()); + + return new HttpEntity<>(content, headers); + + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/JSONValidator.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/JSONValidator.java new file mode 100644 index 0000000..89135f9 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/JSONValidator.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator.validation; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.fge.jackson.JsonLoader; +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import com.github.fge.jsonschema.core.report.LogLevel; +import com.github.fge.jsonschema.core.report.ProcessingMessage; +import com.github.fge.jsonschema.core.report.ProcessingReport; +import com.github.fge.jsonschema.main.JsonSchema; +import com.github.fge.jsonschema.main.JsonSchemaFactory; +import com.google.gson.JsonParser; +import java.io.FileReader; +import java.io.IOException; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class JSONValidator { + + public void validate(String data, String jsonSchemaPath) + throws ValidationException, ProcessingException, IOException { + String jsonSchema = readJsonSchemaAsString(jsonSchemaPath); + JsonNode jsonData = JsonLoader.fromString(data); + ProcessingReport report = createJsonSchema(jsonSchema).validate(jsonData); + + if (!report.isSuccess()) { + throw new ValidationException(constructValidationErrors(report)); + } + } + + private String readJsonSchemaAsString(String schemaPath) throws IOException { + try (FileReader reader = new FileReader(schemaPath)) { + return new JsonParser().parse(reader).toString(); + } + } + + private JsonSchema createJsonSchema(String schema) throws ProcessingException, IOException { + return JsonSchemaFactory.byDefault().getJsonSchema(JsonLoader.fromString(schema)); + } + + private String constructValidationErrors(ProcessingReport report) { + return StreamSupport.stream(report.spliterator(), false) + .filter(entry -> entry.getLogLevel() == LogLevel.ERROR) + .map(ProcessingMessage::getMessage) + .collect(Collectors.joining("\n")); + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/NoRopFilesException.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/NoRopFilesException.java new file mode 100644 index 0000000..d3765a8 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/NoRopFilesException.java @@ -0,0 +1,28 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator.validation; + +public class NoRopFilesException extends Exception { + + public NoRopFilesException(String message) { + super(message); + } +} diff --git a/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/ValidationException.java b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/ValidationException.java new file mode 100644 index 0000000..a934917 --- /dev/null +++ b/pnf-sim-lightweight/src/main/java/org/onap/pnfsimulator/simulator/validation/ValidationException.java @@ -0,0 +1,28 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator.validation; + +public class ValidationException extends Exception { + + public ValidationException(String message) { + super(message); + } +} diff --git a/pnf-sim-lightweight/src/main/resources/application.properties b/pnf-sim-lightweight/src/main/resources/application.properties new file mode 100644 index 0000000..9740eff --- /dev/null +++ b/pnf-sim-lightweight/src/main/resources/application.properties @@ -0,0 +1,6 @@ +server.port=5000 +logging.level.root=ERROR +logging.level.org.springframework=ERROR +logging.level.org.springframework.data=ERROR +logging.level.org.onap.pnfsimulator=TRACE +logging.file=logs/log/application.log \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/JSONObjectFactoryTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/JSONObjectFactoryTest.java new file mode 100644 index 0000000..da41afd --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/JSONObjectFactoryTest.java @@ -0,0 +1,95 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.message; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.onap.pnfsimulator.message.MessageConstants.EVENT_ID; +import static org.onap.pnfsimulator.message.MessageConstants.INTERNAL_HEADER_FIELDS; +import static org.onap.pnfsimulator.message.MessageConstants.LAST_EPOCH_MICROSEC; +import static org.onap.pnfsimulator.message.MessageConstants.NOTIFICATION_FIELDS_VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.NOTIFICATION_FIELDS_VERSION_VALUE; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_LAST_SERVICE_DATE; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_MANUFACTURE_DATE; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_REGISTRATION_FIELDS_VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_REGISTRATION_FIELDS_VERSION_VALUE; +import static org.onap.pnfsimulator.message.MessageConstants.PRIORITY; +import static org.onap.pnfsimulator.message.MessageConstants.PRIORITY_NORMAL; +import static org.onap.pnfsimulator.message.MessageConstants.REPORTING_ENTITY_NAME; +import static org.onap.pnfsimulator.message.MessageConstants.SEQUENCE; +import static org.onap.pnfsimulator.message.MessageConstants.SEQUENCE_NUMBER; +import static org.onap.pnfsimulator.message.MessageConstants.SOURCE_NAME; +import static org.onap.pnfsimulator.message.MessageConstants.START_EPOCH_MICROSEC; +import static org.onap.pnfsimulator.message.MessageConstants.TIME_ZONE_OFFSET; +import static org.onap.pnfsimulator.message.MessageConstants.VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.VERSION_NUMBER; +import static org.onap.pnfsimulator.message.MessageConstants.VES_EVENT_LISTENER_VERSION; +import static org.onap.pnfsimulator.message.MessageConstants.VES_EVENT_LISTENER_VERSION_NUMBER; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; + +public class JSONObjectFactoryTest { + + @Test + public void generateConstantCommonEventHeader_shouldCreateProperly(){ + JSONObject commonEventHeader = JSONObjectFactory.generateConstantCommonEventHeader(); + assertEquals(11,commonEventHeader.toMap().size()); + assertTrue(commonEventHeader.has(EVENT_ID)); + assertTrue(commonEventHeader.has(TIME_ZONE_OFFSET)); + assertTrue(commonEventHeader.has(LAST_EPOCH_MICROSEC)); + assertTrue(commonEventHeader.has(PRIORITY)); + assertTrue(commonEventHeader.has(SEQUENCE)); + assertTrue(commonEventHeader.has(START_EPOCH_MICROSEC)); + assertTrue(commonEventHeader.has(INTERNAL_HEADER_FIELDS)); + assertTrue(commonEventHeader.has(VERSION)); + assertTrue(commonEventHeader.has(SOURCE_NAME)); + assertTrue(commonEventHeader.has(REPORTING_ENTITY_NAME)); + assertEquals(commonEventHeader.get(PRIORITY),PRIORITY_NORMAL); + assertEquals(commonEventHeader.get(SEQUENCE),SEQUENCE_NUMBER); + assertEquals(commonEventHeader.get(VERSION),VERSION_NUMBER); + assertEquals(commonEventHeader.get(VES_EVENT_LISTENER_VERSION),VES_EVENT_LISTENER_VERSION_NUMBER); + } + + @Test + public void generateConstantPnfRegistrationFields_shouldCreateProperly(){ + JSONObject pnfRegistrationFields = JSONObjectFactory.generatePnfRegistrationFields(); + assertEquals(3,pnfRegistrationFields.toMap().size()); + assertTrue(pnfRegistrationFields.has(PNF_REGISTRATION_FIELDS_VERSION)); + assertEquals(pnfRegistrationFields.get(PNF_REGISTRATION_FIELDS_VERSION), PNF_REGISTRATION_FIELDS_VERSION_VALUE); + assertTrue(pnfRegistrationFields.has(PNF_LAST_SERVICE_DATE)); + assertTrue(pnfRegistrationFields.has(PNF_MANUFACTURE_DATE)); + } + + @Test + public void generateEventId_shouldCreateProperly(){ + String eventId = JSONObjectFactory.generateEventId(); + assertTrue(eventId.startsWith("FileReady_")); + } + + @Test + public void generateNotificationFields_shouldCreateProperly(){ + JSONObject notificationFields = JSONObjectFactory.generateNotificationFields(); + assertEquals(1,notificationFields.keySet().size()); + assertEquals(NOTIFICATION_FIELDS_VERSION_VALUE,notificationFields.get(NOTIFICATION_FIELDS_VERSION)); + + } + +} diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/MessageProviderTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/MessageProviderTest.java new file mode 100644 index 0000000..0fa8a12 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/message/MessageProviderTest.java @@ -0,0 +1,122 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.message; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.onap.pnfsimulator.message.MessageConstants.COMMON_EVENT_HEADER; +import static org.onap.pnfsimulator.message.MessageConstants.EVENT; +import static org.onap.pnfsimulator.message.MessageConstants.NOTIFICATION_FIELDS; +import static org.onap.pnfsimulator.message.MessageConstants.PNF_REGISTRATION_FIELDS; +import java.util.Optional; +import org.json.JSONObject; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class MessageProviderTest { + + private static final String testParamsPnfRegistration = + "{\"pnfKey1\": \"pnfVal1\",\"pnfKey2\": \"pnfVal2\",\"pnfKey3\": \"pnfVal3\",\"pnfKey4\": \"pnfVal4\"}"; + + private static final String testParamsNotification = + "{\"notKey1\": \"notVal1\",\"notKey2\": \"notVal2\",\"notKey3\": \"notVal3\",\"notKey4\": \"notVal4\"}"; + + private static MessageProvider messageProvider; + + @BeforeAll + public static void setup() { + messageProvider = new MessageProvider(); + } + + @Test + public void createMessage_should_throw_when_given_empty_arguments() { + assertThrows(IllegalArgumentException.class, + () -> messageProvider.createMessage(new JSONObject(), Optional.empty(), Optional.empty()), + "Params object cannot be null"); + } + + @Test + public void createMessage_should_create_constant_message_when_no_params_specified() { + JSONObject message = messageProvider.createMessage(new JSONObject(), Optional.ofNullable(new JSONObject()), + Optional.ofNullable(new JSONObject())); + JSONObject event = message.getJSONObject(EVENT); + + JSONObject commonEventHeader = event.getJSONObject(COMMON_EVENT_HEADER); + JSONObject pnfRegistrationFields = event.getJSONObject(PNF_REGISTRATION_FIELDS); + JSONObject notificationFields = event.getJSONObject(NOTIFICATION_FIELDS); + + JSONObject expectedCommonEventHeader = JSONObjectFactory.generateConstantCommonEventHeader(); + JSONObject expectedPnfRegistrationFields = JSONObjectFactory.generatePnfRegistrationFields(); + JSONObject expectedNotificationFields = JSONObjectFactory.generateNotificationFields(); + + expectedCommonEventHeader + .toMap() + .forEach((key, val) -> assertTrue(commonEventHeader.has(key), + () -> String.format("Key %s is not present", key))); + + expectedPnfRegistrationFields + .toMap() + .forEach((key, val) -> assertTrue(pnfRegistrationFields.has(key), + () -> String.format("Key %s is not present", key))); + + expectedNotificationFields + .toMap() + .forEach((key, val) -> assertTrue(notificationFields.has(key), + () -> String.format("Key %s is not present", key))); + } + + @Test + public void createMessage_should_throw_exception_when_params_specified_as_empty() { + assertThrows(IllegalArgumentException.class, + () -> messageProvider.createMessage(new JSONObject(), Optional.empty(), + Optional.empty())); + } + + @Test + public void createMessage_should_add_specified_params_to_valid_subobjects_with_event_pnf_registration() { + JSONObject message = messageProvider + .createMessage(new JSONObject(), Optional.of(new JSONObject(testParamsPnfRegistration)), Optional.empty()); + JSONObject event = message.getJSONObject(EVENT); + + JSONObject commonEventHeader = event.getJSONObject(COMMON_EVENT_HEADER); + assertEquals(13, commonEventHeader.keySet().size()); + + JSONObject pnfRegistrationFields = event.getJSONObject(PNF_REGISTRATION_FIELDS); + assertEquals("pnfVal1", pnfRegistrationFields.getString("pnfKey1")); + assertEquals("pnfVal2", pnfRegistrationFields.getString("pnfKey2")); + } + + @Test + public void createMessage_should_add_specified_params_to_valid_subobjects_with_event_notification() { + JSONObject message = messageProvider + .createMessage(new JSONObject(), Optional.empty(), Optional.of(new JSONObject(testParamsNotification))); + JSONObject event = message.getJSONObject(EVENT); + + JSONObject commonEventHeader = event.getJSONObject(COMMON_EVENT_HEADER); + assertEquals(12, commonEventHeader.keySet().size()); + + JSONObject notificationFields = event.getJSONObject(NOTIFICATION_FIELDS); + assertEquals("notVal1", notificationFields.getString("notKey1")); + assertEquals("notVal2", notificationFields.getString("notKey2")); + } + +} diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTaskTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTaskTest.java new file mode 100644 index 0000000..df5a13d --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfConfigurationCheckingTaskTest.java @@ -0,0 +1,95 @@ +/// * +// * ============LICENSE_START======================================================= +// * PNF-REGISTRATION-HANDLER +// * ================================================================================ +// * Copyright (C) 2018 NOKIA Intellectual Property. 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. +// * ============LICENSE_END========================================================= +// */ +// +// package org.onap.pnfsimulator.netconfmonitor; +// +// import static org.mockito.ArgumentMatchers.any; +// import static org.mockito.Mockito.never; +// import static org.mockito.Mockito.verify; +// import static org.mockito.Mockito.when; +// +// import com.tailf.jnc.JNCException; +// import java.io.IOException; +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.mockito.Mock; +// import org.mockito.MockitoAnnotations; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationCache; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationReader; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationWriter; +// +// class NetconfConfigurationCheckingTaskTest { +// +// private NetconfConfigurationCheckingTask checkingTask; +// +// @Mock +// private NetconfConfigurationReader reader; +// @Mock +// private NetconfConfigurationWriter writer; +// @Mock +// private NetconfConfigurationCache cache; +// +// @BeforeEach +// void setup() { +// MockitoAnnotations.initMocks(this); +// checkingTask = new NetconfConfigurationCheckingTask(reader, writer, cache); +// } +// +// @Test +// void run_should_update_configuration_when_changed() throws IOException, JNCException { +// String configuration = "newConfiguration"; +// when(reader.read()).thenReturn(configuration); +// when(cache.getConfiguration()).thenReturn("oldConfiguration"); +// +// checkingTask.run(); +// +// verify(reader).read(); +// verify(cache).getConfiguration(); +// verify(writer).writeToFile(configuration); +// verify(cache).update(configuration); +// } +// +// @Test +// void run_should_not_update_configuration_when_same() throws IOException, JNCException { +// String configuration = "configuration"; +// when(reader.read()).thenReturn(configuration); +// when(cache.getConfiguration()).thenReturn("configuration"); +// +// checkingTask.run(); +// +// verify(reader).read(); +// verify(cache).getConfiguration(); +// verify(writer, never()).writeToFile(configuration); +// verify(cache, never()).update(configuration); +// } +// +// @Test +// void run_should_not_take_any_action_when_failed_to_read_configuration() throws IOException, +/// JNCException { +// when(reader.read()).thenThrow(new IOException()); +// +// checkingTask.run(); +// +// verify(reader).read(); +// verify(cache, never()).getConfiguration(); +// verify(writer, never()).writeToFile(any()); +// verify(cache, never()).update(any()); +// } +// } diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfigurationTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfigurationTest.java new file mode 100644 index 0000000..3ff234b --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceConfigurationTest.java @@ -0,0 +1,72 @@ +/// * +// * ============LICENSE_START======================================================= +// * PNF-REGISTRATION-HANDLER +// * ================================================================================ +// * Copyright (C) 2018 NOKIA Intellectual Property. 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. +// * ============LICENSE_END========================================================= +// */ +// +// package org.onap.pnfsimulator.netconfmonitor; +// +// import static org.junit.jupiter.api.Assertions.assertNotNull; +// import static org.mockito.ArgumentMatchers.any; +// import static org.mockito.Mockito.doReturn; +// import static org.mockito.Mockito.mock; +// import static org.mockito.Mockito.spy; +// import static org.mockito.Mockito.verify; +// +// import com.tailf.jnc.JNCException; +// import com.tailf.jnc.NetconfSession; +// import java.io.IOException; +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.mockito.Mock; +// +// class NetconfMonitorServiceConfigurationTest { +// +// private NetconfMonitorServiceConfiguration configuration; +// +// @Mock +// private NetconfSession netconfSession; +// +// @BeforeEach +// void setup() { +// netconfSession = mock(NetconfSession.class); +// configuration = spy(new NetconfMonitorServiceConfiguration()); +// } +// +// @Test +// void readNetconfConfiguration() throws IOException, JNCException { +// doReturn(netconfSession).when(configuration).createNetconfSession(any()); +// +// assertNotNull(configuration.configurationReader()); +// verify(configuration).createNetconfSession(any()); +// } +// +// @Test +// void configurationCacheIsNotNull() { +// assertNotNull(configuration.configurationCache()); +// } +// +// @Test +// void netconfConfigurationWriterIsNotNull() { +// assertNotNull(configuration.netconfConfigurationWriter()); +// } +// +// @Test +// void timerIsNotNull() { +// assertNotNull(configuration.timer()); +// } +// } diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceTest.java new file mode 100644 index 0000000..f8690c5 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/NetconfMonitorServiceTest.java @@ -0,0 +1,73 @@ +/// * +// * ============LICENSE_START======================================================= +// * PNF-REGISTRATION-HANDLER +// * ================================================================================ +// * Copyright (C) 2018 NOKIA Intellectual Property. 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. +// * ============LICENSE_END========================================================= +// */ +// +// package org.onap.pnfsimulator.netconfmonitor; +// +// import static org.mockito.ArgumentMatchers.anyString; +// import static org.mockito.Mockito.any; +// import static org.mockito.Mockito.anyLong; +// import static org.mockito.Mockito.doNothing; +// import static org.mockito.Mockito.times; +// import static org.mockito.Mockito.verify; +// import static org.mockito.Mockito.when; +// +// import com.tailf.jnc.JNCException; +// import java.io.IOException; +// import java.util.Timer; +// import org.junit.jupiter.api.BeforeEach; +// import org.junit.jupiter.api.Test; +// import org.mockito.Mock; +// import org.mockito.MockitoAnnotations; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationCache; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationReader; +// import org.onap.pnfsimulator.netconfmonitor.netconf.NetconfConfigurationWriter; +// +// class NetconfMonitorServiceTest { +// +// private NetconfMonitorService service; +// +// @Mock +// private Timer timer; +// @Mock +// private NetconfConfigurationReader reader; +// @Mock +// private NetconfConfigurationWriter writer; +// @Mock +// private NetconfConfigurationCache cache; +// +// @BeforeEach +// void setup() { +// MockitoAnnotations.initMocks(this); +// service = new NetconfMonitorService(timer, reader, writer, cache); +// } +// +// @Test +// void startNetconfService() throws IOException, JNCException { +// when(reader.read()).thenReturn("message"); +// doNothing().when(writer).writeToFile(anyString()); +// doNothing().when(cache).update(anyString()); +// +// service.start(); +// +// verify(cache, times(1)).update(anyString()); +// verify(writer, times(1)).writeToFile(anyString()); +// verify(timer, times(1)).scheduleAtFixedRate(any(), anyLong(), anyLong()); +// } +// } diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCacheTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCacheTest.java new file mode 100644 index 0000000..56f62ac --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationCacheTest.java @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.netconfmonitor.netconf; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class NetconfConfigurationCacheTest { + + private static final String CONFIGURATION = "sampleConfiguration"; + + @Test + void changeConfigurationAfterUpdate() { + NetconfConfigurationCache configurationCache = new NetconfConfigurationCache(); + configurationCache.update(CONFIGURATION); + + assertEquals(CONFIGURATION, configurationCache.getConfiguration()); + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReaderTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReaderTest.java new file mode 100644 index 0000000..65b2bc3 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationReaderTest.java @@ -0,0 +1,70 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.netconfmonitor.netconf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.tailf.jnc.Element; +import com.tailf.jnc.JNCException; +import com.tailf.jnc.NetconfSession; +import com.tailf.jnc.NodeSet; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +class NetconfConfigurationReaderTest { + + private static final String NETCONF_MODEL_PATH = ""; + private static final String EXPECTED_STRING_XML = ""; + private NetconfConfigurationReader reader; + + @Mock + private NetconfSession netconfSession; + @Mock + private NodeSet nodeSet; + @Mock + private Element element; + + @BeforeEach + void setup() { + MockitoAnnotations.initMocks(this); + reader = new NetconfConfigurationReader(netconfSession, NETCONF_MODEL_PATH); + } + + @Test + void properlyReadXML() throws IOException, JNCException { + when(netconfSession.getConfig(anyString())).thenReturn(nodeSet); + when(nodeSet.first()).thenReturn(element); + when(element.toXMLString()).thenReturn(EXPECTED_STRING_XML); + + String result = reader.read(); + + verify(netconfSession).getConfig(anyString()); + verify(nodeSet).first(); + verify(element).toXMLString(); + assertEquals(EXPECTED_STRING_XML, result); + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriterTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriterTest.java new file mode 100644 index 0000000..2baee21 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/netconfmonitor/netconf/NetconfConfigurationWriterTest.java @@ -0,0 +1,67 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.netconfmonitor.netconf; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport; +import org.junit.rules.TemporaryFolder; + +@EnableRuleMigrationSupport +class NetconfConfigurationWriterTest { + + private static final String TEST_CONFIGURATION = "test-configuration"; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + void writeToFile_should_write_sample_config_when_directory_exists() throws IOException { + File file = temporaryFolder.newFolder("temp"); + NetconfConfigurationWriter configurationWriter = new NetconfConfigurationWriter(file.getPath()); + + configurationWriter.writeToFile(TEST_CONFIGURATION); + + File[] files = file.listFiles(); + assertEquals(1, files.length); + + String content = FileUtils.readFileToString(files[0], "UTF-8"); + assertEquals(TEST_CONFIGURATION, content); + } + + @Test + void writeToFile_should_not_write_config_when_directory_doesnt_exist() { + String logFolderPath = "/not/existing/logs"; + NetconfConfigurationWriter configurationWriter = new NetconfConfigurationWriter(logFolderPath); + + configurationWriter.writeToFile(TEST_CONFIGURATION); + + assertFalse(Files.exists(Paths.get(logFolderPath))); + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java new file mode 100644 index 0000000..215cee0 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java @@ -0,0 +1,226 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.rest; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_COMMON_EVENT_HEADER_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_NOTIFICATION_PARAMS; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.pnfsimulator.FileProvider; +import org.onap.pnfsimulator.simulator.Simulator; +import org.onap.pnfsimulator.simulator.SimulatorFactory; +import org.onap.pnfsimulator.simulator.client.RestTemplateAdapter; +import org.onap.pnfsimulator.simulator.validation.JSONValidator; +import org.onap.pnfsimulator.simulator.validation.NoRopFilesException; +import org.onap.pnfsimulator.simulator.validation.ValidationException; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +class SimulatorControllerTest { + + private static final String START_URL = "/simulator/start"; + private static final String STOP_URL = "/simulator/stop"; + private static final String STATUS_URL = "/simulator/status"; + private static final String JSON_MSG_EXPRESSION = "$.message"; + private static final String JSON_STATUS_EXPRESSION = "$.simulatorStatus"; + private static final String TEST_VES_URL = "http://localhost:10000/eventListener/v7"; + private static final String TEST_XNF_URL = "sftp://onap:pano@10.11.0.68" + "/"; + private static final String PROPER_JSON = "{\n" + + " \"simulatorParams\": {\n" + + " \"testDuration\": \"10\",\n" + + " \"messageInterval\": \"1\"\n" + + " },\n" + + " \"commonEventHeaderParams\": {\n" + + " \"eventName\": \"val11\",\n" + + " \"nfNamingCode\": \"val12\",\n" + + " \"nfcNamingCode\": \"val13\",\n" + + " \"sourceName\": \"val14\",\n" + + " \"sourceId\": \"val15\",\n" + + " \"reportingEntityName\": \"val16\",\n" + + " },\n" + + + " \"pnfRegistrationParams\": {\n" + + " \"SerialNumber\": \"val1\",\n" + + " \"VendorName\": \"val2\",\n" + + " \"OamIpv4Address\": \"val3\",\n" + + " \"OamIpv6Address\": \"val4\",\n" + + " \"Family\": \"val5\",\n" + + " \"ModelNumber\": \"val6\",\n" + + " \"SoftwareVersion\": \"val7\",\n" + + " }\n" + + "}"; + private static final String WRONG_JSON = "{\n" + + " \"mes\": {\n" + + " \"vesServerUrl\": \"http://10.154.187.70:8080/eventListener/v5\",\n" + + " \"testDuration\": \"10\",\n" + + " \"messageInterval\": \"1\"\n" + + " },\n" + + " \"messageParams\": {\n" + + " \"sourceName\": \"val12\",\n" + + " \"sourceId\": \"val13\",\n" + + " \"reportingEntityName\": \"val14\"\n" + + " }\n" + + "}\n"; + + private MockMvc mockMvc; + + @InjectMocks + private SimulatorController controller; + + @Mock + private SimulatorFactory factory; + @Mock + private JSONValidator validator; + + private Simulator simulator; + + private FileProvider fileProvider = mock(FileProvider.class); + + private void createSampleFileList() { + List fileList = new ArrayList<>(); + fileList.add("A20190401.1608+0000-1622+0000_excl-eeiwbue-perf-large-pnf-sim-lw-1.xml.gz"); + fileList.add("A20190401.1623+0000-1637+0000_excl-eeiwbue-perf-large-pnf-sim-lw-1.xml.gz"); + + try { + doReturn(fileList).when(fileProvider).getFiles(); + } catch (NoRopFilesException e) { + e.printStackTrace(); + } + } + + @BeforeEach + void setup() { + MockitoAnnotations.initMocks(this); + createSampleFileList(); + simulator = createEndlessSimulator(); + mockMvc = MockMvcBuilders + .standaloneSetup(controller) + .build(); + } + + private Simulator createEndlessSimulator() { + return spy(Simulator.builder() + .withCustomRestTemplateAdapter(mock(RestTemplateAdapter.class)) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(Optional.empty()) + .withNotificationParams(VALID_NOTIFICATION_PARAMS) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withFileProvider(fileProvider) + .withInterval(Duration.ofMinutes(1)) + .build()); + } + + @Test + void wrongJSONFormatOnStart() throws Exception { + when(factory.create(any(),any(), any(),any())).thenReturn(simulator); + doThrow(new ValidationException("")).when(validator).validate(anyString(), anyString()); + + mockMvc.perform(post("/simulator/start").content(WRONG_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("Cannot start simulator - Json format " + + "is not compatible with schema definitions")); + verify(validator).validate(anyString(), anyString()); + } + + @Test + void startSimulatorProperly() throws Exception { + startSimulator(); + + verify(validator).validate(anyString(), anyString()); + verify(factory).create(any(),any(), any(),any()); + verify(simulator).start(); + } + + @Test + void notStartWhenAlreadyRunning() throws Exception { + startSimulator(); + + mockMvc + .perform(post(START_URL).content(PROPER_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath(JSON_MSG_EXPRESSION).value("Cannot start simulator since it's already running")); + } + + @Test + void stopSimulatorWhenRunning() throws Exception { + startSimulator(); + + mockMvc + .perform(post(STOP_URL)) + .andExpect(status().isOk()) + .andExpect(jsonPath(JSON_MSG_EXPRESSION).value("Simulator successfully stopped")); + } + + @Test + void getNotRunningMessageWhenOff() throws Exception { + mockMvc + .perform(post(STOP_URL)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath(JSON_MSG_EXPRESSION).value("Cannot stop simulator, because it's not running")); + } + + @Test + void getRunningStatusWhenOn() throws Exception { + startSimulator(); + + mockMvc + .perform(get(STATUS_URL)) + .andExpect(status().isOk()) + .andExpect(jsonPath(JSON_STATUS_EXPRESSION).value("RUNNING")); + } + + @Test + void getNotRunningStatusWhenOff() throws Exception { + mockMvc + .perform(get(STATUS_URL)) + .andExpect(status().isOk()) + .andExpect(jsonPath(JSON_STATUS_EXPRESSION).value("NOT RUNNING")); + } + + private void startSimulator() throws Exception { + when(factory.create(any(), any(), any(),any())).thenReturn(simulator); + + mockMvc + .perform(post(START_URL).content(PROPER_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath(JSON_MSG_EXPRESSION).value("Simulator started")); + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java new file mode 100644 index 0000000..99b9af7 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/DateUtilTest.java @@ -0,0 +1,38 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.rest.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import org.junit.jupiter.api.Test; + +class DateUtilTest { + + @Test + void getFormattedDate() { + Calendar currentCalendar = Calendar.getInstance(); + String expectedResult = String.valueOf(currentCalendar.get(Calendar.YEAR)); + + assertEquals(expectedResult, DateUtil.getTimestamp(new SimpleDateFormat("yyyy"))); + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java new file mode 100644 index 0000000..59e1e3b --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/rest/util/ResponseBuilderTest.java @@ -0,0 +1,65 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.rest.util; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class ResponseBuilderTest { + + + private static final HttpStatus SAMPLE_STATUS = HttpStatus.OK; + + @Test + void response_should_have_empty_body_when_built_immediately() { + ResponseEntity responseEntity = ResponseBuilder.status(SAMPLE_STATUS).build(); + + assertAll( + () -> assertEquals(responseEntity.getStatusCode(), SAMPLE_STATUS), + () -> assertNull(responseEntity.getBody()) + ); + } + + @Test + void builder_should_set_response_status_and_body() { + String key = "key"; + String value = "value"; + ResponseEntity response = ResponseBuilder + .status(SAMPLE_STATUS) + .put(key, value) + .build(); + + Map body = (Map) response.getBody(); + + assertAll( + () -> assertEquals(SAMPLE_STATUS, response.getStatusCode()), + () -> assertEquals(value, body.get(key)) + ); + } + + +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorFactoryTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorFactoryTest.java new file mode 100644 index 0000000..d8e60c1 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorFactoryTest.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator; + +import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.onap.pnfsimulator.simulator.TestMessages.INVALID_SIMULATOR_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_COMMON_EVENT_HEADER_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_NOTIFICATION_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_PNF_REGISTRATION_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_SIMULATOR_PARAMS; +import java.util.Optional; +import org.json.JSONException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SimulatorFactoryTest { + + + private SimulatorFactory simulatorFactory; + + @BeforeEach + void setUp() { + simulatorFactory = new SimulatorFactory(); + } + + @Test + void should_successfully_create_simulator_given_valid_pnf_registration_params() { + assertNotNull(simulatorFactory.create(VALID_SIMULATOR_PARAMS, VALID_COMMON_EVENT_HEADER_PARAMS, + VALID_PNF_REGISTRATION_PARAMS, Optional.empty())); + } + + @Test + void should_successfully_create_simulator_given_valid_notification_params_and_valid_output_message() { + assertNotNull(simulatorFactory.create(VALID_SIMULATOR_PARAMS, VALID_COMMON_EVENT_HEADER_PARAMS, + Optional.empty(), VALID_NOTIFICATION_PARAMS)); + } + + @Test + void should_throw_given_invalid_simulator_params() { + assertThrows( + JSONException.class, + () -> simulatorFactory.create(INVALID_SIMULATOR_PARAMS, VALID_COMMON_EVENT_HEADER_PARAMS, + VALID_PNF_REGISTRATION_PARAMS, VALID_NOTIFICATION_PARAMS)); + } +} + + diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorTest.java new file mode 100644 index 0000000..1f15c01 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/SimulatorTest.java @@ -0,0 +1,204 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator; + +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTimeout; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.onap.pnfsimulator.simulator.TestMessages.INVALID_NOTIFICATION_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.INVALID_PNF_REGISTRATION_PARAMS_1; +import static org.onap.pnfsimulator.simulator.TestMessages.INVALID_PNF_REGISTRATION_PARAMS_2; +import static org.onap.pnfsimulator.simulator.TestMessages.INVALID_PNF_REGISTRATION_PARAMS_3; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_COMMON_EVENT_HEADER_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_NOTIFICATION_PARAMS; +import static org.onap.pnfsimulator.simulator.TestMessages.VALID_PNF_REGISTRATION_PARAMS; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import org.mockito.Mockito; +import org.onap.pnfsimulator.FileProvider; +import org.onap.pnfsimulator.simulator.client.RestTemplateAdapter; +import org.onap.pnfsimulator.simulator.validation.NoRopFilesException; +import org.onap.pnfsimulator.simulator.validation.ValidationException; + +public class SimulatorTest { + + private static final String TEST_VES_URL = "http://localhost:10000/eventListener/v7"; + private static final String TEST_XNF_URL = "sftp://onap:pano@10.11.0.68" + "/"; + private FileProvider fileProvider = mock(FileProvider.class); + + private void createSampleFileList() { + List fileList = new ArrayList<>(); + fileList.add("A20190401.1608+0000-1622+0000_excl-eeiwbue-perf-large-pnf-sim-lw-1.xml.gz"); + fileList.add("A20190401.1623+0000-1637+0000_excl-eeiwbue-perf-large-pnf-sim-lw-1.xml.gz"); + + try { + doReturn(fileList).when(fileProvider).getFiles(); + } catch (NoRopFilesException e) { + e.printStackTrace(); + } + } + + @Test + void builder_should_create_endless_simulator_when_duration_not_specified() { + Simulator simulator = Simulator + .builder() + .withDuration(Duration.ofSeconds(1)) + .withVesUrl(TEST_VES_URL).build(); + + assertFalse(simulator.isEndless()); + + simulator = Simulator + .builder() + .withVesUrl(TEST_VES_URL).build(); + + assertTrue(simulator.isEndless()); + } + + @Test + void simulator_should_stop_when_interrupted() { + createSampleFileList(); + + RestTemplateAdapter httpClientMock = Mockito.mock(RestTemplateAdapter.class); + Simulator simulator = Simulator.builder() + .withInterval(Duration.ofSeconds(1)) + .withCustomRestTemplateAdapter(httpClientMock) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(Optional.empty()) + .withNotificationParams(VALID_NOTIFICATION_PARAMS) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withCustomRestTemplateAdapter(httpClientMock) + .withFileProvider(fileProvider).build(); + + simulator.start(); + simulator.interrupt(); + + assertTimeout(Duration.ofSeconds(1), (Executable) simulator::join); + } + + @Test + void should_throw_noropfiles_exception_given_empty_filelist() { + Simulator simulator = Simulator.builder() + .withDuration(Duration.ofMillis(100)) + .withInterval(Duration.ofMillis(100)) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(VALID_PNF_REGISTRATION_PARAMS) + .withNotificationParams(Optional.empty()) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withFileProvider(new FileProvider()).build(); + simulator.run(); + Exception e = simulator.getThrownException(); + assertTrue(e instanceof NoRopFilesException); + } + + @Test + void should_throw_validation_exception_given_invalid_params() { + createSampleFileList(); + + Simulator simulator = Simulator.builder() + .withDuration(Duration.ofMillis(100)) + .withInterval(Duration.ofMillis(100)) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(INVALID_PNF_REGISTRATION_PARAMS_1) + .withNotificationParams(Optional.empty()) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withFileProvider(fileProvider).build(); + simulator.run(); + Exception e = simulator.getThrownException(); + assertTrue(e instanceof ValidationException); + + simulator = Simulator.builder() + .withDuration(Duration.ofMillis(100)) + .withInterval(Duration.ofMillis(100)) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(INVALID_PNF_REGISTRATION_PARAMS_2) + .withNotificationParams(Optional.empty()) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withFileProvider(fileProvider).build(); + simulator.run(); + e = simulator.getThrownException(); + assertTrue(e instanceof ValidationException); + + simulator = Simulator.builder() + .withDuration(Duration.ofMillis(100)) + .withInterval(Duration.ofMillis(100)) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(INVALID_PNF_REGISTRATION_PARAMS_3) + .withNotificationParams(Optional.empty()) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withFileProvider(fileProvider).build(); + simulator.run(); + e = simulator.getThrownException(); + assertTrue(e instanceof ValidationException); + + simulator = Simulator.builder() + .withDuration(Duration.ofMillis(100)) + .withInterval(Duration.ofMillis(100)) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(VALID_PNF_REGISTRATION_PARAMS) + .withNotificationParams(INVALID_NOTIFICATION_PARAMS) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withFileProvider(fileProvider).build(); + simulator.run(); + e = simulator.getThrownException(); + assertTrue(e instanceof ValidationException); + } + + @Test + void simulator_should_send_fileready_message() { + createSampleFileList(); + + RestTemplateAdapter httpClientMock = Mockito.mock(RestTemplateAdapter.class); + Simulator simulator = Simulator.builder() + .withDuration(Duration.ofMillis(100)) + .withInterval(Duration.ofMillis(100)) + .withCommonEventHeaderParams(VALID_COMMON_EVENT_HEADER_PARAMS) + .withPnfRegistrationParams(Optional.empty()) + .withNotificationParams(VALID_NOTIFICATION_PARAMS) + .withVesUrl(TEST_VES_URL) + .withXnfUrl(TEST_XNF_URL) + .withCustomRestTemplateAdapter(httpClientMock) + .withFileProvider(fileProvider).build(); + simulator.run(); + Exception e = simulator.getThrownException(); + assertNull(e); + + assertTimeout(Duration.ofMillis(150), (Executable) simulator::join); + verify(httpClientMock, times(1)).send(anyString(), eq(TEST_VES_URL)); + } +} + diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/TestMessages.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/TestMessages.java new file mode 100644 index 0000000..d92b3c2 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/TestMessages.java @@ -0,0 +1,116 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; +import org.json.JSONObject; + +public final class TestMessages { + + static final JSONObject VALID_SIMULATOR_PARAMS = new JSONObject(getContent("validSimulatorParams.json")); + public static final JSONObject VALID_COMMON_EVENT_HEADER_PARAMS = new JSONObject(getContent("validCommonEventHeaderParams.json")); + static final Optional VALID_PNF_REGISTRATION_PARAMS = Optional + .of(new JSONObject(getContent("validPnfRegistrationParams.json"))); + public static final Optional VALID_NOTIFICATION_PARAMS = Optional + .of(new JSONObject(getContent("validNotificationParams.json"))); + + static final JSONObject INVALID_SIMULATOR_PARAMS = new JSONObject( + "{\n" + + " \"vesServerUrl\": \"http://10.42.111.42:8080/eventListener/v5\",\n" + + " \"messageInterval\": \"1\"\n" + + "}"); + + + static final Optional INVALID_PNF_REGISTRATION_PARAMS_1 = Optional.of(new JSONObject( + "{\n" + + " \"pnfSerialNumber\": \"val1\",\n" + + " \"pnfVendorName\": \"val2\",\n" + + " \"pnfFamily\": \"val5\",\n" + + " \"pnfModelNumber\": \"val6\",\n" + + " \"pnfSoftwareVersion\": \"val7\",\n" + + " \"pnfType\": \"val8\",\n" + + " \"eventName\": \"val9\",\n" + + " \"nfNamingCode\": \"val10\",\n" + + " \"nfcNamingCode\": \"val11\",\n" + + " \"sourceName\": \"val12\",\n" + + " \"sourceId\": \"val13\",\n" + + " \"reportingEntityName\": \"val14\"\n" + + "}")); + + static final Optional INVALID_PNF_REGISTRATION_PARAMS_2 = Optional.of(new JSONObject( + "{\n" + + " \"pnfVendorName\": \"val2\",\n" + + " \"pnfOamIpv4Address\": \"val3\",\n" + + " \"pnfOamIpv6Address\": \"val4\",\n" + + " \"pnfFamily\": \"val5\",\n" + + " \"pnfModelNumber\": \"val6\",\n" + + " \"pnfSoftwareVersion\": \"val7\",\n" + + " \"pnfType\": \"val8\",\n" + + " \"eventName\": \"val9\",\n" + + " \"nfNamingCode\": \"val10\",\n" + + " \"nfcNamingCode\": \"val11\",\n" + + " \"sourceName\": \"val12\",\n" + + " \"sourceId\": \"val13\",\n" + + " \"reportingEntityName\": \"val14\"\n" + + "}")); + + static final Optional INVALID_PNF_REGISTRATION_PARAMS_3 = Optional.of(new JSONObject( + "{\n" + + " \"pnfSerialNumber\": \"val1\",\n" + + " \"pnfOamIpv4Address\": \"val3\",\n" + + " \"pnfFamily\": \"val5\",\n" + + " \"pnfModelNumber\": \"val6\",\n" + + " \"pnfSoftwareVersion\": \"val7\",\n" + + " \"pnfType\": \"val8\",\n" + + " \"eventName\": \"val9\",\n" + + " \"nfNamingCode\": \"val10\",\n" + + " \"nfcNamingCode\": \"val11\",\n" + + " \"sourceName\": \"val12\",\n" + + " \"sourceId\": \"val13\",\n" + + " \"reportingEntityName\": \"val14\"\n" + + "}")); + + static final Optional INVALID_NOTIFICATION_PARAMS = Optional.of(new JSONObject( + "{\n" + + " \"mother\": \"val1\",\n" + + " \"father\": \"val3\",\n" + + "}")); + + + private TestMessages() { + } + + private static String getContent(String fileName) { + try { + String pathAsString = TestMessages.class.getResource(fileName).getPath(); + StringBuilder stringBuilder = new StringBuilder(); + Files.readAllLines(Paths.get(pathAsString)).forEach(line -> { + stringBuilder.append(line); + }); + return stringBuilder.toString(); + } catch (IOException e) { + throw new RuntimeException(String.format("Cannot read JSON file %s", fileName)); + } + } +} diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImplTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImplTest.java new file mode 100644 index 0000000..d423222 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/client/RestTemplateAdapterImplTest.java @@ -0,0 +1,134 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator.client; + +import org.mockito.Mock; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.notNull; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RestTemplateAdapterImplTest { + + private RestTemplateAdapter adapter; + + @Mock + private RestTemplate restTemplate; + @Mock + ResponseEntity response; + + @BeforeEach + void setup() { + initMocks(this); + adapter = new RestTemplateAdapterImpl(restTemplate); + } + + @Test + void send_should_successfully_send_request_given_valid_http_url() + throws HttpClientErrorException { + + String content = "test-msg"; + String urlHttp = "http://valid-url"; + + when( + restTemplate.postForEntity( + (String) notNull(), + (HttpEntity) notNull(), + eq(String.class) + ) + ).thenReturn(response); + + adapter.send(content, urlHttp); + + verify( + restTemplate, times(1)).postForEntity( + (String) notNull(), + (HttpEntity) notNull(), + eq(String.class) + ); + verify(response).getStatusCode(); + } + + @Test + void send_should_successfully_send_request_given_valid_https_url() + throws HttpClientErrorException { + + String content = "test-msg"; + String urlHttps = "https://valid-url"; + + when( + restTemplate.postForEntity( + (String) notNull(), + (HttpEntity) notNull(), + eq(String.class) + ) + ).thenReturn(response); + + adapter.send(content, urlHttps); + + verify( + restTemplate, times(1)).postForEntity( + (String) notNull(), + (HttpEntity) notNull(), + eq(String.class) + ); + verify(response).getStatusCode(); + } + + @Test + void send_should_not_send_request_given_invalid_url() + throws HttpClientErrorException { + + String content = "test-msg"; + String url = "http://invalid-url"; + + doThrow( + new HttpClientErrorException(HttpStatus.BAD_REQUEST)).when( + restTemplate).postForEntity( + (String) notNull(), + (HttpEntity) notNull(), + eq(String.class) + ); + + adapter.send(content, url); + + verify( + restTemplate, times(1)).postForEntity( + (String) notNull(), + (HttpEntity) notNull(), + eq(String.class) + ); + verify(response, never()).getStatusCode(); + } +} diff --git a/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/validation/JSONValidatorTest.java b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/validation/JSONValidatorTest.java new file mode 100644 index 0000000..30dfe06 --- /dev/null +++ b/pnf-sim-lightweight/src/test/java/org/onap/pnfsimulator/simulator/validation/JSONValidatorTest.java @@ -0,0 +1,123 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2018 NOKIA Intellectual Property. 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.pnfsimulator.simulator.validation; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.github.fge.jsonschema.core.exceptions.InvalidSchemaException; +import com.github.fge.jsonschema.core.exceptions.ProcessingException; +import java.io.IOException; +import java.net.URL; +import org.json.JSONObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class JSONValidatorTest { + + private final static String VALID_SCHEMA_NAME = "valid-test-schema.json"; + private final static String INVALID_SCHEMA_NAME = "invalid-test-schema.json"; + + private JSONValidator validator; + + @BeforeEach + void setUp() { + validator = new JSONValidator(); + } + + @Test + void validate_should_not_throw_given_valid_json() throws ProcessingException, IOException, ValidationException { + validator.validate(getValidJsonString(), getResourcePath(VALID_SCHEMA_NAME)); + } + + @Test + void validate_should_not_throw_when_optional_parameter_missing() + throws ProcessingException, IOException, ValidationException { + + String invalidJsonString = new JSONObject() + .put("key1", "value1") + .put("key2", "value2") + .toString(); + + validator.validate(invalidJsonString, getResourcePath(VALID_SCHEMA_NAME)); + } + + @Test + void validate_should_throw_when_mandatory_parameter_missing() { + + String invalidJsonString = new JSONObject() + .put("key1", "value1") + .put("key3", "value3") + .toString(); + + assertThrows( + ValidationException.class, + () -> validator.validate(invalidJsonString, getResourcePath(VALID_SCHEMA_NAME))); + } + + @Test + void validate_should_throw_when_invalid_json_format() { + String invalidJsonString = "{" + + "\"key1\": \"value1\"" + + "\"key2\": \"value2" + + "}"; + + assertThrows( + IOException.class, + () -> validator.validate(invalidJsonString, getResourcePath(VALID_SCHEMA_NAME))); + } + + @Test + void validate_should_throw_when_invalid_schema_format() { + assertThrows( + InvalidSchemaException.class, + () -> validator.validate(getValidJsonString(), getResourcePath(INVALID_SCHEMA_NAME))); + } + + @Test + void validate_should_throw_when_invalid_schema_path() { + + assertThrows( + IOException.class, + () -> validator.validate(getValidJsonString(), "/not/existing/path/schema.json")); + } + + private String getResourcePath(String schemaFileName) { + URL result = getClass() + .getClassLoader() + .getResource(schemaFileName); + + if (result == null) { + throw new IllegalArgumentException("Given file doesn't exist"); + } else { + return result + .toString() + .replace("file:", ""); + } + } + + private String getValidJsonString() { + return new JSONObject() + .put("key1", "value1") + .put("key2", "value2") + .put("key3", "value3") + .toString(); + } +} \ No newline at end of file diff --git a/pnf-sim-lightweight/src/test/resources/invalid-test-schema.json b/pnf-sim-lightweight/src/test/resources/invalid-test-schema.json new file mode 100644 index 0000000..8c37c82 --- /dev/null +++ b/pnf-sim-lightweight/src/test/resources/invalid-test-schema.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "key1": { + "type": "string" + }, + "key2": { + "type": "string" + }, + "key3": { + "type": "string" + }, + "required": [ + "key1", + "key2" + ] + } +} diff --git a/pnf-sim-lightweight/src/test/resources/logback-test.xml b/pnf-sim-lightweight/src/test/resources/logback-test.xml new file mode 100644 index 0000000..d7966fe --- /dev/null +++ b/pnf-sim-lightweight/src/test/resources/logback-test.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + %nopexception%logger + |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC} + |%level + |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'} + |%thread + |%n + + + + + + %nopexception%logger + |%date{yyyy-MM-dd'T'HH:mm:ss.SSSXXX,UTC} + |%level + |%replace(%replace(%message){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%mdc){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%rootException){'\t','\\\\t'}){'\n','\\\\n'} + |%replace(%replace(%marker){'\t','\\\\t'}){'\n','\\\\n'} + |%thread + |%n + + ${log-path}/${outputFilename}.log + + ${log-path}/${outputFilename}.%d{yyyy-MM-dd}.%i.log.zip + ${maxFileSize} + ${maxHistory} + ${totalSizeCap} + + + + + + + + diff --git a/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validCommonEventHeaderParams.json b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validCommonEventHeaderParams.json new file mode 100644 index 0000000..54faff8 --- /dev/null +++ b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validCommonEventHeaderParams.json @@ -0,0 +1,5 @@ +{ + "eventName": "Noti_RnNode-Ericsson_FileReady", + "nfNamingCode": "gNB", + "nfcNamingCode": "oam" +} diff --git a/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validNotificationParams.json b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validNotificationParams.json new file mode 100644 index 0000000..af0cdf4 --- /dev/null +++ b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validNotificationParams.json @@ -0,0 +1,4 @@ +{ + "changeIdentifier": "PM_MEAS_FILES", + "changeType": "FileReady" +} diff --git a/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validPnfRegistrationParams.json b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validPnfRegistrationParams.json new file mode 100644 index 0000000..5b15286 --- /dev/null +++ b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validPnfRegistrationParams.json @@ -0,0 +1,10 @@ +{ + "serialNumber": "6061ZW3", + "vendorName": "Nokia", + "oamV4IpAddress": "val3", + "oamV6IpAddress": "val4", + "unitFamily": "BBU", + "modelNumber": "val6", + "softwareVersion": "val7", + "unitType": "val8" +} diff --git a/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validSimulatorParams.json b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validSimulatorParams.json new file mode 100644 index 0000000..19e1b86 --- /dev/null +++ b/pnf-sim-lightweight/src/test/resources/org/onap/pnfsimulator/simulator/validSimulatorParams.json @@ -0,0 +1,4 @@ +{ + "testDuration": "10", + "messageInterval": "1" +} diff --git a/pnf-sim-lightweight/src/test/resources/valid-test-schema.json b/pnf-sim-lightweight/src/test/resources/valid-test-schema.json new file mode 100644 index 0000000..26e48a5 --- /dev/null +++ b/pnf-sim-lightweight/src/test/resources/valid-test-schema.json @@ -0,0 +1,19 @@ +{ + "type": "object", + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "key1": { + "type": "string" + }, + "key2": { + "type": "string" + }, + "key3": { + "type": "string" + } + }, + "required": [ + "key1", + "key2" + ] +} diff --git a/pnf-sim-lightweight/templates/.gitattributes b/pnf-sim-lightweight/templates/.gitattributes new file mode 100644 index 0000000..f7d3c00 --- /dev/null +++ b/pnf-sim-lightweight/templates/.gitattributes @@ -0,0 +1 @@ +*.gz binary \ No newline at end of file diff --git a/pnf-sim-lightweight/templates/file_template_new.xml.gz b/pnf-sim-lightweight/templates/file_template_new.xml.gz new file mode 100644 index 0000000..1ec9ef4 Binary files /dev/null and b/pnf-sim-lightweight/templates/file_template_new.xml.gz differ diff --git a/pnf-sim-lightweight/tls/README.md b/pnf-sim-lightweight/tls/README.md new file mode 100644 index 0000000..5edfedd --- /dev/null +++ b/pnf-sim-lightweight/tls/README.md @@ -0,0 +1,4 @@ +# To verify the certificate expiration dates: + +openssl x509 -enddate -noout -in dfc.crt +openssl x509 -enddate -noout -in ftp.crt diff --git a/pnf-sim-lightweight/tls/dfc.crt b/pnf-sim-lightweight/tls/dfc.crt new file mode 100644 index 0000000..f747f20 --- /dev/null +++ b/pnf-sim-lightweight/tls/dfc.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFjCCAf4CCQCqH10CLXcbUDANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJT +RTEKMAgGA1UECAwBMDEKMAgGA1UEBwwBMDEKMAgGA1UECgwBMDEMMAoGA1UECwwD +RVNUMQwwCgYDVQQDDANFU1QwHhcNMTkwNDA0MDgwMjQwWhcNMjAwNDAzMDgwMjQw +WjBNMQswCQYDVQQGEwJTRTEKMAgGA1UECAwBMDEKMAgGA1UEBwwBMDEKMAgGA1UE +CgwBMDEMMAoGA1UECwwDRVNUMQwwCgYDVQQDDANFU1QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDAY7wM9khd7OIaZsfZv8EzWUnLTepzSx6hlAvjSbU/ +ZBpq94QOJWt22hQQSHA+Vdl8184PoyOKX+qRfbKJCtyArbv0DoWjlv16WNs938A/ +0TGFh+6xA464/GQmP/AXEuI0tSa2GEgXkhZ0uy6Pmdq+8sD6YcRyVCeqLTq8bqSq +YbpPrKnbZsd3l7PzpYCZgZLfWoENQ7nuT+C7j4pGGnPKpGn4ubiscV3nTI6nwU19 +ANexToikIL1v18z+gJdva8QtKih5dt9+2QJuJ6lPcwTa7xVkMmuVyr5FXwL11yII +gKRtknWObU7BjcKkQOG3xnWXZTNzLV/th8GCCWJuRFPbAgMBAAEwDQYJKoZIhvcN +AQELBQADggEBAGZa23j04vghZY+/81FyRsoeJs8o9aDeK5KLT9JUpRaZlu4AEXSN +I3oEhencocc07DTndfxw7eSALnTD9ibGUxnRyEvgtW595ajb7A3TtEKGsFzXFvar +y9RdVm6RRA4Bty8UyLW75ZpAlfc3twCmsRwKjShXeN90Yn3sjpcEU83q8fZBU35C +xrgrW34gphPtuzZ7kvCJGjhYIfIsTcWObvDGUROtlRhb8w2v1K4G3UcfwDTBixK/ +e61mMUbhoqVyoMYgSuU4idY+n4Evjs5IwalXM1daUZmCGGZoreD/9aSpDEeUWnQy +vqYC0YY0VJkOe+WI6JZ2r49BtRtl/jxWDUI= +-----END CERTIFICATE----- diff --git a/pnf-sim-lightweight/tls/ftp.crt b/pnf-sim-lightweight/tls/ftp.crt new file mode 100644 index 0000000..f412d01 --- /dev/null +++ b/pnf-sim-lightweight/tls/ftp.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDFjCCAf4CCQDaiGveWOXqNjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQGEwJT +RTEKMAgGA1UECAwBMDEKMAgGA1UEBwwBMDEKMAgGA1UECgwBMDEMMAoGA1UECwwD +RVNUMQwwCgYDVQQDDANFU1QwHhcNMTkwNDA0MDgwMjQwWhcNMjAwNDAzMDgwMjQw +WjBNMQswCQYDVQQGEwJTRTEKMAgGA1UECAwBMDEKMAgGA1UEBwwBMDEKMAgGA1UE +CgwBMDEMMAoGA1UECwwDRVNUMQwwCgYDVQQDDANFU1QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDSsF7lN/gXpevQpGj8W/3g3h9AVOE83Z49yJAdyIHF +PQz6PI+bKutYdORCUZkzsl2fegLzkXl4CmoVIkJRBL1SZkzQXKe+fjfuRr9PQKCC +lp/LA161Qak+9pz2Oc1lfLbgEdv22RLji3akCQso3G9vlx+rLHPRgbew0iiTViJP +v3CHwiY89t1ai149OEywhjsJBJjBoj6fvxfvv46QmK7FuV5Tz0vTL/eB/Z9P7jm+ +twHRz9Ae4s97c6UhbFKafHLrwdMK+yz7qe55tpgthCgoedeSB0gXWIiS7RY18dEN +JUB/FAt64LfOiKBl0aUbcQOgUinorhCN8gcNTn7Hrn+1AgMBAAEwDQYJKoZIhvcN +AQELBQADggEBAKVGHkTLe5R/fG/C7prxiknD+QXo9WACcZNVKMuKhpJwQh1iwc4h +4tq9lj//giyRrt+yPVQF8pRTiIdeewLVyf5O1ugxzb68UtHzVJWD6ooFqGmyPFkm +WOdLvtgbasGPZvO6y8HZA3kxKgONbYcL0sdtRWpp5b+KTEyvN/50jAtvkB/wlaee +emgdRdsSVZqg1p8dUfF6j3Alzsuff7YzEZEZPoJKYdb1vikvj21+LdzTDSj5WRno +PWXQhdTTqN5/TNMZRHJp/UZY6hVmQL+ILqVYGiOPotfxGNUyo+WsKJwZfZnq4adh +BzdSIIsDCZB34Njz/qjIXh307/seNWWhNFw= +-----END CERTIFICATE----- diff --git a/pnf-sim-lightweight/tls/ftp.key b/pnf-sim-lightweight/tls/ftp.key new file mode 100644 index 0000000..f90c781 --- /dev/null +++ b/pnf-sim-lightweight/tls/ftp.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA0rBe5Tf4F6Xr0KRo/Fv94N4fQFThPN2ePciQHciBxT0M+jyP +myrrWHTkQlGZM7Jdn3oC85F5eApqFSJCUQS9UmZM0Fynvn437ka/T0CggpafywNe +tUGpPvac9jnNZXy24BHb9tkS44t2pAkLKNxvb5cfqyxz0YG3sNIok1YiT79wh8Im +PPbdWotePThMsIY7CQSYwaI+n78X77+OkJiuxbleU89L0y/3gf2fT+45vrcB0c/Q +HuLPe3OlIWxSmnxy68HTCvss+6nuebaYLYQoKHnXkgdIF1iIku0WNfHRDSVAfxQL +euC3zoigZdGlG3EDoFIp6K4QjfIHDU5+x65/tQIDAQABAoIBAEs+G5XG6D4hzlbD +8I53l/JvwT9rUMk46GNuNjG8wsOa6wCPEkY7DLOZg08/7vOsdo0WuOkdggDhz0Le +6koe5DICQNqEzI9WakkZUQdPsEMS4dxRxsf6vCO1VRcGS5k78d+R4TmGCp3i16r7 +Y9Xi65UxpmiuRmqC5gQq+bysnTJXKUhK/3NCPa7Bwo7hgASJcI55Nk58KHokIv84 +7dweKuIIxeKAR4/094q243lTu1n273J+ckjVMWWZROIIn2E+CrjAVh59DaC7QX6d +kWdPwvjYiwH2LBqzJ3dKvLmk6XZZ5bcjmBYXSiS1ahjQ8zhDdLoHBt/gDXcmtOVw +gZPjgn0CgYEA6nJGMig2SRB25CqtA/gwLbQV54th0Vxj1SapoO+W4jIUEPBRJN1t +0JkM9mTMWWBiEzZi4ICpJpgT/+iGXx6q6WZZqOvbWWS4yjuC+wLCttt2yriFkmlA +eylz0rYTGm5gQ3wpAUuf0wOfqVICfQ2KnASY0p8g2fSjOI0/pULpX18CgYEA5g72 +UVspmBLqaA/PGYip/7neh00sGs95b9Wh1UqvcrstxkgR4LyMOReTBNwL+39kyZPv +LNsfMtaiezIfSF+o77RiXnBsYQ/ZnKBNZ7cTRr76f5fKStzRSzHoQrf+98YkSfg3 +sI1vYH/hnfbd/6ti8Wiloc7O28IHCwG4vGXWPWsCgYEA4Oqaj1nmonfKJ6ENdSZw +Shhff1BHmovxNrctuzi/Xue+OnXi0uQfiySZu/P926zMyjO97jVgkacKYNMZvj10 +qA/J6nXDbOJlKZaoVNlUJgsrztXxRwH0m3OsKzUD5LKJZZTC3fxIKy9pyA3mV0Rb +eswqNL32zUKWKBXSPmCP9S8CgYEAoJIk3dfRCjF5pjQSinHWpYRniuwUMopI6gOj +tqkwF9YJNvGqeL9g56BKnwOu4VSjVgaI0zgFaIhiU7ZCG1OZJ+UxG1VAb+aOLkG8 +hP1E2QYG9PNO4D2LXv5wa9DchrenMHGs/O9ao3HeWVCq1f4bSv+NS4h63Otp2wbS +l7xEcg0CgYBcrapVzHfGn73Z9tgs7GOVEmoXKoT6Q8yrdwwhVrcWdDHHtLFPso21 +eA9dhbbawaEmIReWtNKvo+Q2kxn+WcLIL6jWaASsJH4cnnRl58IYcUv8cb6o6G34 +nTkWuhXm5XBUupacr8lqz5bXtoRBU7BcSnqXd20dTR5QEHU5Rrg/uw== +-----END RSA PRIVATE KEY----- diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..99d722c --- /dev/null +++ b/setup.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +# COPYRIGHT NOTICE STARTS HERE +# +# Copyright 2020 Samsung Electronics Co., Ltd. +# +# 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. +# +# COPYRIGHT NOTICE ENDS HERE + +# This file is only meant to be a single source of truth for package +# dependencies. It's consumed by bin/setup.sh and tox hence shouldn't +# be run directly for package builds as currently vcpe scripts are not +# provided as a python package. + +import setuptools + +# Define MassPnfSim module dependencies below +setuptools.setup( + name="MassPnfSim", + install_requires=[ + 'argparse', + 'ipaddress', + 'requests', + 'pyyaml', + 'docker' + ] +) diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..34481df --- /dev/null +++ b/setup.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Setup runtime environment for the Python scripts + +virtualenv --version > /dev/null 2>&1 || { echo 'Virtualenv command is not available, exiting'; exit 1; } +pip3 --version > /dev/null 2>&1 || { echo 'python3-pip package is not available, exiting' ; exit 1; } +tox --version > /dev/null 2>&1 || { echo 'tox command is not available, exiting' ; exit 1; } + +tox -e MassPnfSim-runtime +echo -e "\n\nNow run:\nsource .tox/MassPnfSim-runtime/bin/activate" diff --git a/test_cli.py b/test_cli.py new file mode 100644 index 0000000..06d018f --- /dev/null +++ b/test_cli.py @@ -0,0 +1,75 @@ +import pytest +from MassPnfSim import MassPnfSim +from test_settings import SIM_INSTANCES + +@pytest.mark.parametrize(('expect_string, cli_opts'), [ + ("bootstrap: error: the following arguments are required: --urlves, --ipfileserver, --typefileserver, " +\ + "--user, --password, --ipstart", + ['bootstrap']), + ("bootstrap: error: argument --typefileserver: invalid choice: 'dummy' (choose from 'sftp', 'ftps')", + ['bootstrap', '--typefileserver', 'dummy']), + ("bootstrap: error: argument --urlves: invalid_url is not a valid URL", + ['bootstrap', '--urlves', 'invalid_url']), + ("bootstrap: error: argument --ipstart: x.x.x.x is not a valid IP address", + ['bootstrap', '--ipstart', 'x.x.x.x']), + ("trigger_custom: error: the following arguments are required: --triggerstart, --triggerend", + ['trigger_custom']) + ]) +def test_subcommands(parser, capsys, expect_string, cli_opts): + try: + parser.parse_args(cli_opts) + except SystemExit: + pass + assert expect_string in capsys.readouterr().err + +def test_validate_trigger_custom(parser, caplog): + args = parser.parse_args(['trigger_custom', '--triggerstart', '0', + '--triggerend', str(SIM_INSTANCES)]) + try: + MassPnfSim().trigger_custom(args) + except SystemExit as e: + assert e.code == 1 + assert "--triggerend value greater than existing instance count" in caplog.text + caplog.clear() + +@pytest.mark.parametrize(("subcommand"), [ + 'bootstrap', + 'start', + 'stop', + 'trigger', + 'status', + 'stop_simulator' + ]) +def test_count_option(parser, capsys, subcommand): + '''Test case where no arg passed to '--count' opt''' + try: + parser.parse_args([subcommand, '--count']) + except SystemExit: + pass + assert f"{subcommand}: error: argument --count: expected one argument" in capsys.readouterr().err + +@pytest.mark.parametrize(("subcommand"), [ + 'start', + 'stop', + 'trigger', + 'status', + 'stop_simulator' + ]) +def test_count_option_bad_value(parser, caplog, subcommand): + '''Test case where invalid value passed to '--count' opt''' + try: + args = parser.parse_args([subcommand, '--count', str(SIM_INSTANCES + 1)]) + m = getattr(MassPnfSim(), subcommand) + m(args) + except SystemExit: + pass + assert '--count value greater that existing instance count' in caplog.text + caplog.clear() + +def test_empty(parser, capsys): + try: + parser.parse_args([]) + except SystemExit: + pass + assert '' is capsys.readouterr().err + assert '' is capsys.readouterr().out diff --git a/test_lifecycle.py b/test_lifecycle.py new file mode 100644 index 0000000..1e040d3 --- /dev/null +++ b/test_lifecycle.py @@ -0,0 +1,201 @@ +from MassPnfSim import MassPnfSim +from glob import glob +from os import popen, stat +from yaml import load, SafeLoader +from ipaddress import ip_address +from test_settings import * +import pytest +from time import sleep + +# These test routines perform functional testing in current file tree context +# thus they require that no simulator instances are bootstrapped and running +# prior to running tests + +@pytest.mark.parametrize("action", ['start', 'stop', 'trigger', 'status', 'stop_simulator']) +def test_not_bootstrapped(action, caplog, args_start, args_stop, args_trigger, args_status, args_stop_simulator): # pylint: disable=W0613 + try: + m = getattr(MassPnfSim(), action) + m(eval(f'args_{action}')) + except SystemExit as e: + assert e.code == 1 + assert 'No bootstrapped instance found' in caplog.text + caplog.clear() + +def test_bootstrap(args_bootstrap, caplog): + # Initial bootstrap + MassPnfSim().bootstrap(args_bootstrap) + for instance in range(SIM_INSTANCES): + assert f'Creating pnf-sim-lw-{instance}' in caplog.text + assert f'Done setting up instance #{instance}' in caplog.text + caplog.clear() + + # Verify bootstrap idempotence + try: + MassPnfSim().bootstrap(args_bootstrap) + except SystemExit as e: + assert e.code == 1 + assert 'Bootstrapped instances detected, not overwiriting, clean first' in caplog.text + caplog.clear() + + # Verify simulator dirs created + sim_dirname_pattern = MassPnfSim().sim_dirname_pattern + assert len(glob(f"{sim_dirname_pattern}*")) == SIM_INSTANCES + + # Verify simulators configs content is valid + start_port = 2000 + for instance in range(SIM_INSTANCES): + instance_ip_offset = instance * 16 + ip_offset = 2 + with open(f"{sim_dirname_pattern}{instance}/{INSTANCE_CONFIG}") as f: + yml = load(f, Loader=SafeLoader) + assert URLVES == yml['urlves'] + assert TYPEFILESERVER == yml['typefileserver'] + assert f'sftp://{USER}:{PASSWORD}@{IPFILESERVER}:{start_port + 1}' in yml['urlsftp'] + assert f'ftps://{USER}:{PASSWORD}@{IPFILESERVER}:{start_port + 2}' in yml['urlftps'] + assert str(ip_address(IPSTART) + ip_offset + instance_ip_offset) == yml['ippnfsim'] + start_port += 2 + print(yml['ippnfsim']) + + # Verify vsftpd config file has proper permissions + for cfg in glob(f'{sim_dirname_pattern}*/config/vsftpd_ssl.conf'): + assert stat(cfg).st_uid == 0 + +def test_bootstrap_status(args_status, caplog): + MassPnfSim().status(args_status) + for _ in range(SIM_INSTANCES): + assert 'Simulator containers are down' in caplog.text + assert 'Simulator response' not in caplog.text + caplog.clear() + +def test_start(args_start, caplog): + MassPnfSim().start(args_start) + for instance in range(SIM_INSTANCES): + instance_ip_offset = instance * 16 + ip_offset = 2 + assert f'Starting pnf-sim-lw-{instance} instance:' in caplog.text + assert f'PNF-Sim IP: {str(ip_address(IPSTART) + ip_offset + instance_ip_offset)}' in caplog.text + assert 'Starting simulator containers' in caplog.text + assert f"ROP_file_creator.sh {instance} successfully started" in caplog.text + assert f"3GPP measurements file generator for instance {instance} is already running" not in caplog.text + # Verify ROP_file_creator.sh running + assert f"ROP_file_creator.sh {instance}" in popen('ps afx').read() + caplog.clear() + +def test_start_status(args_status, docker_containers, caplog): + sleep(5) # Wait for the simulator to settle + MassPnfSim().status(args_status) + for instance in range(SIM_INSTANCES): + assert '"simulatorStatus":"NOT RUNNING"' in caplog.text + assert '"simulatorStatus":"RUNNING"' not in caplog.text + assert f"{PNF_SIM_CONTAINER_NAME}{instance}" in docker_containers + caplog.clear() + +def test_start_idempotence(args_start, caplog): + '''Verify start idempotence''' + MassPnfSim().start(args_start) + assert 'containers are already up' in caplog.text + assert 'Starting simulator containers' not in caplog.text + assert f"is already running" in caplog.text + caplog.clear() + +def test_trigger(args_trigger, caplog): + MassPnfSim().trigger(args_trigger) + for instance in range(SIM_INSTANCES): + instance_ip_offset = instance * 16 + ip_offset = 2 + assert f'Triggering pnf-sim-lw-{instance} instance:' in caplog.text + assert f'PNF-Sim IP: {str(ip_address(IPSTART) + ip_offset + instance_ip_offset)}' in caplog.text + assert 'Simulator started' in caplog.text + caplog.clear() + +def test_trigger_status(args_status, capfd, caplog): + MassPnfSim().status(args_status) + msg = capfd.readouterr() + for _ in range(SIM_INSTANCES): + assert '"simulatorStatus":"RUNNING"' in caplog.text + assert '"simulatorStatus":"NOT RUNNING"' not in caplog.text + assert 'Up' in msg.out + assert 'Exit' not in msg.out + caplog.clear() + +def test_trigger_idempotence(args_trigger, caplog): + MassPnfSim().trigger(args_trigger) + assert "Cannot start simulator since it's already running" in caplog.text + assert 'Simulator started' not in caplog.text + caplog.clear() + +def test_stop_simulator(args_stop_simulator, caplog): + MassPnfSim().stop_simulator(args_stop_simulator) + for instance in range(SIM_INSTANCES): + instance_ip_offset = instance * 16 + ip_offset = 2 + assert f'Stopping pnf-sim-lw-{instance} instance:' in caplog.text + assert f'PNF-Sim IP: {str(ip_address(IPSTART) + ip_offset + instance_ip_offset)}' in caplog.text + assert "Simulator successfully stopped" in caplog.text + assert "not running" not in caplog.text + caplog.clear() + +def test_stop_simulator_status(args_status, capfd, caplog): + MassPnfSim().status(args_status) + msg = capfd.readouterr() + for _ in range(SIM_INSTANCES): + assert '"simulatorStatus":"RUNNING"' not in caplog.text + assert '"simulatorStatus":"NOT RUNNING"' in caplog.text + assert 'Up' in msg.out + assert 'Exit' not in msg.out + caplog.clear() + +def test_stop_simulator_idempotence(args_stop_simulator, caplog): + MassPnfSim().stop_simulator(args_stop_simulator) + for instance in range(SIM_INSTANCES): + instance_ip_offset = instance * 16 + ip_offset = 2 + assert f'Stopping pnf-sim-lw-{instance} instance:' in caplog.text + assert f'PNF-Sim IP: {str(ip_address(IPSTART) + ip_offset + instance_ip_offset)}' in caplog.text + assert "Cannot stop simulator, because it's not running" in caplog.text + assert "Simulator successfully stopped" not in caplog.text + caplog.clear() + +def test_trigger_custom(args_trigger_custom, caplog): + MassPnfSim().trigger_custom(args_trigger_custom) + for instance in range(SIM_INSTANCES): + instance_ip_offset = instance * 16 + ip_offset = 2 + assert f'Triggering pnf-sim-lw-{instance} instance:' in caplog.text + assert f'PNF-Sim IP: {str(ip_address(IPSTART) + ip_offset + instance_ip_offset)}' in caplog.text + assert 'Simulator started' in caplog.text + assert "Cannot start simulator since it's already running" not in caplog.text + caplog.clear() + +def test_stop(args_stop, caplog): + MassPnfSim().stop(args_stop) + for instance in range(SIM_INSTANCES): + instance_ip_offset = instance * 16 + ip_offset = 2 + assert f'Stopping pnf-sim-lw-{instance} instance:' in caplog.text + assert f'PNF-Sim IP: {str(ip_address(IPSTART) + ip_offset + instance_ip_offset)}' in caplog.text + assert f'ROP_file_creator.sh {instance} successfully killed' in caplog.text + assert f"ROP_file_creator.sh {instance}" not in popen('ps afx').read() + caplog.clear() + +def test_stop_status(args_status, docker_containers, caplog): + MassPnfSim().status(args_status) + for instance in range(SIM_INSTANCES): + assert f"{PNF_SIM_CONTAINER_NAME}{instance}" not in docker_containers + assert 'Simulator containers are down' in caplog.text + caplog.clear() + +def test_stop_idempotence(args_stop, caplog, docker_containers): + MassPnfSim().stop(args_stop) + for instance in range(SIM_INSTANCES): + assert f'Stopping pnf-sim-lw-{instance} instance:' in caplog.text + assert f'ROP_file_creator.sh {instance} already not running' in caplog.text + assert 'Simulator containers are already down' in caplog.text + assert f"ROP_file_creator.sh {instance}" not in popen('ps afx').read() + assert f"{PNF_SIM_CONTAINER_NAME}{instance}" not in docker_containers + caplog.clear() + +def test_clean(args_clean): + m = MassPnfSim() + m.clean(args_clean) + assert not glob(f"{m.sim_dirname_pattern}*") diff --git a/test_settings.py b/test_settings.py new file mode 100644 index 0000000..fe88e31 --- /dev/null +++ b/test_settings.py @@ -0,0 +1,9 @@ +SIM_INSTANCES = 2 +URLVES = 'http://127.0.0.1:10000/eventListener/v7' +IPFILESERVER = '127.0.0.1' +TYPEFILESERVER = 'sftp' +USER = 'testuser' +PASSWORD = 'testpassword' +IPSTART = '10.11.0.1' +INSTANCE_CONFIG = 'config/config.yml' +PNF_SIM_CONTAINER_NAME = 'pnf-simulator-' diff --git a/tox.ini b/tox.ini index 9a019f4..3ba56ca 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 3.2.0 -envlist = json,yaml,py,md +envlist = json,yaml,py,md,pytest skipsdist = true requires = pip >= 8 @@ -42,3 +42,14 @@ commands_pre = /bin/sh -c "git --no-pager diff HEAD HEAD^ --name-only '*.md' > /tmp/.coalist_md" commands = /bin/bash -c "coala --non-interactive --disable-caching --no-autoapply-warn md --files $(