class TestInstanceStarted(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def template_test_instance_not_started(self, stateboard):
        # console sock doesn't exists
        self.instance.remove_file(self.console_sock)
        res = call_check_instance_started(self.console_sock, stateboard)
        self.assertFalse(res.success)
        self.assertIn('Instance socket not found', res.msg)

        # cannot connect to console sock
        bad_socket_path = 'bad-socket-path'
        self.instance.write_file(bad_socket_path)

        res = call_check_instance_started(bad_socket_path, stateboard)

        self.assertFalse(res.success)
        self.assertIn('Failed to connect to socket', res.msg)

    def test_instance_not_started(self):
        self.template_test_instance_not_started(stateboard=False)

    def test_stateboard_not_started(self):
        self.template_test_instance_not_started(stateboard=True)

    def test_alive(self):
        # not stateboard
        # require('membership').myself().status is 'active'
        set_myself(self.instance, status='alive')
        self.instance.clear_calls('membership_myself')
        res = call_check_instance_started(self.console_sock)
        self.assertTrue(res.success, msg=res.msg)

        calls = self.instance.get_calls('membership_myself')
        self.assertEqual(len(calls), 1)

        # stateboard
        set_myself(self.instance, status='alive')
        self.instance.clear_calls('membership_myself')
        res = call_check_instance_started(self.console_sock, stateboard=True)
        self.assertTrue(res.success, msg=res.msg)

        calls = self.instance.get_calls('membership_myself')
        self.assertEqual(len(calls), 0)

    def test_dead(self):
        # not stateboard
        # require('membership').myself().status is 'dead'
        set_myself(self.instance, status='dead')
        self.instance.clear_calls('membership_myself')
        res = call_check_instance_started(self.console_sock)
        self.assertFalse(res.success)

    def tearDown(self):
        self.instance.stop()
class TestBootstrapVshard(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_can_not_bootstrap_vshard(self):
        self.instance.set_variable('can_bootstrap_vshard', False)

        res = call_bootstrap_vshard(self.console_sock)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        self.assertEqual(len(self.instance.get_calls('bootstrap_vshard')), 0)

    def test_bootstrap_successfully(self):
        self.instance.set_variable('can_bootstrap_vshard', True)

        res = call_bootstrap_vshard(self.console_sock)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        self.assertEqual(len(self.instance.get_calls('bootstrap_vshard')), 1)

    def test_bootstrap_fails(self):
        self.instance.set_variable('can_bootstrap_vshard', True)
        self.instance.set_fail_on('bootstrap_vshard')

        res = call_bootstrap_vshard(self.console_sock)
        self.assertFalse(res.success)
        self.assertIn('Bootstrap vshard failed', res.msg)
        self.assertIn('cartridge err', res.msg)

        self.assertEqual(len(self.instance.get_calls('bootstrap_vshard')), 1)

    def tearDown(self):
        self.instance.stop()
Esempio n. 3
0
class TestFailoverDeprecated(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_enable_failover_old_cartridge(self):
        self.instance.set_cartridge_version('1.2.0')

        # failover disabled
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover_deprecated(self.console_sock, True)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], 'enable')

        # failover enabled
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover_deprecated(self.console_sock, True)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 0)

    def test_disable_failover_old_cartridge(self):
        self.instance.set_cartridge_version('1.2.0')

        # failover enabled
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover_deprecated(self.console_sock, False)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], 'disable')

        # failover disabled
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover_deprecated(self.console_sock, False)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 0)

    def test_fail_on_manage_failover_old_cartridge(self):
        self.instance.set_cartridge_version('1.2.0')

        # enable failover
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')
        self.instance.set_fail_on('manage_failover')

        res = call_manage_failover_deprecated(self.console_sock, True)
        self.assertFalse(res.success)
        self.assertIn('Failed admin_enable_failover', res.msg)
        self.assertIn('cartridge err', res.msg)

        # disable failover
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')
        self.instance.set_fail_on('manage_failover')

        res = call_manage_failover_deprecated(self.console_sock, False)
        self.assertFalse(res.success)
        self.assertIn('Failed admin_disable_failover', res.msg)
        self.assertIn('cartridge err', res.msg)

    def test_enable_failover(self):
        self.instance.set_cartridge_version('2.1.0')

        # failover disabled
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover_deprecated(self.console_sock, True)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'eventual'})

        # failover enabled
        self.instance.set_variable('failover_params', {'mode': 'eventual'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover_deprecated(self.console_sock, True)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'eventual'})

    def test_disable_failover(self):
        self.instance.set_cartridge_version('2.1.0')

        # failover enabled
        self.instance.set_variable('failover_params', {'mode': 'eventual'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover_deprecated(self.console_sock, False)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'disabled'})

        # failover disabled
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover_deprecated(self.console_sock, False)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'disabled'})

    def test_fail_on_manage_failover(self):
        self.instance.set_cartridge_version('2.1.0')

        # enable failover
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')
        self.instance.set_fail_on('failover_set_params')

        res = call_manage_failover_deprecated(self.console_sock, True)
        self.assertFalse(res.success)
        self.assertIn('Failed to set failover params', res.msg)
        self.assertIn('cartridge err', res.msg)

        # disable failover
        self.instance.set_variable('failover_params', {'mode': 'enabled'})
        self.instance.clear_calls('failover_set_params')
        self.instance.set_fail_on('failover_set_params')

        res = call_manage_failover_deprecated(self.console_sock, False)
        self.assertFalse(res.success)
        self.assertIn('Failed to set failover params', res.msg)
        self.assertIn('cartridge err', res.msg)

    def tearDown(self):
        self.instance.stop()
