def test_merge(self): d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2 } } d2 = { 'a': None, 'c': { 'd': None, 'e': 3, 'f': [ 1 ] } } d12 = { 'b': 1, 'c': { 'e': 3, 'f': [ 1 ] } } m12 = d1 data.merge(m12, d2) self.assertEqual(d12, m12) self.assertRaises(data.DataTypeError, data.merge, d1, "a") self.assertRaises(data.DataTypeError, data.merge, 1, d2) self.assertRaises(data.DataTypeError, data.merge, None, None)
def __handle_set_config_module(self, module_name, cmd): # the answer comes (or does not come) from the relevant module # so we need a variable to see if we got it answer = None # todo: use api (and check the data against the definition?) old_data = copy.deepcopy(self.config.data) conf_part = data.find_no_exc(self.config.data, module_name) update_cmd = None use_part = None if conf_part: data.merge(conf_part, cmd) use_part = conf_part else: conf_part = data.set(self.config.data, module_name, {}) data.merge(conf_part[module_name], cmd) use_part = conf_part[module_name] # The command to send update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE, use_part) # TODO: This design might need some revisiting. We might want some # polymorphism instead of branching. But it just might turn out it # will get solved by itself when we move everything to virtual modules # (which is possible solution to the offline configuration problem) # or when we solve the incorrect behaviour here when a config is # rejected (spying modules don't know it was rejected and some modules # might have been committed already). if module_name in self.virtual_modules: # The module is virtual, so call it to get the answer try: error = self.virtual_modules[module_name](use_part) if error is None: answer = ccsession.create_answer(0) # OK, it is successful, send the notify, but don't wait # for answer seq = self.cc.group_sendmsg(update_cmd, module_name) else: answer = ccsession.create_answer(1, error) # Make sure just a validating plugin don't kill the whole manager except Exception as excp: # Provide answer answer = ccsession.create_answer(1, "Exception: " + str(excp)) else: # Real module, send it over the wire to it # send out changed info and wait for answer seq = self.cc.group_sendmsg(update_cmd, module_name) try: # replace 'our' answer with that of the module answer, env = self.cc.group_recvmsg(False, seq) except bundy.cc.SessionTimeout: answer = ccsession.create_answer(1, "Timeout waiting for answer from " + module_name) except bundy.cc.SessionError as se: logger.error(CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE, module_name, se) answer = ccsession.create_answer(1, "Unable to parse response from " + module_name + ": " + str(se)) if answer: rcode, val = ccsession.parse_answer(answer) if rcode == 0: self.write_config() else: self.config.data = old_data return answer
def __handle_set_config_module(self, module_name, new_conf): # Reject attempts of overridding system reserved items. reserved_items = [ item for item in new_conf.keys() if item and item[0] == '_' ] if reserved_items: return ccsession.create_answer( 1, 'system reserved items cannot be set manually: ' + ' '.join(reserved_items)) # the answer comes (or does not come) from the relevant module # so we need a variable to see if we got it answer = None # todo: use api (and check the data against the definition?) old_data = copy.deepcopy(self.config.data) conf_part = data.find_no_exc(self.config.data, module_name) update_cmd = None use_part = None if conf_part: data.merge(conf_part, new_conf) use_part = conf_part else: conf_part = data.set(self.config.data, module_name, {}) data.merge(conf_part[module_name], new_conf) use_part = conf_part[module_name] # set/increment the generation ID of the module config old_genid = use_part.get('_generation_id') use_part['_generation_id'] = 1 if old_genid is None else old_genid + 1 # The command to send update_cmd = ccsession.create_command(ccsession.COMMAND_CONFIG_UPDATE, use_part) # TODO: This design might need some revisiting. We might want some # polymorphism instead of branching. But it just might turn out it # will get solved by itself when we move everything to virtual modules # (which is possible solution to the offline configuration problem) # or when we solve the incorrect behaviour here when a config is # rejected (spying modules don't know it was rejected and some modules # might have been committed already). if module_name in self.virtual_modules: # The module is virtual, so call it to get the answer try: error = self.virtual_modules[module_name](use_part) if error is None: answer = ccsession.create_answer(0) # OK, it is successful, send the notify, but don't wait # for answer seq = self.cc.group_sendmsg(update_cmd, module_name) else: answer = ccsession.create_answer(1, error) # Make sure just a validating plugin don't kill the whole manager except Exception as excp: # Provide answer answer = ccsession.create_answer(1, "Exception: " + str(excp)) else: # Real module, send it over the wire to it # send out changed info and wait for answer seq = self.cc.group_sendmsg(update_cmd, module_name) try: # replace 'our' answer with that of the module answer, env = self.cc.group_recvmsg(False, seq) except bundy.cc.SessionTimeout: answer = ccsession.create_answer( 1, "Timeout waiting for answer from " + module_name) except bundy.cc.SessionError as se: logger.error(CFGMGR_BAD_UPDATE_RESPONSE_FROM_MODULE, module_name, se) answer = ccsession.create_answer( 1, "Unable to parse response from " + module_name + ": " + str(se)) if answer: rcode, val = ccsession.parse_answer(answer) if rcode == 0: self.write_config() else: self.config.data = old_data return answer