diff --git a/.gitignore b/.gitignore index 5c68cf0f..22cf6874 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ func-results.json __pycache__ .stestr .idea +.pydevproject diff --git a/.pydevproject b/.pydevproject index 66f5d7bc..7fd21e9e 100644 --- a/.pydevproject +++ b/.pydevproject @@ -1,14 +1,10 @@ - python 2.7 - - Default - + python3 /keystone/hooks /keystone/unit_tests /${PROJECT_DIR_NAME} - diff --git a/hooks/keystone_hooks.py b/hooks/keystone_hooks.py index 4cb9a014..186bebc7 100755 --- a/hooks/keystone_hooks.py +++ b/hooks/keystone_hooks.py @@ -128,6 +128,7 @@ from keystone_utils import ( remove_old_packages, stop_manager_instance, assemble_endpoints, + endpoints_dict, endpoints_checksum, ) @@ -426,7 +427,8 @@ def db_departed_or_broken(): @hooks.hook('identity-service-relation-changed') @restart_on_change(restart_map(), restart_functions=restart_function_map()) def identity_changed(relation_id=None, remote_unit=None): - notifications = {} + notifications_checksums = {} + notifications_endpoints = {} if is_elected_leader(CLUSTER_RES): if not is_db_ready(): log("identity-service-relation-changed hook fired before db " @@ -454,7 +456,8 @@ def identity_changed(relation_id=None, remote_unit=None): service = settings.get('service') if service: key = '%s-endpoint-changed' % service - notifications[key] = endpoints_checksum(settings) + notifications_endpoints[key] = endpoints_dict(settings) + notifications_checksums[key] = endpoints_checksum(settings) else: # Some services don't set their name in the 'service' key in the # relation, for those their name is calculated from the prefix of @@ -466,7 +469,12 @@ def identity_changed(relation_id=None, remote_unit=None): if single.issubset(endpoints[ep]): key = '%s-endpoint-changed' % ep log('endpoint: %s' % ep) - notifications[key] = endpoints_checksum(endpoints[ep]) + notifications_endpoints[key] = ( + endpoints_dict(endpoints[ep]) + ) + notifications_checksums[key] = ( + endpoints_checksum(endpoints[ep]) + ) else: # Each unit needs to set the db information otherwise if the unit # with the info dies the settings die with it Bug# 1355848 @@ -479,8 +487,9 @@ def identity_changed(relation_id=None, remote_unit=None): log('Deferring identity_changed() to service leader.') - if notifications: - send_notifications(notifications) + if notifications_endpoints or notifications_checksums: + send_notifications(notifications_checksums, + notifications_endpoints) @hooks.hook('identity-credentials-relation-joined', diff --git a/hooks/keystone_utils.py b/hooks/keystone_utils.py index 480b5c8b..c8467030 100644 --- a/hooks/keystone_utils.py +++ b/hooks/keystone_utils.py @@ -2003,7 +2003,13 @@ def send_id_service_notifications(data): """ id_svc_rel_ids = relation_ids('identity-service') for rid in id_svc_rel_ids: - changed = {} + changed = relation_get(unit=local_unit(), + rid=rid, + attribute='ep_changed') + if changed: + changed = json.loads(changed) + else: + changed = {} for unit in related_units(rid): rs = relation_get( unit=unit, @@ -2021,9 +2027,9 @@ def send_id_service_notifications(data): 'ep_changed': json.dumps(changed, sort_keys=True)}) -def send_notifications(data, force=False): - send_id_notifications(data, force=force) - send_id_service_notifications(data) +def send_notifications(checksum_data, endpoint_data, force=False): + send_id_notifications(checksum_data, force=force) + send_id_service_notifications(endpoint_data) def send_id_notifications(data, force=False): @@ -2565,3 +2571,18 @@ def endpoints_checksum(settings): csum.update(settings.get('admin_url', None).encode('utf-8')) csum.update(settings.get('internal_url', None).encode('utf-8')) return csum.hexdigest() + + +def endpoints_dict(settings): + """ + Build a dictionary of endpoint types using settings + + :param settings: dict with urls registered in keystone. + :returns: dict of endpoints from settings + """ + endpoints = { + 'public': settings.get('public_url', None), + 'admin': settings.get('admin_url', None), + 'internal': settings.get('internal_url', None), + } + return endpoints diff --git a/unit_tests/test_keystone_utils.py b/unit_tests/test_keystone_utils.py index fa4c6d9d..102f7aa1 100644 --- a/unit_tests/test_keystone_utils.py +++ b/unit_tests/test_keystone_utils.py @@ -818,29 +818,28 @@ class TestKeystoneUtils(CharmTestCase): def test_send_id_notifications(self, mock_is_elected_leader, mock_relation_ids, mock_relation_get, mock_relation_set, mock_uuid): + checksums = {'foo-endpoint-changed': 1} relation_id = 'testrel:0' mock_uuid.uuid4.return_value = '1234' mock_relation_ids.return_value = [relation_id] mock_is_elected_leader.return_value = False - utils.send_notifications({'foo-endpoint-changed': 1}) + utils.send_id_notifications(checksums) self.assertFalse(mock_relation_set.called) mock_is_elected_leader.return_value = True - utils.send_notifications({}) + utils.send_id_notifications({}) self.assertFalse(mock_relation_set.called) - settings = {'foo-endpoint-changed': 1} - utils.send_notifications(settings) + utils.send_id_notifications(checksums) self.assertTrue(mock_relation_set.called) mock_relation_set.assert_called_once_with(relation_id=relation_id, - relation_settings=settings) + relation_settings=checksums) mock_relation_set.reset_mock() - settings = {'foo-endpoint-changed': 1} - utils.send_notifications(settings, force=True) + utils.send_id_notifications(checksums, force=True) self.assertTrue(mock_relation_set.called) - settings['trigger'] = '1234' + checksums['trigger'] = '1234' mock_relation_set.assert_called_once_with(relation_id=relation_id, - relation_settings=settings) + relation_settings=checksums) @patch.object(utils, 'relation_ids') @patch.object(utils, 'related_units') @@ -873,7 +872,9 @@ class TestKeystoneUtils(CharmTestCase): 'glance/0': { 'admin_url': 'http://172.20.0.32:9292'}, 'glance/1': {}, - 'glance/2': {}} + 'glance/2': {}, + 'keystone/0': {} + } def _relation_get(unit, rid, attribute): return id_svc_rel_data[unit].get(attribute) @@ -881,29 +882,40 @@ class TestKeystoneUtils(CharmTestCase): mock_relation_ids.return_value = id_svc_rel_units.keys() mock_related_units.side_effect = _related_units mock_relation_get.side_effect = _relation_get + self.local_unit.return_value = 'keystone/0' # Check all services subscribed to placement changes are notified. mock_relation_set.reset_mock() utils.send_id_service_notifications( - {'placement-endpoint-changed': '4d0633ee'}) + {'placement-endpoint-changed': {"internal": "http://demo.com"}}) mock_relation_set.assert_called_once_with( relation_id='identity-service:2', relation_settings={ - 'ep_changed': '{"placement": "4d0633ee"}'}) + 'ep_changed': + '{"placement": {"internal": "http://demo.com"}}' + } + ) # Check all services subscribed to neutron changes are notified. mock_relation_set.reset_mock() utils.send_id_service_notifications( - {'neutron-endpoint-changed': '1c261658'}) + {'neutron-endpoint-changed': {"internal": "http://demo.com"}}) expected_rel_set_calls = [ call( relation_id='identity-service:1', relation_settings={ - 'ep_changed': '{"neutron": "1c261658"}'}), + 'ep_changed': + '{"neutron": {"internal": "http://demo.com"}}' + } + ), call( relation_id='identity-service:2', relation_settings={ - 'ep_changed': '{"neutron": "1c261658"}'})] + 'ep_changed': + '{"neutron": {"internal": "http://demo.com"}}' + } + ) + ] mock_relation_set.assert_has_calls( expected_rel_set_calls, any_order=True) @@ -911,19 +923,26 @@ class TestKeystoneUtils(CharmTestCase): # Check multiple ep changes with app subscribing to multiple eps mock_relation_set.reset_mock() utils.send_id_service_notifications( - {'neutron-endpoint-changed': '1c261658', - 'placement-endpoint-changed': '4d0633ee'}) + {'neutron-endpoint-changed': {"internal": "http://demo.com"}, + 'placement-endpoint-changed': {"internal": "http://demo.com"}}) expected_rel_set_calls = [ call( relation_id='identity-service:1', relation_settings={ - 'ep_changed': '{"neutron": "1c261658"}'}), + 'ep_changed': + '{"neutron": {"internal": "http://demo.com"}}' + } + ), call( relation_id='identity-service:2', relation_settings={ 'ep_changed': ( - '{"neutron": "1c261658", ' - '"placement": "4d0633ee"}')})] + '{"neutron": {"internal": "http://demo.com"}, ' + '"placement": {"internal": "http://demo.com"}}' + ) + } + ) + ] mock_relation_set.assert_has_calls( expected_rel_set_calls, any_order=True)