b8efc0e698
* Re-trigger `ceph_access_joined` from `ceph_replication_device_changed`. Without this, we could end up with incomplete `ceph-access` relation if `ceph_access_joined` is executed before `ceph_replication_device_changed`. * Seed `replication-device-secret-uuid` early in `config-changed`, to make sure that it's set if `storage_backend` is executed before `ceph_access_joined`. * Set `secret_uuid` as part of the `replication_device` config in the backend config. Without this, Nova won't be able to access the proper secret for the Cinder Ceph volumes, after a failover. Change-Id: Ic023d05d5d17a663e1719de393bdd15f18a40484
542 lines
23 KiB
Python
542 lines
23 KiB
Python
# Copyright 2016 Canonical Ltd
|
|
#
|
|
# 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 unittest.mock import MagicMock, patch, call, ANY
|
|
import os
|
|
import json
|
|
import cinder_utils as utils
|
|
|
|
from test_utils import (
|
|
CharmTestCase,
|
|
)
|
|
|
|
# Need to do some early patching to get the module loaded.
|
|
_register_configs = utils.register_configs
|
|
utils.register_configs = MagicMock()
|
|
import cinder_hooks as hooks # noqa
|
|
utils.register_configs = _register_configs
|
|
|
|
TO_PATCH = [
|
|
# cinder_utils
|
|
'ensure_ceph_keyring',
|
|
'register_configs',
|
|
'restart_map',
|
|
'scrub_old_style_ceph',
|
|
'is_request_complete',
|
|
'send_request_if_needed',
|
|
'CONFIGS',
|
|
'CEPH_CONF',
|
|
'ceph_config_file',
|
|
'ceph_replication_device_config_file',
|
|
# charmhelpers.core.hookenv
|
|
'application_name',
|
|
'config',
|
|
'relation_ids',
|
|
'relation_set',
|
|
'service_name',
|
|
'service_restart',
|
|
'log',
|
|
'leader_get',
|
|
'leader_set',
|
|
'is_leader',
|
|
# charmhelpers.core.host
|
|
'apt_install',
|
|
'apt_update',
|
|
# charmhelpers.contrib.hahelpers.cluster_utils
|
|
'execd_preinstall',
|
|
'CephSubordinateContext',
|
|
'delete_keyring',
|
|
'remove_alternative',
|
|
'status_set',
|
|
'os_application_version_set',
|
|
'send_application_name',
|
|
]
|
|
|
|
|
|
class TestCinderHooks(CharmTestCase):
|
|
def setUp(self):
|
|
super(TestCinderHooks, self).setUp(hooks, TO_PATCH)
|
|
self.config.side_effect = self.test_config.get
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_install(self, mock_config):
|
|
hooks.hooks.execute(['hooks/install'])
|
|
self.assertTrue(self.execd_preinstall.called)
|
|
self.assertTrue(self.apt_update.called)
|
|
self.apt_install.assert_called_with(['ceph-common'], fatal=True)
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
@patch('os.mkdir')
|
|
def test_ceph_joined(self, mkdir, mock_config):
|
|
'''It correctly prepares for a ceph changed hook'''
|
|
with patch('os.path.isdir') as isdir:
|
|
isdir.return_value = False
|
|
hooks.hooks.execute(['hooks/ceph-relation-joined'])
|
|
mkdir.assert_called_with('/etc/ceph')
|
|
self.send_application_name.assert_called_with()
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_ceph_changed_no_key(self, mock_config):
|
|
'''It does nothing when ceph key is not available'''
|
|
self.CONFIGS.complete_contexts.return_value = ['']
|
|
hooks.hooks.execute(['hooks/ceph-relation-changed'])
|
|
m = 'ceph relation incomplete. Peer not ready?'
|
|
self.log.assert_called_with(m)
|
|
|
|
@patch.object(hooks, 'get_ceph_request')
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_ceph_changed(self, mock_config, mock_get_ceph_request):
|
|
'''It ensures ceph assets created on ceph changed'''
|
|
# confirm ValueError is caught and logged
|
|
self.is_request_complete.side_effect = ValueError
|
|
hooks.hooks.execute(['hooks/ceph-relation-changed'])
|
|
self.assertFalse(self.CONFIGS.write_all.called)
|
|
self.assertTrue(self.log.called)
|
|
self.is_request_complete.side_effect = None
|
|
# normal operation
|
|
self.is_request_complete.return_value = True
|
|
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.service_name.return_value = 'cinder'
|
|
self.ensure_ceph_keyring.return_value = True
|
|
hooks.hooks.execute(['hooks/ceph-relation-changed'])
|
|
self.ensure_ceph_keyring.assert_called_with(service='cinder',
|
|
user='cinder',
|
|
group='cinder')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
|
|
@patch.object(hooks, 'get_ceph_request')
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_ceph_changed_newrq(self, mock_config, mock_get_ceph_request):
|
|
'''It ensures ceph assets created on ceph changed'''
|
|
mock_get_ceph_request.return_value = 'cephreq'
|
|
self.is_request_complete.return_value = False
|
|
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.service_name.return_value = 'cinder'
|
|
self.ensure_ceph_keyring.return_value = True
|
|
hooks.hooks.execute(['hooks/ceph-relation-changed'])
|
|
self.ensure_ceph_keyring.assert_called_with(service='cinder',
|
|
user='cinder',
|
|
group='cinder')
|
|
self.send_request_if_needed.assert_called_with('cephreq')
|
|
|
|
@patch.object(hooks, 'CephBlueStoreCompressionContext')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_request_access_to_group')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_create_replicated_pool')
|
|
def test_create_pool_op(self, mock_create_pool,
|
|
mock_request_access, mock_bluestore_compression):
|
|
self.service_name.return_value = 'cinder'
|
|
self.test_config.set('ceph-osd-replication-count', 4)
|
|
self.test_config.set('ceph-pool-weight', 20)
|
|
hooks.get_ceph_request()
|
|
mock_create_pool.assert_called_with(name='cinder', replica_count=4,
|
|
weight=20, group='volumes',
|
|
app_name='rbd',
|
|
rbd_mirroring_mode='pool')
|
|
mock_request_access.assert_not_called()
|
|
|
|
self.test_config.set('restrict-ceph-pools', True)
|
|
hooks.get_ceph_request()
|
|
mock_create_pool.assert_called_with(name='cinder', replica_count=4,
|
|
weight=20, group='volumes',
|
|
app_name='rbd',
|
|
rbd_mirroring_mode='pool')
|
|
mock_request_access.assert_has_calls([
|
|
call(
|
|
name='volumes',
|
|
object_prefix_permissions={'class-read': ['rbd_children']},
|
|
permission='rwx'),
|
|
call(
|
|
name='images',
|
|
object_prefix_permissions={'class-read': ['rbd_children']},
|
|
permission='rwx'),
|
|
call(
|
|
name='vms',
|
|
object_prefix_permissions={'class-read': ['rbd_children']},
|
|
permission='rwx'),
|
|
])
|
|
|
|
@patch.object(hooks, 'CephBlueStoreCompressionContext')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_request_access_to_group')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_create_replicated_pool')
|
|
def test_create_pool_wth_name_op(self, mock_create_pool,
|
|
mock_request_access,
|
|
mock_bluestore_compression):
|
|
self.service_name.return_value = 'cinder'
|
|
self.test_config.set('ceph-osd-replication-count', 4)
|
|
self.test_config.set('ceph-pool-weight', 20)
|
|
self.test_config.set('rbd-pool-name', 'cinder-test')
|
|
hooks.get_ceph_request()
|
|
mock_create_pool.assert_called_with(name='cinder-test',
|
|
replica_count=4,
|
|
weight=20,
|
|
group='volumes',
|
|
app_name='rbd',
|
|
rbd_mirroring_mode='pool')
|
|
# confirm operation with bluestore compression
|
|
mock_create_pool.reset_mock()
|
|
mock_bluestore_compression().get_kwargs.return_value = {
|
|
'compression_mode': 'fake',
|
|
}
|
|
hooks.get_ceph_request()
|
|
mock_create_pool.assert_called_once_with(name='cinder-test',
|
|
replica_count=4,
|
|
weight=20,
|
|
group='volumes',
|
|
app_name='rbd',
|
|
rbd_mirroring_mode='pool',
|
|
compression_mode='fake')
|
|
|
|
@patch.object(hooks, 'CephBlueStoreCompressionContext')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_create_erasure_pool')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_create_erasure_profile')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_request_access_to_group')
|
|
@patch('charmhelpers.contrib.storage.linux.ceph.CephBrokerRq'
|
|
'.add_op_create_pool')
|
|
def test_create_pool_erasure_coded(self, mock_create_pool,
|
|
mock_request_access,
|
|
mock_create_erasure_profile,
|
|
mock_create_erasure_pool,
|
|
mock_bluestore_compression):
|
|
self.service_name.return_value = 'cinder'
|
|
self.test_config.set('ceph-osd-replication-count', 4)
|
|
self.test_config.set('ceph-pool-weight', 20)
|
|
self.test_config.set('pool-type', 'erasure-coded')
|
|
self.test_config.set('ec-profile-plugin', 'isa')
|
|
hooks.get_ceph_request()
|
|
mock_create_pool.assert_called_with(
|
|
name='cinder-metadata',
|
|
replica_count=4,
|
|
weight=0.2,
|
|
group='volumes',
|
|
app_name='rbd'
|
|
)
|
|
mock_create_erasure_pool.assert_called_with(
|
|
name='cinder',
|
|
erasure_profile='cinder-profile',
|
|
weight=19.8,
|
|
group='volumes',
|
|
app_name='rbd',
|
|
allow_ec_overwrites=True
|
|
)
|
|
mock_create_erasure_profile.assert_called_with(
|
|
name='cinder-profile',
|
|
k=1, m=2,
|
|
lrc_locality=None,
|
|
lrc_crush_locality=None,
|
|
shec_durability_estimator=None,
|
|
clay_helper_chunks=None,
|
|
clay_scalar_mds=None,
|
|
device_class=None,
|
|
erasure_type='isa',
|
|
erasure_technique=None
|
|
)
|
|
# confirm operation with bluestore compression
|
|
mock_create_erasure_pool.reset_mock()
|
|
mock_bluestore_compression().get_kwargs.return_value = {
|
|
'compression_mode': 'fake',
|
|
}
|
|
hooks.get_ceph_request()
|
|
mock_create_erasure_pool.assert_called_with(
|
|
name='cinder',
|
|
erasure_profile='cinder-profile',
|
|
weight=19.8,
|
|
group='volumes',
|
|
app_name='rbd',
|
|
allow_ec_overwrites=True,
|
|
compression_mode='fake',
|
|
)
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_ceph_changed_no_keys(self, mock_config):
|
|
'''It ensures ceph assets created on ceph changed'''
|
|
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.service_name.return_value = 'cinder'
|
|
self.is_request_complete.return_value = True
|
|
self.ensure_ceph_keyring.return_value = False
|
|
hooks.hooks.execute(['hooks/ceph-relation-changed'])
|
|
# NOTE(jamespage): If ensure_ceph keyring fails, then
|
|
# the hook should just exit 0 and return.
|
|
self.assertTrue(self.log.called)
|
|
self.assertFalse(self.CONFIGS.write_all.called)
|
|
|
|
@patch.object(hooks, 'get_ceph_request')
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_ceph_broken(self, mock_config, mock_get_ceph_request):
|
|
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.service_name.return_value = 'cinder-ceph'
|
|
with patch.object(hooks, 'CEPH_CONF', new="/some/random/file"):
|
|
hooks.hooks.execute(['hooks/ceph-relation-changed'])
|
|
hooks.hooks.execute(['hooks/ceph-relation-broken'])
|
|
self.delete_keyring.assert_called_with(service='cinder-ceph')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
self.remove_alternative.assert_called_with(
|
|
os.path.basename("/some/random/file"),
|
|
self.ceph_config_file())
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
@patch.object(hooks, 'storage_backend')
|
|
def test_upgrade_charm_related(self, _storage_backend, mock_config):
|
|
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.relation_ids.return_value = ['ceph:1']
|
|
hooks.hooks.execute(['hooks/upgrade-charm'])
|
|
_storage_backend.assert_called_with('ceph:1')
|
|
assert self.CONFIGS.write_all.called
|
|
self.scrub_old_style_ceph.assert_called_once_with()
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
@patch.object(hooks, 'storage_backend')
|
|
def test_storage_backend_changed(self, _storage_backend, mock_config):
|
|
hooks.hooks.execute(['hooks/storage-backend-relation-changed'])
|
|
_storage_backend.assert_called_with()
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_storage_backend_joined_no_ceph(self, mock_config):
|
|
self.CONFIGS.complete_contexts.return_value = []
|
|
hooks.hooks.execute(['hooks/storage-backend-relation-joined'])
|
|
assert self.log.called
|
|
assert not self.relation_set.called
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_storage_backend_joined_ceph(self, mock_config):
|
|
def func():
|
|
return {'test': 1}
|
|
self.CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.service_name.return_value = 'test'
|
|
self.CephSubordinateContext.return_value = func
|
|
hooks.hooks.execute(['hooks/storage-backend-relation-joined'])
|
|
self.relation_set.assert_called_with(
|
|
relation_id=None,
|
|
backend_name='test',
|
|
subordinate_configuration=json.dumps({'test': 1}),
|
|
stateless=True,
|
|
)
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_storage_backend_replication_device(self, mock_config):
|
|
self.application_name.return_value = 'test'
|
|
app_name = '{}-replication-device'.format(self.application_name())
|
|
self.service_name.return_value = app_name
|
|
self.leader_get.return_value = 'test-secret-uuid'
|
|
self.CONFIGS.complete_contexts.return_value = [
|
|
'ceph', 'ceph-replication-device']
|
|
|
|
def func():
|
|
return {
|
|
'cinder': {
|
|
'/etc/cinder/cinder.conf': {
|
|
'sections': {
|
|
'test': []
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.CephSubordinateContext.return_value = func
|
|
hooks.hooks.execute(['hooks/storage-backend-relation-joined'])
|
|
|
|
replication_device = {
|
|
'backend_id': 'ceph',
|
|
'conf': self.ceph_replication_device_config_file(),
|
|
'user': 'test-replication-device',
|
|
'secret_uuid': 'test-secret-uuid',
|
|
}
|
|
replication_device_str = ','.join(
|
|
['{}:{}'.format(k, v) for k, v in replication_device.items()])
|
|
expected_config = {
|
|
'cinder': {
|
|
'/etc/cinder/cinder.conf': {
|
|
'sections': {
|
|
'test': [
|
|
('replication_device', replication_device_str)
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.leader_get.assert_called_once_with(
|
|
'replication-device-secret-uuid')
|
|
self.relation_set.assert_called_with(
|
|
relation_id=None,
|
|
backend_name='test-replication-device',
|
|
subordinate_configuration=json.dumps(expected_config),
|
|
stateless=True,
|
|
)
|
|
|
|
@patch.object(hooks, 'ceph_access_joined')
|
|
@patch.object(hooks, 'storage_backend')
|
|
def test_leader_settings_changed(self,
|
|
storage_backend,
|
|
ceph_access_joined):
|
|
self.relation_ids.side_effect = [['ceph-access:1'],
|
|
['storage-backend:23']]
|
|
hooks.leader_settings_changed()
|
|
ceph_access_joined.assert_called_with('ceph-access:1')
|
|
storage_backend.assert_called_with('storage-backend:23')
|
|
|
|
@patch.object(hooks, 'CONFIGS')
|
|
def test_ceph_access_joined_no_ceph(self,
|
|
CONFIGS):
|
|
CONFIGS.complete_contexts.return_value = []
|
|
hooks.ceph_access_joined()
|
|
self.relation_set.assert_not_called()
|
|
|
|
@patch.object(hooks, 'CONFIGS')
|
|
def test_ceph_access_joined_follower_unseeded(self,
|
|
CONFIGS):
|
|
CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.is_leader.return_value = False
|
|
self.leader_get.return_value = None
|
|
hooks.ceph_access_joined()
|
|
self.relation_set.assert_not_called()
|
|
|
|
@patch.object(hooks, 'CephContext')
|
|
@patch.object(hooks, 'CONFIGS')
|
|
def test_ceph_access_joined_leader(self,
|
|
CONFIGS,
|
|
CephContext):
|
|
CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.is_leader.return_value = True
|
|
self.leader_get.side_effect = [None, 'newuuid']
|
|
context = MagicMock()
|
|
context.return_value = {'key': 'mykey'}
|
|
CephContext.return_value = context
|
|
hooks.ceph_access_joined()
|
|
self.leader_get.assert_called_with('secret-uuid')
|
|
self.leader_set.assert_called_with({'secret-uuid': ANY})
|
|
self.relation_set.assert_called_with(
|
|
relation_id=None,
|
|
relation_settings={'key': 'mykey',
|
|
'secret-uuid': 'newuuid'}
|
|
)
|
|
|
|
@patch.object(hooks, 'CephContext')
|
|
@patch.object(hooks, 'CONFIGS')
|
|
def test_ceph_access_joined_follower_seeded(self,
|
|
CONFIGS,
|
|
CephContext):
|
|
CONFIGS.complete_contexts.return_value = ['ceph']
|
|
self.is_leader.return_value = False
|
|
self.leader_get.return_value = 'newuuid'
|
|
context = MagicMock()
|
|
context.return_value = {'key': 'mykey'}
|
|
CephContext.return_value = context
|
|
hooks.ceph_access_joined()
|
|
self.leader_get.assert_called_with('secret-uuid')
|
|
self.leader_set.assert_not_called()
|
|
self.relation_set.assert_called_with(
|
|
relation_id=None,
|
|
relation_settings={'key': 'mykey',
|
|
'secret-uuid': 'newuuid'}
|
|
)
|
|
|
|
@patch.object(hooks, 'ceph_changed')
|
|
@patch.object(hooks.uuid, 'uuid4')
|
|
def test_write_and_restart(self, mock_uuid4, mock_ceph_changed):
|
|
# confirm normal operation for any unit type
|
|
mock_ceph_changed.side_effect = None
|
|
hooks.write_and_restart()
|
|
self.CONFIGS.write_all.assert_called_once_with()
|
|
# confirm normal operation for leader
|
|
self.leader_get.reset_mock()
|
|
self.leader_get.return_value = None
|
|
self.is_leader.return_value = True
|
|
mock_uuid4.side_effect = [42, 64]
|
|
hooks.write_and_restart()
|
|
self.leader_get.assert_has_calls([
|
|
call('secret-uuid'),
|
|
call('replication-device-secret-uuid')
|
|
])
|
|
self.leader_set.assert_has_calls([
|
|
call({'secret-uuid': '42'}),
|
|
call({'replication-device-secret-uuid': '64'})
|
|
])
|
|
|
|
@patch.object(hooks, 'get_ceph_request')
|
|
@patch.object(hooks, 'is_request_complete')
|
|
@patch.object(hooks, 'CephBlueStoreCompressionContext')
|
|
@patch.object(hooks, 'set_os_workload_status')
|
|
def test_assess_status(self,
|
|
mock_set_os_workload_status,
|
|
mock_bluestore_compression,
|
|
is_request_complete,
|
|
get_ceph_request):
|
|
is_request_complete.return_value = True
|
|
hooks.assess_status()
|
|
self.os_application_version_set.assert_called_once_with(
|
|
hooks.VERSION_PACKAGE)
|
|
mock_set_os_workload_status.assert_called_once_with(
|
|
ANY, hooks.REQUIRED_INTERFACES)
|
|
mock_bluestore_compression().validate.assert_called_once_with()
|
|
self.assertFalse(self.status_set.called)
|
|
# confirm incomplete request is caught
|
|
self.status_set.reset_mock()
|
|
is_request_complete.return_value = False
|
|
hooks.assess_status()
|
|
self.status_set.assert_called_once_with(
|
|
'waiting', 'Ceph broker request incomplete')
|
|
# confirm operation when user have provided invalid configuration
|
|
is_request_complete.return_value = True
|
|
self.status_set.reset_mock()
|
|
mock_bluestore_compression().validate.side_effect = ValueError(
|
|
'fake message')
|
|
hooks.assess_status()
|
|
self.status_set.assert_called_once_with(
|
|
'blocked', 'Invalid configuration: fake message')
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
@patch.object(hooks, 'ceph_access_joined')
|
|
@patch.object(hooks, 'storage_backend')
|
|
def test_ceph_replication_device_changed(self,
|
|
storage_backend,
|
|
ceph_access_joined,
|
|
mock_config):
|
|
self.CONFIGS.complete_contexts.return_value = [
|
|
'ceph-replication-device']
|
|
self.ensure_ceph_keyring.return_value = True
|
|
self.relation_ids.side_effect = [
|
|
['storage-backend:1'], ['ceph-access:1']]
|
|
app_name = '{}-replication-device'.format(self.application_name())
|
|
hooks.hooks.execute(['hooks/ceph-replication-device-relation-changed'])
|
|
self.ensure_ceph_keyring.assert_called_with(
|
|
service=app_name,
|
|
relation='ceph-replication-device',
|
|
user='cinder',
|
|
group='cinder')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
storage_backend.assert_called_with('storage-backend:1')
|
|
ceph_access_joined.assert_called_with('ceph-access:1')
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_ceph_replication_device_broken(self, mock_config):
|
|
app_name = '{}-replication-device'.format(self.application_name())
|
|
self.service_name.return_value = app_name
|
|
hooks.hooks.execute(['hooks/ceph-replication-device-relation-broken'])
|
|
self.delete_keyring.assert_called_with(
|
|
service=self.service_name.return_value)
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
|
|
@patch('charmhelpers.core.hookenv.config')
|
|
def test_ceph_replication_device_joined(self, mock_config):
|
|
data = {'application-name': '{}-replication-device'.format(
|
|
self.application_name())}
|
|
hooks.hooks.execute(['hooks/ceph-replication-device-relation-joined'])
|
|
self.relation_set.assert_called_with(relation_settings=data)
|