Example #1
0
 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 ] ] })
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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 } } })