summaryrefslogtreecommitdiffstats
path: root/vnftest
diff options
context:
space:
mode:
Diffstat (limited to 'vnftest')
-rw-r--r--vnftest/__init__.py8
-rw-r--r--vnftest/common/exceptions.py4
-rw-r--r--vnftest/common/openstack_utils.py51
-rw-r--r--vnftest/common/rest_client.py15
-rw-r--r--vnftest/common/utils.py131
-rw-r--r--vnftest/contexts/base.py2
-rw-r--r--vnftest/core/task.py11
-rwxr-xr-xvnftest/crawlers/base.py15
-rw-r--r--vnftest/crawlers/default.py8
-rw-r--r--vnftest/dispatcher/base.py2
-rw-r--r--vnftest/onap/__init__.py7
-rw-r--r--vnftest/onap/lifecycle/__init__.py0
-rw-r--r--vnftest/onap/lifecycle/create_vf_module.yaml6
-rw-r--r--vnftest/onap/onap_api_call.py52
-rw-r--r--vnftest/onap/steps/__init__.py0
-rw-r--r--vnftest/onap/steps/validation/__init__.py0
-rw-r--r--vnftest/onap/steps/validation/aai_get_vf_module.yaml25
-rw-r--r--vnftest/onap/steps/validation/vf_module_validator.py63
-rw-r--r--vnftest/openstack/__init__.py0
-rw-r--r--vnftest/openstack/steps/__init__.py0
-rw-r--r--vnftest/openstack/steps/heat.py54
-rw-r--r--vnftest/openstack/steps/nova.py58
-rwxr-xr-xvnftest/runners/base.py4
-rw-r--r--vnftest/steps/base.py6
-rw-r--r--vnftest/tests/unit/common/test_utils.py33
-rw-r--r--vnftest/tests/unit/core/no_constraint_with_args_step_sample.yaml5
-rw-r--r--vnftest/tests/unit/core/test_task.py17
27 files changed, 474 insertions, 103 deletions
diff --git a/vnftest/__init__.py b/vnftest/__init__.py
index 212c153..9ce7a86 100644
--- a/vnftest/__init__.py
+++ b/vnftest/__init__.py
@@ -21,6 +21,7 @@ import errno
# not require loggers to be created, so this cannot
# include vnftest.common.utils
from vnftest.common import constants
+import vnftest.common.utils as utils
try:
# do not use vnftest.common.utils.makedirs
@@ -59,3 +60,10 @@ def _init_logging():
logging.root.addHandler(_LOG_STREAM_HDLR)
logging.root.addHandler(_LOG_FILE_HDLR)
logging.debug("logging.root.handlers = %s", logging.root.handlers)
+
+
+utils.import_modules_from_package("vnftest.contexts")
+utils.import_modules_from_package("vnftest.runners")
+utils.import_modules_from_package("vnftest.steps")
+utils.import_modules_from_package("vnftest.crawlers")
+utils.import_modules_from_package("vnftest.openstack")
diff --git a/vnftest/common/exceptions.py b/vnftest/common/exceptions.py
index 85cb203..9e29c52 100644
--- a/vnftest/common/exceptions.py
+++ b/vnftest/common/exceptions.py
@@ -43,3 +43,7 @@ class MandatoryKeyException(VnftestException):
class InputParameterMissing(VnftestException):
message_tmplate = 'No value found for parameter "{param_name}" in "{source}"'
+
+
+class ResourceNotFound(VnftestException):
+ message_tmplate = 'Resource not found "{resource}"'
diff --git a/vnftest/common/openstack_utils.py b/vnftest/common/openstack_utils.py
index c97c1c2..829b916 100644
--- a/vnftest/common/openstack_utils.py
+++ b/vnftest/common/openstack_utils.py
@@ -27,6 +27,7 @@ from cinderclient import client as cinderclient
from novaclient import client as novaclient
from glanceclient import client as glanceclient
from neutronclient.neutron import client as neutronclient
+from heatclient.client import Client as heatclient
log = logging.getLogger(__name__)
@@ -159,6 +160,11 @@ def get_neutron_client(): # pragma: no cover
return neutronclient.Client(get_neutron_client_version(), session=sess)
+def get_heat_client(): # pragma: no cover
+ sess = get_session()
+ return heatclient(get_heat_api_version(), session=sess)
+
+
def get_glance_client_version(): # pragma: no cover
try:
api_version = os.environ['OS_IMAGE_API_VERSION']
@@ -199,6 +205,14 @@ def get_instance_by_name(nova_client, instance_name): # pragma: no cover
instance_name)
+def get_instance_by_id(instance_id): # pragma: no cover
+ try:
+ return get_nova_client().servers.find(id=instance_id)
+ except Exception:
+ log.exception("Error [get_instance_by_id(nova_client, '%s')]",
+ instance_id)
+
+
def get_aggregates(nova_client): # pragma: no cover
try:
return nova_client.aggregates.list()
@@ -440,6 +454,15 @@ def delete_keypair(nova_client, key): # pragma: no cover
# *********************************************
# NEUTRON
# *********************************************
+def get_network_by_name(network_name): # pragma: no cover
+ try:
+ networks = get_neutron_client().list_networks()['networks']
+ return next((n for n in networks if n['name'] == network_name), None)
+ except Exception:
+ log.exception("Error [get_instance_by_id(nova_client, '%s')]",
+ network_name)
+
+
def get_network_id(neutron_client, network_name): # pragma: no cover
networks = neutron_client.list_networks()['networks']
return next((n['id'] for n in networks if n['name'] == network_name), None)
@@ -760,3 +783,31 @@ def detach_volume(server_id, volume_id): # pragma: no cover
log.exception("Error [detach_server_volume(nova_client, '%s', '%s')]",
server_id, volume_id)
return False
+# *********************************************
+# HEAT
+# *********************************************
+
+
+def get_stack(heat_stack_id): # pragma: no cover
+ try:
+ client = get_heat_client()
+ return client.stacks.get(heat_stack_id)
+ except Exception as e:
+ log.exception("Error [get_stack(heat_stack_id)]", e)
+
+
+def get_stack_resources(heat_stack_id): # pragma: no cover
+ try:
+ client = get_heat_client()
+ return client.resources.list(heat_stack_id)
+ except Exception as e:
+ log.exception("Error [get_stack_resources(heat_stack_id)]", e)
+
+
+def get_stack_vms(heat_stack_id): # pragma: no cover
+ resources = get_stack_resources(heat_stack_id)
+ ret_vms = []
+ for resource in resources:
+ if resource.resource_type == "OS::Nova::Server":
+ ret_vms.append(resource)
+ return ret_vms
diff --git a/vnftest/common/rest_client.py b/vnftest/common/rest_client.py
index 23a108c..051f5dd 100644
--- a/vnftest/common/rest_client.py
+++ b/vnftest/common/rest_client.py
@@ -14,9 +14,17 @@
##############################################################################
import json
+
+import logging
+import os
import urllib2
import requests
+from vnftest.common import utils
+
+logger = logging.getLogger(__name__)
+os.putenv('PYTHONHTTPSVERIFY', "0")
+
def post(url, headers, data, logger):
return call(url, 'POST', headers, data, logger)
@@ -31,10 +39,15 @@ def call(url, method, headers, data, logger):
f = urllib2.urlopen(req)
return_code = f.code
response_body = f.read()
+ headers = f.headers
+ content_type = headers.dict['content-type'] if 'content-type' in headers.dict else 'application/json'
f.close()
if len(str(response_body)) == 0:
response_body = "{}"
- response_body = json.loads(response_body)
+ if 'application/xml' in content_type:
+ response_body = utils.xml_to_dict(response_body)
+ else:
+ response_body = json.loads(response_body)
result = {'return_code': return_code, 'body': response_body}
return result
diff --git a/vnftest/common/utils.py b/vnftest/common/utils.py
index ad1b2f3..406796d 100644
--- a/vnftest/common/utils.py
+++ b/vnftest/common/utils.py
@@ -15,10 +15,13 @@
# yardstick/common/utils.py
import collections
+import formatter
from contextlib import closing
import datetime
import errno
import importlib
+from string import Formatter
+
import ipaddress
import logging
import os
@@ -27,16 +30,21 @@ import socket
import subprocess
import sys
+import pkg_resources
import six
from flask import jsonify
from six.moves import configparser
from oslo_serialization import jsonutils
-
+import xml.etree.ElementTree
import vnftest
+from vnftest.common.exceptions import ResourceNotFound
+
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
+class_implementations = {}
+
# Decorator for cli-args
def cliargs(*args, **kwargs):
@@ -46,23 +54,24 @@ def cliargs(*args, **kwargs):
return _decorator
-def itersubclasses(cls, _seen=None):
- """Generator over all subclasses of a given class in depth first order."""
+def findsubclasses(cls):
+ if cls.__name__ not in class_implementations:
+ # Load entrypoint classes just once.
+ if len(class_implementations) == 0:
+ for entrypoint in pkg_resources.iter_entry_points(group='vnftest.extension'):
+ loaded_type = entrypoint.load()
+ logger.info("Loaded: " + str(loaded_type))
- if not isinstance(cls, type):
- raise TypeError("itersubclasses must be called with "
- "new-style classes, not %.100r" % cls)
- _seen = _seen or set()
- try:
- subs = cls.__subclasses__()
- except TypeError: # fails only when cls is type
- subs = cls.__subclasses__(cls)
- for sub in subs:
- if sub not in _seen:
- _seen.add(sub)
- yield sub
- for sub in itersubclasses(sub, _seen):
- yield sub
+ subclasses = []
+ class_implementations[cls.__name__] = subclasses
+
+ def getallnativesubclasses(clazz):
+ for subclass in clazz.__subclasses__():
+ subclasses.append(subclass)
+ getallnativesubclasses(subclass)
+
+ getallnativesubclasses(cls)
+ return class_implementations[cls.__name__]
def import_modules_from_package(package):
@@ -431,3 +440,91 @@ class dotdict(dict):
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
+
+
+def normalize_data_struct(obj):
+ if isinstance(obj, basestring):
+ return [obj]
+ if isinstance(obj, list):
+ nomalized_list = []
+ for element in obj:
+ element = normalize_data_struct(element)
+ nomalized_list.append(element)
+ return nomalized_list
+ if isinstance(obj, dict):
+ normalized_dict = {}
+ for k, v in obj:
+ v = normalize_data_struct(v)
+ normalized_dict[k] = v
+ return normalized_dict
+ return change_obj_to_dict(obj)
+
+
+def xml_to_dict(xml_str):
+ return element_tree_to_dict(xml.etree.ElementTree.fromstring(xml_str))
+
+
+def element_tree_to_dict(element_tree):
+ def internal_iter(tree, accum):
+ if tree is None:
+ return accum
+ attribute_target = None
+ if tree.getchildren():
+ accum[tree.tag] = {}
+ attribute_target = accum[tree.tag]
+ for each in tree.getchildren():
+ result = internal_iter(each, {})
+ if each.tag in accum[tree.tag]:
+ if not isinstance(accum[tree.tag][each.tag], list):
+ accum[tree.tag][each.tag] = [
+ accum[tree.tag][each.tag]
+ ]
+ accum[tree.tag][each.tag].append(result[each.tag])
+ else:
+ accum[tree.tag].update(result)
+ else:
+ attribute_target = accum
+ accum[tree.tag] = tree.text
+ # Add attributes
+ attributes = tree.attrib or {}
+ for att_name, att_value in attributes.iteritems():
+ attribute_target[att_name] = att_value
+
+ return accum
+
+ return internal_iter(element_tree, {})
+
+
+def resource_as_string(path):
+ split_path = os.path.split(path)
+ package = split_path[0].replace("/", ".")
+ if not pkg_resources.resource_exists(package, split_path[1]):
+ raise ResourceNotFound(resource=path)
+ return pkg_resources.resource_string(package, split_path[1])
+
+
+def load_resource(path):
+ split_path = os.path.split(path)
+ package = split_path[0].replace("/", ".")
+ if not pkg_resources.resource_exists(package, split_path[1]):
+ raise ResourceNotFound(resource=path)
+ return pkg_resources.resource_stream(package, split_path[1])
+
+
+def format(st, params):
+ if not isinstance(st, basestring):
+ return st
+ ret_str = ""
+ ret_obj = None
+ for literal_text, field_name, format_spec, conversion in \
+ Formatter().parse(st):
+ if field_name is None:
+ ret_str = ret_str + literal_text
+ else:
+ dict = ret_obj or params
+ value = dict[field_name]
+ if isinstance(value, basestring):
+ ret_str = ret_str + value
+ else:
+ ret_obj = value
+ return ret_obj or ret_str
diff --git a/vnftest/contexts/base.py b/vnftest/contexts/base.py
index abfef57..47dbf01 100644
--- a/vnftest/contexts/base.py
+++ b/vnftest/contexts/base.py
@@ -51,7 +51,7 @@ class Context(object):
@staticmethod
def get_cls(context_type):
"""Return class of specified type."""
- for context in utils.itersubclasses(Context):
+ for context in utils.findsubclasses(Context):
if context_type == context.__context_type__:
return context
raise RuntimeError("No such context_type %s" % context_type)
diff --git a/vnftest/core/task.py b/vnftest/core/task.py
index 939696c..5e0267f 100644
--- a/vnftest/core/task.py
+++ b/vnftest/core/task.py
@@ -52,7 +52,6 @@ from vnftest.common import constants
from vnftest.common.html_template import report_template
output_file_default = "/tmp/vnftest.out"
-test_cases_dir_default = "tests/onap/test_cases/"
LOG = logging.getLogger(__name__)
@@ -395,12 +394,6 @@ class TaskParser(object): # pragma: no cover
self._check_schema(cfg["schema"], "suite")
LOG.info("\nStarting step:%s", cfg["name"])
- test_cases_dir = cfg.get("test_cases_dir", test_cases_dir_default)
- test_cases_dir = os.path.join(constants.VNFTEST_ROOT_PATH,
- test_cases_dir)
- if test_cases_dir[-1] != os.sep:
- test_cases_dir += os.sep
-
cur_pod = os.environ.get('NODE_NAME', None)
cur_installer = os.environ.get('INSTALLER_TYPE', None)
@@ -418,7 +411,7 @@ class TaskParser(object): # pragma: no cover
continue
# 2.check constraint
if self._meet_constraint(task, cur_pod, cur_installer):
- valid_task_files.append(test_cases_dir + task_fname)
+ valid_task_files.append(task_fname)
else:
continue
# 3.fetch task parameters
@@ -444,7 +437,7 @@ class TaskParser(object): # pragma: no cover
raise TypeError()
try:
- with open(self.path) as f:
+ with utils.load_resource(self.path) as f:
try:
input_task = f.read()
rendered_task = TaskTemplate.render(input_task, **kw)
diff --git a/vnftest/crawlers/base.py b/vnftest/crawlers/base.py
index 8b5a526..c7813e8 100755
--- a/vnftest/crawlers/base.py
+++ b/vnftest/crawlers/base.py
@@ -25,7 +25,7 @@ class Crawler(object):
@staticmethod
def get_cls(crawler_type):
"""return class of specified type"""
- for crawler in utils.itersubclasses(Crawler):
+ for crawler in utils.findsubclasses(Crawler):
if crawler_type == crawler.__crawler_type__:
return crawler
raise RuntimeError("No such crawler_type %s" % crawler_type)
@@ -35,3 +35,16 @@ class Crawler(object):
def crawl(self, dictionary, path):
raise NotImplementedError
+
+ @staticmethod
+ def crawl(json_as_dict, output_config):
+ output = {}
+ for output_parameter in output_config:
+ param_name = output_parameter['parameter_name']
+ param_value = output_parameter.get('value', "[]")
+ crawler_type = output_parameter.get('type', 'default')
+ crawler_class = Crawler.get_cls(crawler_type)
+ crawler = crawler_class()
+ param_value = crawler.crawl(json_as_dict, param_value)
+ output[param_name] = param_value
+ return output
diff --git a/vnftest/crawlers/default.py b/vnftest/crawlers/default.py
index da4df0a..74f9554 100644
--- a/vnftest/crawlers/default.py
+++ b/vnftest/crawlers/default.py
@@ -13,6 +13,9 @@
##############################################################################
from __future__ import absolute_import
+
+from vnftest.common.exceptions import MandatoryKeyException
+
from vnftest.crawlers import base
import logging
@@ -23,6 +26,9 @@ class DefaultCrawler(base.Crawler):
__crawler_type__ = 'default'
def crawl(self, dictionary, path):
+ if path.find("[") < 0:
+ return path # the path is a hardcoded value
+
path_list = path.split("[")
value = dictionary
for path_element in path_list:
@@ -32,4 +38,6 @@ class DefaultCrawler(base.Crawler):
if isinstance(value, list):
path_element = int(path_element)
value = value[path_element]
+ if value is None:
+ raise MandatoryKeyException(key_name='param_path', class_name=str(dictionary))
return value
diff --git a/vnftest/dispatcher/base.py b/vnftest/dispatcher/base.py
index 133b792..e53dd96 100644
--- a/vnftest/dispatcher/base.py
+++ b/vnftest/dispatcher/base.py
@@ -30,7 +30,7 @@ class Base(object):
@staticmethod
def get_cls(dispatcher_type):
"""Return class of specified type."""
- for dispatcher in utils.itersubclasses(Base):
+ for dispatcher in utils.findsubclasses(Base):
if dispatcher_type == dispatcher.__dispatcher_type__:
return dispatcher
raise RuntimeError("No such dispatcher_type %s" % dispatcher_type)
diff --git a/vnftest/onap/__init__.py b/vnftest/onap/__init__.py
index 7382128..293d41b 100644
--- a/vnftest/onap/__init__.py
+++ b/vnftest/onap/__init__.py
@@ -11,10 +11,3 @@
# See the License for the specific language governing permissions and limitations under
# the License
##############################################################################
-
-from __future__ import absolute_import
-import vnftest.common.utils as utils
-
-utils.import_modules_from_package("vnftest.benchmark.contexts")
-utils.import_modules_from_package("vnftest.benchmark.runners")
-utils.import_modules_from_package("vnftest.benchmark.steps")
diff --git a/vnftest/onap/lifecycle/__init__.py b/vnftest/onap/lifecycle/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vnftest/onap/lifecycle/__init__.py
diff --git a/vnftest/onap/lifecycle/create_vf_module.yaml b/vnftest/onap/lifecycle/create_vf_module.yaml
index 1728a07..5499ee7 100644
--- a/vnftest/onap/lifecycle/create_vf_module.yaml
+++ b/vnftest/onap/lifecycle/create_vf_module.yaml
@@ -40,6 +40,12 @@ body:
requestParameters:
usePreload: true
+ userParams:
+ {% for user_parameter in user_parameters %}
+ -
+ name: {{user_parameter.name}}
+ value: {{user_parameter.value}}
+ {% endfor %}
cloudConfiguration:
lcpCloudRegionId: RegionOne
diff --git a/vnftest/onap/onap_api_call.py b/vnftest/onap/onap_api_call.py
index 417e259..9ab95e7 100644
--- a/vnftest/onap/onap_api_call.py
+++ b/vnftest/onap/onap_api_call.py
@@ -12,7 +12,6 @@
# the License
##############################################################################
from __future__ import absolute_import
-
import copy
import logging
import time
@@ -20,7 +19,7 @@ import time
import os
import yaml
-from vnftest.common import constants as consts
+from vnftest.common import constants as consts, utils
from vnftest.common import rest_client
from vnftest.common.utils import dotdict
from vnftest.common.exceptions import MandatoryKeyException, InputParameterMissing
@@ -44,10 +43,10 @@ class OnapApiCall(base.Step):
self.input_params = input_params
self.input_cfg = None
self.output_cfg = None
+
self.rest_def_file = None
self.delay = None
self.setup_done = False
- self.curr_path = os.path.dirname(os.path.abspath(__file__))
def setup(self):
options = self.step_cfg['options']
@@ -68,7 +67,7 @@ class OnapApiCall(base.Step):
value = None
if 'value' in input_parameter:
value_def = input_parameter['value']
- value = self.format_string(value_def, self.input_params)
+ value = utils.format(value_def, self.input_params)
if value is None or value == "":
raise InputParameterMissing(param_name=param_name, source="task configuration")
params[param_name] = value
@@ -92,23 +91,12 @@ class OnapApiCall(base.Step):
def run_impl(self, result):
if not self.setup_done:
self.setup()
- output = {}
params = copy.deepcopy(consts.component_constants)
self.eval_input(params)
execution_result = self.execute_operation(params)
result_body = execution_result['body']
- for output_parameter in self.output_cfg:
- param_name = output_parameter['parameter_name']
- param_value = output_parameter.get('value', "[]")
- if param_value.find("[") > -1:
- crawler_type = output_parameter.get('type', 'default')
- crawler_class = Crawler.get_cls(crawler_type)
- crawler = crawler_class()
- param_value = crawler.crawl(result_body, param_value)
- if param_value is None:
- raise MandatoryKeyException(key_name='param_path', class_name=str(result_body))
- result[param_name] = param_value
- output[param_name] = param_value
+ output = Crawler.crawl(result_body, self.output_cfg)
+ result.update(output)
return output
def execute_operation(self, params, attempt=0):
@@ -129,10 +117,8 @@ class OnapApiCall(base.Step):
def execute_operation_impl(self, params):
operation = self.load_file(params)
url = operation['url']
- headers = operation['headers']
- body = {}
- if 'body' in operation:
- body = operation['body']
+ headers = operation.get('headers', {}) or {}
+ body = operation.get('body', {}) or {}
LOG.info(url)
LOG.info(headers)
LOG.info(body)
@@ -153,30 +139,14 @@ class OnapApiCall(base.Step):
LOG.info("Results: " + str(result))
return result
- @staticmethod
- def format_string(st, params):
- if not isinstance(st, basestring):
- return st
- try:
- return st.format(**params)
- except Exception as e:
- s = str(e)
- s = s.replace("'", "")
- LOG.info(s)
- params[s] = ""
- LOG.info("param" + params[s])
- return st.format(**params)
-
def handle_sla(self, output):
if self.sla_cfg.get('action', "") == 'assert' and 'equals' in self.sla_cfg:
value_def = self.sla_cfg['value']
- value = self.format_string(value_def, output)
+ value = utils.format(value_def, output)
expected_value = self.sla_cfg['equals']
assert value == expected_value
def load_file(self, params):
- yaml_path = os.path.join(self.curr_path, self.rest_def_file)
- with open(yaml_path) as f:
- operation_template = f.read()
- operation = jinja2.Template(operation_template).render(**params)
- return yaml.load(operation)
+ operation_template = utils.resource_as_string(self.rest_def_file)
+ operation = jinja2.Template(operation_template).render(**params)
+ return yaml.load(operation)
diff --git a/vnftest/onap/steps/__init__.py b/vnftest/onap/steps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vnftest/onap/steps/__init__.py
diff --git a/vnftest/onap/steps/validation/__init__.py b/vnftest/onap/steps/validation/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vnftest/onap/steps/validation/__init__.py
diff --git a/vnftest/onap/steps/validation/aai_get_vf_module.yaml b/vnftest/onap/steps/validation/aai_get_vf_module.yaml
new file mode 100644
index 0000000..e57ba15
--- /dev/null
+++ b/vnftest/onap/steps/validation/aai_get_vf_module.yaml
@@ -0,0 +1,25 @@
+##############################################################################
+# Copyright 2018 EuropeanSoftwareMarketingLtd.
+# ===================================================================
+# Licensed under the ApacheLicense, Version2.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
+#
+# 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
+##############################################################################
+
+---
+method: GET
+
+url: http://{{aai_ip}}:30232/aai/v11/network/generic-vnfs/generic-vnf/{{vnf_instance_id}}/vf-modules/vf-module/{{vf_module_instance_id}}
+headers:
+ Content-Type: application/json
+ Accept: application/json
+ Authorization: Basic QUFJOkFBSQ==
+ X-FromAppId: AAI
+
+body:
diff --git a/vnftest/onap/steps/validation/vf_module_validator.py b/vnftest/onap/steps/validation/vf_module_validator.py
new file mode 100644
index 0000000..63caf58
--- /dev/null
+++ b/vnftest/onap/steps/validation/vf_module_validator.py
@@ -0,0 +1,63 @@
+##############################################################################
+# Copyright 2018 EuropeanSoftwareMarketingLtd.
+# ===================================================================
+# Licensed under the ApacheLicense, Version2.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
+#
+# 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
+##############################################################################
+
+from __future__ import absolute_import
+
+from vnftest.common import openstack_utils, utils
+
+from vnftest.onap.onap_api_call import OnapApiCall
+import logging
+
+from vnftest.steps import base
+
+LOG = logging.getLogger(__name__)
+
+
+class VfModuleValidator(base.Step):
+ __step_type__ = "VfModuleValidator"
+
+ def __init__(self, step_cfg, context, input_params):
+ self.validation_cfg = step_cfg
+ self.context = context
+ self.input_params = input_params
+ self.vnf_instance_id = None
+ self.vf_module_instance_id = None
+
+ def setup(self):
+ options = self.validation_cfg['options']
+ vnf_instance_id_def = options.get("vnf_instance_id")
+ self.vnf_instance_id = utils.format(vnf_instance_id_def, self.input_params)
+ vf_module_instance_id_def = options.get("vf_module_instance_id")
+ self.vf_module_instance_id = utils.format(vf_module_instance_id_def, self.input_params)
+
+ def run(self, result):
+ heat_stack_id = self.get_heat_stack_id()
+ vm_resources = openstack_utils.get_stack_vms(heat_stack_id)
+ for resource in vm_resources:
+ assert resource.resource_status == 'CREATE_COMPLETE', "Unexpected VM status: " + str(resource.resource_status)
+
+ # Get the heat stack id from AAI
+ def get_heat_stack_id(self):
+ step_conf = {}
+ step_conf['file'] = "aai_get_vf_module.yaml"
+ step_conf['input'] = [{'parameter_name': 'vnf_instance_id',
+ 'value': self.vnf_instance_id},
+ {'parameter_name': 'vf_module_instance_id',
+ 'value': self.vf_module_instance_id}
+ ]
+ step_conf['output'] = {'heat_stack_id': '[heat-stack-id]'}
+ onap_api_call = OnapApiCall(step_conf, self.context, self.input_params)
+ output = onap_api_call.run({})
+ return output['heat_stack_id']
+
diff --git a/vnftest/openstack/__init__.py b/vnftest/openstack/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vnftest/openstack/__init__.py
diff --git a/vnftest/openstack/steps/__init__.py b/vnftest/openstack/steps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/vnftest/openstack/steps/__init__.py
diff --git a/vnftest/openstack/steps/heat.py b/vnftest/openstack/steps/heat.py
new file mode 100644
index 0000000..2d5822c
--- /dev/null
+++ b/vnftest/openstack/steps/heat.py
@@ -0,0 +1,54 @@
+##############################################################################
+# Copyright 2018 EuropeanSoftwareMarketingLtd.
+# ===================================================================
+# Licensed under the ApacheLicense, Version2.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
+#
+# 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
+##############################################################################
+
+from __future__ import absolute_import
+
+from vnftest.crawlers.base import Crawler
+
+from vnftest.common import openstack_utils, utils
+
+import logging
+
+from vnftest.steps import base
+
+LOG = logging.getLogger(__name__)
+
+
+class Heat(base.Step):
+ __step_type__ = "Heat"
+
+ def __init__(self, step_cfg, context, input_params):
+ self.step_cfg = step_cfg
+ self.context = context
+ self.input_params = input_params
+ self.output_cfg = None
+ self.operation = None
+ self.heat_stack_id = None
+
+ def setup(self):
+ options = self.step_cfg['options']
+ self.operation = options.get("operation")
+ self.output_cfg = options.get("output", {})
+ heat_stack_id_def = options.get("heat_stack_id")
+ self.heat_stack_id = utils.format(heat_stack_id_def, self.input_params)
+
+ def run(self, result):
+ op_result = getattr(self, self.operation)()
+ op_result = utils.normalize_data_struct(op_result)
+ output = Crawler.crawl(op_result, self.output_cfg)
+ result.update(output)
+ return output
+
+ def get_stack_vms(self):
+ return openstack_utils.get_stack_vms(self.heat_stack_id)
diff --git a/vnftest/openstack/steps/nova.py b/vnftest/openstack/steps/nova.py
new file mode 100644
index 0000000..fad5d7d
--- /dev/null
+++ b/vnftest/openstack/steps/nova.py
@@ -0,0 +1,58 @@
+##############################################################################
+# Copyright 2018 EuropeanSoftwareMarketingLtd.
+# ===================================================================
+# Licensed under the ApacheLicense, Version2.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
+#
+# 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
+##############################################################################
+
+from __future__ import absolute_import
+
+from vnftest.crawlers.base import Crawler
+
+from vnftest.common import openstack_utils, utils
+
+import logging
+
+from vnftest.steps import base
+
+LOG = logging.getLogger(__name__)
+
+
+class Nova(base.Step):
+ __step_type__ = "Nova"
+
+ def __init__(self, step_cfg, context, input_params):
+ self.step_cfg = step_cfg
+ self.context = context
+ self.input_params = input_params
+ self.output_cfg = None
+ self.operation = None
+ self.resource_id = None
+
+ def setup(self):
+ options = self.step_cfg['options']
+ self.operation = options.get("operation")
+ self.output_cfg = options.get("output", {})
+ resource_id_def = options.get("resource_id")
+ self.resource_id = utils.format(resource_id_def, self.input_params)
+
+ def run(self, result):
+ op_result = getattr(self, self.operation)()
+ op_result = utils.normalize_data_struct(op_result)
+ output = Crawler.crawl(op_result, self.output_cfg)
+ result.update(output)
+ return output
+
+ def get_vm_external_ip(self):
+ instance = openstack_utils.get_instance_by_id(self.resource_id)
+ for network_name, ip_list in instance.networks.iteritems():
+ network = openstack_utils.get_network_by_name(network_name)
+ if network['router:external']:
+ return ip_list[0]
diff --git a/vnftest/runners/base.py b/vnftest/runners/base.py
index f13d0d6..7310d9b 100755
--- a/vnftest/runners/base.py
+++ b/vnftest/runners/base.py
@@ -82,7 +82,7 @@ class Runner(object):
@staticmethod
def get_cls(runner_type):
"""return class of specified type"""
- for runner in utils.itersubclasses(Runner):
+ for runner in utils.findsubclasses(Runner):
if runner_type == runner.__execution_type__:
return runner
raise RuntimeError("No such runner_type %s" % runner_type)
@@ -91,7 +91,7 @@ class Runner(object):
def get_types():
"""return a list of known runner type (class) names"""
types = []
- for runner in utils.itersubclasses(Runner):
+ for runner in utils.findsubclasses(Runner):
types.append(runner)
return types
diff --git a/vnftest/steps/base.py b/vnftest/steps/base.py
index d5c606a..454ab81 100644
--- a/vnftest/steps/base.py
+++ b/vnftest/steps/base.py
@@ -39,14 +39,14 @@ class Step(object):
def get_types():
"""return a list of known runner type (class) names"""
steps = []
- for step in utils.itersubclasses(Step):
+ for step in utils.findsubclasses(Step):
steps.append(step)
return steps
@staticmethod
def get_cls(step_type):
"""return class of specified type"""
- for step in utils.itersubclasses(Step):
+ for step in utils.findsubclasses(Step):
if step_type == step.__step_type__:
return step
@@ -56,7 +56,7 @@ class Step(object):
def get(step_type):
"""Returns instance of a step runner for execution type.
"""
- for step in utils.itersubclasses(Step):
+ for step in utils.findsubclasses(Step):
if step_type == step.__step_type__:
return step.__module__ + "." + step.__name__
diff --git a/vnftest/tests/unit/common/test_utils.py b/vnftest/tests/unit/common/test_utils.py
index da64d4e..e8860cb 100644
--- a/vnftest/tests/unit/common/test_utils.py
+++ b/vnftest/tests/unit/common/test_utils.py
@@ -49,7 +49,7 @@ class IterSubclassesTestCase(unittest.TestCase):
class D(C):
pass
- self.assertEqual([B, C, D], list(utils.itersubclasses(A)))
+ self.assertEqual([B, C, D], list(utils.findsubclasses(A)))
class ImportModulesFromPackageTestCase(unittest.TestCase):
@@ -1123,3 +1123,34 @@ class ReadMeminfoTestCase(unittest.TestCase):
'Active(anon)': '3015676',
'HugePages_Total': '8',
'Hugepagesize': '1048576'}
+
+
+class TestUtils(unittest.TestCase):
+
+ def test_convert_xml_to_dict(self):
+ input_str = "<a><b>dummy1</b><b>dummy2</b></a>"
+ result = utils.xml_to_dict(input_str)
+ self.assertEqual(result, {'a': {'b': ['dummy1', 'dummy2']}})
+
+ def test_format(self):
+ input_str = "{aaa}"
+ params = {'aaa': 'dummy'}
+ result = utils.format(input_str, params)
+ self.assertEqual(result, "dummy")
+
+ def test_obj_to_dict(self):
+ dummy_class = DummyClass()
+ result = utils.normalize_data_struct(dummy_class)
+ self.assertEqual(result, {'aaa': 'aaa', 'bbb': ["1", "2"], 'ccc': {"x": "y"}})
+
+ def test_load_resource(self):
+ input_str = "vnftest/tests/unit/common/config_sample.yaml"
+ resource = utils.load_resource(input_str)
+ assert resource is not None
+
+
+class DummyClass(object):
+ def __init__(self):
+ self.aaa = "aaa"
+ self.bbb = ["1", "2"]
+ self.ccc = {"x": "y"}
diff --git a/vnftest/tests/unit/core/no_constraint_with_args_step_sample.yaml b/vnftest/tests/unit/core/no_constraint_with_args_step_sample.yaml
index 3667230..9b8b09a 100644
--- a/vnftest/tests/unit/core/no_constraint_with_args_step_sample.yaml
+++ b/vnftest/tests/unit/core/no_constraint_with_args_step_sample.yaml
@@ -19,12 +19,11 @@
schema: "vnftest:suite:0.1"
name: "suite_1"
-test_cases_dir: "tests/onap/test_cases/"
test_cases:
-
- file_name: onap_vnftest_tc001.yaml
+ file_name: tests/onap/test_cases/onap_vnftest_tc001.yaml
-
- file_name: onap_vnftest_tc002.yaml
+ file_name: tests/onap/test_cases/onap_vnftest_tc002.yaml
task_args:
huawei-pod1: '{"host": "node1.LF","target": "node2.LF"}'
diff --git a/vnftest/tests/unit/core/test_task.py b/vnftest/tests/unit/core/test_task.py
index b136960..21947c9 100644
--- a/vnftest/tests/unit/core/test_task.py
+++ b/vnftest/tests/unit/core/test_task.py
@@ -74,10 +74,6 @@ class TaskTestCase(unittest.TestCase):
new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}):
task_files, task_args, task_args_fnames = t.parse_suite()
- self.assertEqual(task_files[0], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc001.yaml'))
- self.assertEqual(task_files[1], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc002.yaml'))
self.assertIsNone(task_args[0])
self.assertIsNone(task_args[1])
self.assertIsNone(task_args_fnames[0])
@@ -90,10 +86,6 @@ class TaskTestCase(unittest.TestCase):
new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}):
task_files, task_args, task_args_fnames = t.parse_suite()
- self.assertEqual(task_files[0], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc001.yaml'))
- self.assertEqual(task_files[1], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc002.yaml'))
self.assertIsNone(task_args[0])
self.assertEqual(task_args[1],
'{"host": "node1.LF","target": "node2.LF"}')
@@ -106,10 +98,7 @@ class TaskTestCase(unittest.TestCase):
with mock.patch.object(os, 'environ',
new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}):
task_files, task_args, task_args_fnames = t.parse_suite()
- self.assertEqual(task_files[0], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc001.yaml'))
- self.assertEqual(task_files[1], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc002.yaml'))
+
self.assertIsNone(task_args[0])
self.assertIsNone(task_args[1])
self.assertIsNone(task_args_fnames[0])
@@ -122,10 +111,6 @@ class TaskTestCase(unittest.TestCase):
new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}):
task_files, task_args, task_args_fnames = t.parse_suite()
- self.assertEqual(task_files[0], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc001.yaml'))
- self.assertEqual(task_files[1], self.change_to_abspath(
- 'tests/onap/test_cases/onap_vnftest_tc002.yaml'))
self.assertIsNone(task_args[0])
self.assertEqual(task_args[1],
'{"host": "node1.LF","target": "node2.LF"}')