Support enhancement for Subscription Post

- Added support for the following attributes:
       *operationStates
       *vnfInstanceSubscriptionFilter

  - The filtering criteria is enhanced by adding the
    above attributes to effectively send_notifications

  - We also updated the following table in DB:
      vnf_lcm_filters

Implements: blueprint support-fundamental-lcm
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-fundamental-vnf-lcm-based-on-ETSI-NFV.html
Change-Id: Iebe70c43ef1a5b653f8ec204b1b3a79ca882399f
This commit is contained in:
Aldinson Esto 2021-02-09 05:44:46 +09:00
parent ede9755ab5
commit 8242c132ad
13 changed files with 587 additions and 70 deletions

View File

@ -631,6 +631,18 @@ filter_notification_types:
in: body in: body
required: false required: false
type: string type: string
filter_operation_states:
description: |
Match particular LCM operation state
values as reported in notifications of type
VnfLcmOperationOccurrenceNotification.
May be present if the "notificationTypes"
attribute contains the value
"VnfLcmOperationOccurrenceNotification" and
shall be absent otherwise.
in: body
required: false
type: string
filter_operation_types: filter_operation_types:
description: | description: |
Match particular VNF lifecycle operation types for Match particular VNF lifecycle operation types for
@ -1237,6 +1249,13 @@ vnf_instance_name:
in: body in: body
required: false required: false
type: string type: string
vnf_instance_subscription_filter:
description: |
Filter criteria to select VNF instances
about which to notify.
in: body
required: false
type: object
vnf_instance_vim_connection_info: vnf_instance_vim_connection_info:
description: | description: |
Information about VIM connections to be used for managing the resources Information about VIM connections to be used for managing the resources

View File

@ -2,7 +2,24 @@
"filter": { "filter": {
"notificationTypes": [ "notificationTypes": [
"VnfLcmOperationOccurrenceNotification" "VnfLcmOperationOccurrenceNotification"
] ],
"vnfInstanceSubscriptionFilter": {
"vnfdIds": [],
"vnfProductsFromProviders": {
"vnfProvider": "Vnf Provider 1",
"vnfProducts": [
{
"vnfProductName": "Vnf Product 1",
"versions": [
{
"vnfSoftwareVersion": "v1",
"vnfdVersions": ["vnfd.v1.1"]
}
]
}
]
}
}
}, },
"callbackUri": "http://sample1.com/notification" "callbackUri": "http://sample1.com/notification"
} }

View File

@ -1,14 +1,31 @@
{ {
"id": "76057f8e65ab37fb82d9382dfc3f3c8b", "id": "76057f8e65ab37fb82d9382dfc3f3c8b",
"filter": { "filter": {
"notificationTypes": [ "vnfInstanceSubscriptionFilter": {
"VnfLcmOperationOccurrenceNotification" "vnfdIds": [],
] "vnfProductsFromProviders": {
}, "vnfProvider": "Vnf Provider 1",
"callbackUri": "http://sample1.com/notification", "vnfProducts": [
"_links": { {
"self": { "vnfProductName": "Vnf Product 1",
"href": "https://sample1.com/vnflcm/v1/subscriptions/76057f8e65ab37fb82d9382dfc3f3c8b" "versions": [
} {
} "vnfSoftwareVersion": "v1",
} "vnfdVersions": ["vnfd.v1.1"]
}
]
}
]
}
},
"notificationTypes": [
"VnfLcmOperationOccurrenceNotification"
]
},
"callbackUri": "http://sample1.com/notification",
"_links": {
"self": {
"href": "https://sample1.com/vnflcm/v1/subscriptions/76057f8e65ab37fb82d9382dfc3f3c8b"
}
}
}

View File

@ -1155,8 +1155,10 @@ Request Parameters
.. rest_parameters:: parameters_vnflcm.yaml .. rest_parameters:: parameters_vnflcm.yaml
- filter: filter - filter: filter
- vnfInstanceSubscriptionFilter: vnf_instance_subscription_filter
- notificationTypes: filter_notification_types - notificationTypes: filter_notification_types
- operationTypes: filter_operation_types - operationTypes: filter_operation_types
- operationStates: filter_operation_states
- callbackUri : callback_uri - callbackUri : callback_uri
- authentication: authentication - authentication: authentication
- authType: authentication_auth_type - authType: authentication_auth_type

