def test_unset(self): d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': [ 1, 2, 3 ] } } data.unset(d1, 'a') data.unset(d1, 'c/d') data.unset(d1, 'does/not/exist') self.assertEqual(d1, { 'b': 1, 'c': { 'e': [ 1, 2, 3 ] } }) data.unset(d1, 'c/e[0]') self.assertEqual(d1, { 'b': 1, 'c': { 'e': [ 2, 3 ] } }) data.unset(d1, 'c/e[1]') self.assertEqual(d1, { 'b': 1, 'c': { 'e': [ 2 ] } }) # index 1 should now be out of range self.assertRaises(data.DataNotFoundError, data.unset, d1, 'c/e[1]') d2 = { 'a': [ { 'b': [ 1, 2 ] } ] } data.unset(d2, 'a[0]/b[1]') self.assertEqual(d2, { 'a': [ { 'b': [ 1 ] } ] }) d3 = { 'a': [ [ 1, 2 ] ] } data.set(d3, "a[0][1]", 3) self.assertEqual(d3, { 'a': [ [ 1, 3 ] ] }) data.unset(d3, 'a[0][1]') self.assertEqual(d3, { 'a': [ [ 1 ] ] })
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
def test_set(self): d1 = { 'a': 'a', 'b': 1, 'c': { 'd': 'd', 'e': 2 } } d12 = { 'b': 1, 'c': { 'e': 3, 'f': [ 1 ] } } d13 = { 'b': 1, 'c': { 'e': 3, 'f': [ 2 ] } } d14 = { 'b': 1, 'c': { 'e': 3, 'f': [ { 'g': [ 1, 2 ] } ] } } d15 = { 'b': 1, 'c': { 'e': 3, 'f': [ { 'g': [ 1, 3 ] } ] } } data.set(d1, 'a', None) data.set(d1, 'c/d', None) data.set(d1, 'c/e/', 3) data.set(d1, 'c/f', [ 1 ] ) self.assertEqual(d1, d12) data.set(d1, 'c/f[0]', 2 ) self.assertEqual(d1, d13) data.set(d1, 'c/f[0]', { 'g': [ 1, 2] } ) self.assertEqual(d1, d14) data.set(d1, 'c/f[0]/g[1]', 3) self.assertEqual(d1, d15) self.assertRaises(data.DataTypeError, data.set, d1, 1, 2) self.assertRaises(data.DataTypeError, data.set, 1, "", 2) self.assertRaises(data.DataTypeError, data.set, d1, 'c[1]', 2) self.assertRaises(data.DataTypeError, data.set, d1, 'c[1][2]', 2) self.assertRaises(data.DataNotFoundError, data.set, d1, 'c/f[5]', 2) self.assertRaises(data.DataNotFoundError, data.set, d1, 'c/f[5][2]', 2) d3 = {} e3 = data.set(d3, "does/not/exist", 123) self.assertEqual(d3, { 'does': { 'not': { 'exist': 123 } } }) self.assertEqual(e3, { 'does': { 'not': { 'exist': 123 } } })