From ed85457dd43e9a092a8766a65e47540419b1bd7f Mon Sep 17 00:00:00 2001 From: tierno Date: Tue, 3 Oct 2017 16:18:09 +0200 Subject: [PATCH] Allow test_RO.py to consume OSM IM Change-Id: Icd8749e0138809082f6d35fb367ef46ddd8fbd0f Signed-off-by: tierno --- osm_ro/openmanoclient.py | 240 +++++++++++++++++++++++++++++------- osm_ro/vimconn_openstack.py | 1 - osm_ro/vimconn_openvim.py | 1 - test/basictest.sh | 10 +- test/test_RO.py | 141 ++++++++++++--------- 5 files changed, 284 insertions(+), 109 deletions(-) diff --git a/osm_ro/openmanoclient.py b/osm_ro/openmanoclient.py index 19a430d4..15662037 100644 --- a/osm_ro/openmanoclient.py +++ b/osm_ro/openmanoclient.py @@ -22,20 +22,22 @@ # contact with: nfvlabs@tid.es ## -''' -openmano python client used to interact with openmano-server -''' -__author__="Alfonso Tierno, Pablo Montes" -__date__ ="$09-Mar-2016 09:09:48$" -__version__="0.0.2-r468" -version_date="Feb 2017" +""" +openmano python client used to interact with openmano-server +""" import requests import json import yaml import logging import sys -if sys.version_info.major == 3: + +__author__ = "Alfonso Tierno, Pablo Montes" +__date__ = "$09-Mar-2016 09:09:48$" +__version__ = "0.1.0-r470" +version_date = "Oct 2017" + +if sys.version_info.major == 3: from urllib.parse import quote elif sys.version_info.major == 2: from urllib import quote @@ -221,24 +223,29 @@ class openmanoclient(): self.datacenter = self._get_item_uuid("datacenters", self.datacenter_id, self.datacenter_name, False) return self.datacenter - def _create_item(self, item, descriptor, all_tenants=False): + def _create_item(self, item, descriptor, all_tenants=False, api_version=None): if all_tenants: tenant_text = "/any" - elif all_tenants==None: + elif all_tenants is None: tenant_text = "" else: tenant_text = "/"+self._get_tenant() payload_req = yaml.safe_dump(descriptor) + + api_version_text = "" + if api_version: + api_version_text = "/v3" #print payload_req - URLrequest = "{}{}/{}".format(self.endpoint_url, tenant_text, item) + URLrequest = "{}{apiver}{tenant}/{item}".format(self.endpoint_url, apiver=api_version_text, tenant=tenant_text, + item=item) self.logger.debug("openmano POST %s %s", URLrequest, payload_req) - mano_response = requests.post(URLrequest, headers = self.headers_req, data=payload_req) - self.logger.debug("openmano response: %s", mano_response.text ) + mano_response = requests.post(URLrequest, headers=self.headers_req, data=payload_req) + self.logger.debug("openmano response: %s", mano_response.text) content = self._parse_yaml(mano_response.text, response=True) - if mano_response.status_code==200: + if mano_response.status_code == 200: return content else: raise OpenmanoResponseException(str(content)) @@ -592,6 +599,7 @@ class openmanoclient(): must be a dictionary or a json/yaml text. must be a dictionary or a json/yaml text. Other parameters can be: + #TODO, revise name: the vnf name. Overwrite descriptor name if any image_path: Can be a string or a string list. Overwrite the image_path at descriptor description: vnf descriptor.. Overwrite descriptor description if any @@ -608,33 +616,153 @@ class openmanoclient(): pass else: raise OpenmanoBadParamsException("Missing descriptor") - - if 'vnf' not in descriptor or len(descriptor)>2: - raise OpenmanoBadParamsException("Descriptor must contain only one 'vnf' field, and an optional version") - for param in kwargs: - if param == 'image_path': - #print args.name - try: - if isinstance(kwargs[param], str): - descriptor['vnf']['VNFC'][0]['VNFC image']=kwargs[param] - elif isinstance(kwargs[param], tuple) or isinstance(kwargs[param], list): - index=0 - for image_path_ in kwargs[param]: - #print "image-path", image_path_ - descriptor['vnf']['VNFC'][index]['VNFC image']=image_path_ - index=index+1 - else: - raise OpenmanoBadParamsException("Wrong image_path type. Expected text or a text list") - except (KeyError, TypeError) as e: - if str(e)=='vnf': error_pos= "missing field 'vnf'" - elif str(e)=='VNFC': error_pos= "missing field 'vnf':'VNFC'" - elif str(e)==str(index): error_pos= "field 'vnf':'VNFC' must be an array" - elif str(e)=='VNFC image': error_pos= "missing field 'vnf':'VNFC'['VNFC image']" - else: error_pos="wrong format" - raise OpenmanoBadParamsException("Wrong VNF descriptor: " + error_pos) + + try: + if "vnfd:vnfd-catalog" in descriptor or "vnfd-catalog" in descriptor: + api_version = "v3" + token = "vnfd" + vnfd_catalog = descriptor.get("vnfd:vnfd-catalog") + if not vnfd_catalog: + vnfd_catalog = descriptor.get("vnfd-catalog") + vnfds = vnfd_catalog.get("vnfd:vnfd") + if not vnfds: + vnfds = vnfd_catalog.get("vnfd") + vnfd = vnfds[0] + vdu_list = vnfd["vdu"] + elif "vnf" in descriptor: # old API + api_version = None + token = "vnfs" + vnfd = descriptor['vnf'] + vdu_list = vnfd["VNFC"] else: - descriptor['vnf'][param] = kwargs[param] - return self._create_item("vnfs", descriptor) + raise OpenmanoBadParamsException("Invalid VNF Descriptor must contain only one 'vnf' field or vnd-catalog") + except (KeyError, TypeError) as e: + raise OpenmanoBadParamsException("Invalid VNF Descriptor. Missing field {}".format(e)) + + if kwargs: + try: + if kwargs.get('name'): + vnfd['name'] = kwargs['name'] + if kwargs.get('description'): + vnfd['description'] = kwargs['description'] + if kwargs.get('image_path'): + error_param = 'image_path' + image_list = kwargs['image_path'].split(",") + image_item = image_list.pop(0) + # print "image-path", image_path_ + for vdu in vdu_list: + if api_version == "v3": + if vdu.get("image"): + if image_item: + vdu['image'] = image_item + if "image-checksum" in vdu: + del vdu["image-checksum"] + if image_list: + image_item = image_list.pop(0) + for vol in vdu.get("volumes", ()): # image name in volumes + if image_item: + vol["image"] = image_item + if "image-checksum" in vol: + del vol["image-checksum"] + if image_list: + image_item = image_list.pop(0) + else: + if image_item: + vdu['VNFC image'] = image_item + if "image name" in vdu: + del vdu["image name"] + if "image checksum" in vdu: + del vdu["image checksum"] + if image_list: + image_item = image_list.pop(0) + for vol in vdu.get('devices', ()): + if vol['type'] != 'disk': + continue + if image_item: + vol['image'] = image_item + if "image name" in vol: + del vol["image name"] + if "image checksum" in vol: + del vol["image checksum"] + if image_list: + image_item = image_list.pop(0) + if kwargs.get('image_name'): # image name precedes if both are supplied + error_param = 'image_name' + image_list = kwargs['image_name'].split(",") + image_item = image_list.pop(0) + for vdu in vdu_list: + if api_version == "v3": + if vdu.get("image"): + if image_item: + vdu['image'] = image_item + if "image-checksum" in vdu: + del vdu["image-checksum"] + if image_list: + image_item = image_list.pop(0) + for vol in vdu.get("volumes", ()): # image name in volumes + if image_item: + vol["image"] = image_item + if "image-checksum" in vol: + del vol["image-checksum"] + if image_list: + image_item = image_list.pop(0) + else: + if image_item: + vdu['image name'] = image_item + if "VNFC image" in vdu: + del vdu["VNFC image"] + if image_list: + image_item = image_list.pop(0) + for vol in vdu.get('devices', ()): + if vol['type'] != 'disk': + continue + if image_item: + vol['image name'] = image_item + if "image" in vol: + del vol["image"] + if "image checksum" in vol: + del vol["image checksum"] + if image_list: + image_item = image_list.pop(0) + + if kwargs.get('image_checksum'): + error_param = 'image_checksum' + image_list = kwargs['image_checksum'].split(",") + image_item = image_list.pop(0) + for vdu in vdu_list: + if api_version == "v3": + if vdu.get("image"): + if image_item: + vdu['image-checksum'] = image_item + if image_list: + image_item = image_list.pop(0) + for vol in vdu.get("volumes", ()): # image name in volumes + if image_item: + vol["mage-checksum"] = image_item + if image_list: + image_item = image_list.pop(0) + else: + if image_item: + vdu['image checksum'] = image_item + if "VNFC image" in vdu: + del vdu["VNFC image"] + if image_list: + image_item = image_list.pop(0) + for vol in vdu.get('devices', ()): + if vol['type'] != 'disk': + continue + if image_item: + vol['image checksum'] = image_item + if "image" in vol: + del vol["image"] + if image_list: + image_item = image_list.pop(0) + except IndexError: + raise OpenmanoBadParamsException("{} contains more items than {} at descriptor".format( + error_param, "vnfd-catalog:vnfd:vdu" if api_version else "vnf:VNFC")) + except (KeyError, TypeError) as e: + raise OpenmanoBadParamsException("Invalid VNF Descriptor. Missing field {}".format(e)) + return self._create_item(token, descriptor, api_version=api_version) # def edit_vnf(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs): # '''Edit the parameters of a vnf @@ -696,7 +824,7 @@ class openmanoclient(): return self._del_item("scenarios", uuid, name, all_tenants) def create_scenario(self, descriptor=None, descriptor_format=None, **kwargs): - '''Creates a scenario + """Creates a scenario Params: must supply a descriptor descriptor: with format {'scenario':{new_scenario_info}} must be a dictionary or a json/yaml text. @@ -707,7 +835,7 @@ class openmanoclient(): tenant_id. Propietary tenant Return: Raises an exception on error Obtain a dictionary with format {'scenario':{new_scenario_info}} - ''' + """ if isinstance(descriptor, str): descriptor = self.parse(descriptor, descriptor_format) elif descriptor: @@ -715,11 +843,29 @@ class openmanoclient(): else: raise OpenmanoBadParamsException("Missing descriptor") - if 'scenario' not in descriptor or len(descriptor)>2: - raise OpenmanoBadParamsException("Descriptor must contain only one 'scenario' field, and an optional version") + try: + if "nsd:nsd-catalog" in descriptor or "nsd-catalog" in descriptor: + api_version = "v3" + token = "nsd" + nsd_catalog = descriptor.get("nsd:nsd-catalog") + if not nsd_catalog: + nsd_catalog = descriptor.get("nsd-catalog") + nsds = nsd_catalog.get("nsd:nsd") + if not nsds: + nsds = nsd_catalog.get("nsd") + nsd = nsds[0] + elif "scenario" in descriptor: # old API + api_version = None + token = "scenarios" + nsd = descriptor['scenario'] + else: + raise OpenmanoBadParamsException("Invalid NS Descriptor must contain only one 'scenario' field or nsd-catalog") + except (KeyError, TypeError) as e: + raise OpenmanoBadParamsException("Invalid NS Descriptor. Missing field {}".format(e)) + for param in kwargs: - descriptor['scenario'][param] = kwargs[param] - return self._create_item("scenarios", descriptor) + nsd[param] = kwargs[param] + return self._create_item(token, descriptor, api_version=api_version) def edit_scenario(self, uuid=None, name=None, descriptor=None, descriptor_format=None, all_tenants=False, **kwargs): '''Edit the parameters of a scenario @@ -798,7 +944,7 @@ class openmanoclient(): elif descriptor: pass elif name and ("scenario_name" in kwargs or "scenario_id" in kwargs): - descriptor = {"instance":{"name": name}} + descriptor = {"instance": {"name": name}} else: raise OpenmanoBadParamsException("Missing descriptor") diff --git a/osm_ro/vimconn_openstack.py b/osm_ro/vimconn_openstack.py index c839d655..6df830dc 100644 --- a/osm_ro/vimconn_openstack.py +++ b/osm_ro/vimconn_openstack.py @@ -42,7 +42,6 @@ import netaddr import time import yaml import random -import sys import re import copy diff --git a/osm_ro/vimconn_openvim.py b/osm_ro/vimconn_openvim.py index abfffbe0..135cfc6c 100644 --- a/osm_ro/vimconn_openvim.py +++ b/osm_ro/vimconn_openvim.py @@ -695,7 +695,6 @@ class vimconnector(vimconn.vimconnector): except (requests.exceptions.RequestException, js_e.ValidationError) as e: self._format_request_exception(e) - def get_image_id_from_path(self, path): '''Get the image id from image path in the VIM database. Returns the image_id''' try: diff --git a/test/basictest.sh b/test/basictest.sh index e2169d89..6e956f0f 100755 --- a/test/basictest.sh +++ b/test/basictest.sh @@ -254,7 +254,7 @@ then elif [[ $action == "create" ]] then - for VNF in linux dataplaneVNF1 dataplaneVNF2 dataplaneVNF_2VMs dataplaneVNF_2VMs_v02 dataplaneVNF3 linux_2VMs_v02 dataplaneVNF4 v3_3vdu_vnfd + for VNF in linux dataplaneVNF1 dataplaneVNF2 dataplaneVNF_2VMs dataplaneVNF_2VMs_v02 dataplaneVNF3 linux_2VMs_v02 dataplaneVNF4 do printf "%-50s" "Creating VNF '${VNF}': " result=`$openmano vnf-create $DIRmano/vnfs/examples/${VNF}.yaml` @@ -263,6 +263,14 @@ then ! is_valid_uuid $vnf && echo FAIL && echo " $result" && $_exit 1 echo $vnf done + + printf "%-50s" "Creating VNF '${VNF}': " + result=`$openmano vnf-create $DIRmano/vnfs/examples/v3_3vdu_vnfd.yaml --image-name=cirros034` + vnf=`echo $result |gawk '{print $1}'` + #check a valid uuid is obtained + ! is_valid_uuid $vnf && echo FAIL && echo " $result" && $_exit 1 + echo $vnf + for NS in simple complex complex2 complex3 complex4 complex5 v3_3vdu_2vnf_nsd do printf "%-50s" "Creating scenario '${NS}':" diff --git a/test/test_RO.py b/test/test_RO.py index 5b47182d..0e7a5e6b 100755 --- a/test/test_RO.py +++ b/test/test_RO.py @@ -20,33 +20,34 @@ # ## -''' +""" Module for testing openmano functionality. It uses openmanoclient.py for invoking openmano -''' -__author__ = "Pablo Montes, Alfonso Tierno" -__date__ = "$16-Feb-2017 17:08:16$" -__version__ = "0.0.4" -version_date = "Jun 2017" +""" import logging import os -from argparse import ArgumentParser import argcomplete import unittest import string import inspect import random -import traceback +# import traceback import glob import yaml import sys import time -from pyvcloud.vcloudair import VCA import uuid import json +from pyvcloud.vcloudair import VCA +from argparse import ArgumentParser + +__author__ = "Pablo Montes, Alfonso Tierno" +__date__ = "$16-Feb-2017 17:08:16$" +__version__ = "0.1.0" +version_date = "Oct 2017" + +test_config = {} # used for global variables with the test configuration -global test_config # used for global variables with the test configuration -test_config = {} class test_base(unittest.TestCase): test_index = 1 @@ -279,6 +280,7 @@ class test_VIM_tenant_operations(test_base): logger.debug("{}".format(tenant)) assert ('deleted' in tenant.get('result', "")) + class test_vimconn_connect(test_base): def test_000_connect(self): @@ -1373,6 +1375,7 @@ class test_vimconn_new_tenant(test_base): self.assertEqual((context.exception).http_code, 404) + ''' IMPORTANT NOTE The following unittest class does not have the 'test_' on purpose. This test is the one used for the @@ -1381,14 +1384,13 @@ scenario based tests. class descriptor_based_scenario_test(test_base): test_index = 0 scenario_test_path = None - scenario_uuid = None - instance_scenario_uuid = None - to_delete_list = [] @classmethod def setUpClass(cls): cls.test_index = 1 cls.to_delete_list = [] + cls.scenario_uuids = [] + cls.instance_scenario_uuids = [] cls.scenario_test_path = test_config["test_directory"] + '/' + test_config["test_folder"] logger.info("{}. {} {}".format(test_config["test_number"], cls.__name__, test_config["test_folder"])) @@ -1401,55 +1403,72 @@ class descriptor_based_scenario_test(test_base): inspect.currentframe().f_code.co_name, test_config["test_folder"]) self.__class__.test_index += 1 - vnfd_files = glob.glob(self.__class__.scenario_test_path+'/vnfd_*.yaml') + # load VNFD and NSD + descriptor_files = glob.glob(self.__class__.scenario_test_path+'/*.yaml') + vnf_descriptors = [] + scenario_descriptors = [] + for descriptor_file in descriptor_files: + with open(descriptor_file, 'r') as stream: + descriptor = yaml.load(stream) + if "vnf" in descriptor or "vnfd:vnfd-catalog" in descriptor or "vnfd-catalog" in descriptor: + vnf_descriptors.append(descriptor) + else: + scenario_descriptors.append(descriptor) + scenario_file = glob.glob(self.__class__.scenario_test_path + '/scenario_*.yaml') - if len(vnfd_files) == 0 or len(scenario_file) > 1: + if not vnf_descriptors or not scenario_descriptors or len(scenario_descriptors) > 1: raise Exception("Test '{}' not valid. It must contain an scenario file and at least one vnfd file'".format( test_config["test_folder"])) - #load all vnfd - for vnfd in vnfd_files: - with open(vnfd, 'r') as stream: - vnf_descriptor = yaml.load(stream) - - vnfc_list = vnf_descriptor['vnf']['VNFC'] - for vnfc in vnfc_list: - vnfc['image name'] = test_config["image_name"] - devices = vnfc.get('devices',[]) - for device in devices: - if device['type'] == 'disk' and 'image name' in device: - device['image name'] = test_config["image_name"] - + # load all vnfd + for vnf_descriptor in vnf_descriptors: logger.debug("VNF descriptor: {}".format(vnf_descriptor)) - vnf = test_config["client"].create_vnf(descriptor=vnf_descriptor) + vnf = test_config["client"].create_vnf(descriptor=vnf_descriptor, image_name=test_config["image_name"]) logger.debug(vnf) + if 'vnf' in vnf: + vnf_uuid = vnf['vnf']['uuid'] + else: + vnf_uuid = vnf['vnfd'][0]['uuid'] self.__class__.to_delete_list.insert(0, {"item": "vnf", "function": test_config["client"].delete_vnf, - "params": {"uuid": vnf['vnf']['uuid']}}) - - #load the scenario definition - with open(scenario_file[0], 'r') as stream: - scenario_descriptor = yaml.load(stream) - networks = scenario_descriptor['scenario']['networks'] - networks[test_config["mgmt_net"]] = networks.pop('mgmt') - logger.debug("Scenario descriptor: {}".format(scenario_descriptor)) - scenario = test_config["client"].create_scenario(descriptor=scenario_descriptor) - logger.debug(scenario) - self.__class__.to_delete_list.insert(0,{"item": "scenario", "function": test_config["client"].delete_scenario, - "params":{"uuid": scenario['scenario']['uuid']} }) - self.__class__.scenario_uuid = scenario['scenario']['uuid'] + "params": {"uuid": vnf_uuid}}) + + # load the scenario definition + for scenario_descriptor in scenario_descriptors: + # networks = scenario_descriptor['scenario']['networks'] + # networks[test_config["mgmt_net"]] = networks.pop('mgmt') + logger.debug("Scenario descriptor: {}".format(scenario_descriptor)) + scenario = test_config["client"].create_scenario(descriptor=scenario_descriptor) + logger.debug(scenario) + if 'scenario' in scenario: + scenario_uuid = scenario['scenario']['uuid'] + else: + scenario_uuid = scenario['nsd'][0]['uuid'] + self.__class__.to_delete_list.insert(0, {"item": "scenario", + "function": test_config["client"].delete_scenario, + "params": {"uuid": scenario_uuid}}) + self.__class__.scenario_uuids.append(scenario_uuid) def test_010_instantiate_scenario(self): self.__class__.test_text = "{}.{}. TEST {} {}".format(test_config["test_number"], self.__class__.test_index, inspect.currentframe().f_code.co_name, test_config["test_folder"]) self.__class__.test_index += 1 - - instance = test_config["client"].create_instance(scenario_id=self.__class__.scenario_uuid, - name=self.__class__.test_text) - self.__class__.instance_scenario_uuid = instance['uuid'] - logger.debug(instance) - self.__class__.to_delete_list.insert(0, {"item": "instance", "function": test_config["client"].delete_instance, - "params": {"uuid": instance['uuid']}}) + for scenario_uuid in self.__class__.scenario_uuids: + instance_descriptor = { + "instance":{ + "name": self.__class__.test_text, + "scenario": scenario_uuid, + "networks":{ + "mgmt": {"sites": [ { "netmap-use": test_config["mgmt_net"]} ]} + } + } + } + instance = test_config["client"].create_instance(instance_descriptor) + self.__class__.instance_scenario_uuids.append(instance['uuid']) + logger.debug(instance) + self.__class__.to_delete_list.insert(0, {"item": "instance", + "function": test_config["client"].delete_instance, + "params": {"uuid": instance['uuid']}}) def test_020_check_deployent(self): self.__class__.test_text = "{}.{}. TEST {} {}".format(test_config["test_number"], self.__class__.test_index, @@ -1462,15 +1481,19 @@ class descriptor_based_scenario_test(test_base): return keep_waiting = test_config["timeout"] - instance_active = False - while True: - result = check_instance_scenario_active(self.__class__.instance_scenario_uuid) - if result[0]: - break - elif 'ERROR' in result[1]: - msg = 'Got error while waiting for the instance to get active: '+result[1] - logging.error(msg) - raise Exception(msg) + pending_instance_scenario_uuids = list(self.__class__.instance_scenario_uuids) # make a copy + while pending_instance_scenario_uuids: + index = 0 + while index < len(pending_instance_scenario_uuids): + result = check_instance_scenario_active(pending_instance_scenario_uuids[index]) + if result[0]: + del pending_instance_scenario_uuids[index] + break + elif 'ERROR' in result[1]: + msg = 'Got error while waiting for the instance to get active: '+result[1] + logging.error(msg) + raise Exception(msg) + index += 1 if keep_waiting >= 5: time.sleep(5) -- 2.17.1