Advertise Nova API readiness

Adds a new relation 'nova-api' that can be used by services that consume
the nova api in some way or another. We use it to simply pass a flag to
remote services that states whether the API service is ready to be used,
based on the complete context of required interfaces.
This commit is contained in:
Adam Gandelman 2016-02-03 11:38:43 -08:00
parent 00791f3f9d
commit 962790239b
4 changed files with 87 additions and 5 deletions

View File

@ -81,6 +81,7 @@ from nova_cc_utils import (
do_openstack_upgrade,
enable_services,
git_install,
is_api_ready,
keystone_ca_cert_b64,
migrate_neutron_database,
migrate_nova_database,
@ -286,6 +287,9 @@ def amqp_changed():
[nova_cell_relation_joined(rid=rid)
for rid in relation_ids('cell')]
for r_id in relation_ids('nova-api'):
nova_api_relation_joined(rid=r_id)
def conditional_neutron_migration():
if os_release('nova-common') <= 'icehouse':
@ -396,6 +400,9 @@ def postgresql_nova_db_changed():
CONFIGS.write_all()
leader_init_db_if_ready(skip_acl_check=True, skip_cells_restarts=True)
for r_id in relation_ids('nova-api'):
nova_api_relation_joined(rid=r_id)
@hooks.hook('pgsql-neutron-db-relation-changed')
@service_guard(guard_map(), CONFIGS,
@ -419,6 +426,12 @@ def image_service_changed():
CONFIGS.write(NOVA_CONF)
# TODO: special case config flag for essex (strip protocol)
for r_id in relation_ids('nova-api'):
nova_api_relation_joined(rid=r_id)
for r_id in relation_ids('nova-api'):
nova_api_relation_joined(rid=r_id)
@hooks.hook('identity-service-relation-joined')
def identity_joined(rid=None):
@ -453,6 +466,9 @@ def identity_changed():
[neutron_api_relation_joined(rid) for rid in relation_ids('neutron-api')]
configure_https()
for r_id in relation_ids('nova-api'):
nova_api_relation_joined(rid=r_id)
@hooks.hook('nova-volume-service-relation-joined',
'cinder-volume-service-relation-joined')
@ -610,6 +626,9 @@ def compute_joined(rid=None, remote_restart=False):
@hooks.hook('cloud-compute-relation-changed')
def compute_changed(rid=None, unit=None):
for r_id in relation_ids('nova-api'):
nova_api_relation_joined(rid=r_id)
rel_settings = relation_get(rid=rid, unit=unit)
if 'migration_auth_type' not in rel_settings:
return
@ -1083,6 +1102,13 @@ def update_nova_consoleauth_config():
log(str(e), level='DEBUG')
def nova_api_relation_joined(rid=None):
rel_data = {
'nova-api-ready': 'yes' if is_api_ready(CONFIGS) else 'no'
}
relation_set(rid, **rel_data)
def main():
try:
hooks.execute(sys.argv)

View File

@ -35,6 +35,7 @@ from charmhelpers.contrib.openstack.utils import (
git_src_dir,
git_pip_venv_dir,
git_yaml_value,
incomplete_relation_data,
is_ip,
os_release,
save_script_rc as _save_script_rc,
@ -1385,3 +1386,7 @@ def check_optional_relations(configs):
return status_get()
else:
return 'unknown', 'No optional relations'
def is_api_ready(configs):
return (not incomplete_relation_data(configs, REQUIRED_INTERFACES))

View File

@ -202,6 +202,12 @@ class NovaCCHooksTests(CharmTestCase):
self.assertTrue(self.save_script_rc.called)
mock_filter_packages.assert_called_with([])
@patch.object(hooks, 'nova_api_relation_joined')
def test_compute_changed_nova_api_trigger(self, api_joined):
self.relation_ids.return_value = ['nova-api/0']
hooks.compute_changed()
api_joined.assert_called_with(rid='nova-api/0')
def test_compute_changed_ssh_migration(self):
self.test_relation.set({
'migration_auth_type': 'ssh', 'ssh_public_key': 'fookey',
@ -441,17 +447,20 @@ class NovaCCHooksTests(CharmTestCase):
configs.write = MagicMock()
hooks.postgresql_nova_db_changed()
@patch.object(hooks, 'nova_api_relation_joined')
@patch.object(hooks, 'is_db_initialised')
@patch.object(hooks, 'conditional_neutron_migration')
@patch.object(hooks, 'CONFIGS')
def test_db_changed(self, configs, cond_neutron_mig,
mock_is_db_initialised):
mock_is_db_initialised, api_joined):
self.relation_ids.return_value = ['nova-api/0']
mock_is_db_initialised.return_value = False
'No database migration is attempted when ACL list is not present'
self._shared_db_test(configs)
self.assertTrue(configs.write_all.called)
self.assertFalse(self.migrate_nova_database.called)
self.assertFalse(cond_neutron_mig.called)
api_joined.asert_called_with(rid='nova-api/0')
@patch.object(hooks, 'is_db_initialised')
@patch.object(hooks, 'CONFIGS')
@ -479,13 +488,19 @@ class NovaCCHooksTests(CharmTestCase):
self.assertTrue(configs.write_all.called)
self.assertFalse(self.migrate_nova_database.called)
@patch.object(hooks, 'nova_api_relation_joined')
@patch.object(hooks, 'is_db_initialised')
@patch.object(hooks, 'CONFIGS')
def test_postgresql_db_changed(self, configs, mock_is_db_initialised):
def test_postgresql_db_changed(self, configs, mock_is_db_initialised,
api_joined):
self.relation_ids.side_effect = [
[],
['nova-api/0']]
mock_is_db_initialised.return_value = False
self._postgresql_db_test(configs)
self.assertTrue(configs.write_all.called)
self.migrate_nova_database.assert_called_with()
api_joined.assert_called_with(rid='nova-api/0')
@patch.object(hooks, 'is_db_initialised')
@patch.object(hooks, 'nova_cell_relation_joined')
@ -524,9 +539,11 @@ class NovaCCHooksTests(CharmTestCase):
self.assertTrue(configs.write_all.called)
cell_joined.assert_called_with(rid='nova-cell-api/0')
@patch.object(hooks, 'nova_api_relation_joined')
@patch.object(hooks, 'nova_cell_relation_joined')
@patch.object(hooks, 'CONFIGS')
def test_amqp_changed_api_rel(self, configs, cell_joined):
def test_amqp_changed_api_rel(self, configs, cell_joined, api_joined):
self.relation_ids.return_value = ['nova-api/0']
configs.complete_contexts = MagicMock()
configs.complete_contexts.return_value = ['amqp']
configs.write = MagicMock()
@ -534,14 +551,19 @@ class NovaCCHooksTests(CharmTestCase):
hooks.amqp_changed()
self.assertEquals(configs.write.call_args_list,
[call('/etc/nova/nova.conf')])
api_joined.assert_called_with(rid='nova-api/0')
@patch.object(hooks, 'nova_api_relation_joined')
@patch.object(hooks, 'nova_cell_relation_joined')
@patch.object(hooks, 'CONFIGS')
def test_amqp_changed_noapi_rel(self, configs, cell_joined):
def test_amqp_changed_noapi_rel(self, configs, cell_joined, api_joined):
configs.complete_contexts = MagicMock()
configs.complete_contexts.return_value = ['amqp']
configs.write = MagicMock()
self.relation_ids.return_value = ['nova-cell-api/0']
self.relation_ids.side_effect = [
['nova-cell-api/0'],
['nova-api/0'],
]
self.is_relation_made.return_value = False
self.network_manager.return_value = 'neutron'
hooks.amqp_changed()
@ -549,6 +571,7 @@ class NovaCCHooksTests(CharmTestCase):
[call('/etc/nova/nova.conf'),
call('/etc/neutron/neutron.conf')])
cell_joined.assert_called_with(rid='nova-cell-api/0')
api_joined.assert_called_with(rid='nova-api/0')
@patch.object(hooks, 'canonical_url')
def test_nova_cell_relation_joined(self, _canonical_url):
@ -886,3 +909,17 @@ class NovaCCHooksTests(CharmTestCase):
])
mock_filter_packages.assert_called_with([])
@patch.object(hooks, 'is_api_ready')
def _test_nova_api_relation_joined(self, tgt, is_api_ready):
is_api_ready.return_value = tgt
exp = 'yes' if tgt else 'no'
hooks.nova_api_relation_joined(rid='foo')
self.relation_set.assert_called_with(
'foo', **{'nova-api-ready': exp})
def test_nova_api_relation_joined_ready(self):
self._test_nova_api_relation_joined(True)
def test_nova_api_relation_joined_not_ready(self):
self._test_nova_api_relation_joined(False)

View File

@ -1140,3 +1140,17 @@ class NovaCCUtilsTests(CharmTestCase):
self.assertTrue(self.apt_update.called)
self.apt_install.assert_called_with(['novnc', 'spice-html5',
'websockify'], fatal=True)
def _test_is_api_ready(self, tgt):
fake_config = MagicMock()
with patch.object(utils, 'incomplete_relation_data') as ird:
ird.return_value = (not tgt)
self.assertEqual(utils.is_api_ready(fake_config), tgt)
ird.assert_called_with(
fake_config, utils.REQUIRED_INTERFACES)
def test_is_api_ready_true(self):
self._test_is_api_ready(True)
def test_is_api_ready_false(self):
self._test_is_api_ready(False)