Initial commit
[ta/config-manager.git] / cmframework / src / cmframework / server / cmprocessor.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 import logging
15
16 from cmframework.utils import cmactivationwork
17 from cmframework.server import cmeventletrwlock
18 from cmframework.server import cmcsn
19 from cmframework.server import cmsnapshot
20 from cmframework.utils.cmflagfile import CMFlagFile
21 from cmframework.utils import cmalarm
22
23
24 class CMProcessor(object):
25     SERVICE_GROUP_NAME = 'config-manager'
26
27     def __init__(self,
28                  backend_handler,
29                  validator,
30                  activator,
31                  changemonitor,
32                  activationstate_handler,
33                  snapshot_handler):
34         logging.debug('CMProcessor constructed')
35
36         self.backend_handler = backend_handler
37         self.lock = cmeventletrwlock.CMEventletRWLock()
38         self.csn = cmcsn.CMCSN(self.backend_handler)
39         self.validator = validator
40         self.activator = activator
41         self.reboot_requests = set()
42         self.automatic_activation_disabled = CMFlagFile('automatic_activation_disabled')
43         self.changemonitor = changemonitor
44         self.activationstate_handler = activationstate_handler
45         self.snapshot = cmsnapshot.CMSnapshot(snapshot_handler)
46
47     def reboot_request(self, node_name):
48         logging.debug('reboot_request called for %s', node_name)
49
50         self.reboot_requests.add(node_name)
51
52     def _clear_reboot_requests(self):
53         logging.debug('_clear_reboot_requests called')
54
55         self.reboot_requests.clear()
56
57     def _raise_reboot_alarms(self):
58         logging.debug('_raise_reboot_alarms called')
59
60         reboot_request_alarm = cmalarm.CMRebootRequestAlarm()
61
62         for node_name in self.reboot_requests:
63             reboot_request_alarm.raise_alarm_for_node(node_name)
64
65     def get_property(self, prop_name, snapshot_name=None):
66         logging.debug('get_property called for %s', prop_name)
67
68         with self.lock.reader():
69             if snapshot_name:
70                 self.snapshot.load(snapshot_name)
71
72                 return self.snapshot.get_property(prop_name)
73
74             return self.backend_handler.get_property(prop_name)
75
76     def get_properties(self, prop_filter, snapshot_name=None):
77         logging.debug('get_properties  called with filter %s', prop_filter)
78
79         with self.lock.reader():
80             if snapshot_name:
81                 self.snapshot.load(snapshot_name)
82
83                 return self.snapshot.get_properties(prop_filter)
84
85             return self.backend_handler.get_properties(prop_filter)
86
87     def set_property(self, prop_name, prop_value):
88         logging.debug('set_property called %s=%s', prop_name, prop_value)
89
90         props = {}
91         props[prop_name] = prop_value
92         return self.set_properties(props)
93
94     def set_properties(self, props, overwrite=False):
95         logging.debug('set_properties called for %s', str(props))
96
97         with self.lock.writer():
98             self._validate_set(props)
99             if overwrite:
100                 logging.debug('Deleting old configuration data as requested')
101                 orig_props = self.backend_handler.get_properties('.*')
102                 self.backend_handler.delete_properties(orig_props.keys())
103             self.backend_handler.set_properties(props)
104             self.csn.increment()
105
106         if not self.automatic_activation_disabled:
107             return self._activate_set(props)
108
109         return "0"
110
111     def delete_property(self, prop_name):
112         logging.debug('delete_property called for %s', prop_name)
113
114         props = []
115         props.append(prop_name)
116         return self._delete_properties(props, None)
117
118     def delete_properties(self, arg):
119         logging.debug('delete_properties called with arg %r', arg)
120
121         keys = []
122         prop_filter = None
123         if isinstance(arg, str):
124             prop_filter = arg
125             props = self.get_properties(prop_filter)
126             keys = props.keys()
127         else:
128             keys = arg
129         return self._delete_properties(keys, prop_filter)
130
131     def _delete_properties(self, props, props_filter):
132         logging.debug('_delete_properties called with props %s filter %s', props, props_filter)
133
134         with self.lock.writer():
135             self._validate_delete(props)
136             if props_filter:
137                 self.backend_handler.delete_properties(props_filter)
138             else:
139                 if len(props) == 1:
140                     self.backend_handler.delete_property(props[0])
141                 else:
142                     self.backend_handler.delete_properties(props)
143             self.csn.increment()
144
145         if not self.automatic_activation_disabled:
146             return self._activate_delete(props)
147
148         return "0"
149
150     def _validate_set(self, props):
151         logging.debug('_validate_set called for %s', str(props))
152
153         self.validator.validate_set(props)
154
155     def _activate_set_no_lock(self, props):
156         logging.debug('_activate_set_no_lock called for %s', str(props))
157
158         uuid_value = self.changemonitor.start_change()
159
160         work = cmactivationwork.CMActivationWork(cmactivationwork.CMActivationWork.OPER_SET,
161                                                  self.csn.get(), props)
162         work.uuid_value = uuid_value
163         self.activator.add_work(work)
164         return uuid_value
165
166     def _activate_set(self, props):
167         logging.debug('_activate_set called')
168
169         with self.lock.reader():
170             return self._activate_set_no_lock(props)
171
172     def _validate_delete(self, props):
173         logging.debug('_validate_delete called for %s', str(props))
174
175         self.validator.validate_delete(props)
176
177     def _activate_delete(self, props):
178         logging.debug('_activate_delete called for %s', str(props))
179
180         with self.lock.reader():
181             uuid_value = self.changemonitor.start_change()
182             work = cmactivationwork.CMActivationWork(cmactivationwork.CMActivationWork.OPER_DELETE,
183                                                      self.csn.get(), props)
184             work.uuid_value = uuid_value
185             self.activator.add_work(work)
186             return uuid_value
187
188     def create_snapshot(self, snapshot_name):
189         logging.debug('create_snapshot called, snapshot name is %s', snapshot_name)
190
191         with self.lock.writer():
192             self.snapshot.create(snapshot_name, self.backend_handler)
193
194     def restore_snapshot(self, snapshot_name):
195         logging.debug('restore_snapshot called, snapshot name is %s', snapshot_name)
196
197         with self.lock.writer():
198             self.snapshot.load(snapshot_name)
199
200             self._validate_set(self.snapshot.get_properties())
201
202             self.snapshot.restore(self.backend_handler)
203
204             self.csn = cmcsn.CMCSN(self.backend_handler)
205
206             self._activate_set_no_lock(self.snapshot.get_properties())
207
208     def list_snapshots(self):
209         logging.debug('list_snapshots called')
210
211         snapshots = []
212         with self.lock.writer():
213             snapshots = self.snapshot.list()
214
215         return snapshots
216
217     def delete_snapshot(self, snapshot_name):
218         logging.debug('delete_snapshot called, snapshot name is %s', snapshot_name)
219
220         with self.lock.writer():
221             self.snapshot.delete(snapshot_name)
222
223     def activate(self, node_name=None, startup_activation=False):
224         logging.debug('activate called, node is %s', node_name)
225
226         activation_alarm = cmalarm.CMActivationFailedAlarm()
227         if node_name:
228             activation_alarm.cancel_alarm_for_node(node_name)
229         else:
230             activation_alarm.cancel_alarm_for_sg(CMProcessor.SERVICE_GROUP_NAME)
231
232         with self.lock.reader():
233             uuid_value = self.changemonitor.start_change()
234             if not node_name:
235                 work = cmactivationwork.CMActivationWork(
236                     cmactivationwork.CMActivationWork.OPER_FULL,
237                     self.csn.get(), {}, None, startup_activation)
238             else:
239                 work = cmactivationwork.CMActivationWork(
240                     cmactivationwork.CMActivationWork.OPER_FULL,
241                     self.csn.get(), {}, node_name)
242             work.uuid_value = uuid_value
243             self.activator.add_work(work)
244
245         logging.debug('activation work added, going to wait for result')
246         failures = work.get_result()
247         logging.debug('got activation result')
248
249         if self.reboot_requests:
250             self._raise_reboot_alarms()
251
252         if not node_name:
253             self.activationstate_handler.clear_full_failed()
254
255         if failures:
256             logging.warning('Activation failed: %s', failures)
257
258             failed_activators = [activator for handler in failures.keys()
259                                  for activator in failures[handler]]
260
261             supplementary_info = {'failed activators': failed_activators}
262
263             if node_name:
264                 activation_alarm.raise_alarm_for_node(node_name, supplementary_info)
265             else:
266                 self.activationstate_handler.set_full_failed(failed_activators)
267
268                 activation_alarm.raise_alarm_for_sg(CMProcessor.SERVICE_GROUP_NAME,
269                                                     supplementary_info)
270         return uuid_value
271
272     def activate_node(self, node_name):
273         logging.debug('activate_node called, node name is %s', node_name)
274
275         if self.automatic_activation_disabled:
276             return False
277
278         with self.lock.reader():
279             node_csn = self.csn.get_node_csn(node_name)
280
281             if self.csn.get() == node_csn:
282                 logging.info('No change in data since last translation, last csn %d',
283                              self.csn.get())
284                 return False
285
286             self._clear_reboot_requests()
287             work = cmactivationwork.CMActivationWork(cmactivationwork.CMActivationWork.OPER_NODE,
288                                                      self.csn.get(), {}, node_name)
289             self.activator.add_work(work)
290
291         activation_alarm = cmalarm.CMActivationFailedAlarm()
292         activation_alarm.cancel_alarm_for_node(node_name)
293
294         failures = work.get_result()
295         if failures:
296             logging.warning('Activation failed: %s', failures)
297
298             failed_activators = [activator for handler in failures.keys()
299                                  for activator in failures[handler]]
300             supplementary_info = {'failed activators': failed_activators}
301             activation_alarm.raise_alarm_for_node(node_name, supplementary_info)
302
303         else:
304             with self.lock.writer():
305                 self.csn.sync_node_csn(node_name)
306
307         return node_name in self.reboot_requests
308
309     def set_automatic_activation_state(self, state):
310         logging.debug('set_automatic_activation_state called, state is %s', state)
311
312         with self.lock.writer():
313             if state:
314                 self.automatic_activation_disabled.unset()
315             else:
316                 self.automatic_activation_disabled.set()