Esempio n. 4
0
class TestAppConfig(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_empty_config(self):
        res = call_config_app(self.console_sock, {})
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

    def test_edit_system_sections(self):
        system_sections = [
            'topology',
            'vshard',
            'vshard_groups',
            'auth',
            'users_acl',
        ]
        for section in system_sections:
            self.instance.clear_calls('config_patch_clusterwide')

            res = call_config_app(self.console_sock, {section: {}})
            self.assertFalse(res.success)
            self.assertIn('Unable to patch config system section', res.msg)

            calls = self.instance.get_calls('config_patch_clusterwide')
            self.assertEqual(len(calls), 0)

    def test_adding_new_sections(self):
        SECTION_NAME = 'new-section'
        SECTION_BODY = 'SECTION BODY'

        # config is empty
        self.instance.set_variable('config', {})
        self.instance.clear_calls('config_patch_clusterwide')

        res = call_config_app(self.console_sock,
                              {SECTION_NAME: {
                                  'body': SECTION_BODY,
                              }})
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('config_patch_clusterwide')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {SECTION_NAME: SECTION_BODY})

        # config is already set (res.changed should be false)
        self.instance.set_variable('config', {SECTION_NAME: SECTION_BODY})
        self.instance.clear_calls('config_patch_clusterwide')

        res = call_config_app(self.console_sock,
                              {SECTION_NAME: {
                                  'body': SECTION_BODY,
                              }})
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('config_patch_clusterwide')
        self.assertEqual(len(calls), 0)

    def test_deleting_sections(self):
        SECTION1_NAME = 'section-1'
        SECTION1_BODY = 'SECTION-1 BODY'
        SECTION2_NAME = 'section-2'
        SECTION2_BODY = 'SECTION-2 BODY'

        # set two sections
        self.instance.set_variable('config', {
            SECTION1_NAME: SECTION1_BODY,
            SECTION2_NAME: SECTION2_BODY,
        })
        self.instance.clear_calls('config_patch_clusterwide')

        # delete section-1
        res = call_config_app(self.console_sock,
                              {SECTION1_NAME: {
                                  'deleted': True,
                              }})
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('config_patch_clusterwide')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {SECTION1_NAME: None})

        # set only section-2
        self.instance.set_variable('config', {
            SECTION2_NAME: SECTION2_BODY,
        })
        self.instance.clear_calls('config_patch_clusterwide')

        # delete section-1
        res = call_config_app(self.console_sock,
                              {SECTION1_NAME: {
                                  'deleted': True,
                              }})
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('config_patch_clusterwide')
        self.assertEqual(len(calls), 0)

        # set two sections
        self.instance.set_variable('config', {
            SECTION1_NAME: SECTION1_BODY,
            SECTION2_NAME: SECTION2_BODY,
        })
        self.instance.clear_calls('config_patch_clusterwide')

        # set deleted to False for section-1
        res = call_config_app(
            self.console_sock,
            {SECTION1_NAME: {
                'deleted': False,
                'body': SECTION1_BODY,
            }})
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('config_patch_clusterwide')
        self.assertEqual(len(calls), 0)

    def test_changing_section(self):
        SECTION1_NAME = 'section-1'
        SECTION1_BODY = 'SECTION-1 BODY'
        SECTION2_NAME = 'section-2'
        SECTION2_BODY = 'SECTION-2 BODY'

        SECTION1_NEW_BODY = {'hi': 'I am section-1 new body'}

        # set two sections
        self.instance.set_variable('config', {
            SECTION1_NAME: SECTION1_BODY,
            SECTION2_NAME: SECTION2_BODY,
        })
        self.instance.clear_calls('config_patch_clusterwide')

        # change only section-1
        res = call_config_app(
            self.console_sock, {
                SECTION1_NAME: {
                    'body': SECTION1_NEW_BODY,
                },
                SECTION2_NAME: {
                    'body': SECTION2_BODY,
                }
            })
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('config_patch_clusterwide')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {SECTION1_NAME: SECTION1_NEW_BODY})

    def test_patch_config_fails(self):
        SECTION_NAME = 'new-section'
        SECTION_BODY = 'SECTION BODY'
        SECTION_NEW_BODY = 'NEW SECTION BODY'

        self.instance.set_variable('config', {SECTION_NAME: SECTION_BODY})
        self.instance.clear_calls('config_patch_clusterwide')
        self.instance.set_fail_on('config_patch_clusterwide')

        # change section
        res = call_config_app(self.console_sock, {
            SECTION_NAME: {
                'body': SECTION_NEW_BODY,
            },
        })
        self.assertFalse(res.success)
        self.assertIn('Config patch failed', res.msg)
        self.assertIn('cartridge err', res.msg)

    def tearDown(self):
        self.instance.stop()