View File

@ -184,6 +184,109 @@ _vimConnectionInfo = {
} }
} }
_versions = {
'type': 'array',
'items': {
'type': 'objects',
'properties': {
'vnfSoftwareVersion': {'type': 'string'},
'vnfdVersions': {
'type': 'array',
'items': {'type': 'string'}
}
},
'required': ['vnfSoftwareVersion']
}
}
_vnf_products = {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'vnfProductName': {'type': 'string'},
'versions': _versions
},
'required': ['vnfProductName']
}
}
_vnf_products_from_providers = {
'type': 'object',
'properties': {
'type': 'object',
'properties': {
'vnfProvider': {'type': 'string'},
'vnfProducts': _vnf_products
}
},
'required': ['vnfProvider']
}
_lifecycle_change_notifications_filter = {
'type': 'object',
'properties': {
'vnfInstanceSubscriptionFilter': {
'type': 'object',
'properties': {
'vnfdIds': {
'type': 'array',
'items': parameter_types.identifier
},
'vnfProductsFromProviders': _vnf_products_from_providers,
'vnfInstanceIds': {
'type': 'array',
'items': parameter_types.identifier
},
'vnfInstanceNames': {
'type': 'array',
'items': {'type': 'string'}
}
}
},
'notificationTypes': {
'type': 'array',
'items': {
'type': 'string',
'enum': [
'VnfLcmOperationOccurrenceNotification',
'VnfIdentifierCreationNotification',
'VnfIdentifierDeletionNotification']
}
},
'operationTypes': {
'type': 'array',
'items': {
'type': 'string',
'enum': [
'INSTANTIATE',
'SCALE',
'SCALE_TO_LEVEL',
'CHANGE_FLAVOUR',
'TERMINATE',
'HEAL',
'OPERATE',
'CHANGE_EXT_CONN',
'MODIFY_INFO']
}
},
'operationStates': {
'type': 'array',
'items': {
'type': 'string',
'enum': [
'STARTING',
'PROCESSING',
'COMPLETED',
'FAILED_TEMP',
'FAILED',
'ROLLING_BACK',
'ROLLED_BACK']
}
}
}
}
create = { create = {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
@ -241,7 +344,7 @@ heal = {
register_subscription = { register_subscription = {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'filter': parameter_types.keyvalue_pairs, 'filter': _lifecycle_change_notifications_filter,
'callbackUri': {'type': 'string', 'maxLength': 255}, 'callbackUri': {'type': 'string', 'maxLength': 255},
'authentication': parameter_types.keyvalue_pairs, 'authentication': parameter_types.keyvalue_pairs,
}, },

View File

@ -162,26 +162,6 @@ def check_vnf_status_and_error_point(action, status=None):
class VnfLcmController(wsgi.Controller): class VnfLcmController(wsgi.Controller):
notification_type_list = ['VnfLcmOperationOccurrenceNotification',
'VnfIdentifierCreationNotification',
'VnfIdentifierDeletionNotification']
operation_type_list = ['INSTANTIATE',
'SCALE',
'SCALE_TO_LEVEL',
'CHANGE_FLAVOUR',
'TERMINATE',
'HEAL',
'OPERATE',
'CHANGE_EXT_CONN',
'MODIFY_INFO']
operation_state_list = ['STARTING',
'PROCESSING',
'COMPLETED',
'FAILED_TEMP',
'FAILED',
'ROLLING_BACK',
'ROLLED_BACK']
_view_builder_class = vnf_lcm_view.ViewBuilder _view_builder_class = vnf_lcm_view.ViewBuilder
def __init__(self): def __init__(self):
@ -904,28 +884,6 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.register_subscription) @validation.schema(vnf_lcm.register_subscription)
def register_subscription(self, request, body): def register_subscription(self, request, body):
subscription_request_data = body subscription_request_data = body
if subscription_request_data.get('filter'):
# notificationTypes check
notification_types = subscription_request_data.get(
"filter").get("notificationTypes")
for notification_type in notification_types:
if notification_type not in self.notification_type_list:
msg = (
_("notificationTypes value mismatch: %s") %
notification_type)
return self._make_problem_detail(
msg, 400, title='Bad Request')
# operationTypes check
operation_types = subscription_request_data.get(
"filter").get("operationTypes")
for operation_type in operation_types:
if operation_type not in self.operation_type_list:
msg = (
_("operationTypes value mismatch: %s") %
operation_type)
return self._make_problem_detail(
msg, 400, title='Bad Request')
subscription_id = uuidutils.generate_uuid() subscription_id = uuidutils.generate_uuid()

View File

@ -337,6 +337,17 @@ def chunkiter(fp, chunk_size=65536):
break break
# TODO(esto.aln): Consider to move this function to
# convert_camelcase_to_snakecase(). We will consider the correct approach
# to modify the common function so as not to introduce degrade.
def convert_string_to_snakecase(name):
"""Converts a string from camelCase to snake_case."""
name_with_underscores = re.sub(
'(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2',
name_with_underscores).lower()
def convert_camelcase_to_snakecase(request_data): def convert_camelcase_to_snakecase(request_data):
"""Converts dict keys or list of dict keys from camelCase to snake_case. """Converts dict keys or list of dict keys from camelCase to snake_case.
@ -347,17 +358,11 @@ def convert_camelcase_to_snakecase(request_data):
:param request_data: dict with keys or list with items, in camelCase. :param request_data: dict with keys or list with items, in camelCase.
""" """
def convert(name):
name_with_underscores = re.sub(
'(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2',
name_with_underscores).lower()
if isinstance(request_data, dict): if isinstance(request_data, dict):
new_dict = {} new_dict = {}
for key, property_value in request_data.items(): for key, property_value in request_data.items():
property_value = convert_camelcase_to_snakecase(property_value) property_value = convert_camelcase_to_snakecase(property_value)
underscore_joined = convert(key) underscore_joined = convert_string_to_snakecase(key)
new_dict[underscore_joined] = property_value new_dict[underscore_joined] = property_value
return new_dict return new_dict

View File

@ -287,6 +287,7 @@ class VnfLcmFilters(model_base.BASE):
sa.ForeignKey('vnf_lcm_subscriptions.id'), sa.ForeignKey('vnf_lcm_subscriptions.id'),
nullable=False) nullable=False)
filter = sa.Column(sa.JSON, nullable=False) filter = sa.Column(sa.JSON, nullable=False)
vnf_products_from_providers = sa.Column(sa.JSON, nullable=True)
notification_types = sa.Column(sa.VARBINARY(255), nullable=True) notification_types = sa.Column(sa.VARBINARY(255), nullable=True)
notification_types_len = sa.Column(sa.Integer, nullable=True) notification_types_len = sa.Column(sa.Integer, nullable=True)
operation_types = sa.Column( operation_types = sa.Column(

View File

@ -1 +1 @@
3adac34764da c31f65e0d099

View File

@ -0,0 +1,137 @@
# Copyright 2021 OpenStack Foundation
#
# 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.
#
"""Add columns to vnf_lcm_filter
Revision ID: c31f65e0d099
Revises: 3adac34764da
Create Date: 2021-02-03 22:53:36.352774
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c31f65e0d099'
down_revision = '3adac34764da'
def upgrade(active_plugins=None, options=None):
sql_text_length = 65535
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_products_from_providers', sa.JSON()))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'operation_states', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.operationStates'))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'operation_states_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`operation_states`),0)")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_ids', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfdIds'))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_ids_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnfd_ids`),0)")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_provider', sa.TEXT(length=sql_text_length),
sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,'$.vnfProvider')),''))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_product_name', sa.TEXT(length=sql_text_length),
sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,"
"'$.vnfProducts[0].vnfProductName')),''))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_software_version', sa.TEXT(length=sql_text_length),
sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,'$.vnfProducts[0]"
".versions[0].vnfSoftwareVersion')),''))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_versions', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`vnf_products_from_providers`,"
"'$.vnfProducts[0].versions[0].vnfdVersions'))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_versions_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnfd_versions`),0)")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_ids', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfInstanceIds'))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_ids_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnf_instance_ids`),0)")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_names', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfInstanceNames'))")))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_names_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnf_instance_names`),0)")))

View File

@ -15,6 +15,7 @@ from oslo_utils import timeutils
from sqlalchemy.sql import text from sqlalchemy.sql import text
from tacker.common import exceptions from tacker.common import exceptions
from tacker.common.utils import convert_string_to_snakecase
import tacker.conf import tacker.conf
from tacker.db import api as db_api from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api from tacker.db.db_sqlalchemy import api
@ -28,6 +29,50 @@ LOG = logging.getLogger(__name__)
CONF = tacker.conf.CONF CONF = tacker.conf.CONF
VNF_INSTANCE_SUBSCRIPTION_FILTER = [
"vnfdIds", "vnfProvider", "vnfProductName",
"vnfSoftwareVersion", "vnfdVersions", "vnfInstanceIds",
"vnfInstanceNames"
]
VNF_INSTANCE_SUBSCRIPTION_FILTER_LISTS = [
"vnfdIds", "vnfdVersions", "vnfInstanceIds", "vnfInstanceNames"
]
def _get_vnf_subscription_filter_values(vnf_subscription_filter):
vnfd_ids = vnf_subscription_filter.get('vnfdIds', [])
vnf_instance_ids = vnf_subscription_filter.get('vnfInstanceIds', [])
vnf_instance_names = vnf_subscription_filter.get('vnfInstanceNames', [])
vnfd_products_from_providers = vnf_subscription_filter.get(
'vnfProductsFromProviders', {})
vnf_provider = vnfd_products_from_providers.get('vnfProvider', "")
vnf_products = vnfd_products_from_providers.get('vnfProducts', [])
vnf_product_name = ""
vnf_software_version = ""
vnfd_versions = []
if vnf_products:
vnf_product_name = vnf_products[0].get('vnfProductName', "")
versions = vnf_products[0].get('versions', [])
if versions:
vnf_software_version = versions[0].get('vnfSoftwareVersion', "")
vnfd_versions = versions[0].get('vnfdVersions', [])
vnf_subscription_array = [
{'vnfdIds': vnfd_ids},
{'vnfInstanceIds': vnf_instance_ids},
{'vnfInstanceNames': vnf_instance_names},
{'vnfProvider': vnf_provider},
{'vnfProductName': vnf_product_name},
{'vnfSoftwareVersion': vnf_software_version},
{'vnfdVersions': vnfd_versions}]
return vnf_subscription_array
def _make_list(value): def _make_list(value):
if isinstance(value, list): if isinstance(value, list):
res = "" res = ""
@ -165,7 +210,9 @@ def _get_by_subscriptionid(context, subscriptionsId):
def _vnf_lcm_subscriptions_id_get(context, def _vnf_lcm_subscriptions_id_get(context,
callbackUri, callbackUri,
notification_type=None, notification_type=None,
operation_type=None operation_type=None,
operation_state=None,
vnf_instance_subscription_filter=None
): ):
sql = ("select " sql = ("select "
@ -175,6 +222,35 @@ def _vnf_lcm_subscriptions_id_get(context,
"(select subscription_uuid from vnf_lcm_filters " "(select subscription_uuid from vnf_lcm_filters "
"where ") "where ")
if vnf_instance_subscription_filter:
included_in_filter = []
column_list = _get_vnf_subscription_filter_values(
vnf_instance_subscription_filter)
for column in column_list:
for key in column:
if key in VNF_INSTANCE_SUBSCRIPTION_FILTER:
value = column[key]
if key in VNF_INSTANCE_SUBSCRIPTION_FILTER_LISTS:
value = _make_list(value)
else:
value = '"{}"'.format(value)
sql = (sql + " JSON_CONTAINS({}, '{}') and ".format(
convert_string_to_snakecase(key),
value
))
included_in_filter.append(key)
not_included_in_filter = list(
set(VNF_INSTANCE_SUBSCRIPTION_FILTER_LISTS) -
set(included_in_filter))
# items not being searched for is excluded by adding
# <name>_len=0 to the sql query
for key in not_included_in_filter:
sql = sql + " {}_len=0 and ".format(
convert_string_to_snakecase(key))
if notification_type: if notification_type:
sql = (sql + " JSON_CONTAINS(notification_types, '" + sql = (sql + " JSON_CONTAINS(notification_types, '" +
_make_list(notification_type) + "') ") _make_list(notification_type) + "') ")
@ -182,6 +258,13 @@ def _vnf_lcm_subscriptions_id_get(context,
sql = sql + " notification_types_len=0 " sql = sql + " notification_types_len=0 "
sql = sql + "and " sql = sql + "and "
if operation_state:
sql = (sql + " JSON_CONTAINS(operation_states, '" +
_make_list(operation_state) + "') ")
else:
sql = sql + " operation_states_len=0 "
sql = sql + "and "
if operation_type: if operation_type:
sql = sql + " JSON_CONTAINS(operation_types, '" + \ sql = sql + " JSON_CONTAINS(operation_types, '" + \
_make_list(operation_type) + "') " _make_list(operation_type) + "') "
@ -200,14 +283,25 @@ def _vnf_lcm_subscriptions_id_get(context,
return line return line
except exceptions.NotFound: except exceptions.NotFound:
return '' return ''
except Exception as exc:
LOG.error("SQL Error: %s" % str(exc))
return ''
def _add_filter_data(context, subscription_id, filter): def _add_filter_data(context, subscription_id, filter):
with db_api.context_manager.writer.using(context): with db_api.context_manager.writer.using(context):
vnf_instance_subscription_filter = \
filter.get('vnfInstanceSubscriptionFilter')
vnf_products_from_providers = \
vnf_instance_subscription_filter.get(
'vnfProductsFromProviders')
new_entries = [] new_entries = []
new_entries.append({"subscription_uuid": subscription_id, new_entries.append({"subscription_uuid": subscription_id,
"filter": filter}) "filter": filter,
"vnf_products_from_providers":
vnf_products_from_providers})
context.session.execute( context.session.execute(
models.VnfLcmFilters.__table__.insert(None), models.VnfLcmFilters.__table__.insert(None),
@ -236,12 +330,16 @@ def _vnf_lcm_subscriptions_create(context, values, filter):
if filter: if filter:
notification_type = filter.get('notificationTypes') notification_type = filter.get('notificationTypes')
operation_type = filter.get('operationTypes') operation_type = filter.get('operationTypes')
operation_state = filter.get('operationStates')
subscription_filter = filter.get('vnfInstanceSubscriptionFilter')
vnf_lcm_subscriptions_id = _vnf_lcm_subscriptions_id_get( vnf_lcm_subscriptions_id = _vnf_lcm_subscriptions_id_get(
context, context,
callbackUri, callbackUri,
notification_type=notification_type, notification_type=notification_type,
operation_type=operation_type) operation_type=operation_type,
operation_state=operation_state,
vnf_instance_subscription_filter=subscription_filter)
if vnf_lcm_subscriptions_id: if vnf_lcm_subscriptions_id:
raise Exception("303" + vnf_lcm_subscriptions_id) raise Exception("303" + vnf_lcm_subscriptions_id)

View File

@ -30,6 +30,23 @@ class Subscription:
""" """
return { return {
"filter": { "filter": {
"vnfInstanceSubscriptionFilter": {
"vnfdIds": ["b1bb0ce7-ebca-4fa7-95ed-4840d7000000"],
"vnfProductsFromProviders": {
"vnfProvider": "Company",
"vnfProducts": [
{
"vnfProductName": "Sample VNF",
"versions": [
{
"vnfSoftwareVersion": "1.0",
"vnfdVersions": ["1.0"]
}
]
}
]
}
},
"notificationTypes": [ "notificationTypes": [
"VnfLcmOperationOccurrenceNotification", "VnfLcmOperationOccurrenceNotification",
"VnfIdentifierCreationNotification", "VnfIdentifierCreationNotification",
@ -42,7 +59,8 @@ class Subscription:
"HEAL", "HEAL",
"MODIFY_INFO", "MODIFY_INFO",
"CHANGE_EXT_CONN" "CHANGE_EXT_CONN"
] ],
"operationStates": ["STARTING"]
}, },
"callbackUri": callback_uri "callbackUri": callback_uri
} }

View File

@ -24,6 +24,7 @@ import urllib
import webob import webob
from webob import exc from webob import exc
from oslo_config import cfg
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from tacker.api.vnflcm.v1 import controller from tacker.api.vnflcm.v1 import controller
@ -37,6 +38,7 @@ from tacker.extensions import vnfm
from tacker.manager import TackerManager from tacker.manager import TackerManager
from tacker import objects from tacker import objects
from tacker.objects import fields from tacker.objects import fields
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
from tacker.tests import constants from tacker.tests import constants
from tacker.tests.unit import base from tacker.tests.unit import base
from tacker.tests.unit.db import utils from tacker.tests.unit.db import utils
@ -3731,3 +3733,143 @@ class TestController(base.TestCase):
self.assertEqual( self.assertEqual(
"Can not find requested vnf: %s" % constants.INVALID_UUID, "Can not find requested vnf: %s" % constants.INVALID_UUID,
resp.json['itemNotFound']['message']) resp.json['itemNotFound']['message'])
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@ddt.data('operationTypes', 'operationStates')
def test_register_subscription_operation_mismatch(
self, attribute, mock_get_service_plugins):
body = {
'callbackUri': 'http://sample_callback_uri',
'filter': {
'notificationType': [
'VnfLcmOperationOccurrenceNotification'],
attribute: ['sample_operation']
}
}
req = fake_request.HTTPRequest.blank(
'/subscriptions')
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
def test_register_subscription_operation_notification_mismatch(
self, mock_get_service_plugins):
body = {
'callbackUri': 'http://sample_callback_uri',
'filter': {
'notificationTypes': ['sample_notification'],
}
}
req = fake_request.HTTPRequest.blank(
'/subscriptions')
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(subscription_obj.LccnSubscriptionRequest, 'create')
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
def test_register_subscription_vnf_instance_subscription_filter(
self, mock_get_service_plugins, mock_create):
cfg.CONF.set_override('test_callback_uri', False,
group='vnf_lcm')
body = {
'callbackUri': 'http://sample_callback_uri',
'filter': {
'notificationTypes': ['VnfLcmOperationOccurrenceNotification'],
'vnfInstanceSubscriptionFilter': {
"vnfdIds": [],
"vnfProductsFromProviders": {
"vnfProvider": "Vnf Provider 1",
"vnfProducts": [
{
"vnfProductName": "Vnf Product 1",
"versions": [
{
"vnfSoftwareVersion": "v1",
"vnfdVersions": ["vnfd.v1.1"]
}
]
}
]
}
}
}
}
req = fake_request.HTTPRequest.blank(
'/subscriptions')
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.CREATED, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
def test_register_subscription_vnf_instance_subscription_filter_error(
self, mock_get_service_plugins):
body = {
'callbackUri': 'http://sample_callback_uri',
'filter': {
'notificationTypes': ['VnfLcmOperationOccurrenceNotification'],
'vnfInstanceSubscriptionFilter': {
"vnfdIds": [],
"vnfProductsFromProviders": {
"vnfProducts": [
{
"vnfProductName": "Vnf Product 1",
"versions": [
{
"vnfSoftwareVersion": "v1",
"vnfdVersions": ["vnfd.v1.1"]
}
]
}
]
}
}
}
}
req = fake_request.HTTPRequest.blank(
'/subscriptions')
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(subscription_obj.LccnSubscriptionRequest, 'create')
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
def test_register_subscription(
self, mock_get_service_plugins, mock_save):
cfg.CONF.set_override('test_callback_uri', False,
group='vnf_lcm')
body = {
'callbackUri': 'http://sample_callback_uri'
}
req = fake_request.HTTPRequest.blank(
'/subscriptions')
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.CREATED, resp.status_code)