ns_terminate,
ns_action,
ns_scale,
+ ns_update,
nsi_instantiate,
)
from osm_nbi.base_topic import (
operation_schema = { # mapping between operation and jsonschema to validate
"instantiate": ns_instantiate,
"action": ns_action,
+ "update": ns_update,
"scale": ns_scale,
"terminate": ns_terminate,
}
"""
Check that user has enter right parameters for the operation
:param session: contains "username", "admin", "force", "public", "project_id", "set_project"
- :param operation: it can be: instantiate, terminate, action, TODO: update, heal
+ :param operation: it can be: instantiate, terminate, action, update. TODO: heal
:param indata: descriptor with the parameters of the operation
:return: None
"""
self._check_action_ns_operation(indata, nsr)
elif operation == "scale":
self._check_scale_ns_operation(indata, nsr)
+ elif operation == "update":
+ self._check_update_ns_operation(indata, nsr)
elif operation == "instantiate":
self._check_instantiate_ns_operation(indata, nsr, session)
)
)
+ def _check_update_ns_operation(self, indata, nsr) -> None:
+ """Validates the ns-update request according to updateType
+
+ If updateType is CHANGE_VNFPKG:
+ - it checks the vnfInstanceId, whether it's available under ns instance
+ - it checks the vnfdId whether it matches with the vnfd-id in the vnf-record of specified VNF.
+ Otherwise exception will be raised.
+
+ Args:
+ indata: includes updateType such as CHANGE_VNFPKG,
+ nsr: network service record
+
+ Raises:
+ EngineException:
+ a meaningful error if given update parameters are not proper such as
+ "Error in validating ns-update request: <ID> does not match
+ with the vnfd-id of vnfinstance
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY"
+
+ """
+ try:
+ if indata["updateType"] == "CHANGE_VNFPKG":
+ # vnfInstanceId, nsInstanceId, vnfdId are mandatory
+ vnf_instance_id = indata["changeVnfPackageData"]["vnfInstanceId"]
+ ns_instance_id = indata["nsInstanceId"]
+ vnfd_id_2update = indata["changeVnfPackageData"]["vnfdId"]
+
+ if vnf_instance_id not in nsr["constituent-vnfr-ref"]:
+
+ raise EngineException(
+ f"Error in validating ns-update request: vnf {vnf_instance_id} does not "
+ f"belong to NS {ns_instance_id}",
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ # Getting vnfrs through the ns_instance_id
+ vnfrs = self.db.get_list("vnfrs", {"nsr-id-ref": ns_instance_id})
+ constituent_vnfd_id = next(
+ (
+ vnfr["vnfd-id"]
+ for vnfr in vnfrs
+ if vnfr["id"] == vnf_instance_id
+ ),
+ None,
+ )
+
+ # Check the given vnfd-id belongs to given vnf instance
+ if constituent_vnfd_id and (vnfd_id_2update != constituent_vnfd_id):
+
+ raise EngineException(
+ f"Error in validating ns-update request: vnfd-id {vnfd_id_2update} does not "
+ f"match with the vnfd-id: {constituent_vnfd_id} of VNF instance: {vnf_instance_id}",
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ # Validating the ns update timeout
+ if (
+ indata.get("timeout_ns_update")
+ and indata["timeout_ns_update"] < 300
+ ):
+ raise EngineException(
+ "Error in validating ns-update request: {} second is not enough "
+ "to upgrade the VNF instance: {}".format(
+ indata["timeout_ns_update"], vnf_instance_id
+ ),
+ http_code=HTTPStatus.UNPROCESSABLE_ENTITY,
+ )
+
+ except (
+ DbException,
+ AttributeError,
+ IndexError,
+ KeyError,
+ ValueError,
+ ) as e:
+ raise type(e)(
+ "Ns update request could not be processed with error: {}.".format(e)
+ )
+
def _check_scale_ns_operation(self, indata, nsr):
vnfd = self._get_vnfd_from_vnf_member_index(
indata["scaleVnfData"]["scaleByStepData"]["member-vnf-index"], nsr["_id"]
"""
Creates a ns-lcm-opp content to be stored at database.
:param nsr_id: internal id of the instance
- :param operation: instantiate, terminate, scale, action, ...
+ :param operation: instantiate, terminate, scale, action, update ...
:param params: user parameters for the operation
:return: dictionary following SOL005 format
"""
:param session: contains "username", "admin", "force", "public", "project_id", "set_project"
:param indata: descriptor with the parameters of the operation. It must contains among others
nsInstanceId: _id of the nsr to perform the operation
- operation: it can be: instantiate, terminate, action, TODO: update, heal
+ operation: it can be: instantiate, terminate, action, update TODO: heal
:param kwargs: used to override the indata descriptor
:param headers: http request headers
:return: id of the nslcmops
action O
scale O5
heal 5
+ update 05
/ns_lcm_op_occs 5 5
/<nsLcmOpOccId> 5 5 5
TO BE COMPLETED 5 5
"METHODS": ("POST",),
"ROLE_PERMISSION": "ns_instances:id:action:",
},
+ "update": {
+ "METHODS": ("POST",),
+ "ROLE_PERMISSION": "ns_instances:id:update:",
+ },
},
},
"ns_lcm_op_occs": {
"POST /nslcm/v1/ns_instances/<nsInstanceId>/action": "ns_instances:id:action:post"
+ "POST /nslcm/v1/ns_instances/<nsInstanceId>/update": "ns_instances:id:update:post"
+
"POST /nslcm/v1/ns_instances/<nsInstanceId>/scale": "ns_instances:id:scale:post"
"GET /nslcm/v1/ns_lcm_op_occs": "ns_instances:opps:get"
# contact: esousa@whitestack.com or alfonso.tiernosepulveda@telefonica.com
##
+from contextlib import contextmanager
import unittest
from time import time
from unittest.mock import Mock, mock_open # patch, MagicMock
self.assertEqual(self.db.get_one.call_args_list[0][0][0], 'vnfrs', "Incorrect first DB lookup")
self.assertEqual(self.db.get_one.call_args_list[1][0][0], 'vnfds_revisions', "Incorrect second DB lookup")
+ @contextmanager
+ def assertNotRaises(self, exception_type):
+ try:
+ yield None
+ except exception_type:
+ raise self.failureException("{} raised".format(exception_type.__name__))
+
+ def test_check_ns_update_operation(self):
+ self.db = DbMemory()
+ self.nslcmop_topic = NsLcmOpTopic(self.db, self.fs, self.msg, None)
+ session = {}
+
+ with self.subTest(i=1, t="VNF instance does not belong to NS"):
+ test_vnfr = yaml.load(db_vnfrs_text, Loader=yaml.Loader)
+ test_vnfr[0]["revision"] = 2
+ test_nsr = yaml.load(db_nsrs_text, Loader=yaml.Loader)
+ test_nsr[0]["constituent-vnfr-ref"][
+ 0
+ ] = "99d90b0c-faff-4b9f-bccd-017f33985984"
+ self.db.create_list("vnfrs", test_vnfr)
+ self.db.create_list("nsrs", test_nsr)
+ nsrs = self.db.get_list("nsrs")[0]
+ indata = {
+ "updateType": "CHANGE_VNFPKG",
+ "changeVnfPackageData": {
+ "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
+ "vnfdId": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
+ },
+ "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
+ }
+ with self.assertRaises(EngineException) as expected_exception:
+ self.nslcmop_topic._check_ns_operation(session, nsrs, "update", indata)
+ self.assertEqual(
+ str(expected_exception.exception),
+ "Error in validating ns-update request: vnf 88d90b0c-faff-4b9f-bccd-017f33985984"
+ " does not belong to NS f48163a6-c807-47bc-9682-f72caef5af85",
+ )
+
+ with self.subTest(i=2, t="Ns update request validated with no exception"):
+ test_vnfr = yaml.load(db_vnfrs_text, Loader=yaml.Loader)
+ test_vnfr[0]["revision"] = 2
+ test_nsr = yaml.load(db_nsrs_text, Loader=yaml.Loader)
+ self.db.create_list("vnfrs", test_vnfr)
+ self.db.create_list("nsrs", test_nsr)
+ nsrs = self.db.get_list("nsrs")[1]
+ indata = {
+ "updateType": "CHANGE_VNFPKG",
+ "changeVnfPackageData": {
+ "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
+ "vnfdId": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
+ },
+ "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
+ }
+ with self.assertNotRaises(EngineException):
+ self.nslcmop_topic._check_ns_operation(session, nsrs, "update", indata)
+
+ with self.subTest(
+ i=3, t="Ns update request rejected because of too small timeout"
+ ):
+ indata = {
+ "updateType": "CHANGE_VNFPKG",
+ "changeVnfPackageData": {
+ "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
+ "vnfdId": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
+ },
+ "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
+ "timeout_ns_update": 50,
+ }
+ with self.assertRaises(EngineException) as expected_exception:
+ self.nslcmop_topic._check_ns_operation(session, nsrs, "update", indata)
+ self.assertEqual(
+ str(expected_exception.exception),
+ "Error in validating ns-update request: 50 second is not enough "
+ "to upgrade the VNF instance: 88d90b0c-faff-4b9f-bccd-017f33985984",
+ )
+
+ with self.subTest(i=4, t="wrong vnfdid is given as an update parameter"):
+ test_vnfr = yaml.load(db_vnfrs_text, Loader=yaml.Loader)
+ test_vnfr[0]["revision"] = 2
+ test_nsr = yaml.load(db_nsrs_text, Loader=yaml.Loader)
+ self.db.create_list("vnfrs", test_vnfr)
+ self.db.create_list("nsrs", test_nsr)
+ nsrs = self.db.get_list("nsrs")[2]
+ indata = {
+ "updateType": "CHANGE_VNFPKG",
+ "changeVnfPackageData": {
+ "vnfInstanceId": "88d90b0c-faff-4b9f-bccd-017f33985984",
+ "vnfdId": "9637bcf8-cf14-42dc-ad70-c66fcf1e6e77",
+ },
+ "nsInstanceId": "f48163a6-c807-47bc-9682-f72caef5af85",
+ }
+ with self.assertRaises(EngineException) as expected_exception:
+ self.nslcmop_topic._check_ns_operation(session, nsrs, "update", indata)
+ self.assertEqual(
+ str(expected_exception.exception),
+ "Error in validating ns-update request: vnfd-id 9637bcf8-cf14-42dc-ad70-c66fcf1e6e77 does not "
+ "match with the vnfd-id: 7637bcf8-cf14-42dc-ad70-c66fcf1e6e77 of "
+ "VNF instance: 88d90b0c-faff-4b9f-bccd-017f33985984",
+ )
+
class TestNsrTopic(unittest.TestCase):
def setUp(self):
"additionalProperties": False,
}
+ns_update = {
+ "title": "ns update input schema",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "properties": {
+ "lcmOperationType": string_schema,
+ "nsInstanceId": id_schema,
+ "updateType": {
+ "enum": ["CHANGE_VNFPKG", "REMOVE_VNF", "MODIFY_VNF_INFORMATION"]
+ },
+ "modifyVnfInfoData": {
+ "type": "object",
+ "properties": {
+ "vnfInstanceId": id_schema,
+ "vnfdId": id_schema,
+ },
+ "required": ["vnfInstanceId", "vnfdId"],
+ },
+ "removeVnfInstanceId": id_schema,
+ "changeVnfPackageData": {
+ "type": "object",
+ "properties": {
+ "vnfInstanceId": id_schema,
+ "vnfdId": id_schema,
+ },
+ "required": ["vnfInstanceId", "vnfdId"],
+ },
+ },
+ "required": ["updateType"],
+ "additionalProperties": False,
+}
+
ns_action = { # TODO for the moment it is only contemplated the vnfd primitive execution
"title": "ns action input schema",
"$schema": "http://json-schema.org/draft-04/schema#",
"ns_instantiate": ns_instantiate,
"ns_action": ns_action,
"ns_scale": ns_scale,
+ "ns_update": ns_update,
"pdus": pdu_new_schema,
}