class TestManageInstance(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_create_replicaset(self):
        # create replicaset with instances not known by cluster
        self.instance.clear_calls('edit_topology')
        res = call_manage_replicaset(self.console_sock,
                                     alias='r1',
                                     failover_priority=['r1-master'],
                                     instances=['r1-master', 'r1-replica'],
                                     roles=['role-1'])
        self.assertFalse(res.success, msg=res.msg)
        self.assertIn(
            'Leader "r1-master" (replicaset "r1") not found in cluster',
            res.msg)
        self.assertEqual(len(self.instance.get_calls('edit_topology')), 0)

        # add unjoined
        self.instance.add_unjoined_server(alias='r1-master',
                                          uri='r1-master-uri')
        self.instance.add_unjoined_server(alias='r1-replica',
                                          uri='r1-replica-uri')

        # create replicaset with instances known by cluster
        self.instance.clear_calls('edit_topology')
        res = call_manage_replicaset(self.console_sock,
                                     alias='r1',
                                     failover_priority=['r1-master'],
                                     instances=['r1-master', 'r1-replica'],
                                     roles=['role-1'])
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        # check performed `edit_topology` calls
        calls = self.instance.get_calls('edit_topology')
        self.assertEqual(len(calls), 2)
        join_leader_call = calls[0]
        join_replica_call = calls[1]

        # check join leader call
        self.assertIn('replicasets', join_leader_call)
        self.assertEqual(len(join_leader_call['replicasets']), 1)
        r_params = join_leader_call['replicasets'][0]
        self.assertEqual(
            r_params, {
                'alias': 'r1',
                'roles': ['role-1'],
                'join_servers': [{
                    'uri': 'r1-master-uri'
                }],
            })

        # check join replica call
        self.assertIn('replicasets', join_replica_call)
        self.assertEqual(len(join_replica_call['replicasets']), 1)
        r_params = join_replica_call['replicasets'][0]
        self.assertEqual(r_params, {
            'uuid': 'r1-uuid',
            'join_servers': [{
                'uri': 'r1-replica-uri'
            }],
        })

        # repeat the call (res.changed should be false)
        self.instance.clear_calls('edit_topology')
        res = call_manage_replicaset(self.console_sock,
                                     alias='r1',
                                     failover_priority=['r1-master'],
                                     instances=['r1-master', 'r1-replica'],
                                     roles=['role-1'])
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        # check performed `edit_topology` calls
        calls = self.instance.get_calls('edit_topology')
        self.assertEqual(len(calls), 1)
        self.assertIn('replicasets', calls[0])
        r_params = calls[0]['replicasets'][0]
        self.assertEqual(
            r_params, {
                'uuid': 'r1-uuid',
                'roles': ['role-1'],
                'failover_priority': ['r1-master-uuid'],
            })

    def test_edit_replicaset_parameters(self):
        add_replicaset(
            self.instance,
            alias='r1',
            roles=['role-1'],
            servers=['r1-master'],
        )

        params = {
            'all_rw': True,
            'weight': 1,
            'vshard_group': 'hot',
            'roles': ['role-2'],
        }

        for param, value in params.items():
            self.instance.clear_calls('edit_topology')

            res = call_manage_replicaset(
                self.console_sock,
                alias='r1',
                failover_priority=['r1-master'],
                instances=['r1-master'],
                **{param: value},
            )
            self.assertTrue(res.success, msg=res.msg)
            self.assertTrue(res.changed)

            # check performed `edit_topology` calls
            calls = self.instance.get_calls('edit_topology')
            self.assertEqual(len(calls), 1)
            self.assertIn('replicasets', calls[0])
            r_params = calls[0]['replicasets'][0]
            self.assertEqual(
                r_params, {
                    'uuid': 'r1-uuid',
                    'failover_priority': ['r1-master-uuid'],
                    **{
                        param: value
                    },
                })

            # set the same parameter again (res.changed should be false)
            self.instance.clear_calls('edit_topology')
            res = call_manage_replicaset(
                self.console_sock,
                alias='r1',
                failover_priority=['r1-master'],
                instances=['r1-master'],
                **{param: value},
            )
            self.assertTrue(res.success, msg=res.msg)
            self.assertFalse(res.changed)

            # check performed `edit_topology` calls
            calls = self.instance.get_calls('edit_topology')
            self.assertEqual(len(calls), 1)
            self.assertIn('replicasets', calls[0])
            r_params = calls[0]['replicasets'][0]
            self.assertEqual(
                r_params, {
                    'uuid': 'r1-uuid',
                    'failover_priority': ['r1-master-uuid'],
                    **{
                        param: value
                    },
                })

    def test_joining_new_servers(self):
        add_replicaset(
            self.instance,
            alias='r1',
            roles=['role-1'],
            servers=['r1-master', 'r1-replica'],
        )

        # add unjoined
        self.instance.add_unjoined_server(alias='r1-new-master',
                                          uri='r1-new-master-uri')

        # join new server and set it to be a new master
        self.instance.clear_calls('edit_topology')
        res = call_manage_replicaset(
            self.console_sock,
            alias='r1',
            failover_priority=['r1-new-master', 'r1-master'],
            instances=['r1-master', 'r1-replica', 'r1-new-master'],
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        # check performed `edit_topology` calls
        calls = self.instance.get_calls('edit_topology')
        self.assertEqual(len(calls), 2)
        join_call = calls[0]
        edit_priority_call = calls[1]

        self.assertIn('replicasets', join_call)
        r_params = join_call['replicasets'][0]
        self.assertEqual(r_params, {
            'uuid': 'r1-uuid',
            'join_servers': [{
                'uri': 'r1-new-master-uri'
            }],
        })

        self.assertIn('replicasets', edit_priority_call)
        r_params = edit_priority_call['replicasets'][0]
        self.assertEqual(
            r_params, {
                'uuid': 'r1-uuid',
                'failover_priority': ['r1-new-master-uuid', 'r1-master-uuid'],
            })

        # call again (res.changed should be false)
        res = call_manage_replicaset(
            self.console_sock,
            alias='r1',
            failover_priority=['r1-new-master', 'r1-master'],
            instances=['r1-master', 'r1-replica', 'r1-new-master'],
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

    def test_fail_on_edit_topology(self):
        # fail on create
        self.instance.add_unjoined_server(alias='r1-master',
                                          uri='r1-master-uri')
        self.instance.clear_calls('edit_topology')
        self.instance.set_fail_on('edit_topology')

        # create replicaset with instances known by cluster
        self.instance.clear_calls('edit_topology')
        res = call_manage_replicaset(self.console_sock,
                                     alias='r1',
                                     failover_priority=['r1-master'],
                                     instances=['r1-master'],
                                     roles=['role-1'])
        self.assertFalse(res.success)
        self.assertIn('Failed to create', res.msg)
        self.assertIn('cartridge err', res.msg)

        # fail on edit
        add_replicaset(
            self.instance,
            alias='r1',
            roles=['role-1'],
            servers=['r1-master', 'r1-replica'],
        )

        self.instance.clear_calls('edit_topology')
        self.instance.set_fail_on('edit_topology')

        res = call_manage_replicaset(
            self.console_sock,
            alias='r1',
            failover_priority=['r1-master'],
            instances=['r1-master'],
            roles=['role-1', 'role-2'],
        )
        self.assertFalse(res.success)
        self.assertIn('Failed to edit replicaset', res.msg)
        self.assertIn('cartridge err', res.msg)

        self.instance.set_fail_on('edit_topology', False)

    def test_replicaset_in_unhealthy(self):
        # try to edit unhealthy replicaset
        add_replicaset(
            self.instance,
            alias='r1',
            roles=['role-1'],
            servers=['r1-master'],
            status='unhealthy',
        )

        res = call_manage_replicaset(
            self.console_sock,
            alias='r1',
            roles=['role-2'],
            failover_priority=['r1-master'],
            instances=['r1-master'],
        )

        self.assertFalse(res.success)
        self.assertIn('Replicaset "r1" is not healthy', res.msg)

        # replicaset becomes unhealthy after creation
        self.instance.set_variable('become_unhealthy_after_edit', True)

        self.instance.add_unjoined_server(alias='r2-master',
                                          uri='r2-master-uri')

        res = call_manage_replicaset(
            self.console_sock,
            alias='r2',
            roles=['role-2'],
            failover_priority=['r2-master'],
            instances=['r2-master'],
        )
        self.assertFalse(res.success)
        self.assertIn('Replicaset "r2" is not healthy', res.msg)

        # replicaset becomes unhealthy after edit
        self.instance.set_variable('become_unhealthy_after_edit', True)

        add_replicaset(
            self.instance,
            alias='r3',
            roles=['role-1'],
            servers=['r3-master'],
            status='unhealthy',
        )

        res = call_manage_replicaset(
            self.console_sock,
            alias='r3',
            roles=['role-2'],
            failover_priority=['r3-master'],
            instances=['r3-master'],
        )
        self.assertFalse(res.success)
        self.assertIn('Replicaset "r3" is not healthy', res.msg)

    def tearDown(self):
        self.instance.stop()
class TestProbeInstance(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_probe_ok(self):
        set_known_servers(self.instance, [URI1, URI2])

        # one instance
        self.instance.clear_calls('admin_probe_server')
        res = call_probe_instance(
            control_sock=self.console_sock,
            hostvars={'instance-1': {
                'config': {
                    'advertise_uri': URI1
                },
            }})
        self.assertTrue(res.success, msg=res.msg)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertEqual(len(calls), 1)
        self.assertIn(URI1, calls)

        # two instances
        self.instance.clear_calls('admin_probe_server')
        res = call_probe_instance(control_sock=self.console_sock,
                                  hostvars={
                                      'instance-1': {
                                          'config': {
                                              'advertise_uri': URI1
                                          },
                                      },
                                      'instance-2': {
                                          'config': {
                                              'advertise_uri': URI2
                                          },
                                      }
                                  })
        self.assertTrue(res.success, msg=res.msg)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertEqual(len(calls), 2)
        self.assertIn(URI1, calls)
        self.assertIn(URI2, calls)

    def test_probe_one_fails(self):
        set_known_servers(self.instance, [URI1])

        # probe not known server
        self.instance.clear_calls('admin_probe_server')
        res = call_probe_instance(
            control_sock=self.console_sock,
            hostvars={'instance-2': {
                'config': {
                    'advertise_uri': URI2
                },
            }})
        self.assertFalse(res.success)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertEqual(len(calls), 1)
        self.assertIn(URI2, calls)

        # probe both known and not known server
        self.instance.clear_calls('admin_probe_server')
        res = call_probe_instance(control_sock=self.console_sock,
                                  hostvars={
                                      'instance-1': {
                                          'config': {
                                              'advertise_uri': URI1
                                          },
                                      },
                                      'instance-2': {
                                          'config': {
                                              'advertise_uri': URI2
                                          },
                                      }
                                  })
        self.assertFalse(res.success)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertIn(len(calls), [1, 2])
        if len(calls) == 1:
            self.assertIn(URI2, calls)
        else:
            self.assertIn(URI1, calls)
            self.assertIn(URI2, calls)

    def test_probe_expelled(self):
        # expelled server shouldn't be probed
        set_known_servers(self.instance, [URI1])

        # probe only expelled
        self.instance.clear_calls('admin_probe_server')
        res = call_probe_instance(control_sock=self.console_sock,
                                  hostvars={
                                      'instance-2': {
                                          'expelled': True,
                                          'config': {
                                              'advertise_uri': URI2
                                          },
                                      }
                                  })
        self.assertTrue(res.success, msg=res.msg)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertEqual(len(calls), 0)

        # probe both expelled and not
        self.instance.clear_calls('admin_probe_server')
        res = call_probe_instance(control_sock=self.console_sock,
                                  hostvars={
                                      'instance-1': {
                                          'config': {
                                              'advertise_uri': URI1
                                          },
                                      },
                                      'instance-2': {
                                          'expelled': True,
                                          'config': {
                                              'advertise_uri': URI2
                                          },
                                      }
                                  })
        self.assertTrue(res.success, msg=res.msg)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertEqual(len(calls), 1)
        self.assertIn(URI1, calls)

    def test_probe_restarted(self):
        # restarted server should be probed
        set_known_servers(self.instance, [])

        self.instance.clear_calls('admin_probe_server')
        res = call_probe_instance(control_sock=self.console_sock,
                                  hostvars={
                                      'instance-2': {
                                          'restarted': True,
                                          'config': {
                                              'advertise_uri': URI2
                                          },
                                      }
                                  })
        self.assertFalse(res.success)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertEqual(len(calls), 1)
        self.assertIn(URI2, calls)

    def test_failed_for_non_play_host(self):
        set_known_servers(self.instance, [URI1])

        # probe can fail for instance not mentioned in play_hosts
        # but it should be probed
        self.instance.clear_calls('admin_probe_server')

        res = call_probe_instance(control_sock=self.console_sock,
                                  hostvars={
                                      'instance-1': {
                                          'config': {
                                              'advertise_uri': URI1
                                          },
                                      },
                                      'instance-2': {
                                          'config': {
                                              'advertise_uri': URI2
                                          },
                                      }
                                  },
                                  play_hosts=['instance-1'])
        self.assertTrue(res.success, msg=res.msg)

        calls = self.instance.get_calls('admin_probe_server')
        self.assertEqual(len(calls), 2)
        self.assertIn(URI1, calls)
        self.assertIn(URI2, calls)

    def tearDown(self):
        self.instance.stop()
Esempio n. 7
0
class TestFailover(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_enable_failover(self):
        # failover disabled
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, True)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], 'enable')

        # failover enabled
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, True)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 0)

    def test_disable_failover(self):
        # failover enabled
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, False)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], 'disable')

        # failover disabled
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, False)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 0)

    def test_fail_on_manage_failover(self):
        # enable failover
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')
        self.instance.set_fail_on('manage_failover')

        res = call_manage_failover(self.console_sock, True)
        self.assertFalse(res.success)
        self.assertIn('Failed admin_enable_failover', res.msg)
        self.assertIn('cartridge err', res.msg)

        # disable failover
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')
        self.instance.set_fail_on('manage_failover')

        res = call_manage_failover(self.console_sock, False)
        self.assertFalse(res.success)
        self.assertIn('Failed admin_disable_failover', res.msg)
        self.assertIn('cartridge err', res.msg)

    def tearDown(self):
        self.instance.stop()
