68ea83e60d
It's possible that the nova-api-metadata will startup during the time that the nova-conductor processes on the nova-cloud-controller units are still starting up, resulting in a messaging timeout which causes the daemon to exit 0. Upstart will restart a service in this scenario, however systemd is configured in packaging to only restart 'on-failure' so will not attempt to restart. This points to two other bugs - one that a messaging timeout results in a exit code of 0, and that the OpenStack services under systemd behave differently to under upstart. Install an override file for systemd based installs to mimic the behaviour of upstart, and deal with a code logic problem in the restart_trigger handling to ensure that the charm does at least try to restart the nova-api-metadata service at the right points in time. Change-Id: Ia08b7840efa33fd301d0e2c55bb30ae1a102cbfa Closes-Bug: 1547122
395 lines
15 KiB
Python
395 lines
15 KiB
Python
import sys
|
|
|
|
import yaml
|
|
|
|
from mock import MagicMock, patch, call
|
|
|
|
# python-apt is not installed as part of test-requirements but is imported by
|
|
# some charmhelpers modules so create a fake import.
|
|
sys.modules['apt'] = MagicMock()
|
|
sys.modules['apt_pkg'] = MagicMock()
|
|
|
|
import charmhelpers.core.hookenv as hookenv
|
|
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))
|
|
import neutron_hooks as hooks
|
|
|
|
from test_utils import CharmTestCase
|
|
|
|
|
|
TO_PATCH = [
|
|
'config',
|
|
'configure_installation_source',
|
|
'valid_plugin',
|
|
'apt_update',
|
|
'apt_install',
|
|
'apt_purge',
|
|
'filter_installed_packages',
|
|
'get_early_packages',
|
|
'get_packages',
|
|
'git_install',
|
|
'log',
|
|
'do_openstack_upgrade',
|
|
'openstack_upgrade_available',
|
|
'CONFIGS',
|
|
'configure_ovs',
|
|
'relation_set',
|
|
'relation_ids',
|
|
'relation_get',
|
|
'install_ca_cert',
|
|
'execd_preinstall',
|
|
'lsb_release',
|
|
'stop_services',
|
|
'b64decode',
|
|
'create_sysctl',
|
|
'update_nrpe_config',
|
|
'update_legacy_ha_files',
|
|
'install_legacy_ha_files',
|
|
'cache_env_data',
|
|
'get_hacluster_config',
|
|
'remove_legacy_ha_files',
|
|
'cleanup_ovs_netns',
|
|
'stop_neutron_ha_monitor_daemon',
|
|
'use_l3ha',
|
|
'kv',
|
|
'service_restart',
|
|
'is_unit_paused_set',
|
|
'install_systemd_override',
|
|
]
|
|
|
|
|
|
class TestQuantumHooks(CharmTestCase):
|
|
|
|
def setUp(self):
|
|
super(TestQuantumHooks, self).setUp(hooks, TO_PATCH)
|
|
self.config.side_effect = self.test_config.get
|
|
self.test_config.set('openstack-origin', 'cloud:precise-havana')
|
|
self.test_config.set('plugin', 'ovs')
|
|
self.lsb_release.return_value = {'DISTRIB_CODENAME': 'precise'}
|
|
# passthrough
|
|
self.b64decode.side_effect = lambda arg: arg
|
|
hookenv.config.side_effect = self.test_config.get
|
|
hooks.hooks._config_save = False
|
|
|
|
def _call_hook(self, hookname):
|
|
hooks.hooks.execute([
|
|
'hooks/{}'.format(hookname)])
|
|
|
|
def test_install_hook(self):
|
|
self.valid_plugin.return_value = True
|
|
_pkgs = ['foo', 'bar']
|
|
self.filter_installed_packages.return_value = _pkgs
|
|
self._call_hook('install.real')
|
|
self.configure_installation_source.assert_called_with(
|
|
'cloud:precise-havana'
|
|
)
|
|
self.apt_update.assert_called_with(fatal=True)
|
|
self.apt_install.assert_has_calls([
|
|
call(_pkgs, fatal=True),
|
|
call(_pkgs, fatal=True),
|
|
])
|
|
self.assertTrue(self.get_early_packages.called)
|
|
self.assertTrue(self.get_packages.called)
|
|
self.assertTrue(self.execd_preinstall.called)
|
|
self.assertTrue(self.install_systemd_override.called)
|
|
|
|
def test_install_hook_precise_nocloudarchive(self):
|
|
self.test_config.set('openstack-origin', 'distro')
|
|
self._call_hook('install.real')
|
|
self.configure_installation_source.assert_called_with(
|
|
'cloud:precise-icehouse'
|
|
)
|
|
|
|
@patch('sys.exit')
|
|
def test_install_hook_invalid_plugin(self, _exit):
|
|
self.valid_plugin.return_value = False
|
|
self._call_hook('install.real')
|
|
self.assertTrue(self.log.called)
|
|
_exit.assert_called_with(1)
|
|
|
|
@patch('neutron_utils.git_install_requested')
|
|
def test_install_hook_git(self, git_requested):
|
|
git_requested.return_value = True
|
|
self.valid_plugin.return_value = True
|
|
_pkgs = ['foo', 'bar']
|
|
self.filter_installed_packages.return_value = _pkgs
|
|
repo = 'cloud:trusty-juno'
|
|
openstack_origin_git = {
|
|
'repositories': [
|
|
{'name': 'requirements',
|
|
'repository': 'git://git.openstack.org/openstack/requirements', # noqa
|
|
'branch': 'stable/juno'},
|
|
{'name': 'neutron',
|
|
'repository': 'git://git.openstack.org/openstack/neutron',
|
|
'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)
|
|
self._call_hook('install.real')
|
|
self.configure_installation_source.assert_called_with(
|
|
'cloud:trusty-juno'
|
|
)
|
|
self.apt_update.assert_called_with(fatal=True)
|
|
self.apt_install.assert_has_calls([
|
|
call(_pkgs, fatal=True),
|
|
call(_pkgs, fatal=True),
|
|
])
|
|
self.assertTrue(self.get_early_packages.called)
|
|
self.assertTrue(self.get_packages.called)
|
|
self.git_install.assert_called_with(projects_yaml)
|
|
self.assertTrue(self.execd_preinstall.called)
|
|
|
|
@patch.object(hooks, 'git_install_requested')
|
|
def test_config_changed(self, git_requested):
|
|
def mock_relids(rel):
|
|
return ['relid']
|
|
git_requested.return_value = False
|
|
self.test_config.set('sysctl', '{ kernel.max_pid: "1337"}')
|
|
self.openstack_upgrade_available.return_value = True
|
|
self.valid_plugin.return_value = True
|
|
self.relation_ids.side_effect = mock_relids
|
|
_amqp_joined = self.patch('amqp_joined')
|
|
_amqp_nova_joined = self.patch('amqp_nova_joined')
|
|
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
|
|
self._call_hook('config-changed')
|
|
self.assertTrue(self.do_openstack_upgrade.called)
|
|
self.assertTrue(self.configure_ovs.called)
|
|
self.assertTrue(_amqp_joined.called)
|
|
self.assertTrue(_amqp_nova_joined.called)
|
|
self.assertTrue(_zmq_joined.called)
|
|
self.assertTrue(self.create_sysctl.called)
|
|
|
|
@patch.object(hooks, 'git_install_requested')
|
|
def test_config_changed_upgrade(self, git_requested):
|
|
git_requested.return_value = False
|
|
self.openstack_upgrade_available.return_value = True
|
|
self.valid_plugin.return_value = True
|
|
self._call_hook('config-changed')
|
|
self.assertTrue(self.do_openstack_upgrade.called)
|
|
self.assertTrue(self.configure_ovs.called)
|
|
|
|
@patch.object(hooks, 'git_install_requested')
|
|
def test_config_changed_n1kv(self, git_requested):
|
|
git_requested.return_value = False
|
|
self.openstack_upgrade_available.return_value = False
|
|
self.valid_plugin.return_value = True
|
|
self.filter_installed_packages.side_effect = lambda p: p
|
|
self.test_config.set('plugin', 'n1kv')
|
|
self._call_hook('config-changed')
|
|
self.apt_install.assert_called_with('neutron-l3-agent')
|
|
self.test_config.set('enable-l3-agent', False)
|
|
self._call_hook('config-changed')
|
|
self.apt_purge.assert_called_with('neutron-l3-agent')
|
|
|
|
@patch('sys.exit')
|
|
@patch.object(hooks, 'git_install_requested')
|
|
def test_config_changed_invalid_plugin(self, git_requested, _exit):
|
|
git_requested.return_value = False
|
|
self.valid_plugin.return_value = False
|
|
self._call_hook('config-changed')
|
|
self.assertTrue(self.log.called)
|
|
_exit.assert_called_with(1)
|
|
|
|
@patch.object(hooks, 'git_install_requested')
|
|
@patch.object(hooks, 'config_value_changed')
|
|
def test_config_changed_git(self, config_val_changed, git_requested):
|
|
def mock_relids(rel):
|
|
return ['relid']
|
|
git_requested.return_value = True
|
|
self.test_config.set('sysctl', '{ kernel.max_pid: "1337"}')
|
|
self.openstack_upgrade_available.return_value = True
|
|
self.valid_plugin.return_value = True
|
|
self.relation_ids.side_effect = mock_relids
|
|
_amqp_joined = self.patch('amqp_joined')
|
|
_amqp_nova_joined = self.patch('amqp_nova_joined')
|
|
_zmq_joined = self.patch('zeromq_configuration_relation_joined')
|
|
repo = 'cloud:trusty-juno'
|
|
openstack_origin_git = {
|
|
'repositories': [
|
|
{'name': 'requirements',
|
|
'repository':
|
|
'git://git.openstack.org/openstack/requirements',
|
|
'branch': 'stable/juno'},
|
|
{'name': 'neutron',
|
|
'repository': 'git://git.openstack.org/openstack/neutron',
|
|
'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)
|
|
self._call_hook('config-changed')
|
|
self.git_install.assert_called_with(projects_yaml)
|
|
self.assertFalse(self.do_openstack_upgrade.called)
|
|
self.assertTrue(self.configure_ovs.called)
|
|
self.assertTrue(_amqp_joined.called)
|
|
self.assertTrue(_amqp_nova_joined.called)
|
|
self.assertTrue(_zmq_joined.called)
|
|
self.assertTrue(self.create_sysctl.called)
|
|
|
|
def test_upgrade_charm(self):
|
|
_install = self.patch('install')
|
|
_config_changed = self.patch('config_changed')
|
|
self._call_hook('upgrade-charm')
|
|
self.assertTrue(_install.called)
|
|
self.assertTrue(_config_changed.called)
|
|
self.assertTrue(self.install_systemd_override.called)
|
|
|
|
def test_amqp_joined(self):
|
|
self._call_hook('amqp-relation-joined')
|
|
self.relation_set.assert_called_with(
|
|
username='neutron',
|
|
vhost='openstack',
|
|
relation_id=None
|
|
)
|
|
|
|
def test_amqp_changed(self):
|
|
self._call_hook('amqp-relation-changed')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
|
|
def test_amqp_departed_no_rel(self):
|
|
self.CONFIGS.complete_contexts.return_value = []
|
|
self._call_hook('amqp-relation-departed')
|
|
self.assertFalse(self.CONFIGS.write_all.called)
|
|
|
|
def test_amqp_departed(self):
|
|
self.CONFIGS.complete_contexts.return_value = ['amqp']
|
|
self._call_hook('amqp-relation-departed')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
|
|
def test_amqp_nova_joined(self):
|
|
self._call_hook('amqp-nova-relation-joined')
|
|
self.relation_set.assert_called_with(
|
|
username='nova',
|
|
vhost='openstack',
|
|
relation_id=None
|
|
)
|
|
|
|
def test_amqp_nova_changed_no_rel(self):
|
|
self.CONFIGS.complete_contexts.return_value = []
|
|
self._call_hook('amqp-nova-relation-changed')
|
|
self.assertFalse(self.CONFIGS.write_all.called)
|
|
|
|
def test_amqp_nova_changed(self):
|
|
self.CONFIGS.complete_contexts.return_value = ['amqp-nova']
|
|
self._call_hook('amqp-nova-relation-changed')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
|
|
def test_nm_changed(self):
|
|
def _relation_get(key):
|
|
data = {
|
|
'ca_cert': 'cert',
|
|
'restart_trigger': None,
|
|
}
|
|
return data.get(key)
|
|
self.relation_get.side_effect = _relation_get
|
|
self._call_hook('quantum-network-service-relation-changed')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
self.install_ca_cert.assert_called_with('cert')
|
|
|
|
def test_nm_changed_restart_nonce(self):
|
|
'''Ensure first set of restart_trigger restarts nova-api-metadata'''
|
|
def _relation_get(key):
|
|
data = {
|
|
'ca_cert': 'cert',
|
|
'restart_trigger': '1111111222222333333',
|
|
}
|
|
return data.get(key)
|
|
self.relation_get.side_effect = _relation_get
|
|
self.is_unit_paused_set.return_value = False
|
|
kv_mock = MagicMock()
|
|
self.kv.return_value = kv_mock
|
|
kv_mock.get.return_value = None
|
|
self._call_hook('quantum-network-service-relation-changed')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
self.install_ca_cert.assert_called_with('cert')
|
|
self.service_restart.assert_called_with('nova-api-metadata')
|
|
kv_mock.get.assert_called_with('restart_nonce')
|
|
kv_mock.set.assert_called_with('restart_nonce',
|
|
'1111111222222333333')
|
|
self.assertTrue(kv_mock.flush.called)
|
|
|
|
def test_nm_changed_restart_nonce_changed(self):
|
|
'''Ensure change of restart_trigger restarts nova-api-metadata'''
|
|
def _relation_get(key):
|
|
data = {
|
|
'ca_cert': 'cert',
|
|
'restart_trigger': '1111111222222333333',
|
|
}
|
|
return data.get(key)
|
|
self.relation_get.side_effect = _relation_get
|
|
self.is_unit_paused_set.return_value = False
|
|
kv_mock = MagicMock()
|
|
self.kv.return_value = kv_mock
|
|
kv_mock.get.return_value = ('22222233333344444')
|
|
self._call_hook('quantum-network-service-relation-changed')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
self.install_ca_cert.assert_called_with('cert')
|
|
self.service_restart.assert_called_with('nova-api-metadata')
|
|
kv_mock.get.assert_called_with('restart_nonce')
|
|
kv_mock.set.assert_called_with('restart_nonce',
|
|
'1111111222222333333')
|
|
self.assertTrue(kv_mock.flush.called)
|
|
|
|
def test_nm_changed_restart_nonce_nochange(self):
|
|
'''Ensure no change in restart_trigger skips restarts'''
|
|
def _relation_get(key):
|
|
data = {
|
|
'ca_cert': 'cert',
|
|
'restart_trigger': '1111111222222333333',
|
|
}
|
|
return data.get(key)
|
|
self.relation_get.side_effect = _relation_get
|
|
self.is_unit_paused_set.return_value = False
|
|
kv_mock = MagicMock()
|
|
self.kv.return_value = kv_mock
|
|
kv_mock.get.return_value = ('1111111222222333333')
|
|
self._call_hook('quantum-network-service-relation-changed')
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
self.install_ca_cert.assert_called_with('cert')
|
|
self.assertFalse(self.service_restart.called)
|
|
kv_mock.get.assert_called_with('restart_nonce')
|
|
self.assertFalse(kv_mock.set.called)
|
|
self.assertFalse(kv_mock.flush.called)
|
|
|
|
def test_neutron_plugin_changed(self):
|
|
self.use_l3ha.return_value = True
|
|
self._call_hook('neutron-plugin-api-relation-changed')
|
|
self.apt_install.assert_called_with(['keepalived', 'conntrack'],
|
|
fatal=True)
|
|
self.assertTrue(self.CONFIGS.write_all.called)
|
|
|
|
def test_cluster_departed_nvp(self):
|
|
self.test_config.set('plugin', 'nvp')
|
|
self._call_hook('cluster-relation-departed')
|
|
self.assertTrue(self.log.called)
|
|
|
|
def test_stop(self):
|
|
self._call_hook('stop')
|
|
self.assertTrue(self.stop_services.called)
|
|
|
|
def test_ha_relation_joined(self):
|
|
self.test_config.set('ha-legacy-mode', True)
|
|
self._call_hook('ha_relation_joined')
|
|
self.assertTrue(self.cache_env_data.called)
|
|
self.assertTrue(self.get_hacluster_config.called)
|
|
self.assertTrue(self.install_legacy_ha_files.called)
|
|
|
|
def test_ha_relation_departed(self):
|
|
self.test_config.set('ha-legacy-mode', True)
|
|
self._call_hook('ha-relation-departed')
|
|
self.assertTrue(self.remove_legacy_ha_files.called)
|
|
self.assertTrue(self.stop_neutron_ha_monitor_daemon.called)
|
|
|
|
def test_quantum_network_service_relation_changed(self):
|
|
self.test_config.set('ha-legacy-mode', True)
|
|
self._call_hook('quantum-network-service-relation-changed')
|
|
self.assertTrue(self.cache_env_data.called)
|