X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=cmframework%2Fsrc%2Fcmframework%2Flib%2Fcmupdateimpl.py;fp=cmframework%2Fsrc%2Fcmframework%2Flib%2Fcmupdateimpl.py;h=9c256ab6c608d3744332ea6743d60a4bc048ff32;hb=c389bdee7b3845b55f443dbf04c0ce4083a55886;hp=0000000000000000000000000000000000000000;hpb=5030f0c004701dd422c78c71c014ef60f48139fc;p=ta%2Fconfig-manager.git diff --git a/cmframework/src/cmframework/lib/cmupdateimpl.py b/cmframework/src/cmframework/lib/cmupdateimpl.py new file mode 100644 index 0000000..9c256ab --- /dev/null +++ b/cmframework/src/cmframework/lib/cmupdateimpl.py @@ -0,0 +1,134 @@ +# Copyright 2019 Nokia + +# 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 logging +import time + +from cmframework.apis.cmerror import CMError +from cmframework.apis.cmmanage import CMManage +from cmframework.utils.cmpluginloader import CMPluginLoader +from cmframework.utils.cmdependencysort import CMDependencySort + +from cmdatahandlers.api.configmanager import ConfigManager +from cmdatahandlers.api import utils + + +class CMUpdateImpl(object): + def __init__(self, plugins_path, server_ip='config-manager', server_port=61100, + client_lib_impl_module='cmframework.lib.CMClientImpl', verbose_logger=None): + logging.info('CMUpdateImpl constructor, plugins_path is %s', plugins_path) + + self._plugins_path = plugins_path + self._handlers = {} + self._sorted_handlers = [] + + self._load_handlers() + self._load_dependencies() + + self.uuid_value = None + + self.api = CMManage(server_ip, server_port, client_lib_impl_module, verbose_logger) + + def _load_handlers(self): + loader = CMPluginLoader(self._plugins_path) + handler_modules, _ = loader.load() + logging.info('Handler module(s): %r', handler_modules) + + for handler_class_name, module in handler_modules.iteritems(): + handler_class = getattr(module, handler_class_name) + handler = handler_class() + self._handlers[handler_class_name] = handler + + def update(self, confman=None): + logging.info('Taking snapshot of the original configuration just in-case') + now = int(round(time.time()*1000)) + snapshot_name = 'cmupdate-' + str(now) + self.api.create_snapshot(snapshot_name) + if not confman: + properties = self.api.get_properties('.*') + propsjson = utils.unflatten_config_data(properties) + confman = ConfigManager(propsjson) + + for handler in self._sorted_handlers: + try: + logging.debug('Calling update for %s', handler) + self._handlers[handler].update(confman) + logging.debug('update for %s done', handler) + except Exception as ex: # pylint: disable=broad-except + logging.warning('Update handler %s failed: %s', handler, str(ex)) + raise + + properties = confman.get_config() + flatprops = utils.flatten_config_data(properties) + + try: + self.uuid_value = self.api.set_properties(flatprops, True) + except Exception as exp: # pylint: disable=broad-except + for handler in self._sorted_handlers: + try: + logging.debug('Calling validation_failed for %s', handler) + self._handlers[handler].validation_failed(confman) + except Exception as ex2: # pylint: disable=broad-except + logging.warning('Update handler %s validation_failed raised exception: %s', + handler, str(ex2)) + raise exp + + def wait_activation(self): + if self.uuid_value: + self.api.wait_activation(self.uuid_value) + + @staticmethod + def _read_dependency_file(file_name): + before_list = [] + after_list = [] + + try: + with open(file_name, 'r') as deps_file: + while True: + line = deps_file.readline() + if not line: + break + if line.startswith('Before:'): + before_list = line[7:].strip().replace(' ', '').split(',') + if line.startswith('After:'): + after_list = line[6:].strip().replace(' ', '').split(',') + except IOError: + logging.debug('Dependency file %s not found.', file_name) + + return (before_list, after_list) + + def _load_dependencies(self): + before_graph = {} + after_graph = {} + + for handler in self._handlers.values(): + deps_filename = '{}/{}.deps'.format(self._plugins_path, handler) + before_list, after_list = self._read_dependency_file(deps_filename) + + for before_dep in before_list: + if not self._handlers.get(before_dep, None): + raise CMError( + 'Unexisting handler {} referred in handler {}\'s "Before" dependencies' + .format(before_dep, handler)) + for after_dep in after_list: + if not self._handlers.get(after_dep, None): + raise CMError( + 'Unexisting handler {} referred in handler {}\'s "After" dependencies' + .format(after_dep, handler)) + + before_graph[str(handler)] = before_list + after_graph[str(handler)] = after_list + + sorter = CMDependencySort(after_graph, before_graph) + self._sorted_handlers = sorter.sort()