class TestManageInstance(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_instance_not_started(self):
        # console sock doesn't exists
        self.instance.clear_calls('box_cfg')
        self.instance.remove_file(self.console_sock)

        res = call_manage_instance(
            control_sock=self.console_sock
        )

        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)
        self.assertEqual(len(self.instance.get_calls('box_cfg')), 0)

        # cennot connect to console sock
        self.instance.clear_calls('box_cfg')
        bad_socket_path = 'bad-socket-path'
        self.instance.write_file(bad_socket_path)

        res = call_manage_instance(
            control_sock=bad_socket_path
        )

        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)
        self.assertEqual(len(self.instance.get_calls('box_cfg')), 0)

        # memtx_memory is nil
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, memtx_memory=None)

        res = call_manage_instance(
            control_sock=self.console_sock
        )

        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)
        self.assertEqual(len(self.instance.get_calls('box_cfg')), 0)

    def test_non_dynamic_params(self):
        param_name = 'advertise_uri'
        old_value = 'localhost:3301'
        new_instance_value = 'localhost:3311'
        new_app_value = 'localhost:3322'

        # changed only in instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: new_instance_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 0)

        # changed only in app config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            cartridge_defaults={
                param_name: new_app_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 0)

        # changed in both app and instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: new_instance_value,
            },
            cartridge_defaults={
                param_name: new_app_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 0)

    @parameterized.expand([
        ["memtx_memory"],
        ["vinyl_memory"],
    ])
    def test_memory_decreasing(self, param_name):
        BIG_MEMORY = 1024
        SMALL_MEMORY = 512

        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: BIG_MEMORY})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: SMALL_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        self.assertEqual(len(self.instance.get_calls('box_cfg')), 0)

    @parameterized.expand([
        ["memtx_memory"],
        ["vinyl_memory"],
    ])
    def test_memtx_memory_increasing(self, param_name):
        SMALL_MEMORY = 512
        INSTANCE_BIG_MEMORY = 1024
        APP_BIG_MEMORY = 2048

        # increased only in instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: SMALL_MEMORY})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: INSTANCE_BIG_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: INSTANCE_BIG_MEMORY}, calls)

        # increased only in app config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: SMALL_MEMORY})

        res = call_manage_instance(
            control_sock=self.console_sock,
            cartridge_defaults={
                param_name: APP_BIG_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: APP_BIG_MEMORY}, calls)

        # increased in both app and instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: SMALL_MEMORY})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: INSTANCE_BIG_MEMORY,
            },
            cartridge_defaults={
                param_name: APP_BIG_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: INSTANCE_BIG_MEMORY}, calls)

    @parameterized.expand([
        ["memtx_memory"],
        ["vinyl_memory"],
    ])
    def test_memtx_memory_increasing_fails(self, param_name):
        SMALL_MEMORY = 512
        INSTANCE_BIG_MEMORY = 1024
        APP_BIG_MEMORY = 2048

        self.instance.set_fail_on('increase_memory_size')

        # increased only in instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: SMALL_MEMORY})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: INSTANCE_BIG_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: INSTANCE_BIG_MEMORY}, calls)

        # increased only in app config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: SMALL_MEMORY})

        res = call_manage_instance(
            control_sock=self.console_sock,
            cartridge_defaults={
                param_name: APP_BIG_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: APP_BIG_MEMORY}, calls)

        # increased in both app and instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: SMALL_MEMORY})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: INSTANCE_BIG_MEMORY,
            },
            cartridge_defaults={
                param_name: APP_BIG_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: INSTANCE_BIG_MEMORY}, calls)

    def test_box_cfg_is_function(self):
        BIG_MEMTX_MEMORY = 1024

        self.instance.clear_calls('box_cfg')
        self.instance.set_box_cfg_function()

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                'memtx_memory': BIG_MEMTX_MEMORY,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 0)

    # test non-dynamic param changed

    @parameterized.expand([
        ['string', 'custom_proc_title', 'old-proc-title', 'new-proc-title', 'new-app-proc-title'],
        ['number', 'checkpoint_interval', 3600, 1800, 900],
        ['boolean', 'read_only', False, True, True],
    ])
    def test_dynamic_params(self, _, param_name, old_value, new_instance_value, new_app_value):
        # changed only in instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: new_instance_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: new_instance_value}, calls)

        # changed only in app config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            cartridge_defaults={
                param_name: new_app_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: new_app_value}, calls)

        # changed in both app and instance config
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: new_instance_value,
            },
            cartridge_defaults={
                param_name: new_app_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: new_instance_value}, calls)

        # specified in instance config, isn't changed
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            config={
                param_name: old_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: old_value}, calls)

        # specified in app config, isn't changed
        self.instance.clear_calls('box_cfg')
        set_box_cfg(self.instance, **{param_name: old_value})

        res = call_manage_instance(
            control_sock=self.console_sock,
            cartridge_defaults={
                param_name: old_value,
            }
        )
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('box_cfg')
        self.assertEqual(len(calls), 1)
        self.assertIn({param_name: old_value}, calls)

    def tearDown(self):
        self.instance.stop()
