Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / server / cmprocessor.py
diff --git a/cmframework/src/cmframework/server/cmprocessor.py b/cmframework/src/cmframework/server/cmprocessor.py
new file mode 100644 (file)
index 0000000..d5e7ea9
--- /dev/null
@@ -0,0 +1,316 @@
+# 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
+
+from cmframework.utils import cmactivationwork
+from cmframework.server import cmeventletrwlock
+from cmframework.server import cmcsn
+from cmframework.server import cmsnapshot
+from cmframework.utils.cmflagfile import CMFlagFile
+from cmframework.utils import cmalarm
+
+
+class CMProcessor(object):
+    SERVICE_GROUP_NAME = 'config-manager'
+
+    def __init__(self,
+                 backend_handler,
+                 validator,
+                 activator,
+                 changemonitor,
+                 activationstate_handler,
+                 snapshot_handler):
+        logging.debug('CMProcessor constructed')
+
+        self.backend_handler = backend_handler
+        self.lock = cmeventletrwlock.CMEventletRWLock()
+        self.csn = cmcsn.CMCSN(self.backend_handler)
+        self.validator = validator
+        self.activator = activator
+        self.reboot_requests = set()
+        self.automatic_activation_disabled = CMFlagFile('automatic_activation_disabled')
+        self.changemonitor = changemonitor
+        self.activationstate_handler = activationstate_handler
+        self.snapshot = cmsnapshot.CMSnapshot(snapshot_handler)
+
+    def reboot_request(self, node_name):
+        logging.debug('reboot_request called for %s', node_name)
+
+        self.reboot_requests.add(node_name)
+
+    def _clear_reboot_requests(self):
+        logging.debug('_clear_reboot_requests called')
+
+        self.reboot_requests.clear()
+
+    def _raise_reboot_alarms(self):
+        logging.debug('_raise_reboot_alarms called')
+
+        reboot_request_alarm = cmalarm.CMRebootRequestAlarm()
+
+        for node_name in self.reboot_requests:
+            reboot_request_alarm.raise_alarm_for_node(node_name)
+
+    def get_property(self, prop_name, snapshot_name=None):
+        logging.debug('get_property called for %s', prop_name)
+
+        with self.lock.reader():
+            if snapshot_name:
+                self.snapshot.load(snapshot_name)
+
+                return self.snapshot.get_property(prop_name)
+
+            return self.backend_handler.get_property(prop_name)
+
+    def get_properties(self, prop_filter, snapshot_name=None):
+        logging.debug('get_properties  called with filter %s', prop_filter)
+
+        with self.lock.reader():
+            if snapshot_name:
+                self.snapshot.load(snapshot_name)
+
+                return self.snapshot.get_properties(prop_filter)
+
+            return self.backend_handler.get_properties(prop_filter)
+
+    def set_property(self, prop_name, prop_value):
+        logging.debug('set_property called %s=%s', prop_name, prop_value)
+
+        props = {}
+        props[prop_name] = prop_value
+        return self.set_properties(props)
+
+    def set_properties(self, props, overwrite=False):
+        logging.debug('set_properties called for %s', str(props))
+
+        with self.lock.writer():
+            self._validate_set(props)
+            if overwrite:
+                logging.debug('Deleting old configuration data as requested')
+                orig_props = self.backend_handler.get_properties('.*')
+                self.backend_handler.delete_properties(orig_props.keys())
+            self.backend_handler.set_properties(props)
+            self.csn.increment()
+
+        if not self.automatic_activation_disabled:
+            return self._activate_set(props)
+
+        return "0"
+
+    def delete_property(self, prop_name):
+        logging.debug('delete_property called for %s', prop_name)
+
+        props = []
+        props.append(prop_name)
+        return self._delete_properties(props, None)
+
+    def delete_properties(self, arg):
+        logging.debug('delete_properties called with arg %r', arg)
+
+        keys = []
+        prop_filter = None
+        if isinstance(arg, str):
+            prop_filter = arg
+            props = self.get_properties(prop_filter)
+            keys = props.keys()
+        else:
+            keys = arg
+        return self._delete_properties(keys, prop_filter)
+
+    def _delete_properties(self, props, props_filter):
+        logging.debug('_delete_properties called with props %s filter %s', props, props_filter)
+
+        with self.lock.writer():
+            self._validate_delete(props)
+            if props_filter:
+                self.backend_handler.delete_properties(props_filter)
+            else:
+                if len(props) == 1:
+                    self.backend_handler.delete_property(props[0])
+                else:
+                    self.backend_handler.delete_properties(props)
+            self.csn.increment()
+
+        if not self.automatic_activation_disabled:
+            return self._activate_delete(props)
+
+        return "0"
+
+    def _validate_set(self, props):
+        logging.debug('_validate_set called for %s', str(props))
+
+        self.validator.validate_set(props)
+
+    def _activate_set_no_lock(self, props):
+        logging.debug('_activate_set_no_lock called for %s', str(props))
+
+        uuid_value = self.changemonitor.start_change()
+
+        work = cmactivationwork.CMActivationWork(cmactivationwork.CMActivationWork.OPER_SET,
+                                                 self.csn.get(), props)
+        work.uuid_value = uuid_value
+        self.activator.add_work(work)
+        return uuid_value
+
+    def _activate_set(self, props):
+        logging.debug('_activate_set called')
+
+        with self.lock.reader():
+            return self._activate_set_no_lock(props)
+
+    def _validate_delete(self, props):
+        logging.debug('_validate_delete called for %s', str(props))
+
+        self.validator.validate_delete(props)
+
+    def _activate_delete(self, props):
+        logging.debug('_activate_delete called for %s', str(props))
+
+        with self.lock.reader():
+            uuid_value = self.changemonitor.start_change()
+            work = cmactivationwork.CMActivationWork(cmactivationwork.CMActivationWork.OPER_DELETE,
+                                                     self.csn.get(), props)
+            work.uuid_value = uuid_value
+            self.activator.add_work(work)
+            return uuid_value
+
+    def create_snapshot(self, snapshot_name):
+        logging.debug('create_snapshot called, snapshot name is %s', snapshot_name)
+
+        with self.lock.writer():
+            self.snapshot.create(snapshot_name, self.backend_handler)
+
+    def restore_snapshot(self, snapshot_name):
+        logging.debug('restore_snapshot called, snapshot name is %s', snapshot_name)
+
+        with self.lock.writer():
+            self.snapshot.load(snapshot_name)
+
+            self._validate_set(self.snapshot.get_properties())
+
+            self.snapshot.restore(self.backend_handler)
+
+            self.csn = cmcsn.CMCSN(self.backend_handler)
+
+            self._activate_set_no_lock(self.snapshot.get_properties())
+
+    def list_snapshots(self):
+        logging.debug('list_snapshots called')
+
+        snapshots = []
+        with self.lock.writer():
+            snapshots = self.snapshot.list()
+
+        return snapshots
+
+    def delete_snapshot(self, snapshot_name):
+        logging.debug('delete_snapshot called, snapshot name is %s', snapshot_name)
+
+        with self.lock.writer():
+            self.snapshot.delete(snapshot_name)
+
+    def activate(self, node_name=None, startup_activation=False):
+        logging.debug('activate called, node is %s', node_name)
+
+        activation_alarm = cmalarm.CMActivationFailedAlarm()
+        if node_name:
+            activation_alarm.cancel_alarm_for_node(node_name)
+        else:
+            activation_alarm.cancel_alarm_for_sg(CMProcessor.SERVICE_GROUP_NAME)
+
+        with self.lock.reader():
+            uuid_value = self.changemonitor.start_change()
+            if not node_name:
+                work = cmactivationwork.CMActivationWork(
+                    cmactivationwork.CMActivationWork.OPER_FULL,
+                    self.csn.get(), {}, None, startup_activation)
+            else:
+                work = cmactivationwork.CMActivationWork(
+                    cmactivationwork.CMActivationWork.OPER_FULL,
+                    self.csn.get(), {}, node_name)
+            work.uuid_value = uuid_value
+            self.activator.add_work(work)
+
+        logging.debug('activation work added, going to wait for result')
+        failures = work.get_result()
+        logging.debug('got activation result')
+
+        if self.reboot_requests:
+            self._raise_reboot_alarms()
+
+        if not node_name:
+            self.activationstate_handler.clear_full_failed()
+
+        if failures:
+            logging.warning('Activation failed: %s', failures)
+
+            failed_activators = [activator for handler in failures.keys()
+                                 for activator in failures[handler]]
+
+            supplementary_info = {'failed activators': failed_activators}
+
+            if node_name:
+                activation_alarm.raise_alarm_for_node(node_name, supplementary_info)
+            else:
+                self.activationstate_handler.set_full_failed(failed_activators)
+
+                activation_alarm.raise_alarm_for_sg(CMProcessor.SERVICE_GROUP_NAME,
+                                                    supplementary_info)
+        return uuid_value
+
+    def activate_node(self, node_name):
+        logging.debug('activate_node called, node name is %s', node_name)
+
+        if self.automatic_activation_disabled:
+            return False
+
+        with self.lock.reader():
+            node_csn = self.csn.get_node_csn(node_name)
+
+            if self.csn.get() == node_csn:
+                logging.info('No change in data since last translation, last csn %d',
+                             self.csn.get())
+                return False
+
+            self._clear_reboot_requests()
+            work = cmactivationwork.CMActivationWork(cmactivationwork.CMActivationWork.OPER_NODE,
+                                                     self.csn.get(), {}, node_name)
+            self.activator.add_work(work)
+
+        activation_alarm = cmalarm.CMActivationFailedAlarm()
+        activation_alarm.cancel_alarm_for_node(node_name)
+
+        failures = work.get_result()
+        if failures:
+            logging.warning('Activation failed: %s', failures)
+
+            failed_activators = [activator for handler in failures.keys()
+                                 for activator in failures[handler]]
+            supplementary_info = {'failed activators': failed_activators}
+            activation_alarm.raise_alarm_for_node(node_name, supplementary_info)
+
+        else:
+            with self.lock.writer():
+                self.csn.sync_node_csn(node_name)
+
+        return node_name in self.reboot_requests
+
+    def set_automatic_activation_state(self, state):
+        logging.debug('set_automatic_activation_state called, state is %s', state)
+
+        with self.lock.writer():
+            if state:
+                self.automatic_activation_disabled.unset()
+            else:
+                self.automatic_activation_disabled.set()