58fbcc371f
'operationStates' field of LifecycleChangeNotificationsFilter was defined as 'operationStatus' wrongly. This patch fixes it. Closes-Bug: #1996242 Change-Id: I09f54edf6b034cadc78f8a42df06419d08c3325a
276 lines
8.9 KiB
Python
276 lines
8.9 KiB
Python
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
|
|
# All Rights Reserved.
|
|
#
|
|
# 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.
|
|
|
|
|
|
import threading
|
|
|
|
from oslo_log import log as logging
|
|
from oslo_utils import timeutils
|
|
from oslo_utils import uuidutils
|
|
|
|
from tacker.sol_refactored.api import api_version
|
|
from tacker.sol_refactored.common import config
|
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
|
from tacker.sol_refactored.common import http_client
|
|
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
|
from tacker.sol_refactored import objects
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
CONF = config.CONF
|
|
|
|
TEST_NOTIFICATION_TIMEOUT = 20 # seconds
|
|
|
|
|
|
def get_subsc(context, subsc_id):
|
|
subsc = objects.LccnSubscriptionV2.get_by_id(context, subsc_id)
|
|
if subsc is None:
|
|
raise sol_ex.LccnSubscriptionNotFound(subsc_id=subsc_id)
|
|
return subsc
|
|
|
|
|
|
def get_subsc_all(context, marker=None):
|
|
return objects.LccnSubscriptionV2.get_all(context, marker)
|
|
|
|
|
|
def subsc_href(subsc_id, endpoint):
|
|
return "{}/vnflcm/v2/subscriptions/{}".format(endpoint, subsc_id)
|
|
|
|
|
|
def _get_notification_auth_handle(subsc):
|
|
if not subsc.obj_attr_is_set('authentication'):
|
|
return http_client.NoAuthHandle()
|
|
elif subsc.authentication.obj_attr_is_set('paramsBasic'):
|
|
param = subsc.authentication.paramsBasic
|
|
return http_client.BasicAuthHandle(param.userName, param.password)
|
|
elif subsc.authentication.obj_attr_is_set(
|
|
'paramsOauth2ClientCredentials'):
|
|
param = subsc.authentication.paramsOauth2ClientCredentials
|
|
return http_client.OAuth2AuthHandle(None,
|
|
param.tokenEndpoint, param.clientId, param.clientPassword)
|
|
|
|
# not reach here
|
|
|
|
|
|
def async_call(func):
|
|
def inner(*args, **kwargs):
|
|
th = threading.Thread(target=func, args=args,
|
|
kwargs=kwargs, daemon=True)
|
|
th.start()
|
|
return inner
|
|
|
|
|
|
@async_call
|
|
def send_notification(subsc, notif_data):
|
|
auth_handle = _get_notification_auth_handle(subsc)
|
|
connect_retries = (CONF.v2_vnfm.notify_connect_retries
|
|
if CONF.v2_vnfm.notify_connect_retries else None)
|
|
client = http_client.HttpClient(auth_handle,
|
|
version=api_version.CURRENT_VERSION,
|
|
connect_retries=connect_retries)
|
|
|
|
url = subsc.callbackUri
|
|
try:
|
|
resp, body = client.do_request(
|
|
url, "POST", expected_status=[204], body=notif_data)
|
|
except sol_ex.SolException:
|
|
# it may occur if test_notification was not executed.
|
|
LOG.exception("send_notification failed")
|
|
|
|
if resp.status_code != 204:
|
|
LOG.error("send_notification failed: %d" % resp.status_code)
|
|
|
|
|
|
def test_notification(subsc):
|
|
auth_handle = _get_notification_auth_handle(subsc)
|
|
client = http_client.HttpClient(auth_handle,
|
|
version=api_version.CURRENT_VERSION,
|
|
timeout=TEST_NOTIFICATION_TIMEOUT)
|
|
|
|
url = subsc.callbackUri
|
|
try:
|
|
resp, _ = client.do_request(url, "GET", expected_status=[204])
|
|
except sol_ex.SolException as e:
|
|
# any sort of error is considered. avoid 500 error.
|
|
raise sol_ex.TestNotificationFailed() from e
|
|
|
|
if resp.status_code != 204:
|
|
raise sol_ex.TestNotificationFailed()
|
|
|
|
|
|
def match_version(version, inst):
|
|
# - vnfSoftwareVersion 1
|
|
# - vnfdVersions 0..N
|
|
if version.vnfSoftwareVersion != inst.vnfSoftwareVersion:
|
|
return False
|
|
|
|
if not version.obj_attr_is_set('vnfdVersions'):
|
|
# OK, no more check necessary.
|
|
return True
|
|
|
|
return inst.vnfdVersion in version.vnfdVersions
|
|
|
|
|
|
def match_products_per_provider(products, inst):
|
|
# - vnfProvider 1
|
|
# - vnfProducts 0..N
|
|
# - vnfProductName 1
|
|
# - versions 0..N
|
|
# - vnfSoftwareVersion 1
|
|
# - vnfdVersions 0..N
|
|
if products.vnfProvider != inst.vnfProvider:
|
|
return False
|
|
|
|
if not products.obj_attr_is_set('vnfProducts'):
|
|
# OK, no more check necessary.
|
|
return True
|
|
|
|
for product in products.vnfProducts:
|
|
if product.vnfProductName == inst.vnfProductName:
|
|
if not product.obj_attr_is_set('versions'):
|
|
# OK, no more check necessary.
|
|
return True
|
|
for ver in product.versions:
|
|
if match_version(ver, inst):
|
|
# OK, match.
|
|
return True
|
|
# no match
|
|
return False
|
|
|
|
|
|
def match_inst_subsc_filter(inst_filter, inst):
|
|
# inst_filter: VnfInstanceSubscriptionFilter
|
|
# - vnfdIds 0..N
|
|
# - VnfProductsFromProviders 0..N
|
|
# - vnfInstanceIds 0..N
|
|
# - vnfInstanceNames 0..N
|
|
if inst_filter.obj_attr_is_set('vnfdIds'):
|
|
if inst.vnfdId not in inst_filter.vnfdIds:
|
|
return False
|
|
|
|
if inst_filter.obj_attr_is_set('vnfProductsFromProviders'):
|
|
products_providers = inst_filter.vnfProductsFromProviders
|
|
match = False
|
|
for products in products_providers:
|
|
if match_products_per_provider(products, inst):
|
|
match = True
|
|
break
|
|
if not match:
|
|
# no match found
|
|
return False
|
|
|
|
if inst_filter.obj_attr_is_set('vnfInstanceIds'):
|
|
if inst.id not in inst_filter.vnfInstanceIds:
|
|
return False
|
|
|
|
if inst_filter.obj_attr_is_set('vnfInstanceNames'):
|
|
if inst.vnfInstanceName not in inst_filter.vnfInstanceNames:
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def get_inst_create_subscs(context, inst):
|
|
return get_matched_subscs(context, inst,
|
|
'VnfIdentifierCreationNotification', None, None)
|
|
|
|
|
|
def get_inst_delete_subscs(context, inst):
|
|
return get_matched_subscs(context, inst,
|
|
'VnfIdentifierDeletionNotification', None, None)
|
|
|
|
|
|
def get_lcmocc_subscs(context, lcmocc, inst):
|
|
return get_matched_subscs(context, inst,
|
|
'VnfLcmOperationOccurrenceNotification',
|
|
lcmocc.operation, lcmocc.operationState)
|
|
|
|
|
|
def get_matched_subscs(context, inst, notif_type, op_type, op_state):
|
|
subscs = []
|
|
for subsc in get_subsc_all(context):
|
|
# subsc: LccnSubscription
|
|
|
|
if not subsc.obj_attr_is_set('filter'):
|
|
# no filter. get it.
|
|
subscs.append(subsc)
|
|
continue
|
|
|
|
# subsc.filter: LifecycleChangeNotificationsFilter
|
|
# - vnfInstanceSubscriptionFilter 0..1
|
|
# - notificationTypes 0..N
|
|
# - operationTypes 0..N
|
|
# - operationStates 0..N
|
|
if subsc.filter.obj_attr_is_set('vnfInstanceSubscriptionFilter'):
|
|
inst_filter = subsc.filter.vnfInstanceSubscriptionFilter
|
|
if not match_inst_subsc_filter(inst_filter, inst):
|
|
continue
|
|
|
|
if subsc.filter.obj_attr_is_set('notificationTypes'):
|
|
if notif_type not in subsc.filter.notificationTypes:
|
|
continue
|
|
|
|
if (op_type is not None and
|
|
subsc.filter.obj_attr_is_set('operationTypes')):
|
|
if op_type not in subsc.filter.operationTypes:
|
|
continue
|
|
|
|
if (op_state is not None and
|
|
subsc.filter.obj_attr_is_set('operationStates')):
|
|
if op_state not in subsc.filter.operationStates:
|
|
continue
|
|
|
|
# OK, matched
|
|
subscs.append(subsc)
|
|
|
|
return subscs
|
|
|
|
|
|
def make_create_inst_notif_data(subsc, inst, endpoint):
|
|
notif_data = objects.VnfIdentifierCreationNotificationV2(
|
|
id=uuidutils.generate_uuid(),
|
|
notificationType="VnfIdentifierCreationNotification",
|
|
subscriptionId=subsc.id,
|
|
timeStamp=timeutils.utcnow(),
|
|
vnfInstanceId=inst.id,
|
|
_links=objects.LccnLinksV2(
|
|
vnfInstance=objects.NotificationLink(
|
|
href=inst_utils.inst_href(inst.id, endpoint)),
|
|
subscription=objects.NotificationLink(
|
|
href=subsc_href(subsc.id, endpoint))
|
|
)
|
|
# vnfLcmOpOcc: is not necessary
|
|
)
|
|
return notif_data
|
|
|
|
|
|
def make_delete_inst_notif_data(subsc, inst, endpoint):
|
|
notif_data = objects.VnfIdentifierDeletionNotificationV2(
|
|
id=uuidutils.generate_uuid(),
|
|
notificationType="VnfIdentifierDeletionNotification",
|
|
subscriptionId=subsc.id,
|
|
timeStamp=timeutils.utcnow(),
|
|
vnfInstanceId=inst.id,
|
|
_links=objects.LccnLinksV2(
|
|
vnfInstance=objects.NotificationLink(
|
|
href=inst_utils.inst_href(inst.id, endpoint)),
|
|
subscription=objects.NotificationLink(
|
|
href=subsc_href(subsc.id, endpoint))
|
|
)
|
|
# vnfLcmOpOcc: is not necessary
|
|
)
|
|
return notif_data
|