329 lines
14 KiB
Python
329 lines
14 KiB
Python
# Copyright 2012 OpenStack Foundation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from mock import DEFAULT, MagicMock, patch
|
|
|
|
from trove.guestagent import backup
|
|
from trove.guestagent.common import configuration
|
|
from trove.guestagent.common.configuration import ImportOverrideStrategy
|
|
from trove.guestagent.common import operating_system
|
|
from trove.guestagent.datastore.experimental.redis import (
|
|
service as redis_service)
|
|
from trove.guestagent.datastore.experimental.redis.manager import (
|
|
Manager as RedisManager)
|
|
from trove.guestagent.volume import VolumeDevice
|
|
from trove.tests.unittests.guestagent.test_datastore_manager import \
|
|
DatastoreManagerTest
|
|
|
|
|
|
class RedisGuestAgentManagerTest(DatastoreManagerTest):
|
|
|
|
@patch.object(redis_service.RedisApp, '_build_admin_client')
|
|
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
|
|
def setUp(self, *args, **kwargs):
|
|
super(RedisGuestAgentManagerTest, self).setUp('redis')
|
|
self.patch_ope = patch('os.path.expanduser')
|
|
self.mock_ope = self.patch_ope.start()
|
|
self.addCleanup(self.patch_ope.stop)
|
|
self.replication_strategy = 'RedisSyncReplication'
|
|
self.patch_rs = patch(
|
|
'trove.guestagent.strategies.replication.get_strategy',
|
|
return_value=self.replication_strategy)
|
|
self.mock_rs = self.patch_rs.start()
|
|
self.addCleanup(self.patch_rs.stop)
|
|
self.manager = RedisManager()
|
|
self.packages = 'redis-server'
|
|
self.origin_RedisAppStatus = redis_service.RedisAppStatus
|
|
self.origin_start_redis = redis_service.RedisApp.start_db
|
|
self.origin_stop_redis = redis_service.RedisApp.stop_db
|
|
self.origin_install_redis = redis_service.RedisApp._install_redis
|
|
self.origin_install_if_needed = \
|
|
redis_service.RedisApp.install_if_needed
|
|
self.origin_format = VolumeDevice.format
|
|
self.origin_mount = VolumeDevice.mount
|
|
self.origin_mount_points = VolumeDevice.mount_points
|
|
self.origin_restore = backup.restore
|
|
self.patch_repl = patch(
|
|
'trove.guestagent.strategies.replication.get_instance')
|
|
self.mock_repl = self.patch_repl.start()
|
|
self.addCleanup(self.patch_repl.stop)
|
|
self.patch_gfvs = patch(
|
|
'trove.guestagent.dbaas.get_filesystem_volume_stats')
|
|
self.mock_gfvs_class = self.patch_gfvs.start()
|
|
self.addCleanup(self.patch_gfvs.stop)
|
|
|
|
def tearDown(self):
|
|
super(RedisGuestAgentManagerTest, self).tearDown()
|
|
redis_service.RedisAppStatus = self.origin_RedisAppStatus
|
|
redis_service.RedisApp.stop_db = self.origin_stop_redis
|
|
redis_service.RedisApp.start_db = self.origin_start_redis
|
|
redis_service.RedisApp._install_redis = self.origin_install_redis
|
|
redis_service.RedisApp.install_if_needed = \
|
|
self.origin_install_if_needed
|
|
VolumeDevice.format = self.origin_format
|
|
VolumeDevice.mount = self.origin_mount
|
|
VolumeDevice.mount_points = self.origin_mount_points
|
|
backup.restore = self.origin_restore
|
|
|
|
def test_update_status(self):
|
|
mock_status = MagicMock()
|
|
self.manager._app.status = mock_status
|
|
self.manager.update_status(self.context)
|
|
mock_status.update.assert_any_call()
|
|
|
|
def test_prepare_redis_not_installed(self):
|
|
self._prepare_dynamic(is_redis_installed=False)
|
|
|
|
def test_prepare_redis_with_snapshot(self):
|
|
snapshot = {'replication_strategy': self.replication_strategy,
|
|
'dataset': {'dataset_size': 1.0},
|
|
'config': None}
|
|
self._prepare_dynamic(snapshot=snapshot)
|
|
|
|
@patch.object(redis_service.RedisApp, 'get_working_dir',
|
|
MagicMock(return_value='/var/lib/redis'))
|
|
def test_prepare_redis_from_backup(self):
|
|
self._prepare_dynamic(backup_id='backup_id_123abc')
|
|
|
|
@patch.multiple(redis_service.RedisApp,
|
|
apply_initial_guestagent_configuration=DEFAULT,
|
|
restart=DEFAULT,
|
|
install_if_needed=DEFAULT)
|
|
@patch.object(operating_system, 'chown')
|
|
@patch.object(configuration.ConfigurationManager, 'save_configuration')
|
|
def _prepare_dynamic(self, save_configuration_mock, chown_mock,
|
|
apply_initial_guestagent_configuration, restart,
|
|
install_if_needed,
|
|
device_path='/dev/vdb', is_redis_installed=True,
|
|
backup_info=None, is_root_enabled=False,
|
|
mount_point='var/lib/redis', backup_id=None,
|
|
snapshot=None):
|
|
|
|
backup_info = None
|
|
if backup_id is not None:
|
|
backup_info = {'id': backup_id,
|
|
'location': 'fake-location',
|
|
'type': 'RedisBackup',
|
|
'checksum': 'fake-checksum',
|
|
}
|
|
|
|
# covering all outcomes is starting to cause trouble here
|
|
mock_status = MagicMock()
|
|
self.manager._app.status = mock_status
|
|
self.manager._build_admin_client = MagicMock(return_value=MagicMock())
|
|
redis_service.RedisApp.stop_db = MagicMock(return_value=None)
|
|
redis_service.RedisApp.start_db = MagicMock(return_value=None)
|
|
mock_status.begin_install = MagicMock(return_value=None)
|
|
VolumeDevice.format = MagicMock(return_value=None)
|
|
VolumeDevice.mount = MagicMock(return_value=None)
|
|
VolumeDevice.mount_points = MagicMock(return_value=[])
|
|
backup.restore = MagicMock(return_value=None)
|
|
mock_replication = MagicMock()
|
|
mock_replication.enable_as_slave = MagicMock()
|
|
self.mock_repl.return_value = mock_replication
|
|
|
|
self.manager.prepare(self.context, self.packages,
|
|
None, '2048',
|
|
None, device_path=device_path,
|
|
mount_point=mount_point,
|
|
backup_info=backup_info,
|
|
overrides=None,
|
|
cluster_config=None,
|
|
snapshot=snapshot)
|
|
|
|
mock_status.begin_install.assert_any_call()
|
|
VolumeDevice.format.assert_any_call()
|
|
install_if_needed.assert_any_call(self.packages)
|
|
save_configuration_mock.assert_any_call(None)
|
|
apply_initial_guestagent_configuration.assert_called_once_with()
|
|
chown_mock.assert_any_call(mount_point, 'redis', 'redis', as_root=True)
|
|
if backup_info:
|
|
backup.restore.assert_called_once_with(self.context,
|
|
backup_info,
|
|
'/var/lib/redis')
|
|
else:
|
|
redis_service.RedisApp.restart.assert_any_call()
|
|
|
|
if snapshot:
|
|
self.assertEqual(1, mock_replication.enable_as_slave.call_count)
|
|
else:
|
|
self.assertEqual(0, mock_replication.enable_as_slave.call_count)
|
|
|
|
@patch.object(redis_service.RedisApp, 'restart')
|
|
def test_restart(self, redis_mock):
|
|
self.manager.restart(self.context)
|
|
redis_mock.assert_any_call()
|
|
|
|
@patch.object(redis_service.RedisApp, 'stop_db')
|
|
def test_stop_db(self, redis_mock):
|
|
self.manager.stop_db(self.context)
|
|
redis_mock.assert_any_call(do_not_start_on_reboot=False)
|
|
|
|
@patch.object(ImportOverrideStrategy, '_initialize_import_directory')
|
|
@patch.object(backup, 'backup')
|
|
@patch.object(configuration.ConfigurationManager, 'parse_configuration',
|
|
MagicMock(return_value={'dir': '/var/lib/redis',
|
|
'dbfilename': 'dump.rdb'}))
|
|
@patch.object(operating_system, 'chown')
|
|
@patch.object(operating_system, 'create_directory')
|
|
def test_create_backup(self, *mocks):
|
|
backup.backup = MagicMock(return_value=None)
|
|
RedisManager().create_backup(self.context, 'backup_id_123')
|
|
backup.backup.assert_any_call(self.context, 'backup_id_123')
|
|
|
|
def test_backup_required_for_replication(self):
|
|
mock_replication = MagicMock()
|
|
mock_replication.backup_required_for_replication = MagicMock()
|
|
self.mock_repl.return_value = mock_replication
|
|
|
|
self.manager.backup_required_for_replication(self.context)
|
|
self.assertEqual(
|
|
1, mock_replication.backup_required_for_replication.call_count)
|
|
|
|
def test_attach_replica(self):
|
|
mock_replication = MagicMock()
|
|
mock_replication.enable_as_slave = MagicMock()
|
|
self.mock_repl.return_value = mock_replication
|
|
|
|
snapshot = {'replication_strategy': self.replication_strategy,
|
|
'dataset': {'dataset_size': 1.0}}
|
|
|
|
self.manager.attach_replica(self.context, snapshot, None)
|
|
self.assertEqual(1, mock_replication.enable_as_slave.call_count)
|
|
|
|
def test_detach_replica(self):
|
|
mock_replication = MagicMock()
|
|
mock_replication.detach_slave = MagicMock()
|
|
self.mock_repl.return_value = mock_replication
|
|
|
|
self.manager.detach_replica(self.context)
|
|
self.assertEqual(1, mock_replication.detach_slave.call_count)
|
|
|
|
def test_enable_as_master(self):
|
|
mock_replication = MagicMock()
|
|
mock_replication.enable_as_master = MagicMock()
|
|
self.mock_repl.return_value = mock_replication
|
|
|
|
self.manager.enable_as_master(self.context, None)
|
|
self.assertEqual(mock_replication.enable_as_master.call_count, 1)
|
|
|
|
def test_demote_replication_master(self):
|
|
mock_replication = MagicMock()
|
|
mock_replication.demote_master = MagicMock()
|
|
self.mock_repl.return_value = mock_replication
|
|
|
|
self.manager.demote_replication_master(self.context)
|
|
self.assertEqual(1, mock_replication.demote_master.call_count)
|
|
|
|
@patch.object(redis_service.RedisApp, 'make_read_only')
|
|
def test_make_read_only(self, redis_mock):
|
|
self.manager.make_read_only(self.context, 'ON')
|
|
redis_mock.assert_any_call('ON')
|
|
|
|
def test_cleanup_source_on_replica_detach(self):
|
|
mock_replication = MagicMock()
|
|
mock_replication.cleanup_source_on_replica_detach = MagicMock()
|
|
self.mock_repl.return_value = mock_replication
|
|
snapshot = {'replication_strategy': self.replication_strategy,
|
|
'dataset': {'dataset_size': '1.0'}}
|
|
|
|
self.manager.cleanup_source_on_replica_detach(self.context, snapshot)
|
|
self.assertEqual(
|
|
1, mock_replication.cleanup_source_on_replica_detach.call_count)
|
|
|
|
def test_get_replication_snapshot(self):
|
|
snapshot_id = None
|
|
log_position = None
|
|
master_ref = 'my_master'
|
|
used_size = 1.0
|
|
total_size = 2.0
|
|
|
|
mock_replication = MagicMock()
|
|
mock_replication.enable_as_master = MagicMock()
|
|
mock_replication.snapshot_for_replication = MagicMock(
|
|
return_value=(snapshot_id, log_position))
|
|
mock_replication.get_master_ref = MagicMock(
|
|
return_value=master_ref)
|
|
self.mock_repl.return_value = mock_replication
|
|
self.mock_gfvs_class.return_value = (
|
|
{'used': used_size, 'total': total_size})
|
|
|
|
expected_replication_snapshot = {
|
|
'dataset': {
|
|
'datastore_manager': self.manager.manager,
|
|
'dataset_size': used_size,
|
|
'volume_size': total_size,
|
|
'snapshot_id': snapshot_id
|
|
},
|
|
'replication_strategy': self.replication_strategy,
|
|
'master': master_ref,
|
|
'log_position': log_position
|
|
}
|
|
|
|
snapshot_info = None
|
|
replica_source_config = None
|
|
replication_snapshot = (
|
|
self.manager.get_replication_snapshot(self.context, snapshot_info,
|
|
replica_source_config))
|
|
self.assertEqual(expected_replication_snapshot, replication_snapshot)
|
|
self.assertEqual(1, mock_replication.enable_as_master.call_count)
|
|
self.assertEqual(
|
|
1, mock_replication.snapshot_for_replication.call_count)
|
|
self.assertEqual(1, mock_replication.get_master_ref.call_count)
|
|
|
|
def test_get_replica_context(self):
|
|
master_ref = {
|
|
'host': '1.2.3.4',
|
|
'port': 3306
|
|
}
|
|
expected_info = {
|
|
'master': master_ref,
|
|
}
|
|
mock_replication = MagicMock()
|
|
mock_replication.get_replica_context = MagicMock(
|
|
return_value=expected_info)
|
|
self.mock_repl.return_value = mock_replication
|
|
|
|
replica_info = self.manager.get_replica_context(self.context)
|
|
self.assertEqual(1, mock_replication.get_replica_context.call_count)
|
|
self.assertEqual(expected_info, replica_info)
|
|
|
|
def test_get_last_txn(self):
|
|
expected_host = '10.0.0.2'
|
|
self.manager._get_master_host = MagicMock(return_value=expected_host)
|
|
expected_txn_id = 199
|
|
repl_info = {'role': 'master', 'master_repl_offset': expected_txn_id}
|
|
self.manager._get_repl_info = MagicMock(return_value=repl_info)
|
|
|
|
(host, txn_id) = self.manager.get_last_txn(self.context)
|
|
self.manager._get_master_host.assert_any_call()
|
|
self.manager._get_repl_info.assert_any_call()
|
|
self.assertEqual(expected_host, host)
|
|
self.assertEqual(expected_txn_id, txn_id)
|
|
|
|
def test_get_latest_txn_id(self):
|
|
expected_txn_id = 199
|
|
repl_info = {'role': 'master', 'master_repl_offset': expected_txn_id}
|
|
self.manager._get_repl_info = MagicMock(return_value=repl_info)
|
|
latest_txn_id = self.manager.get_latest_txn_id(self.context)
|
|
self.assertEqual(expected_txn_id, latest_txn_id)
|
|
self.manager._get_repl_info.assert_any_call()
|
|
|
|
def test_wait_for_txn(self):
|
|
expected_txn_id = 199
|
|
repl_info = {'role': 'master', 'master_repl_offset': expected_txn_id}
|
|
self.manager._get_repl_info = MagicMock(return_value=repl_info)
|
|
self.manager.wait_for_txn(self.context, expected_txn_id)
|
|
self.manager._get_repl_info.assert_any_call()
|