Add PNF simulator execution and cleanup as a BaseStep 72/115372/9
authorEli Halych <illia.halych@t-mobile.pl>
Wed, 25 Nov 2020 10:52:03 +0000 (10:52 +0000)
committerEli Halych <illia.halych@t-mobile.pl>
Mon, 14 Dec 2020 15:14:25 +0000 (15:14 +0000)
1. The simulator is the masspnfsim from integration project. Added as a git submodule.
2. The flow: build image, bootstrap simulator, run simulator, stop simulator,
remove simulator, remove image (only simulator).
3. Additionally trigger (sending a message to VES) is handled in pnf/utils.py. To send a request to VES,
vesip in settings.PNF_VES_CONFIG should be set to the one accessible from simulator container. Default one
(172.17.0.1) communicates with the machine the simulator runs at.
4. commonEventHeader's fields can be overridden in settings.py (here sourceName, reportingEntityName)

Issue-ID: TEST-278
Signed-off-by: Eli Halych <illia.halych@t-mobile.pl>
Change-Id: Id77dc8c517a30458abd81f2aadc416204be522b5

.gitignore
.gitmodules [new file with mode: 0644]
requirements.txt
src/onaptests/integration [new submodule]
src/onaptests/steps/simulator/pnf/__init__.py [new file with mode: 0644]
src/onaptests/steps/simulator/pnf/pnf_config.yaml [new file with mode: 0644]
src/onaptests/steps/simulator/pnf/pnf_instantiate.py [new file with mode: 0644]
src/onaptests/steps/simulator/pnf/pnf_register.py [new file with mode: 0644]
src/onaptests/steps/simulator/pnf/utils.py [new file with mode: 0644]

index c3fd147..69cc86e 100644 (file)
@@ -31,3 +31,4 @@ csit/
 benchmark/
 .tox/
 **/__pycache__/
+*.pyc
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..bf396ae
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "src/onaptests/integration"]
+       path = src/onaptests/integration
+       url = ../../integration
index f56f12e..ead56f4 100644 (file)
@@ -3,3 +3,4 @@ openstacksdk
 onapsdk>=7.1.0
 jinja2
 kubernetes
