From 1d693376205c66af93283d04e8e9740c947a7d02 Mon Sep 17 00:00:00 2001 From: Alex Shatov Date: Fri, 24 Aug 2018 13:15:04 -0400 Subject: 4.2.0 policy-handler - periodic reconfigure - reconfigure == periodically retrieve the policy-handler config from consul-kv and compare to previous config and subconfigs. If changed, reconfigure the subunits - selectively change one or any settings for the following = catch_up timer interval = reconfigure timer interval = deployment-handler url and params (thread-safe) = policy-engine url and params (thread-safe) = web-socket url to policy-engine (through a callback) - each subunit has its own Settings that keep track of changes - try-catch and metrics around discovery - consul API - hidden the secrets from logs - froze the web-socket version to 0.49.0 because 0.50.0 and 0.51.0 are broken - looking around for stable alternatives - fixed-adapted the callbacks passed to the web-socket lib that changed its API in 0.49.0 and later - log the stack on the exception occurring in the web-socket lib - unit test refactoring Change-Id: Id53bad59660a197f59d9aeb7c05ab761d1060cd0 Signed-off-by: Alex Shatov Issue-ID: DCAEGEN2-470 --- policyhandler/config.py | 174 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 130 insertions(+), 44 deletions(-) (limited to 'policyhandler/config.py') diff --git a/policyhandler/config.py b/policyhandler/config.py index 8e6edf9..3d68235 100644 --- a/policyhandler/config.py +++ b/policyhandler/config.py @@ -25,64 +25,131 @@ import logging.config import os from .discovery import DiscoveryClient +from .onap.audit import Audit +from .policy_utils import Utils + +LOGS_DIR = 'logs' + +try: + os.makedirs(LOGS_DIR, mode=0o770, exist_ok=True) +except Exception: + pass logging.basicConfig( - filename='logs/policy_handler.log', \ - format='%(asctime)s.%(msecs)03d %(levelname)+8s ' + \ - '%(threadName)s %(name)s.%(funcName)s: %(message)s', \ + filename=os.path.join(LOGS_DIR, 'policy_handler.log'), + format=('%(asctime)s.%(msecs)03d %(levelname)+8s ' + + '%(threadName)s %(name)s.%(funcName)s: %(message)s'), datefmt='%Y%m%d_%H%M%S', level=logging.DEBUG) +class Settings(object): + """settings of module or an application + that is the config filtered by the collection of config-keys. + + keeps track of changes versus the previous set_config unless committed + """ + def __init__(self, *config_keys): + """provide the collection of top level keys in config to limit the config""" + self._config_keys = config_keys + self._changed = False + self._config = None + self._prev_config = None + + def __str__(self): + """get str of the config""" + return Audit.json_dumps({ + "config_keys": self._config_keys, + "changed": self._changed, + "config": self._config, + "prev_config": self._prev_config + }) + + def is_loaded(self): + """whether loaded already""" + return bool(self._config) + + def commit_change(self): + """set the prev config to the latest config""" + self._prev_config = copy.deepcopy(self._config) + self._changed = False + + def _set_changed(self): + """determine whether the config changed""" + self._changed = not (self._prev_config + and Utils.are_the_same(self._prev_config, self._config, + Audit.json_dumps)) + + def set_config(self, config, auto_commit=False): + """update the config""" + self.commit_change() + + if isinstance(config, Settings): + config = config._config + + if not isinstance(config, dict): + config = {} + + self._config = copy.deepcopy(dict((k, v) for (k, v) in config.items() + if not self._config_keys or k in self._config_keys)) + + if auto_commit: + self.commit_change() + else: + self._set_changed() + + def is_changed(self): + """whether the config has changed""" + return self._changed + + def get_by_key(self, config_key, default=None): + """get the latest sub config by config_key and whether it has changed""" + if not config_key or not isinstance(config_key, str): + return False, default + + value = copy.deepcopy(self._config.get(config_key, default)) + if not self._prev_config: + return True, value + prev_value = self._prev_config.get(config_key, default) + return self._changed and not Utils.are_the_same(prev_value, value, Audit.json_dumps), value + + def update(self, config_key, value=None): + """set the latest sub config by config_key and determine whether the config has changed""" + if not config_key: + return + + self._config[config_key] = copy.deepcopy(value) + self._set_changed() + + class Config(object): """main config of the application""" + _logger = logging.getLogger("policy_handler.config") CONFIG_FILE_PATH = "etc/config.json" LOGGER_CONFIG_FILE_PATH = "etc/common_logger.config" SERVICE_NAME_POLICY_HANDLER = "policy_handler" + FIELD_SYSTEM = "system" FIELD_WSERVICE_PORT = "wservice_port" FIELD_POLICY_ENGINE = "policy_engine" + POOL_CONNECTIONS = "pool_connections" + DEPLOY_HANDLER = "deploy_handler" + THREAD_POOL_SIZE = "thread_pool_size" + POLICY_RETRY_COUNT = "policy_retry_count" + POLICY_RETRY_SLEEP = "policy_retry_sleep" + RECONFIGURE = "reconfigure" + TIMER_INTERVAL = "interval" + + system_name = SERVICE_NAME_POLICY_HANDLER wservice_port = 25577 - _logger = logging.getLogger("policy_handler.config") - settings = None + _local_config = Settings() + discovered_config = Settings() @staticmethod - def merge(new_config): - """merge the new_config into current config - override the values""" - if not new_config: - return - - if not Config.settings: - Config.settings = new_config - return - - new_config = copy.deepcopy(new_config) - Config.settings.update(new_config) - - @staticmethod - def get_system_name(): - """find the name of the policy-handler system - to be used as the key in consul-kv for config of policy-handler - """ - return (Config.settings or {}).get(Config.FIELD_SYSTEM, Config.SERVICE_NAME_POLICY_HANDLER) - - @staticmethod - def discover(): - """bring and merge the config settings from the discovery service""" - discovery_key = Config.get_system_name() - new_config = DiscoveryClient.get_value(discovery_key) - - if not new_config or not isinstance(new_config, dict): - Config._logger.warning("unexpected config from discovery: %s", new_config) + def init_config(file_path=None): + """read and store the config from config file""" + if Config._local_config.is_loaded(): + Config._logger.info("config already inited: %s", Config._local_config) return - Config._logger.debug("loaded config from discovery(%s): %s", \ - discovery_key, json.dumps(new_config)) - Config._logger.debug("config before merge from discovery: %s", json.dumps(Config.settings)) - Config.merge(new_config.get(Config.SERVICE_NAME_POLICY_HANDLER)) - Config._logger.info("merged config from discovery: %s", json.dumps(Config.settings)) - - @staticmethod - def load_from_file(file_path=None): - """read and store the config from config file""" if not file_path: file_path = Config.CONFIG_FILE_PATH @@ -101,6 +168,25 @@ class Config(object): logging.config.dictConfig(logging_config) Config.wservice_port = loaded_config.get(Config.FIELD_WSERVICE_PORT, Config.wservice_port) - Config.merge(loaded_config.get(Config.SERVICE_NAME_POLICY_HANDLER)) - Config._logger.info("config loaded from file: %s", json.dumps(Config.settings)) - return True + + local_config = loaded_config.get(Config.SERVICE_NAME_POLICY_HANDLER) + Config.system_name = local_config.get(Config.FIELD_SYSTEM, Config.system_name) + + Config._local_config.set_config(local_config, auto_commit=True) + Config._logger.info("config loaded from file(%s): %s", file_path, Config._local_config) + + @staticmethod + def discover(audit): + """bring and merge the config settings from the discovery service""" + discovery_key = Config.system_name + new_config = DiscoveryClient.get_value(audit, discovery_key) + + if not new_config or not isinstance(new_config, dict): + Config._logger.warning("unexpected config from discovery: %s", new_config) + return + + Config._logger.debug("loaded config from discovery(%s): %s", + discovery_key, Audit.json_dumps(new_config)) + + Config.discovered_config.set_config(new_config.get(Config.SERVICE_NAME_POLICY_HANDLER)) + Config._logger.info("config from discovery: %s", Config.discovered_config) -- cgit 1.2.3-korg