c45eb67018
Unittests do not have 'CONF.datastore_manager' set (None). As the result all datastore manager/app tests that access configuration by the manager name get the default 'mysql' values. Apart from not testing the production configuration this also makes it difficult to test dynamically looked up datastore-specific properties which do not exist in the mysql group at all. This patch set adds a helper method for patching any given configuration property and also adds a simple interface for patching the manager name to the base TestCase. All existing manager/app tests were updated to properly patch the manager property. The base test patches the manager name to 'mysql' so that all unittests run with this manager unless explicitly overriden. It's now safe to remove the manager parameter from the 'cfg.get_configuration_property()' function. It was there to work around this issue in unittests. Change-Id: I903a0fb1ede8ad6614d2b5a8fc1a256f74412496
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()
|