+docker
\ No newline at end of file
diff --git a/src/onaptests/integration b/src/onaptests/integration
new file mode 160000 (submodule)
index 0000000..b019459
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit b01945987f9a0ab3ac5f4895ffd03c8e975036b4
diff --git a/src/onaptests/steps/simulator/pnf/__init__.py b/src/onaptests/steps/simulator/pnf/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/onaptests/steps/simulator/pnf/pnf_config.yaml b/src/onaptests/steps/simulator/pnf/pnf_config.yaml
new file mode 100644 (file)
index 0000000..13b0289
--- /dev/null
@@ -0,0 +1,16 @@
+setup:
+ count: 1
+ vesprotocol: http  # use `https` for the real VES
+ vesip: 172.17.0.1  # use 172.17.0.1 to communicate with localhost
+ vesport: 10000
+ vesresource: eventListener
+ vesversion: v7
+ ipstart: 10.11.0.16
+ user: ""
+ password: ""
+ ipfileserver: 127.0.0.1
+ typefileserver: sftp
+data:
+ commonEventHeaderParams:
+  sourceName: pyint-000
+  reportingEntityName: pyint-000
diff --git a/src/onaptests/steps/simulator/pnf/pnf_instantiate.py b/src/onaptests/steps/simulator/pnf/pnf_instantiate.py
new file mode 100644 (file)
index 0000000..31ea6bf
--- /dev/null
@@ -0,0 +1,30 @@
+"""Base step that runs a PNF simulator."""
+from onaptests.steps.simulator.pnf import utils
+from onaptests.steps.base import BaseStep
+
+class PNFInstanceStep(BaseStep):
+    """Run PNF simulator containers."""
+
+    @property
+    def description(self) -> str:
+        """Step description."""
+        return "Run PNF simulator containers."
+
+    @property
+    def component(self) -> str:
+        """Component name."""
+        return "Environment"
+
+    @BaseStep.store_state
+    def execute(self) -> None:
+        """Run PNF simulator containers."""
+        utils.build_image()
+        utils.bootstrap_simulator()
+        utils.run_container()
+
+    @BaseStep.store_state
+    def cleanup(self) -> None:
+        """Remove containers and images."""
+        utils.stop_container()
+        utils.remove_simulator()
+        utils.remove_image()
diff --git a/src/onaptests/steps/simulator/pnf/pnf_register.py b/src/onaptests/steps/simulator/pnf/pnf_register.py
new file mode 100644 (file)
index 0000000..8d5467a
--- /dev/null
@@ -0,0 +1,38 @@
+"""Base step that runs a PNF simulator."""
+from onaptests.steps.simulator.pnf import utils
+from onaptests.steps.base import BaseStep
+from onaptests.steps.simulator.pnf.pnf_instantiate import PNFInstanceStep
+
+class PNFRegisterStep(BaseStep):
+    """Run PNF simulator containers."""
+
+    def __init__(self, cleanup=True):
+        """Initialize step.
+
+        Substeps:
+            - PNFInstanceStep
+
+        """
+        super().__init__(cleanup=cleanup)
+        self.add_step(PNFInstanceStep(cleanup=cleanup))
+
+    @property
+    def description(self) -> str:
+        """Step description."""
+        return "Register PNF with VES."
+
+    @property
+    def component(self) -> str:
+        """Component name."""
+        return "Environment"
+
+    @BaseStep.store_state
+    def execute(self) -> None:
+        """Register with VES."""
+        super().execute()
+        utils.register()
+
+    @BaseStep.store_state
+    def cleanup(self) -> None:
+        """Substeps cleanup - no unregister."""
+        super().cleanup()
diff --git a/src/onaptests/steps/simulator/pnf/utils.py b/src/onaptests/steps/simulator/pnf/utils.py
new file mode 100644 (file)
index 0000000..3f9b233
--- /dev/null
@@ -0,0 +1,153 @@
+"""Utility functions that invoke operations of simulator script."""
+import os
+import sys
+import time
+import yaml
+from ipaddress import ip_address
+from typing import Dict, Optional
+from decorator import decorator
+import docker
+from onaptests.integration.test.mocks.masspnfsim.MassPnfSim import (
+    MassPnfSim, get_parser
+)
+
+def get_config() -> Dict:
+    """Read a config YAML file."""
+    config = None
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    with open(f"{dir_path}/pnf_config.yaml", "r") as ymlfile:
+        config = yaml.load(ymlfile)
+    return config
+
+def get_default_args() -> None:
+    """Prepare default arguments for required operations.
+
+    Returns:
+        args (argparse.Namespace): default arguments.
+
+    """
+    parser = get_parser()
+    args = parser.parse_args('')
+    return args
+
+def switch_workdir(back_pwd: str = None) -> Optional[str]:
+    """Switch work directory temporarily for PNF simulator operations.
+
+    When `back_pwd` path is provided, it means go back tp the repository
+    you came from.
+
+    Arguments:
+        back_pwd: path to go back to.
+
+    Returns:
+        old_pwd (str): previous path.
+
+    """
+    sim_file_path = sys.modules[MassPnfSim.__module__].__file__
+    sim_dir_path = os.path.dirname(sim_file_path)
+
+    old_pwd = os.getcwd()
+
+    if not back_pwd:
+        curr_pwd = sim_dir_path
+    else:
+        curr_pwd = back_pwd
+
+    os.chdir(curr_pwd)
+    return old_pwd
+
+@decorator
+def chdir(func, *args, **kwargs):
+    """Switches to and from the simulator workspace."""
+    old_pwd = switch_workdir()
+    ret = func(*args, **kwargs)
+    switch_workdir(old_pwd)
+    return ret
+
+@chdir
+def build_image() -> None:
+    """Build simulator image."""
+    build = getattr(MassPnfSim(), "build")
+    args = get_default_args()
+    build(args)
+
+@chdir
+def remove_image() -> None:
+    """Remove simulator image(s)."""
+    client = docker.from_env()
+    sim_image_name = "nexus3.onap.org:10003/onap/masspnf-simulator"
+    images = client.images.list(sim_image_name)
+    for obj in images:
+        client.images.remove(obj.id, force=True)
+
+@chdir
+def bootstrap_simulator() -> None:
+    """Setup simulator(s) repo, data and configs."""
+    args = get_default_args()
+    config = get_config()
+
+    # collect settings that will be placed in the simulator directory
+    vesprotocol = config["setup"].get('vesprotocol', "http")
+    vesip = config["setup"].get('vesip', "")
+    vesport = config["setup"].get('vesport', "")
+    vesresource = config["setup"].get('vesresource', "")
+    vesversion = config["setup"].get('vesversion', "")
+
+    urlves = f"{vesprotocol}://{vesip}:{vesport}/{vesresource}/{vesversion}"
+
+    # assign to simulator's arguments
+    args.count = config["setup"].get('count', 1)
+    args.urlves = urlves
+    args.ipstart = ip_address(config["setup"].get('ipstart', ''))
+    args.ipfileserver = config["setup"].get('ipfileserver', '')
+    args.typefileserver = config["setup"].get('typefileserver', '')
+    args.user = config["setup"].get('user', '')
+    args.password = config["setup"].get('password', '')
+
+    # bootstrap with assigned arguments
+    bootstrap = getattr(MassPnfSim(), "bootstrap")
+    bootstrap(args)
+
+@chdir
+def run_container() -> None:
+    """Run simulator container(s)."""
+    start = getattr(MassPnfSim(), "start")
+    args = get_default_args()
+    start(args)
+
+@chdir
+def register() -> None:
+    """Send an event to VES.
+
+    Use time.sleep(seconds) if registering with VES right after run_container().
+    Containers take a few seconds to run properly. Normally 5 seconds should be
+    enough.
+
+    """
+    time.sleep(5)
+    config = get_config()
+    trigger = getattr(MassPnfSim(), "trigger")
+    args = get_default_args()
+
+    args.user = config['setup'].get('user', '')
+    args.password = config['setup'].get('password', '')
+
+    custom_data = config['data']
+    if custom_data:
+        args.data = custom_data
+
+    trigger(args)
+
+@chdir
+def stop_container() -> None:
+    """Stop simulator container(s)."""
+    stop = getattr(MassPnfSim(), "stop")
+    args = get_default_args()
+    stop(args)
+
+@chdir
+def remove_simulator() -> None:
+    """Remove simulator container(s)."""
+    clean = getattr(MassPnfSim(), "clean")
+    args = get_default_args()
+    clean(args)