class TestAuth(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_empty(self):
        res = call_manage_auth(self.console_sock)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

    def test_edit_params(self):
        current_auth = {
            'enabled': False,
            'cookie_max_age': 100,
            'cookie_renew_age': 10,
        }

        new_values = {
            'enabled': True,
            'cookie_max_age': 200,
            'cookie_renew_age': 20,
        }

        self.instance.set_variable('auth_params', current_auth)

        for p in ['enabled', 'cookie_max_age', 'cookie_renew_age']:
            # call with current value (res.changed is False)
            auth_patch = {p: current_auth[p]}

            self.instance.clear_calls('auth_set_params')

            res = call_manage_auth(self.console_sock, **auth_patch)
            self.assertTrue(res.success, msg=res.msg)
            self.assertFalse(res.changed)

            calls = self.instance.get_calls('auth_set_params')
            self.assertEqual(len(calls), 1)
            self.assertEqual(calls[0], auth_patch)

            # call with a new value (res.changed is True)
            auth_patch = {p: new_values[p]}

            self.instance.clear_calls('auth_set_params')

            res = call_manage_auth(self.console_sock, **auth_patch)
            self.assertTrue(res.success, msg=res.msg)
            self.assertTrue(res.changed)

            calls = self.instance.get_calls('auth_set_params')
            self.assertEqual(len(calls), 1)
            self.assertEqual(calls[0], auth_patch)

        # fail on auth_set_params
        self.instance.set_fail_on('auth_set_params')
        self.instance.set_variable('auth_params', {})

        res = call_manage_auth(self.console_sock, enabled=True)
        self.assertFalse(res.success)
        self.assertIn('cartridge err', res.msg)

    def test_user_functions_implemented(self):
        USER = {
            'username': '******',
            'password': '******',
        }

        # no one operation is implemented
        self.instance.set_variable('webui_auth_params', {})
        res = call_manage_auth(self.console_sock, users=[USER])
        self.assertFalse(res.success)
        self.assertIn('backend must implement all user management functions', res.msg)

        # check that all operations are required
        required_operations = [
            'implements_list_users', 'implements_remove_user',
            'implements_add_user', 'implements_edit_user',
            'implements_get_user', 'implements_check_password',
        ]
        for missed_op in required_operations:
            self.instance.set_variable('webui_auth_params', {
                op: True for op in required_operations if op != missed_op
            })

            res = call_manage_auth(self.console_sock, users=[USER])
            self.assertFalse(res.success)
            self.assertIn('backend must implement all user management functions', res.msg)

    def test_add_user(self):
        # all required operations are implemented
        set_user_functions_implemented(self.instance)

        USER1 = {
            'username': '******',
            'email': '*****@*****.**',
            'fullname': 'Elizaveta Dokshina',
        }
        USER2 = {
            'username': '******',
            'email': '*****@*****.**',
            'fullname': 'The Princess',
        }

        # add a new user
        self.instance.set_variable('users', [USER1])
        self.instance.clear_calls('auth_add_user')
        self.instance.clear_calls('auth_edit_user')
        self.instance.clear_calls('auth_remove_user')

        res = call_manage_auth(self.console_sock, users=[USER1, USER2])
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('auth_add_user')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], USER2)

        calls = self.instance.get_calls('auth_edit_user')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], USER1)

        calls = self.instance.get_calls('auth_remove_user')
        self.assertEqual(len(calls), 0)

        # add existed user
        self.instance.set_variable('users', [USER1, USER2])
        self.instance.clear_calls('auth_add_user')
        self.instance.clear_calls('auth_edit_user')
        self.instance.clear_calls('auth_remove_user')

        res = call_manage_auth(self.console_sock, users=[USER1, USER2])
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('auth_add_user')
        self.assertEqual(len(calls), 0)

        calls = self.instance.get_calls('auth_edit_user')
        self.assertEqual(len(calls), 2)
        self.assertIn(USER1, calls)
        self.assertIn(USER2, calls)

        calls = self.instance.get_calls('auth_remove_user')
        self.assertEqual(len(calls), 0)

        # fail on auth_add_user
        self.instance.set_fail_on('auth_add_user')
        self.instance.set_variable('users', [])

        res = call_manage_auth(self.console_sock, users=[USER1])
        self.assertFalse(res.success)
        self.assertIn('cartridge err', res.msg)

    def test_edit_user(self):
        # all required operations are implemented
        set_user_functions_implemented(self.instance)

        USER = {
            'username': '******',
            'email': '*****@*****.**',
            'fullname': 'Elizaveta Dokshina',
        }

        new_params = {
            'password': '******',
            'email': '*****@*****.**',
            'fullname': 'The Princess',
        }

        for param, value in new_params.items():
            self.instance.set_variable('users', [USER])

            self.instance.clear_calls('auth_add_user')
            self.instance.clear_calls('auth_edit_user')
            self.instance.clear_calls('auth_remove_user')

            user_patch = {
                'username': USER['username'],
                param: value,
            }

            res = call_manage_auth(self.console_sock, users=[user_patch])
            self.assertTrue(res.success, msg=res.msg)
            if param != 'password':
                self.assertTrue(res.changed)
            else:
                self.assertFalse(res.changed)

            calls = self.instance.get_calls('auth_add_user')
            self.assertEqual(len(calls), 0)

            calls = self.instance.get_calls('auth_edit_user')
            self.assertEqual(len(calls), 1)
            self.assertEqual(calls[0], user_patch)

            calls = self.instance.get_calls('auth_remove_user')
            self.assertEqual(len(calls), 0)

        # fail on auth_edit_user
        self.instance.set_fail_on('auth_edit_user')
        self.instance.set_variable('users', [USER])

        user_patch = {
            'username': USER['username'],
            'password': '******',
        }

        res = call_manage_auth(self.console_sock, users=[user_patch])
        self.assertFalse(res.success)
        self.assertIn('cartridge err', res.msg)

    def test_delete_user(self):
        # all required operations are implemented
        set_user_functions_implemented(self.instance)

        USER1 = {
            'username': '******',
            'email': '*****@*****.**',
            'fullname': 'Elizaveta Dokshina',
        }
        USER2 = {
            'username': '******',
            'email': '*****@*****.**',
            'fullname': 'The Princess',
        }

        # delete existed user
        self.instance.set_variable('users', [USER1, USER2])
        self.instance.clear_calls('auth_add_user')
        self.instance.clear_calls('auth_edit_user')
        self.instance.clear_calls('auth_remove_user')

        deleted_user2 = USER2.copy()
        deleted_user2['deleted'] = True

        res = call_manage_auth(self.console_sock, users=[USER1, deleted_user2])
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('auth_add_user')
        self.assertEqual(len(calls), 0)

        calls = self.instance.get_calls('auth_edit_user')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], USER1)

        calls = self.instance.get_calls('auth_remove_user')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], USER2['username'])

        # delete non-existed user
        self.instance.set_variable('users', [USER1])
        self.instance.clear_calls('auth_add_user')
        self.instance.clear_calls('auth_edit_user')
        self.instance.clear_calls('auth_remove_user')

        deleted_user2 = USER2.copy()
        deleted_user2['deleted'] = True

        res = call_manage_auth(self.console_sock, users=[USER1, deleted_user2])
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('auth_add_user')
        self.assertEqual(len(calls), 0)

        calls = self.instance.get_calls('auth_edit_user')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], USER1)

        calls = self.instance.get_calls('auth_remove_user')
        self.assertEqual(len(calls), 0)

        # fail on auth_remove_user
        self.instance.set_fail_on('auth_remove_user')
        self.instance.set_variable('users', [USER1])

        deleted_user1 = USER1.copy()
        deleted_user1['deleted'] = True

        res = call_manage_auth(self.console_sock, users=[deleted_user1])
        self.assertFalse(res.success)
        self.assertIn('cartridge err', res.msg)

    def test_empty_users(self):
        # all required operations are implemented
        set_user_functions_implemented(self.instance)

        USER = {
            'username': '******',
            'email': '*****@*****.**',
            'fullname': 'Elizaveta Dokshina',
        }

        # delete existed user
        self.instance.set_variable('users', [USER])
        self.instance.clear_calls('auth_add_user')
        self.instance.clear_calls('auth_edit_user')
        self.instance.clear_calls('auth_remove_user')

        res = call_manage_auth(self.console_sock, users=[])
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('auth_add_user')
        self.assertEqual(len(calls), 0)

        calls = self.instance.get_calls('auth_edit_user')
        self.assertEqual(len(calls), 0)

        calls = self.instance.get_calls('auth_remove_user')
        self.assertEqual(len(calls), 0)

    def tearDown(self):
        self.instance.stop()
