charm-cinder/unit_tests/test_cinder_hooks.py
Liam Young 1975c41271 Block endpoint reg if cluster partially formed
When an existing cluster of the service is scaled out the new unit
will join with keystone before it is fully clustered. In identity
joined hook the charmhelpers function canonical_url is called which
in turn uses another charmhelpers function, resolve_address.
resolve_address will only return the vip if the vip is set in config
AND the unit is clustered. This means that the units local address
is returned and that is then registered with keystone.

This change gates registering an endpoint if the cluster is
partially formed.

Change-Id: If483147e17dab8de2883058ee0f2718a3b7f8ca6
Partial-Bug: #1544959
2017-10-06 13:15:53 +00:00

753 lines
34 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.
import json
import sys
import yaml
from mock import (
MagicMock,
patch,
call
)
from test_utils import (
CharmTestCase,
RESTART_MAP,
)
# python-apt is not installed as part of test-requirements but is imported by
# some charmhelpers modules so create a fake import.
mock_apt = MagicMock()
sys.modules['apt'] = mock_apt
mock_apt.apt_pkg = MagicMock()
with patch('charmhelpers.contrib.hardening.harden.harden') as mock_dec:
mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f:
lambda *args, **kwargs: f(*args, **kwargs))
with patch('cinder_utils.register_configs') as register_configs:
with patch('cinder_utils.restart_map') as restart_map:
restart_map.return_value = RESTART_MAP
import cinder_hooks as hooks
hooks.hooks._config_save = False
import cinder_utils as utils
TO_PATCH = [
'check_call',
'send_request_if_needed',
'is_request_complete',
# cinder_utils
'configure_lvm_storage',
'determine_packages',
'do_openstack_upgrade',
'ensure_ceph_keyring',
'git_install',
'is_clustered',
'juju_log',
'log',
'lsb_release',
'migrate_database',
'register_configs',
'restart_map',
'service_enabled',
'set_ceph_env_variables',
'CONFIGS',
'CLUSTER_RES',
'ceph_config_file',
'update_nrpe_config',
# charmhelpers.core.hookenv
'config',
'is_relation_made',
'local_unit',
'relation_get',
'relation_ids',
'relation_set',
'related_units',
'service_name',
'open_port',
# charmhelpers.core.host
'apt_install',
'apt_update',
'service_reload',
'service_restart',
# charmhelpers.contrib.openstack.openstack_utils
'configure_installation_source',
'openstack_upgrade_available',
'os_release',
'run_in_apache',
# charmhelpers.contrib.openstack.openstack.ha.utils
'update_dns_ha_resource_params',
# charmhelpers.contrib.hahelpers.cluster_utils
'is_elected_leader',
'get_hacluster_config',
'execd_preinstall',
'sync_db_with_multi_ipv6_addresses',
'delete_keyring',
'get_relation_ip',
]
class TestInstallHook(CharmTestCase):
def setUp(self):
super(TestInstallHook, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get
@patch.object(utils, 'git_install_requested')
def test_install_precise_distro(self, git_requested):
'It redirects to cloud archive if setup to install precise+distro'
git_requested.return_value = False
self.lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'}
hooks.hooks.execute(['hooks/install.real'])
ca = 'cloud:precise-folsom'
self.configure_installation_source.assert_called_with(ca)
@patch.object(utils, 'git_install_requested')
def test_install_git(self, git_requested):
git_requested.return_value = True
self.determine_packages.return_value = ['foo', 'bar', 'baz']
repo = 'cloud:trusty-juno'
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
'branch': 'stable/juno'},
{'name': 'cinder',
'repository': 'git://git.openstack.org/openstack/cinder',
'branch': 'stable/juno'}
],
'directory': '/mnt/openstack-git',
}
projects_yaml = yaml.dump(openstack_origin_git)
self.test_config.set('openstack-origin', repo)
self.test_config.set('openstack-origin-git', projects_yaml)
hooks.hooks.execute(['hooks/install.real'])
self.assertTrue(self.execd_preinstall.called)
self.configure_installation_source.assert_called_with(repo)
self.apt_update.assert_called_with()
self.apt_install.assert_called_with(['foo', 'bar', 'baz'], fatal=True)
self.git_install.assert_called_with(projects_yaml)
@patch.object(utils, 'git_install_requested')
def test_correct_install_packages(self, git_requested):
'It installs the correct packages based on what is determined'
git_requested.return_value = False
self.determine_packages.return_value = ['foo', 'bar', 'baz']
hooks.hooks.execute(['hooks/install.real'])
self.apt_install.assert_called_with(['foo', 'bar', 'baz'], fatal=True)
class TestChangedHooks(CharmTestCase):
def setUp(self):
super(TestChangedHooks, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get
@patch.object(hooks, 'amqp_joined')
def test_upgrade_charm_no_amqp(self, _joined):
self.relation_ids.return_value = []
hooks.hooks.execute(['hooks/upgrade-charm'])
_joined.assert_not_called()
@patch.object(hooks, 'amqp_joined')
def test_upgrade_charm_with_amqp(self, _joined):
self.relation_ids.return_value = ['amqp:1']
hooks.hooks.execute(['hooks/upgrade-charm'])
_joined.assert_called_with(relation_id='amqp:1')
@patch.object(hooks, 'configure_https')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed(self, config_val_changed,
git_requested, conf_https):
'It writes out all config'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = False
hooks.hooks.execute(['hooks/config-changed'])
self.assertTrue(self.CONFIGS.write_all.called)
self.assertTrue(conf_https.called)
self.configure_lvm_storage.assert_called_with(['sdb'],
'cinder-volumes',
False, False, False)
self.open_port.assert_called_with(8776)
@patch.object(hooks, 'configure_https')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed_block_devices(self, config_val_changed,
git_requested, conf_https):
'It writes out all config'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = False
self.test_config.set('block-device', 'sdb /dev/sdc sde')
self.test_config.set('volume-group', 'cinder-new')
self.test_config.set('overwrite', 'True')
self.test_config.set('remove-missing', True)
hooks.hooks.execute(['hooks/config-changed'])
self.assertTrue(self.CONFIGS.write_all.called)
self.assertTrue(conf_https.called)
self.configure_lvm_storage.assert_called_with(
['sdb', '/dev/sdc', 'sde'],
'cinder-new',
True, True, False)
@patch.object(hooks, 'configure_https')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed_uses_remove_missing_force(self,
config_val_changed,
git_requested,
conf_https):
'It uses the remove-missing-force config option'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = False
self.test_config.set('block-device', 'sdb')
self.test_config.set('remove-missing-force', True)
hooks.hooks.execute(['hooks/config-changed'])
self.configure_lvm_storage.assert_called_with(
['sdb'],
'cinder-volumes',
False, False, True)
@patch.object(hooks, 'configure_https')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed_upgrade_available(self, config_val_changed,
git_requested, conf_https):
'It writes out all config with an available OS upgrade'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = True
hooks.hooks.execute(['hooks/config-changed'])
self.do_openstack_upgrade.assert_called_with(configs=self.CONFIGS)
@patch.object(hooks, 'configure_https')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed_git_updated(self, config_val_changed,
git_requested, conf_https):
git_requested.return_value = True
repo = 'cloud:trusty-juno'
openstack_origin_git = {
'repositories': [
{'name': 'requirements',
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
'branch': 'stable/juno'},
{'name': 'cinder',
'repository': 'git://git.openstack.org/openstack/',
'branch': 'stable/juno'}
],
'directory': '/mnt/openstack-git',
}
projects_yaml = yaml.dump(openstack_origin_git)
self.test_config.set('openstack-origin', repo)
self.test_config.set('openstack-origin-git', projects_yaml)
hooks.hooks.execute(['hooks/config-changed'])
self.git_install.assert_called_with(projects_yaml)
self.assertFalse(self.do_openstack_upgrade.called)
self.assertTrue(conf_https.called)
@patch('charmhelpers.core.host.service')
@patch.object(hooks, 'configure_https')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed_overwrite_changed(self, config_val_changed,
git_requested, conf_https,
_services):
'It uses the overwrite config option'
git_requested.return_value = False
self.openstack_upgrade_available.return_value = False
config_val_changed.return_value = True
hooks.hooks.execute(['hooks/config-changed'])
self.assertTrue(self.CONFIGS.write_all.called)
self.assertTrue(conf_https.called)
self.configure_lvm_storage.assert_called_with(['sdb'],
'cinder-volumes',
False, False, False)
self.service_restart.assert_called_with('cinder-volume')
@patch.object(hooks, 'git_install_requested')
@patch.object(hooks, 'config_value_changed')
def test_config_changed_with_openstack_upgrade_action(self,
config_value_changed,
git_requested):
git_requested.return_value = False
self.openstack_upgrade_available.return_value = True
self.test_config.set('action-managed-upgrade', True)
hooks.hooks.execute(['hooks/config-changed'])
self.assertFalse(self.do_openstack_upgrade.called)
def test_db_changed(self):
'It writes out cinder.conf on db changed'
self.relation_get.return_value = 'cinder/0 cinder/1'
self.local_unit.return_value = 'cinder/0'
self.CONFIGS.complete_contexts.return_value = ['shared-db']
hooks.hooks.execute(['hooks/shared-db-relation-changed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/cinder.conf')
self.assertTrue(self.migrate_database.called)
def test_pgsql_db_changed(self):
'It writes out cinder.conf on db changed'
self.CONFIGS.complete_contexts.return_value = ['pgsql-db']
hooks.hooks.execute(['hooks/pgsql-db-relation-changed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/cinder.conf')
self.assertTrue(self.migrate_database.called)
def test_db_changed_relation_incomplete(self):
'It does not write out cinder.conf with incomplete shared-db rel'
self.relation_get.return_value = 'cinder/0 cinder/1'
self.local_unit.return_value = 'cinder/0'
hooks.hooks.execute(['hooks/shared-db-relation-changed'])
self.assertFalse(self.CONFIGS.write.called)
self.assertFalse(self.migrate_database.called)
def test_db_changed_relation_db_no_acl(self):
'It does not migration when acl entry not present'
self.relation_get.return_value = 'cinder/1 cinder/2'
self.local_unit.return_value = 'cinder/0'
self.CONFIGS.complete_contexts.return_value = ['shared-db']
self.is_elected_leader.return_value = True
hooks.hooks.execute(['hooks/shared-db-relation-changed'])
self.assertFalse(self.migrate_database.called)
def test_db_changed_relation_db_missing_acls(self):
'No database migration is attempted when ACL list is not present'
self.relation_get.return_value = None
self.local_unit.return_value = 'cinder/0'
self.CONFIGS.complete_contexts.return_value = ['shared-db']
self.is_elected_leader.return_value = True
hooks.hooks.execute(['hooks/shared-db-relation-changed'])
self.assertFalse(self.migrate_database.called)
def test_pgsql_db_changed_relation_incomplete(self):
'It does not write out cinder.conf with incomplete pgsql-db rel'
hooks.hooks.execute(['hooks/pgsql-db-relation-changed'])
self.assertFalse(self.CONFIGS.write.called)
self.assertFalse(self.migrate_database.called)
def test_db_changed_not_leader(self):
'It does not migrate database when not leader'
self.relation_get.return_value = 'cinder/0 cinder/1'
self.local_unit.return_value = 'cinder/0'
self.is_elected_leader.return_value = False
self.CONFIGS.complete_contexts.return_value = ['shared-db']
hooks.hooks.execute(['hooks/shared-db-relation-changed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/cinder.conf')
self.assertFalse(self.migrate_database.called)
def test_pgsql_db_changed_not_leader(self):
'It does not migrate database when not leader'
self.is_elected_leader.return_value = False
self.CONFIGS.complete_contexts.return_value = ['pgsql-db']
hooks.hooks.execute(['hooks/pgsql-db-relation-changed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/cinder.conf')
self.assertFalse(self.migrate_database.called)
def test_amqp_changed(self):
'It writes out cinder.conf on amqp changed with complete relation'
self.CONFIGS.complete_contexts.return_value = ['amqp']
hooks.hooks.execute(['hooks/amqp-relation-changed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/cinder.conf')
def test_amqp_changed_incomplete(self):
'It does not write out cinder.conf with incomplete relation'
self.CONFIGS.complete_contexts.return_value = ['']
hooks.hooks.execute(['hooks/amqp-relation-changed'])
self.assertFalse(self.CONFIGS.write.called)
def test_identity_joined_partial_cluster(self):
self.is_clustered.return_value = False
self.test_config.set('vip', '10.0.0.10')
hooks.identity_joined()
self.assertFalse(self.relation_set.called)
@patch.object(hooks, 'configure_https')
def test_identity_changed(self, conf_https):
'It writes out api-paste.ini on identity-service changed'
self.CONFIGS.complete_contexts.return_value = ['identity-service']
hooks.hooks.execute(['hooks/identity-service-relation-changed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/api-paste.ini')
self.assertTrue(conf_https.called)
def test_identity_changed_incomplete(self):
'It does not write api-paste.ini with incomplete identity-service'
hooks.hooks.execute(['hooks/identity-service-relation-changed'])
self.assertFalse(self.CONFIGS.write.called)
@patch.object(hooks, 'identity_joined')
def test_configure_https_enable(self, identity_joined):
'It enables https from hooks when we have https data'
self.CONFIGS.complete_contexts.return_value = ['https']
self.relation_ids.return_value = ['identity-service:0']
hooks.configure_https()
self.check_call.assert_called_with(['a2ensite',
'openstack_https_frontend'])
self.service_reload.assert_called_with('apache2',
restart_on_failure=True)
identity_joined.assert_called_with(rid='identity-service:0')
@patch.object(hooks, 'identity_joined')
def test_configure_https_disable(self, identity_joined):
'It enables https from hooks when we have https data'
self.CONFIGS.complete_contexts.return_value = []
self.relation_ids.return_value = ['identity-service:0']
hooks.configure_https()
self.check_call.assert_called_with(['a2dissite',
'openstack_https_frontend'])
self.service_reload.assert_called_with('apache2',
restart_on_failure=True)
identity_joined.assert_called_with(rid='identity-service:0')
def test_image_service_changed(self):
'Ensure all configuration files written if image service changes'
hooks.hooks.execute(['hooks/image-service-relation-changed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/cinder.conf')
def test_relation_broken(self):
'Ensure all configuration files written if image service changes'
hooks.hooks.execute(['hooks/image-service-relation-broken'])
self.assertTrue(self.CONFIGS.write_all.called)
def test_storage_backend_changed(self):
hooks.hooks.execute(['hooks/storage-backend-relation-changed'])
self.CONFIGS.write.assert_called_with(utils.CINDER_CONF)
def test_storage_backend_broken(self):
hooks.hooks.execute(['hooks/storage-backend-relation-broken'])
self.CONFIGS.write.assert_called_with(utils.CINDER_CONF)
class TestJoinedHooks(CharmTestCase):
def setUp(self):
super(TestJoinedHooks, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get
def test_db_joined(self):
'It properly requests access to a shared-db service'
self.get_relation_ip.return_value = '10.0.0.1'
self.is_relation_made.return_value = False
hooks.hooks.execute(['hooks/shared-db-relation-joined'])
expected = {'username': 'cinder',
'hostname': '10.0.0.1', 'database': 'cinder'}
self.relation_set.assert_called_with(**expected)
def test_db_joined_with_postgresql(self):
self.is_relation_made.return_value = True
with self.assertRaises(Exception) as context:
hooks.hooks.execute(['hooks/shared-db-relation-joined'])
self.assertEqual(context.exception.message,
'Attempting to associate a mysql database when there '
'is already associated a postgresql one')
def test_postgresql_db_joined(self):
'It properly requests access to a postgresql-db service'
self.is_relation_made.return_value = False
hooks.hooks.execute(['hooks/pgsql-db-relation-joined'])
expected = {'database': 'cinder'}
self.relation_set.assert_called_with(**expected)
def test_postgresql_joined_with_db(self):
self.is_relation_made.return_value = True
with self.assertRaises(Exception) as context:
hooks.hooks.execute(['hooks/pgsql-db-relation-joined'])
self.assertEqual(context.exception.message,
'Attempting to associate a postgresql database when'
' there is already associated a mysql one')
def test_amqp_joined(self):
'It properly requests access to an amqp service'
hooks.hooks.execute(['hooks/amqp-relation-joined'])
self.relation_set.assert_called_with(username='cinder',
vhost='openstack',
relation_id=None)
def test_amqp_joined_passes_relation_id(self):
'''Ensures relation_id correct passed to relation_set for out of
hook execution
'''
hooks.amqp_joined(relation_id='amqp:1')
self.relation_set.assert_called_with(username='cinder',
vhost='openstack',
relation_id='amqp:1')
@patch.object(hooks, 'canonical_url')
def test_identity_service_joined(self, _canonical_url):
'It properly requests unclustered endpoint via identity-service'
self.os_release.return_value = 'havana'
self.config.side_effect = self.test_config.get
_canonical_url.return_value = 'http://cindernode1'
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
expected = {
'region': None,
'service': None,
'public_url': None,
'internal_url': None,
'admin_url': None,
'cinder_service': 'cinder',
'cinder_region': 'RegionOne',
'cinder_public_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'cinder_admin_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'cinder_internal_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'relation_id': None,
}
self.relation_set.assert_called_with(**expected)
def test_identity_service_joined_no_api(self):
'endpoint registration is skipped if api service is not enabled'
self.config.side_effect = self.test_config.get
self.service_enabled.return_value = False
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
self.assertFalse(self.relation_set.called)
self.service_enabled.assert_called_with('api')
@patch.object(hooks, 'canonical_url')
def test_identity_service_joined_icehouse(self, _canonical_url):
'It properly requests unclustered endpoint via identity-service'
self.os_release.return_value = 'icehouse'
self.config.side_effect = self.test_config.get
_canonical_url.return_value = 'http://cindernode1'
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
expected = {
'region': None,
'service': None,
'public_url': None,
'internal_url': None,
'admin_url': None,
'cinder_service': 'cinder',
'cinder_region': 'RegionOne',
'cinder_public_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'cinder_admin_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'cinder_internal_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'cinderv2_service': 'cinderv2',
'cinderv2_region': 'RegionOne',
'cinderv2_public_url': 'http://cindernode1:8776/v2/$(tenant_id)s',
'cinderv2_admin_url': 'http://cindernode1:8776/v2/$(tenant_id)s',
'cinderv2_internal_url': 'http://cindernode1:8776/'
'v2/$(tenant_id)s',
'relation_id': None,
}
self.relation_set.assert_called_with(**expected)
@patch.object(hooks, 'canonical_url')
def test_identity_service_joined_pike(self, _canonical_url):
'It properly requests unclustered endpoint via identity-service'
self.os_release.return_value = 'pike'
self.config.side_effect = self.test_config.get
_canonical_url.return_value = 'http://cindernode1'
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
expected = {
'cinderv2_service': 'cinderv2',
'cinderv2_region': 'RegionOne',
'cinderv2_public_url': 'http://cindernode1:8776/v2/$(tenant_id)s',
'cinderv2_admin_url': 'http://cindernode1:8776/v2/$(tenant_id)s',
'cinderv2_internal_url': 'http://cindernode1:8776/'
'v2/$(tenant_id)s',
'cinderv3_service': 'cinderv3',
'cinderv3_region': 'RegionOne',
'cinderv3_public_url': 'http://cindernode1:8776/v3/$(tenant_id)s',
'cinderv3_admin_url': 'http://cindernode1:8776/v3/$(tenant_id)s',
'cinderv3_internal_url': 'http://cindernode1:8776/'
'v3/$(tenant_id)s',
'relation_id': None,
}
self.relation_set.assert_called_with(**expected)
@patch('charmhelpers.contrib.openstack.ip.config')
@patch('charmhelpers.contrib.openstack.ip.unit_get')
@patch('charmhelpers.contrib.openstack.ip.is_clustered')
def test_identity_service_joined_public_name(self, _is_clustered,
_unit_get, _config):
self.os_release.return_value = 'icehouse'
_unit_get.return_value = 'cindernode1'
self.config.side_effect = self.test_config.get
_config.side_effect = self.test_config.get
self.test_config.set('os-public-hostname', 'public.example.com')
_is_clustered.return_value = False
hooks.hooks.execute(['hooks/identity-service-relation-joined'])
v1_url = 'http://public.example.com:8776/v1/$(tenant_id)s'
v2_url = 'http://public.example.com:8776/v2/$(tenant_id)s'
expected = {
'region': None,
'service': None,
'public_url': None,
'internal_url': None,
'admin_url': None,
'cinder_service': 'cinder',
'cinder_region': 'RegionOne',
'cinder_public_url': v1_url,
'cinder_admin_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'cinder_internal_url': 'http://cindernode1:8776/v1/$(tenant_id)s',
'cinderv2_service': 'cinderv2',
'cinderv2_region': 'RegionOne',
'cinderv2_public_url': v2_url,
'cinderv2_admin_url': 'http://cindernode1:8776/v2/$(tenant_id)s',
'cinderv2_internal_url': ('http://cindernode1:8776/'
'v2/$(tenant_id)s'),
'relation_id': None,
}
self.relation_set.assert_called_with(**expected)
@patch('os.mkdir')
def test_ceph_joined(self, mkdir):
'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.apt_install.assert_called_with('ceph-common', fatal=True)
def test_ceph_changed_no_key(self):
'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.juju_log.assert_called_with(m)
@patch.object(hooks, 'get_ceph_request')
def test_ceph_changed_broker_send_rq(self, mget_ceph_request):
mget_ceph_request.return_value = 'cephrq'
self.CONFIGS.complete_contexts.return_value = ['ceph']
self.service_name.return_value = 'cinder'
self.ensure_ceph_keyring.return_value = True
self.is_request_complete.return_value = False
self.ceph_config_file.return_value = '/var/lib/charm/cinder/ceph.conf'
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('cephrq')
for c in [call('/var/lib/charm/cinder/ceph.conf'),
call('/etc/cinder/cinder.conf')]:
self.assertNotIn(c, self.CONFIGS.write.call_args_list)
self.assertFalse(self.set_ceph_env_variables.called)
@patch('charmhelpers.core.host.service')
@patch("cinder_hooks.relation_get", autospec=True)
def test_ceph_changed_broker_success(self, mock_relation_get,
_service):
'It ensures ceph assets created on ceph changed'
self.CONFIGS.complete_contexts.return_value = ['ceph']
self.service_name.return_value = 'cinder'
self.ensure_ceph_keyring.return_value = True
self.ceph_config_file.return_value = '/var/lib/charm/cinder/ceph.conf'
mock_relation_get.return_value = {'broker_rsp':
json.dumps({'exit-code': 0})}
hooks.hooks.execute(['hooks/ceph-relation-changed'])
self.ensure_ceph_keyring.assert_called_with(service='cinder',
user='cinder',
group='cinder')
for c in [call('/var/lib/charm/cinder/ceph.conf'),
call('/etc/cinder/cinder.conf')]:
self.assertIn(c, self.CONFIGS.write.call_args_list)
self.set_ceph_env_variables.assert_called_with(service='cinder')
self.service_restart.assert_called_with('cinder-volume')
def test_ceph_changed_broker_nonzero_rc(self):
self.CONFIGS.complete_contexts.return_value = ['ceph']
self.service_name.return_value = 'cinder'
self.ensure_ceph_keyring.return_value = True
self.ceph_config_file.return_value = '/var/lib/charm/cinder/ceph.conf'
self.is_request_complete.return_value = False
hooks.hooks.execute(['hooks/ceph-relation-changed'])
self.ensure_ceph_keyring.assert_called_with(service='cinder',
user='cinder',
group='cinder')
for c in [call('/var/lib/charm/cinder/ceph.conf'),
call('/etc/cinder/cinder.conf')]:
self.assertNotIn(c, self.CONFIGS.write.call_args_list)
self.assertFalse(self.set_ceph_env_variables.called)
def test_ceph_changed_no_keys(self):
'It ensures ceph assets created on ceph changed'
self.CONFIGS.complete_contexts.return_value = ['ceph']
self.service_name.return_value = 'cinder'
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.juju_log.called)
self.assertFalse(self.CONFIGS.write.called)
def test_ceph_broken(self):
self.service_name.return_value = 'cinder'
hooks.hooks.execute(['hooks/ceph-relation-broken'])
self.delete_keyring.assert_called_with(service='cinder')
self.assertTrue(self.CONFIGS.write_all.called)
def test_ceph_changed_no_leadership(self):
'''It does not attempt to create ceph pool if not leader'''
self.is_elected_leader.return_value = False
self.service_name.return_value = 'cinder'
self.ensure_ceph_keyring.return_value = True
hooks.hooks.execute(['hooks/ceph-relation-changed'])
def test_ha_joined_dns_ha(self):
def _fake_update(resources, resource_params, relation_id=None):
resources.update({'res_cinder_public_hostname': 'ocf:maas:dns'})
resource_params.update({'res_cinder_public_hostname':
'params fqdn="keystone.maas" '
'ip_address="10.0.0.1"'})
self.test_config.set('dns-ha', True)
self.get_hacluster_config.return_value = {
'vip': None,
'ha-bindiface': 'em0',
'ha-mcastport': '8080',
'os-admin-hostname': None,
'os-internal-hostname': None,
'os-public-hostname': 'keystone.maas',
}
args = {
'relation_id': None,
'corosync_bindiface': 'em0',
'corosync_mcastport': '8080',
'init_services': {'res_cinder_haproxy': 'haproxy'},
'resources': {'res_cinder_public_hostname': 'ocf:maas:dns',
'res_cinder_haproxy': 'lsb:haproxy'},
'resource_params': {
'res_cinder_public_hostname': 'params fqdn="keystone.maas" '
'ip_address="10.0.0.1"',
'res_cinder_haproxy': 'op monitor interval="5s"'},
'clones': {'cl_cinder_haproxy': 'res_cinder_haproxy'}
}
self.update_dns_ha_resource_params.side_effect = _fake_update
hooks.ha_joined()
self.assertTrue(self.update_dns_ha_resource_params.called)
self.relation_set.assert_called_with(**args)
class TestDepartedHooks(CharmTestCase):
def setUp(self):
super(TestDepartedHooks, self).setUp(hooks, TO_PATCH)
self.config.side_effect = self.test_config.get_all
def test_amqp_departed(self):
self.CONFIGS.complete_contexts.return_value = ['amqp']
hooks.hooks.execute(['hooks/amqp-relation-departed'])
self.CONFIGS.write.assert_called_with('/etc/cinder/cinder.conf')
def test_amqp_departed_incomplete(self):
self.CONFIGS.complete_contexts.return_value = []
hooks.hooks.execute(['hooks/amqp-relation-departed'])
assert not self.CONFIGS.write.called
assert self.juju_log.called