--- /dev/null
+#!/usr/bin/env python
+"""
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+@file test_onboard.py
+@author Varun Prasad (varun.prasad@riftio.com)
+@brief Onboard descriptors
+"""
+
+import json
+import logging
+import os
+import pytest
+import shlex
+import requests
+import shutil
+import subprocess
+import time
+import uuid
+
+import rift.auto.mano
+import rift.auto.session
+
+import gi
+gi.require_version('RwNsrYang', '1.0')
+gi.require_version('RwVnfdYang', '1.0')
+gi.require_version('RwLaunchpadYang', '1.0')
+gi.require_version('RwBaseYang', '1.0')
+
+from gi.repository import (
+ RwcalYang,
+ NsdYang,
+ RwNsrYang,
+ RwVnfrYang,
+ NsrYang,
+ VnfrYang,
+ VldYang,
+ RwVnfdYang,
+ RwLaunchpadYang,
+ RwBaseYang
+)
+
+logging.basicConfig(level=logging.DEBUG)
+
+
+@pytest.fixture(scope='module')
+def vnfd_proxy(request, mgmt_session):
+ return mgmt_session.proxy(RwVnfdYang)
+
+@pytest.fixture(scope='module')
+def rwvnfr_proxy(request, mgmt_session):
+ return mgmt_session.proxy(RwVnfrYang)
+
+@pytest.fixture(scope='module')
+def vld_proxy(request, mgmt_session):
+ return mgmt_session.proxy(VldYang)
+
+
+@pytest.fixture(scope='module')
+def nsd_proxy(request, mgmt_session):
+ return mgmt_session.proxy(NsdYang)
+
+
+@pytest.fixture(scope='module')
+def rwnsr_proxy(request, mgmt_session):
+ return mgmt_session.proxy(RwNsrYang)
+
+@pytest.fixture(scope='module')
+def base_proxy(request, mgmt_session):
+ return mgmt_session.proxy(RwBaseYang)
+
+
+@pytest.fixture(scope="module")
+def endpoint():
+ return "upload"
+
+def create_nsr(nsd, input_param_list, cloud_account_name):
+ """
+ Create the NSR record object
+
+ Arguments:
+ nsd - NSD
+ input_param_list - list of input-parameter objects
+
+ Return:
+ NSR object
+ """
+ nsr = RwNsrYang.YangData_Nsr_NsInstanceConfig_Nsr()
+
+ nsr.id = str(uuid.uuid4())
+ nsr.name = rift.auto.mano.resource_name(nsr.id)
+ nsr.short_name = "nsr_short_name"
+ nsr.description = "This is a description"
+ nsr.nsd.from_dict(nsd.as_dict())
+ nsr.admin_status = "ENABLED"
+ nsr.input_parameter.extend(input_param_list)
+ nsr.cloud_account = cloud_account_name
+
+ return nsr
+
+
+def upload_descriptor(
+ logger,
+ descriptor_file,
+ scheme,
+ cert,
+ host="127.0.0.1",
+ endpoint="upload"):
+ curl_cmd = ('curl --cert {cert} --key {key} -F "descriptor=@{file}" -k '
+ '{scheme}://{host}:4567/api/{endpoint}'.format(
+ cert=cert[0],
+ key=cert[1],
+ scheme=scheme,
+ endpoint=endpoint,
+ file=descriptor_file,
+ host=host,
+ ))
+
+ logger.debug("Uploading descriptor %s using cmd: %s", descriptor_file, curl_cmd)
+ stdout = subprocess.check_output(shlex.split(curl_cmd), universal_newlines=True)
+
+ json_out = json.loads(stdout)
+ transaction_id = json_out["transaction_id"]
+
+ return transaction_id
+
+
+class DescriptorOnboardError(Exception):
+ pass
+
+
+def wait_onboard_transaction_finished(
+ logger,
+ transaction_id,
+ scheme,
+ cert,
+ timeout=600,
+ host="127.0.0.1",
+ endpoint="upload"):
+
+ logger.info("Waiting for onboard trans_id %s to complete", transaction_id)
+ uri = '%s://%s:4567/api/%s/%s/state' % (scheme, host, endpoint, transaction_id)
+
+ elapsed = 0
+ start = time.time()
+ while elapsed < timeout:
+ reply = requests.get(uri, cert=cert, verify=False)
+ state = reply.json()
+ if state["status"] == "success":
+ break
+ if state["status"] != "pending":
+ raise DescriptorOnboardError(state)
+
+ time.sleep(1)
+ elapsed = time.time() - start
+
+
+ if state["status"] != "success":
+ raise DescriptorOnboardError(state)
+ logger.info("Descriptor onboard was successful")
+
+
+def onboard_descriptor(host, file_name, logger, endpoint, scheme, cert):
+ """On-board/update the descriptor.
+
+ Args:
+ host (str): Launchpad IP
+ file_name (str): Full file path.
+ logger: Logger instance
+ endpoint (str): endpoint to be used for the upload operation.
+
+ """
+ logger.info("Onboarding package: %s", file_name)
+ trans_id = upload_descriptor(
+ logger,
+ file_name,
+ scheme,
+ cert,
+ host=host,
+ endpoint=endpoint)
+ wait_onboard_transaction_finished(
+ logger,
+ trans_id,
+ scheme,
+ cert,
+ host=host,
+ endpoint=endpoint)
+
+def terminate_nsr(rwvnfr_proxy, rwnsr_proxy, logger, wait_after_kill=True):
+ """
+ Terminate the instance and check if the record is deleted.
+
+ Asserts:
+ 1. NSR record is deleted from instance-config.
+
+ """
+ logger.debug("Terminating NSRs")
+
+ nsr_path = "/ns-instance-config"
+ nsr = rwnsr_proxy.get_config(nsr_path)
+ nsrs = nsr.nsr
+
+ xpaths = []
+ for nsr in nsrs:
+ xpath = "/ns-instance-config/nsr[id='{}']".format(nsr.id)
+ rwnsr_proxy.delete_config(xpath)
+ xpaths.append(xpath)
+
+ if wait_after_kill:
+ time.sleep(30)
+ else:
+ time.sleep(5)
+
+ for xpath in xpaths:
+ nsr = rwnsr_proxy.get_config(xpath)
+ assert nsr is None
+
+ # Get the ns-instance-config
+ ns_instance_config = rwnsr_proxy.get_config("/ns-instance-config")
+
+ # Termination tests
+ vnfr = "/vnfr-catalog/vnfr"
+ vnfrs = rwvnfr_proxy.get(vnfr, list_obj=True)
+ assert vnfrs is None or len(vnfrs.vnfr) == 0
+
+ # nsr = "/ns-instance-opdata/nsr"
+ # nsrs = rwnsr_proxy.get(nsr, list_obj=True)
+ # assert len(nsrs.nsr) == 0
+
+
+
+@pytest.mark.setup('nsr')
+@pytest.mark.depends('launchpad')
+@pytest.mark.incremental
+class TestNsrStart(object):
+ """A brief overview of the steps performed.
+ 1. Generate & on-board new descriptors
+ 2. Start the NSR
+ """
+
+ def test_upload_descriptors(
+ self,
+ logger,
+ vnfd_proxy,
+ nsd_proxy,
+ mgmt_session,
+ scheme,
+ cert,
+ descriptors
+ ):
+ """Generates & On-boards the descriptors.
+ """
+ endpoint = "upload"
+
+ for file_name in descriptors:
+ onboard_descriptor(
+ mgmt_session.host,
+ file_name,
+ logger,
+ endpoint,
+ scheme,
+ cert)
+
+ descriptor_vnfds, descriptor_nsd = descriptors[:-1], descriptors[-1]
+
+ catalog = vnfd_proxy.get_config('/vnfd-catalog')
+ actual_vnfds = catalog.vnfd
+ assert len(actual_vnfds) == len(descriptor_vnfds), \
+ "There should {} vnfds".format(len(descriptor_vnfds))
+
+ catalog = nsd_proxy.get_config('/nsd-catalog')
+ actual_nsds = catalog.nsd
+ assert len(actual_nsds) == 1, "There should only be a single nsd"
+
+ @pytest.mark.feature("upload-image")
+ def test_upload_images(self, descriptor_images, cloud_host, cloud_user, cloud_tenants):
+
+ openstack = rift.auto.mano.OpenstackManoSetup(
+ cloud_host,
+ cloud_user,
+ [(tenant, "private") for tenant in cloud_tenants])
+
+ for image_location in descriptor_images:
+ image = RwcalYang.ImageInfoItem.from_dict({
+ 'name': os.path.basename(image_location),
+ 'location': image_location,
+ 'disk_format': 'qcow2',
+ 'container_format': 'bare'})
+ openstack.create_image(image)
+
+
+ def test_set_scaling_params(self, nsd_proxy):
+ nsds = nsd_proxy.get('/nsd-catalog')
+ nsd = nsds.nsd[0]
+ for scaling_group in nsd.scaling_group_descriptor:
+ scaling_group.max_instance_count = 2
+
+ nsd_proxy.replace_config('/nsd-catalog/nsd[id="{}"]'.format(
+ nsd.id), nsd)
+
+
+ def test_instantiate_nsr(self, logger, nsd_proxy, rwnsr_proxy, base_proxy, cloud_account_name):
+
+ def verify_input_parameters(running_config, config_param):
+ """
+ Verify the configured parameter set against the running configuration
+ """
+ for run_input_param in running_config.input_parameter:
+ if (run_input_param.xpath == config_param.xpath and
+ run_input_param.value == config_param.value):
+ return True
+
+ assert False, ("Verification of configured input parameters: { xpath:%s, value:%s} "
+ "is unsuccessful.\nRunning configuration: %s" % (config_param.xpath,
+ config_param.value,
+ running_config.input_parameter))
+
+ catalog = nsd_proxy.get_config('/nsd-catalog')
+ nsd = catalog.nsd[0]
+
+ input_parameters = []
+ descr_xpath = "/nsd:nsd-catalog/nsd:nsd[nsd:id='%s']/nsd:description" % nsd.id
+ descr_value = "New NSD Description"
+ in_param_id = str(uuid.uuid4())
+
+ input_param_1 = NsrYang.YangData_Nsr_NsInstanceConfig_Nsr_InputParameter(
+ xpath=descr_xpath,
+ value=descr_value)
+
+ input_parameters.append(input_param_1)
+
+ nsr = create_nsr(nsd, input_parameters, cloud_account_name)
+
+ logger.info("Instantiating the Network Service")
+ rwnsr_proxy.create_config('/ns-instance-config/nsr', nsr)
+
+ nsr_opdata = rwnsr_proxy.get('/ns-instance-opdata/nsr[ns-instance-config-ref="{}"]'.format(nsr.id))
+ assert nsr_opdata is not None
+
+ # Verify the input parameter configuration
+ running_config = rwnsr_proxy.get_config("/ns-instance-config/nsr[id='%s']" % nsr.id)
+ for input_param in input_parameters:
+ verify_input_parameters(running_config, input_param)
+
+ def test_wait_for_nsr_started(self, rwnsr_proxy):
+ nsr_opdata = rwnsr_proxy.get('/ns-instance-opdata')
+ nsrs = nsr_opdata.nsr
+
+ for nsr in nsrs:
+ xpath = "/ns-instance-opdata/nsr[ns-instance-config-ref='{}']/operational-status".format(nsr.ns_instance_config_ref)
+ rwnsr_proxy.wait_for(xpath, "running", fail_on=['failed'], timeout=240)
+
+
+@pytest.mark.teardown('nsr')
+@pytest.mark.depends('launchpad')
+@pytest.mark.incremental
+class TestNsrTeardown(object):
+ def test_terminate_nsr(self, rwvnfr_proxy, rwnsr_proxy, logger, cloud_type):
+ """
+ Terminate the instance and check if the record is deleted.
+
+ Asserts:
+ 1. NSR record is deleted from instance-config.
+
+ """
+ logger.debug("Terminating NSR")
+
+ wait_after_kill = True
+ if cloud_type == "mock":
+ wait_after_kill = False
+
+ terminate_nsr(rwvnfr_proxy, rwnsr_proxy, logger, wait_after_kill=wait_after_kill)
+
+ def test_delete_records(self, nsd_proxy, vnfd_proxy):
+ """Delete the NSD & VNFD records
+
+ Asserts:
+ The records are deleted.
+ """
+ nsds = nsd_proxy.get("/nsd-catalog/nsd", list_obj=True)
+ for nsd in nsds.nsd:
+ xpath = "/nsd-catalog/nsd[id='{}']".format(nsd.id)
+ nsd_proxy.delete_config(xpath)
+
+ nsds = nsd_proxy.get("/nsd-catalog/nsd", list_obj=True)
+ assert nsds is None or len(nsds.nsd) == 0
+
+ vnfds = vnfd_proxy.get("/vnfd-catalog/vnfd", list_obj=True)
+ for vnfd_record in vnfds.vnfd:
+ xpath = "/vnfd-catalog/vnfd[id='{}']".format(vnfd_record.id)
+ vnfd_proxy.delete_config(xpath)
+
+ vnfds = vnfd_proxy.get("/vnfd-catalog/vnfd", list_obj=True)
+ assert vnfds is None or len(vnfds.vnfd) == 0