class TestExpelInstance(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_non_existent_instance(self):
        self.instance.clear_calls('edit_topology')

        res = call_expel_intstance(self.console_sock, 'non-existent-instance')
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('edit_topology')
        self.assertEqual(len(calls), 0)

    def test_unjoined_instance(self):
        ALIAS = 'instance'
        self.instance.add_unjoined_server(alias=ALIAS,
                                          uri='{}-uri'.format(ALIAS))

        self.instance.clear_calls('edit_topology')

        res = call_expel_intstance(self.console_sock, ALIAS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('edit_topology')
        self.assertEqual(len(calls), 0)

    def test_instance_from_replicaset(self):
        add_replicaset(
            self.instance,
            alias='r1',
            roles=['role-1'],
            servers=['r1-master', 'r1-replica'],
        )

        self.instance.clear_calls('edit_topology')
        res = call_expel_intstance(self.console_sock, 'r1-master')
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        # check `edit_topology` call
        calls = self.instance.get_calls('edit_topology')
        self.assertEqual(len(calls), 1)
        self.assertIn('servers', calls[0])
        self.assertEqual(len(calls[0]['servers']), 1)
        s_params = calls[0]['servers'][0]
        self.assertEqual(s_params, {
            'uuid': 'r1-master-uuid',
            'expelled': True,
        })

        # call again (res.changed should be false)
        # in fact, bacause server is expelled from topology
        self.instance.clear_calls('edit_topology')
        res = call_expel_intstance(self.console_sock, 'r1-master')
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('edit_topology')
        self.assertEqual(len(calls), 0)

    def test_fail_on_edit_topology(self):
        add_replicaset(
            self.instance,
            alias='r1',
            roles=['role-1'],
            servers=['r1-master', 'r1-replica'],
        )

        self.instance.clear_calls('edit_topology')
        self.instance.set_fail_on('edit_topology')

        res = call_expel_intstance(self.console_sock, 'r1-master')
        self.assertFalse(res.success)
        self.assertIn('Failed to expel instance', res.msg)

    def tearDown(self):
        self.instance.stop()
class TestFailover(unittest.TestCase):
    def setUp(self):
        self.cookie = 'secret'
        self.console_sock = './tmp/x.sock'

        self.instance = Instance(self.console_sock, self.cookie)
        self.instance.start()

    def test_enable_failover_old_cartridge(self):
        self.instance.set_cartridge_version('1.2.0')

        # failover disabled -> enable eventual
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, mode='eventual')
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], 'enable')

        # failover enabled -> enable eventual
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, mode='eventual')
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 0)

        # failover disabled -> enable stateful
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, mode='stateful')
        self.assertFalse(res.success)
        self.assertIn('Stateful failover is supported since cartridge 2',
                      res.msg)

    def test_disable_failover_old_cartridge(self):
        self.instance.set_cartridge_version('1.2.0')

        # failover enabled -> disable
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, mode='disabled')
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], 'disable')

        # failover disabled -> disable
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')

        res = call_manage_failover(self.console_sock, mode='disabled')
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('manage_failover')
        self.assertEqual(len(calls), 0)

    def test_fail_on_manage_failover_old_cartridge(self):
        self.instance.set_cartridge_version('1.2.0')

        # enable eventual failover
        self.instance.set_variable('failover', False)
        self.instance.clear_calls('manage_failover')
        self.instance.set_fail_on('manage_failover')

        res = call_manage_failover(self.console_sock, mode='eventual')
        self.assertFalse(res.success)
        self.assertIn('Failed admin_enable_failover', res.msg)
        self.assertIn('cartridge err', res.msg)

        # disable failover
        self.instance.set_variable('failover', True)
        self.instance.clear_calls('manage_failover')
        self.instance.set_fail_on('manage_failover')

        res = call_manage_failover(self.console_sock, mode='disabled')
        self.assertFalse(res.success)
        self.assertIn('Failed admin_disable_failover', res.msg)
        self.assertIn('cartridge err', res.msg)

    def test_enable_eventual_failover(self):
        self.instance.set_cartridge_version('2.1.0')

        # failover disabled
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock, mode='eventual')
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'eventual'})

        # failover enabled
        self.instance.set_variable('failover_params', {'mode': 'eventual'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock, mode='eventual')
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'eventual'})

    def test_disable_eventual_failover(self):
        self.instance.set_cartridge_version('2.1.0')

        # failover enabled
        self.instance.set_variable('failover_params', {'mode': 'eventual'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock, mode='disabled')
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'disabled'})

        # failover disabled
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock, mode='disabled')
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {'mode': 'disabled'})

    def test_stateful_failover_with_stateboard(self):
        self.instance.set_cartridge_version('2.1.0')

        STATEBOARD_PARAMS = {
            'uri': 'localhost:3310',
            'password': '******',
        }

        # failover disabled
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='stateboard',
                                   stateboard_params=STATEBOARD_PARAMS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(
            calls[0], {
                'mode': 'stateful',
                'state_provider': 'tarantool',
                'tarantool_params': STATEBOARD_PARAMS,
            })

        # stateful failover enabled - params aren't changed
        self.instance.set_variable(
            'failover_params', {
                'mode': 'stateful',
                'state_provider': 'tarantool',
                'tarantool_params': STATEBOARD_PARAMS,
            })
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='stateboard',
                                   stateboard_params=STATEBOARD_PARAMS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(
            calls[0], {
                'mode': 'stateful',
                'state_provider': 'tarantool',
                'tarantool_params': STATEBOARD_PARAMS,
            })

        for p in ['uri', 'password']:
            # stateful failover enabled - one param is changed
            self.instance.set_variable(
                'failover_params', {
                    'mode': 'stateful',
                    'state_provider': 'tarantool',
                    'tarantool_params': STATEBOARD_PARAMS,
                })
            self.instance.clear_calls('failover_set_params')

            new_params = STATEBOARD_PARAMS.copy()
            new_params[p] = 'other-string-value'

            res = call_manage_failover(self.console_sock,
                                       mode='stateful',
                                       state_provider='stateboard',
                                       stateboard_params=new_params)
            self.assertTrue(res.success, msg=res.msg)
            self.assertTrue(res.changed)

            calls = self.instance.get_calls('failover_set_params')
            self.assertEqual(len(calls), 1)
            self.assertEqual(
                calls[0], {
                    'mode': 'stateful',
                    'state_provider': 'tarantool',
                    'tarantool_params': new_params,
                })

    def test_stateful_failover_with_etcd2(self):
        self.instance.set_cartridge_version('2.1.0')

        ETCD2_PARAMS = {
            'prefix': '/',
            'lock_delay': 30,
            'username': '******',
            'password': '******',
            'endpoints': ['localhost:2379', 'localhost:2380']
        }

        # failover disabled
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='etcd2',
                                   etcd2_params=ETCD2_PARAMS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(
            calls[0], {
                'mode': 'stateful',
                'state_provider': 'etcd2',
                'etcd2_params': ETCD2_PARAMS,
            })

        # failover disabled
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='etcd2',
                                   etcd2_params=None)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(calls[0], {
            'mode': 'stateful',
            'state_provider': 'etcd2',
            'etcd2_params': [],
        })

        # stateful failover enabled - params aren't changed
        self.instance.set_variable(
            'failover_params', {
                'mode': 'stateful',
                'state_provider': 'etcd2',
                'etcd2_params': ETCD2_PARAMS,
            })
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='etcd2',
                                   etcd2_params=ETCD2_PARAMS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertFalse(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(
            calls[0], {
                'mode': 'stateful',
                'state_provider': 'etcd2',
                'etcd2_params': ETCD2_PARAMS,
            })

        for p in ['prefix', 'lock_delay', 'username', 'password', 'endpoints']:
            # stateful failover enabled - one param is changed
            self.instance.set_variable(
                'failover_params', {
                    'mode': 'stateful',
                    'state_provider': 'etcd2',
                    'etcd2_params': ETCD2_PARAMS,
                })
            self.instance.clear_calls('failover_set_params')

            new_params = ETCD2_PARAMS.copy()
            if p in ['prefix', 'username', 'password']:
                new_params[p] = 'other-string-value'
            elif p in ['lock_delay']:
                new_params[p] = new_params[p] + 15
            elif p in ['endpoints']:
                new_params[p].append('localhost:2381')

            res = call_manage_failover(self.console_sock,
                                       mode='stateful',
                                       state_provider='etcd2',
                                       etcd2_params=new_params)
            self.assertTrue(res.success, msg=res.msg)
            self.assertTrue(res.changed)

            calls = self.instance.get_calls('failover_set_params')
            self.assertEqual(len(calls), 1)
            self.assertEqual(
                calls[0], {
                    'mode': 'stateful',
                    'state_provider': 'etcd2',
                    'etcd2_params': new_params,
                })

    def test_stateful_failover_mixed(self):
        self.instance.set_cartridge_version('2.1.0')

        STATEBOARD_PARAMS = {
            'uri': 'localhost:3310',
            'password': '******',
        }

        ETCD2_PARAMS = {
            'prefix': '/',
            'lock_delay': 30,
            'username': '******',
            'password': '******',
            'endpoints': ['localhost:2379', 'localhost:2380']
        }

        # failover disabled -> enable stateboard
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='stateboard',
                                   etcd2_params=ETCD2_PARAMS,
                                   stateboard_params=STATEBOARD_PARAMS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(
            calls[0], {
                'mode': 'stateful',
                'state_provider': 'tarantool',
                'tarantool_params': STATEBOARD_PARAMS,
            })

        # failover disabled -> enable etcd2
        self.instance.set_variable('failover_params', {'mode': 'disabled'})
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='etcd2',
                                   etcd2_params=ETCD2_PARAMS,
                                   stateboard_params=STATEBOARD_PARAMS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(
            calls[0], {
                'mode': 'stateful',
                'state_provider': 'etcd2',
                'etcd2_params': ETCD2_PARAMS,
            })

        # stateboard state provider enabled -> switch to etcd2
        self.instance.set_variable(
            'failover_params', {
                'mode': 'stateful',
                'state_provider': 'tarantool',
                'tarantool_params': STATEBOARD_PARAMS,
            })
        self.instance.clear_calls('failover_set_params')

        res = call_manage_failover(self.console_sock,
                                   mode='stateful',
                                   state_provider='etcd2',
                                   etcd2_params=ETCD2_PARAMS,
                                   stateboard_params=STATEBOARD_PARAMS)
        self.assertTrue(res.success, msg=res.msg)
        self.assertTrue(res.changed)

        calls = self.instance.get_calls('failover_set_params')
        self.assertEqual(len(calls), 1)
        self.assertEqual(
            calls[0], {
                'mode': 'stateful',
                'state_provider': 'etcd2',
                'etcd2_params': ETCD2_PARAMS,
            })

    def tearDown(self):
        self.instance.stop()