From e161cae314703d7717d55099566c2d3fe112ca52 Mon Sep 17 00:00:00 2001 From: "Zhang Rong(Jon)" Date: Tue, 13 Jun 2023 16:41:23 +0800 Subject: [PATCH] INF-417 InfrastructureInventoryObject implemented Issue-ID: INF-417 Change-Id: I722bf7ca509ed32e317a6247c1339c3a87a0e8cd Signed-off-by: Zhang Rong(Jon) --- o2common/domain/base.py | 14 + o2ims/domain/ocloud.py | 37 ++- o2ims/service/command/notify_handler.py | 353 +++++++------------------- o2ims/service/command/registration_handler.py | 10 +- tests/unit/test_notify.py | 50 ++++ 5 files changed, 186 insertions(+), 278 deletions(-) create mode 100644 tests/unit/test_notify.py diff --git a/o2common/domain/base.py b/o2common/domain/base.py index 128f950..698843c 100644 --- a/o2common/domain/base.py +++ b/o2common/domain/base.py @@ -20,6 +20,20 @@ from sqlalchemy.exc import NoInspectionAvailable from .events import Event +class InfrastructureInventoryObject: + ObjectState = {} + + def __init__(self) -> None: + # self.ObjectState = {} + pass + + def get_fields_as_dict(self, fields): + for field in fields: + if hasattr(self, field): + self.ObjectState[field] = getattr(self, field) + return self.ObjectState + + class AgRoot: events = [] diff --git a/o2ims/domain/ocloud.py b/o2ims/domain/ocloud.py index ee3da89..ce46e96 100644 --- a/o2ims/domain/ocloud.py +++ b/o2ims/domain/ocloud.py @@ -16,7 +16,8 @@ from __future__ import annotations import json from o2common.config import config -from o2common.domain.base import AgRoot, Serializer +from o2common.domain.base import AgRoot, Serializer, \ + InfrastructureInventoryObject # from dataclasses import dataclass # from datetime import date # from typing import Optional, List, Set @@ -29,7 +30,7 @@ DeploymentManagerProfileSOL018 = 'sol018' DeploymentManagerProfileSOL018HelmCLI = 'sol018_helmcli' -class DeploymentManager(AgRoot, Serializer): +class DeploymentManager(InfrastructureInventoryObject, AgRoot, Serializer): def __init__(self, id: str, name: str, ocloudid: str, dmsendpoint: str, description: str = '', supportedLocations: str = '', capabilities: str = '', @@ -67,8 +68,13 @@ class DeploymentManager(AgRoot, Serializer): d['capabilities'] = json.loads(d['capabilities']) return d + def get_notification_dict(self): + return self.get_fields_as_dict( + ['deploymentManagerId', 'name', 'oCloudId', 'serviceUri', + 'description']) -class ResourcePool(AgRoot, Serializer): + +class ResourcePool(InfrastructureInventoryObject, AgRoot, Serializer): def __init__(self, id: str, name: str, location: str, ocloudid: str, gLocationId: str = '', description: str = '') -> None: @@ -84,8 +90,13 @@ class ResourcePool(AgRoot, Serializer): self.version_number = 0 + def get_notification_dict(self): + return self.get_fields_as_dict( + ['resourcePoolId', 'oCloudId', 'globalLocationId', 'name', + 'description']) + -class ResourceType(AgRoot, Serializer): +class ResourceType(InfrastructureInventoryObject, AgRoot, Serializer): def __init__(self, typeid: str, name: str, typeEnum: ResourceTypeEnum, vendor: str = '', model: str = '', version: str = '', @@ -112,8 +123,13 @@ class ResourceType(AgRoot, Serializer): d['alarmDictionary'] = d['alarmDictionary'].serialize() return d + def get_notification_dict(self): + return self.get_fields_as_dict( + ['resourceTypeId', 'name', 'description', 'vendor', 'model', + 'version']) -class Resource(AgRoot, Serializer): + +class Resource(InfrastructureInventoryObject, AgRoot, Serializer): def __init__(self, resourceId: str, resourceTypeId: str, resourcePoolId: str, parentId: str = '', gAssetId: str = '', elements: str = '', @@ -153,8 +169,13 @@ class Resource(AgRoot, Serializer): d['children'].append(child.serialize()) return d + def get_notification_dict(self): + return self.get_fields_as_dict( + ['resourceId', 'resourceTypeId', 'resourcePoolId', 'globalAssetId', + 'description']) + -class Ocloud(AgRoot, Serializer): +class Ocloud(InfrastructureInventoryObject, AgRoot, Serializer): def __init__(self, ocloudid: str, name: str, imsendpoint: str, globalcloudId: str = '', description: str = '', version_number: int = 0) -> None: @@ -172,6 +193,10 @@ class Ocloud(AgRoot, Serializer): self.version_number = version_number + def get_notification_dict(self): + return self.get_fields_as_dict( + ['oCloudId', 'globalcloudId', 'globalCloudId', 'name', + 'description', 'serviceUri']) # def addDeploymentManager(self, # deploymentManager: DeploymentManager): diff --git a/o2ims/service/command/notify_handler.py b/o2ims/service/command/notify_handler.py index 19ff0c0..88a0e90 100644 --- a/o2ims/service/command/notify_handler.py +++ b/o2ims/service/command/notify_handler.py @@ -50,93 +50,62 @@ def notify_change_to_smo( _notify_resource(uow, notifications, cmd.data) -# def get_resource_dict(resource_type): -# return { -# 'resourceTypeId': resource_type.resourceTypeId, -# 'name': resource_type.name, -# 'description': resource_type.description, -# 'vendor': resource_type.vendor, -# 'model': resource_type.model, -# 'version': resource_type.version, -# } -# -# -# def handle_filter(filter: str, f_type: str): -# if not filter: -# return -# -# filter_list = filter.strip(' []').split('|') -# -# match_type_count = 0 -# filters = [] -# for sub_filter in filter_list: -# objectType, objectTypeValue = get_object_type_and_value(sub_filter) -# if objectTypeValue == f_type: -# match_type_count += 1 -# filters.append(sub_filter) -# elif not objectType and f_type == 'ResourceInfo': -# filters.append(sub_filter) -# -# return match_type_count, filters -# -# -# def get_object_type_and_value(sub_filter): -# exprs = sub_filter.split(';') -# for expr in exprs: -# items = expr.strip(' ()').split(',') -# item_key = items[1].strip() -# if item_key == 'objectType': -# return True, items[2].strip() -# return False, '' -# -# -# def check_filters(filters, sub_data, uow, id): -# for filter in filters[1]: -# if isinstance(filter, bool) and filter: -# return True -# -# try: -# args = gen_orm_filter(ocloud.ResourceType, filter) -# except KeyError: -# logger.warning( -# 'Subscription {} filter {} has wrong attribute ' -# 'name or value. Ignore the filter.'.format( -# sub_data['subscriptionId'], -# sub_data['filter'])) -# continue -# -# if len(args) == 0 and 'objectType' in filter: -# return True -# -# args.append(ocloud.ResourceType.resourceTypeId == id) -# obj_count, _ = uow.resource_types.list_with_count(*args) -# if obj_count > 0: -# return True -# return False -# -# -# def _notify_resourcetype(uow, notifications, data): -# with uow: -# resource_type = uow.resource_types.get(data.id) -# if resource_type is None: -# logger.warning('ResourceType {} does not exists.'.format(data.id)) -# return -# -# resource_type_dict = get_resource_dict(resource_type) -# -# subs = uow.subscriptions.list() -# for sub in subs: -# sub_data = sub.serialize() -# filters = handle_filter(sub_data['filter'], 'ResourceTypeInfo') -# logger.debug(f'filters: {filters}, sub_data: {sub_data}') -# -# if not filters or filters[0] == 0 or check_filters( -# filters, sub_data, uow, data.id): -# callback_smo(notifications, sub, data, resource_type_dict) -# continue -# -# logger.info('Subscription {} filter hit, skip ResourceType {}.' -# .format(sub_data['subscriptionId'], data.id)) +def __get_object_type_and_value(sub_filter): + exprs = sub_filter.split(';') + for expr in exprs: + items = expr.strip(' ()').split(',') + item_key = items[1].strip() + if item_key == 'objectType': + return True, items[2].strip() + return False, '' + + +def handle_filter(filter: str, f_type: str): + print(filter) + if not filter: + return + + filter_list = filter.strip(' []').split('|') + if not filter_list: + return + + match_type_count = 0 + filters = [] + for sub_filter in filter_list: + objectType, objectTypeValue = __get_object_type_and_value(sub_filter) + if objectTypeValue == f_type: + match_type_count += 1 + filters.append(sub_filter) + elif not objectType and f_type == 'ResourceInfo': + match_type_count += 1 + filters.append(sub_filter) + + return match_type_count, filters + + +def check_filters(filters, sub_data, uow_cls, obj_cls, attr_id, id): + for filter in filters[1]: + logger.debug(f'filter: {filter}') + try: + args = gen_orm_filter(obj_cls, filter) + except KeyError: + logger.warning( + 'Subscription {} filter {} has wrong attribute ' + 'name or value. Ignore the filter.'.format( + sub_data['subscriptionId'], + sub_data['filter'])) + continue + logger.debug(f'args: {args}') + + if len(args) == 0 and 'objectType' in filter: + return False + + args.append(attr_id == id) + obj_count, _ = uow_cls.list_with_count(*args) + if obj_count > 0: + return True + return False + def _notify_resourcetype(uow, notifications, data): with uow: @@ -144,51 +113,24 @@ def _notify_resourcetype(uow, notifications, data): if resource_type is None: logger.warning('ResourceType {} does not exists.'.format(data.id)) return - resource_type_dict = { - 'resourceTypeId': resource_type.resourceTypeId, - 'name': resource_type.name, - 'description': resource_type.description, - 'vendor': resource_type.vendor, - 'model': resource_type.model, - 'version': resource_type.version, - # 'alarmDictionary': resource_type.alarmDictionary.serialize() - } + + resource_type_dict = resource_type.get_notification_dict() subs = uow.subscriptions.list() for sub in subs: sub_data = sub.serialize() logger.debug('Subscription: {}'.format(sub_data['subscriptionId'])) filters = handle_filter(sub_data['filter'], 'ResourceTypeInfo') - if not filters or filters[0] == 0: + logger.debug(f'filters: {filters}, sub_data: {sub_data}') + + if not filters or filters[0] == 0 or check_filters( + filters, sub_data, uow.resource_types, ocloud.ResourceType, + ocloud.ResourceType.resourceTypeId, data.id): callback_smo(notifications, sub, data, resource_type_dict) continue - if filters[0] > 0 and not filters[1]: - continue - filter_hit = False - for filter in filters[1]: - try: - args = gen_orm_filter(ocloud.ResourceType, filter) - except KeyError: - logger.warning( - 'Subscription {} filter {} has wrong attribute ' - 'name or value. Ignore the filter.'.format( - sub_data['subscriptionId'], - sub_data['filter'])) - continue - if len(args) == 0 and 'objectType' in filter: - filter_hit = True - break - args.append(ocloud.ResourceType.resourceTypeId == data.id) - obj_count, _ = uow.resource_types.list_with_count(*args) - if obj_count > 0: - filter_hit = True - break - if filter_hit: - logger.info('Subscription {} filter hit, skip ResourceType {}.' - .format(sub_data['subscriptionId'], data.id)) - else: - callback_smo(notifications, sub, data, resource_type_dict) + logger.info('Subscription {} filter hit, skip ResourceType {}.' + .format(sub_data['subscriptionId'], data.id)) def _notify_resourcepool(uow, notifications, data): @@ -197,48 +139,24 @@ def _notify_resourcepool(uow, notifications, data): if resource_pool is None: logger.warning('ResourcePool {} does not exists.'.format(data.id)) return - resource_pool_dict = { - 'resourcePoolId': resource_pool.resourcePoolId, - 'oCloudId': resource_pool.oCloudId, - 'globalLocationId': resource_pool.globalLocationId, - 'name': resource_pool.name, - 'description': resource_pool.description - } + + resource_pool_dict = resource_pool.get_notification_dict() subs = uow.subscriptions.list() for sub in subs: sub_data = sub.serialize() logger.debug('Subscription: {}'.format(sub_data['subscriptionId'])) filters = handle_filter(sub_data['filter'], 'ResourcePoolInfo') - if not filters or filters[0] == 0: + logger.debug(f'filters: {filters}, sub_data: {sub_data}') + + if not filters or filters[0] == 0 or check_filters( + filters, sub_data, uow.resource_pools, ocloud.ResourcePool, + ocloud.ResourcePool.resourcePoolId, data.id): callback_smo(notifications, sub, data, resource_pool_dict) continue - if filters[0] > 0 and not filters[1]: - continue - filter_hit = False - for filter in filters[1]: - try: - args = gen_orm_filter(ocloud.ResourcePool, filter) - except KeyError: - logger.warning( - 'Subscription {} filter {} has wrong attribute ' - 'name or value. Ignore the filter.'.format( - sub_data['subscriptionId'], - sub_data['filter'])) - continue - if len(args) == 0 and 'objectType' in filter: - filter_hit = True - break - args.append(ocloud.ResourcePool.resourcePoolId == data.id) - obj_count, _ = uow.resource_pools.list_with_count(*args) - if obj_count > 0: - filter_hit = True - break - if filter_hit: - logger.info('Subscription {} filter hit, skip ResourcePool {}.' - .format(sub_data['subscriptionId'], data.id)) - else: - callback_smo(notifications, sub, data, resource_pool_dict) + + logger.info('Subscription {} filter hit, skip ResourcePool {}.' + .format(sub_data['subscriptionId'], data.id)) def _notify_dms(uow, notifications, data): @@ -248,52 +166,27 @@ def _notify_dms(uow, notifications, data): logger.warning( 'DeploymentManager {} does not exists.'.format(data.id)) return - dms_dict = { - 'deploymentManagerId': dms.deploymentManagerId, - 'name': dms.name, - 'description': dms.description, - 'oCloudId': dms.oCloudId, - 'serviceUri': dms.serviceUri - } + + dms_dict = dms.get_notification_dict() subs = uow.subscriptions.list() for sub in subs: sub_data = sub.serialize() logger.debug('Subscription: {}'.format(sub_data['subscriptionId'])) - filters_rst = handle_filter( + filters = handle_filter( sub_data['filter'], 'DeploymentManagerInfo') - if not filters_rst or filters_rst[0] == 0: + logger.debug(f'filters: {filters}, sub_data: {sub_data}') + + if not filters or filters[0] == 0 or check_filters( + filters, sub_data, uow.deployment_managers, + ocloud.DeploymentManager, + ocloud.DeploymentManager.deploymentManagerId, data.id): callback_smo(notifications, sub, data, dms_dict) continue - if filters_rst[0] > 0 and not filters_rst[1]: - continue - filter_hit = False - for filter in filters_rst[1]: - try: - args = gen_orm_filter(ocloud.DeploymentManager, filter) - except KeyError: - logger.warning( - 'Subscription {} filter {} has wrong attribute ' - 'name or value. Ignore the filter.'.format( - sub_data['subscriptionId'], - sub_data['filter'])) - continue - if len(args) == 0 and 'objectType' in filter: - filter_hit = True - break - args.append( - ocloud.DeploymentManager.deploymentManagerId == data.id) - obj_count, _ = uow.deployment_managers.list_with_count(*args) - if obj_count > 0: - filter_hit = True - break - if filter_hit: - logger.info('Subscription {} filter hit, skip ' - 'DeploymentManager {}.' - .format(sub_data['subscriptionId'], data.id)) - else: - continue - # callback_smo(notifications, sub, data, dms_dict) + + logger.info('Subscription {} filter hit, skip ' + 'DeploymentManager {}.' + .format(sub_data['subscriptionId'], data.id)) def _notify_resource(uow, notifications, data): @@ -304,13 +197,8 @@ def _notify_resource(uow, notifications, data): return res_pool_id = resource.serialize()['resourcePoolId'] logger.debug('res pool id is {}'.format(res_pool_id)) - res_dict = { - 'resourceId': resource.resourceId, - 'description': resource.description, - 'resourceTypeId': resource.resourceTypeId, - 'resourcePoolId': resource.resourcePoolId, - 'globalAssetId': resource.globalAssetId - } + + res_dict = resource.get_notification_dict() subs = uow.subscriptions.list() for sub in subs: @@ -349,37 +237,6 @@ def _notify_resource(uow, notifications, data): callback_smo(notifications, sub, data, res_dict) -def handle_filter(filter: str, f_type: str): - if not filter: - return - match_type_count = 0 - filter_strip = filter.strip(' []') - filter_list = filter_strip.split('|') - if not filter_list: - return - filters = list() - for sub_filter in filter_list: - exprs = sub_filter.split(';') - objectType = False - objectTypeValue = '' - for expr in exprs: - expr_strip = expr.strip(' ()') - items = expr_strip.split(',') - item_key = items[1].strip() - if item_key != 'objectType': - continue - objectType = True - objectTypeValue = items[2].strip() - if not objectType: - if f_type == 'ResourceInfo': - filters.append(sub_filter) - continue - if objectTypeValue == f_type: - match_type_count += 1 - filters.append(sub_filter) - return (match_type_count, filters) - - def callback_smo(notifications: AbstractNotifications, sub: Subscription, msg: Message2SMO, obj_dict: dict = None): sub_data = sub.serialize() @@ -401,33 +258,3 @@ def callback_smo(notifications: AbstractNotifications, sub: Subscription, logger.debug('callback data: {}'.format(json.dumps(callback))) return notifications.send(sub_data['callback'], callback) - - # Call SMO through the SMO callback url - # o = urlparse(sub_data['callback']) - # if o.scheme == 'https': - # conn = get_https_conn_default(o.netloc) - # else: - # conn = get_http_conn(o.netloc) - # try: - # rst, status = post_data(conn, o.path, callback_data) - # if rst is True: - # logger.info( - # 'Notify to SMO successed with status: {}'.format(status)) - # return - # logger.error('Notify Response code is: {}'.format(status)) - # except ssl.SSLCertVerificationError as e: - # logger.debug( - # 'Notify try to post data with trusted ca failed: {}'.format(e)) - # if 'self signed' in str(e): - # conn = get_https_conn_selfsigned(o.netloc) - # try: - # return post_data(conn, o.path, callback_data) - # except Exception as e: - # logger.info( - # 'Notify post data with self-signed ca \ - # failed: {}'.format(e)) - # return False - # return False - # except Exception as e: - # logger.critical('Notify except: {}'.format(e)) - # return False diff --git a/o2ims/service/command/registration_handler.py b/o2ims/service/command/registration_handler.py index e6f4904..a7978f5 100644 --- a/o2ims/service/command/registration_handler.py +++ b/o2ims/service/command/registration_handler.py @@ -47,15 +47,7 @@ def registry_to_smo( logger.warning('Ocloud {} does not exists.'.format(data.id)) return logger.debug('O-Cloud Global UUID: {}'.format(ocloud.globalCloudId)) - # ocloud_dict = ocloud.serialize() - ocloud_dict = { - 'oCloudId': ocloud.oCloudId, - 'globalcloudId': ocloud.globalCloudId, - 'globalCloudId': ocloud.globalCloudId, - 'name': ocloud.name, - 'description': ocloud.description, - 'serviceUri': ocloud.serviceUri - } + ocloud_dict = ocloud.get_notification_dict() if data.notificationEventType == NotificationEventEnum.CREATE: register_smo(notifications, ocloud_dict) elif data.notificationEventType in [NotificationEventEnum.MODIFY, diff --git a/tests/unit/test_notify.py b/tests/unit/test_notify.py new file mode 100644 index 0000000..a60e8e8 --- /dev/null +++ b/tests/unit/test_notify.py @@ -0,0 +1,50 @@ +# Copyright (C) 2023 Wind River Systems, 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. +from o2ims.service.command import notify_handler + + +def test_handle_filter(): + assert notify_handler.handle_filter( + "(eq,resourceId,resid1234)", "ResourceInfo") == ( + 1, ['(eq,resourceId,resid1234)']) + assert notify_handler.handle_filter( + "[(eq,objectType,ResourceInfo);(eq,resourceId,resid1234)]", + "ResourceInfo") == ( + 1, ['(eq,objectType,ResourceInfo);(eq,resourceId,resid1234)']) + assert notify_handler.handle_filter( + "[eq,objectType,ResourceTypeInfo|eq,objectType,ResourcePoolInfo]", + "ResourceInfo") == (0, []) + assert notify_handler.handle_filter( + "", "ResourceInfo") is None + assert notify_handler.handle_filter( + "", "ResourceTypeInfo") is None + assert notify_handler.handle_filter( + "[eq,objectType,ResourceInfo|eq,objectType,ResourceTypeInfo]", + "ResourceInfo") == (1, ['eq,objectType,ResourceInfo']) + assert notify_handler.handle_filter( + "[(eq,objectType,ResourceInfo);(eq,resourceId,resourceid1234) | " + + "(eq,objectType,ResourceInfo);(eq,resourceTypeId,restype1234)", + "ResourceInfo") == \ + (2, ['(eq,objectType,ResourceInfo);(eq,resourceId,resourceid1234) ', + ' (eq,objectType,ResourceInfo);(eq,resourceTypeId,restype1234)' + ]) + assert notify_handler.handle_filter( + "(eq,objectType,ResourceTypeInfo)|(eq,objectType,ResourcePoolInfo)" + + "|(eq,objectType,CloudInfo)|(eq,objectType,ResourceInfo)", + "DeploymentManagerInfo" + ) == (0, []) + + assert notify_handler.handle_filter( + "(eq,objectType,ResourceInfo)", "ResourceInfo") == ( + 1, ['(eq,objectType,ResourceInfo)']) -- 2.16.6