# 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()