Douglas Henrique Koerich b6457447e6 Allow subscription to all nodes with '*'
The O-RAN standard was not being followed when resource address was
being specified with '*' as the "nodeName", which means that
subscription should be created to be notified about all nodes.

Test Plan:
PASS: Submitting POST request with '*' wildcard in resource address;
PASS: Rejecting POST for specific node when there is a subscription for
      '*' in repo;
PASS: Rejecting POST for current node ('.') when there is a subscription
      for '*' in repo.

Closes-Bug: #1996929
Signed-off-by: Douglas Henrique Koerich <douglashenrique.koerich@windriver.com>
Change-Id: Ifdaa2d2b9c9a45ad41981d2a75f7101bfa2f7c9c
2022-11-21 15:19:38 -03:00

349 lines
15 KiB
Python

#
# Copyright (c) 2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import time
import oslo_messaging
import logging
from notificationclientsdk.model.dto.rpc_endpoint import RpcEndpointInfo
from notificationclientsdk.common.helpers import constants
from notificationclientsdk.common.helpers import log_helper
from notificationclientsdk.client.locationservice import LocationServiceClient
from notificationclientsdk.client.notificationservice \
import NotificationServiceClient
LOG = logging.getLogger(__name__)
log_helper.config_logger(LOG)
class BrokerConnectionManager:
def __init__(self, broker_location_handler, notification_handler,
broker_connection_contexts):
'''
broker_watchers: {
"<broker name1>": {
"broker_client": client1,
"subscribed_resource_list": ['PTP', ...]
},
{...}
...
}
'''
self.shared_broker_context = broker_connection_contexts
self.registration_endpoint = RpcEndpointInfo(
self.shared_broker_context['REGISTRATION_TRANSPORT_ENDPOINT'])
self.broker_watchers = {}
self.location_watcher = LocationServiceClient(
self.registration_endpoint.TransportEndpoint)
self.__broker_location_handler = broker_location_handler
self.__notification_handler = notification_handler
def __del__(self):
if self.location_watcher:
self.location_watcher.cleanup()
del self.location_watcher
self.location_watcher = None
def start(self):
self.__start_watch_all_nodes()
def stop(self):
self.__stop_watch_all_nodes()
def __validate(self, brokerstate):
valid = brokerstate.BrokerName or brokerstate.BrokerIP
return valid
def start_watching_broker(self, brokerstate):
try:
if not self.__validate(brokerstate):
return False
broker_name = brokerstate.BrokerName
# must make sure the location is updated/watched:
# 1, check and start location watcher
if not self.location_watcher.is_listening_on_location(broker_name):
# start watching on the location announcement
self.location_watcher.add_location_listener(
broker_name,
location_handler=self.__broker_location_handler)
LOG.debug("Start watching location announcement of "
"notificationservice@{0}".format(broker_name))
# try to update location by query
try:
location_info = self.location_watcher.query_location(
broker_name, timeout=5, retry=2)
LOG.debug("Pulled location info@{0}:{1}".format(
broker_name, location_info))
if location_info:
podip = location_info.get("PodIP", None)
resourcetypes = location_info.get("ResourceTypes",
None)
brokerstate.update_broker_ip(podip)
brokerstate.update_resources(resourcetypes)
else:
return False
except Exception as ex:
LOG.warning("Failed to update location of node:{0} "
"due to: {1}".format(broker_name, str(ex)))
raise ex
# 2, create broker connection
broker_watcher = self.broker_watchers.get(broker_name, {})
broker_client = broker_watcher.get("broker_client", None)
if not broker_client:
LOG.debug("Start watching notifications from "
"notificationservice@{0}".format(broker_name))
broker_client = self.__create_client(broker_name,
brokerstate.BrokerIP)
broker_watcher["broker_client"] = broker_client
self.broker_watchers[broker_name] = broker_watcher
# 3, update watching resources
result = self.__update_watching_resources(broker_watcher,
broker_client,
brokerstate)
return result
except Exception as ex:
LOG.warning("failed to start watching:{0},{1}".format(
brokerstate, str(ex)))
return False
def __stop_watching_broker_resource(self, broker_client, broker_name,
resource_type):
try:
if broker_client.is_listening_on_resource(resource_type):
broker_client.remove_resource_status_listener(resource_type)
return True
except Exception as ex:
LOG.warning("failed to stop watching resource:{0}@{1},{2}".format(
broker_name, resource_type, str(ex)))
return False
def __start_watching_broker_resource(self, broker_client, broker_name,
resource_type):
try:
if not broker_client.is_listening_on_resource(resource_type):
broker_client.add_resource_status_listener(
resource_type, status_handler=self.__notification_handler)
LOG.debug("Start watching {0}@{1}".format(resource_type,
broker_name))
return True
except Exception as ex:
LOG.warning("failed to start watching resource:{0}@{1},{2}".format(
resource_type, broker_name, str(ex)))
return False
def stop_watching_broker(self, broker_name):
try:
# 1, stop listening to broker's location announcement
if self.location_watcher.is_listening_on_location(broker_name):
self.location_watcher.remove_location_listener(broker_name)
LOG.debug("Stop watching location announcement for broker@{0}"
"".format(broker_name))
# 2, remove broker client
broker_watcher = self.broker_watchers.get(broker_name, {})
broker_client = broker_watcher.get("broker_client", None)
if broker_client:
broker_client.cleanup()
del broker_client
broker_client = None
self.broker_watchers.pop(broker_name, None)
LOG.debug("Stop watching notificationservice@{0}".format(
broker_name))
return True
except Exception as ex:
LOG.warning("failed to start watching:{0},{1}".format(
broker_name, str(ex)))
return False
def restart_watching_broker(self, brokerstate):
try:
broker_name = brokerstate.BrokerName
LOG.debug("Try to restart watching notificationservice@{0}".format(
broker_name))
broker_watcher = self.broker_watchers.get(broker_name, {})
broker_client = broker_watcher.get("broker_client", None)
if broker_client:
broker_client.cleanup()
del broker_client
broker_client = None
self.broker_watchers.pop(broker_name, None)
return self.start_watching_broker(brokerstate)
except Exception as ex:
LOG.warning("failed to restart watching:{0},{1}".format(
brokerstate, str(ex)))
return False
def update_watching_resources(self, brokerstate):
try:
broker_watcher = self.broker_watchers.get(brokerstate.BrokerName,
{})
broker_client = broker_watcher.get("broker_client", None)
if broker_client:
result = self.__update_watching_resources(broker_watcher,
broker_client,
brokerstate)
return result
return False
except Exception as ex:
LOG.warning("failed to start watching:{0},{1}".format(
brokerstate, str(ex)))
return False
def __update_watching_resources(self, broker_watcher, broker_client,
brokerstate):
try:
result = True
# 1, filter out those unsubscribed resources
subscribed_resource_list = broker_watcher.get(
"subscribed_resource_list", [])
if subscribed_resource_list != brokerstate.ResourceTypesSubscribed:
# stop watching those uninterested
for resource_type in subscribed_resource_list:
if resource_type not in \
brokerstate.ResourceTypesSubscribed:
result = self.__stop_watching_broker_resource(
broker_client, brokerstate.BrokerName,
resource_type)
# 2, update the list
subscribed_resource_list = brokerstate.ResourceTypesSubscribed
broker_watcher["subscribed_resource_list"] = \
subscribed_resource_list
# 3, start watching the subscribed resources
for resource_type in subscribed_resource_list:
result = self.__start_watching_broker_resource(
broker_client, brokerstate.BrokerName, resource_type) and \
result
return result
except Exception as ex:
LOG.warning("failed to update resources:{0},{1}".format(
brokerstate, str(ex)))
return False
def is_watching_broker(self, broker_name):
broker_watcher = self.broker_watchers.get(broker_name, {})
broker_client = broker_watcher.get("broker_client", None)
return broker_client is not None
def is_watching_resource(self, broker_name, resource_type):
broker_watcher = self.broker_watchers.get(broker_name, {})
broker_client = broker_watcher.get("broker_client", None)
return broker_client.is_listening_on_resource(
resource_type) if broker_client else False
def __create_client(self, broker_name, broker_pod_ip):
if broker_name == constants.WILDCARD_ALL_NODES:
# special case: if monitor all node, then use the same broker as
# locationservice
return self.location_watcher
broker_host = "[{0}]".format(broker_pod_ip)
broker_transport_endpoint = "rabbit://{0}:{1}@{2}:{3}".format(
self.shared_broker_context['NOTIFICATION_BROKER_USER'],
self.shared_broker_context['NOTIFICATION_BROKER_PASS'],
broker_host,
self.shared_broker_context['NOTIFICATION_BROKER_PORT'])
return NotificationServiceClient(broker_name,
broker_transport_endpoint,
broker_pod_ip)
def __start_watch_all_nodes(self, retry_interval=5):
try:
LOG.debug("Start watching location announcement of "
"notificationservice@{0}".format(
constants.WILDCARD_ALL_NODES))
while not self.location_watcher.is_listening_on_location(
constants.WILDCARD_ALL_NODES):
# start watching on the location announcement
self.location_watcher.add_location_listener(
constants.WILDCARD_ALL_NODES,
location_handler=self.__broker_location_handler)
if not self.location_watcher.is_listening_on_location(
constants.WILDCARD_ALL_NODES):
# retry later and forever
LOG.debug(
"Retry indefinitely to start listening to {0}..."
.format(constants.WILDCARD_ALL_NODES))
time.sleep(retry_interval)
LOG.debug(
"Trigger the location announcement of notificationservice@{0}"
.format(constants.WILDCARD_ALL_NODES))
self.location_watcher.trigger_location_annoucement(timeout=20,
retry=10)
except Exception as ex:
LOG.warning("exception: {0}".format(str(ex)))
pass
finally:
pass
return
def __stop_watch_all_nodes(self):
pass
def __syncup_data_by_resourcetype(self, broker_client, broker_name,
resource_type):
# check to sync up resource status on a node
LOG.debug("try to sync up data for {0}@{1}".format(
resource_type, broker_name))
try:
if broker_name == constants.WILDCARD_ALL_NODES:
self.location_watcher.trigger_publishing_status(
resource_type, timeout=5, retry=10)
return True
# 1, query resource status
broker_client = self.broker_watchers.get(broker_name, None)
if not broker_client:
raise Exception("watcher is not ready for broker: {0}".format(
broker_name))
resource_status = broker_client.query_resource_status(
resource_type, timeout=5, retry=10)
# 2, deliver resource by comparing LastDelivery time with
# EventTimestamp
# 3, update the LastDelivery with EventTimestamp
self.__notification_handler.handle(resource_status)
except oslo_messaging.exceptions.MessagingTimeout as ex:
LOG.warning("Fail to sync up data {0}@{1}, due to {2}".format(
resource_type, broker_name, str(ex)))
return False
except Exception as ex:
LOG.warning("Fail to sync up data {0}@{1}, due to {2}".format(
resource_type, broker_name, str(ex)))
raise ex
finally:
pass
return True
def syncup_broker_data(self, brokerstate):
aggregated_result = True
broker_name = brokerstate.BrokerName
try:
broker_watcher = self.broker_watchers.get(broker_name, {})
broker_client = broker_watcher.get("broker_client", None)
subscribed_resource_list = broker_watcher.get(
"subscribed_resource_list", [])
for resource_type in subscribed_resource_list:
if not brokerstate.is_data_syncup(resource_type):
continue
result = self.__syncup_data_by_resourcetype(
broker_client, broker_name, resource_type)
if result:
brokerstate.ack_data_syncup(resource_type)
aggregated_result = aggregated_result and result
return aggregated_result
except Exception as ex:
LOG.warning("failed to sync up data for resources:{0},{1}".format(
broker_name, str(ex)))
return False