Merge ~zzehring/charm-openstack-service-checks:bugfix/lp1882822-keystone-client-errors-block-resolving-hooks into ~llama-charmers/charm-openstack-service-checks:master

Proposed by Zachary Zehring
Status: Merged
Merged at revision: 69129cd99c5d125f69deb124255e21a5c676e82b
Proposed branch: ~zzehring/charm-openstack-service-checks:bugfix/lp1882822-keystone-client-errors-block-resolving-hooks
Merge into: ~llama-charmers/charm-openstack-service-checks:master
Diff against target: 205 lines (+108/-18)
3 files modified
lib/lib_openstack_service_checks.py (+48/-12)
reactive/openstack_service_checks.py (+17/-6)
tests/unit/test_lib.py (+43/-0)
Reviewer Review Type Date Requested Status
Andrea Ieri Needs Fixing
BootStack Reviewers Pending
Review via email: mp+386763@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Andrea Ieri (aieri) wrote :

looks good, but there are a couple of things that might be done a little differently (see in-line comments).

The workload_status of OSCKeystoneClientError must be fixed, but the other comments are related more to taste.

review: Needs Fixing
Revision history for this message
Adam Dyess (addyess) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lib_openstack_service_checks.py b/lib/lib_openstack_service_checks.py
2index 47a136b..99a0706 100644
3--- a/lib/lib_openstack_service_checks.py
4+++ b/lib/lib_openstack_service_checks.py
5@@ -22,8 +22,31 @@ class OSCCredentialsError(Exception):
6 pass
7
8
9-class OSCEndpointError(OSCCredentialsError):
10- pass
11+class OSCKeystoneError(Exception):
12+ @property
13+ def workload_status(self):
14+ raise NotImplementedError
15+
16+
17+class OSCKeystoneServerError(OSCKeystoneError):
18+ @property
19+ def workload_status(self):
20+ return 'Keystone server error encountered trying to list keystone resources. ' \
21+ 'Check keystone server health. View juju logs for more info.'
22+
23+
24+class OSCKeystoneClientError(OSCKeystoneError):
25+ @property
26+ def workload_status(self):
27+ return 'Keystone client request error encountered trying to keystone resources. ' \
28+ 'Check keystone auth creds and url. View juju logs for more info.'
29+
30+
31+class OSCSslError(OSCKeystoneError):
32+ @property
33+ def workload_status(self):
34+ return 'SSL error encountered when requesting Keystone for resource list. ' \
35+ 'Check trusted_ssl_ca config option. View juju logs for more info.'
36
37
38 class OSCHelper():
39@@ -409,25 +432,38 @@ class OSCHelper():
40 self._keystone_client = client.Client(session=sess)
41
42 if self._keystone_client is None:
43- raise OSCEndpointError('Unable to list the endpoint errors, yet: '
44- 'could not connect to the Identity Service')
45+ raise OSCKeystoneServerError('Unable to list the endpoints yet: '
46+ 'could not connect to the Identity Service')
47
48 @property
49 def keystone_endpoints(self):
50- try:
51- endpoints = self._keystone_client.endpoints.list()
52- hookenv.log("Endpoints from keystone: {}".format(endpoints))
53- return endpoints
54- except keystoneauth1.exceptions.http.InternalServerError as error:
55- raise OSCEndpointError(
56- 'Unable to list the keystone endpoints, yet: {}'.format(error))
57+ endpoints = self._safe_keystone_client_command(self._keystone_client.endpoints.list,
58+ object_type='endpoints')
59+ hookenv.log("Endpoints from keystone: {}".format(endpoints))
60+ return endpoints
61
62 @property
63 def keystone_services(self):
64- services = self._keystone_client.services.list()
65+ services = self._safe_keystone_client_command(self._keystone_client.services.list,
66+ object_type='services')
67 hookenv.log("Services from keystone: {}".format(services))
68 return services
69
70+ @staticmethod
71+ def _safe_keystone_client_command(client_command, object_type='objects'):
72+ try:
73+ response = client_command()
74+ except (keystoneauth1.exceptions.http.InternalServerError,
75+ keystoneauth1.exceptions.connection.ConnectFailure) as server_error:
76+ raise OSCKeystoneServerError(
77+ 'Keystone server unable to list keystone {}: {}'.format(server_error, object_type))
78+ except keystoneauth1.exceptions.http.BadRequest as client_error:
79+ raise OSCKeystoneClientError(
80+ 'Keystone client error when listing {}: {}'.format(client_error, object_type))
81+ except keystoneauth1.exceptions.connection.SSLError as ssl_error:
82+ raise OSCSslError('Keystone ssl error when listing {}: {}'.format(ssl_error, object_type))
83+ return response
84+
85 @property
86 def _load_envvars(self, novarc='/var/lib/nagios/nagios.novarc'):
87 if not os.path.exists(novarc):
88diff --git a/reactive/openstack_service_checks.py b/reactive/openstack_service_checks.py
89index 95dfaf0..cce8b28 100644
90--- a/reactive/openstack_service_checks.py
91+++ b/reactive/openstack_service_checks.py
92@@ -24,7 +24,7 @@ from charms.reactive import any_flags_set, clear_flag, is_flag_set, set_flag, wh
93 from lib_openstack_service_checks import (
94 OSCHelper,
95 OSCCredentialsError,
96- OSCEndpointError
97+ OSCKeystoneError
98 )
99
100 CERT_FILE = '/usr/local/share/ca-certificates/openstack-service-checks.crt'
101@@ -176,8 +176,8 @@ def render_config():
102 try:
103 helper.render_checks(creds)
104 set_flag('openstack-service-checks.endpoints.configured')
105- except OSCEndpointError as error:
106- hookenv.log(error)
107+ except OSCKeystoneError as keystone_error:
108+ _set_keystone_error_workload_status(keystone_error)
109
110 if not helper.deploy_rally():
111 # Rally could not be installed (if enabled). No further actions taken
112@@ -207,8 +207,8 @@ def configure_nrpe_endpoints():
113 helper.create_endpoint_checks(creds)
114 set_flag('openstack-service-checks.endpoints.configured')
115 clear_flag('openstack-service-checks.started')
116- except OSCEndpointError as error:
117- hookenv.log(error)
118+ except OSCKeystoneError as keystone_error:
119+ _set_keystone_error_workload_status(keystone_error)
120
121
122 @when('identity-notifications.available.updated')
123@@ -221,10 +221,15 @@ def endpoints_changed():
124 def do_restart():
125 hookenv.log('Reloading nagios-nrpe-server')
126 host.service_restart('nagios-nrpe-server')
127- hookenv.status_set('active', 'Unit is ready')
128 set_flag('openstack-service-checks.started')
129
130
131+@when('openstack-service-checks.started')
132+@when('openstack-service-checks.endpoints.configured')
133+def set_active():
134+ hookenv.status_set('active', 'Unit is ready')
135+
136+
137 @when('nrpe-external-master.available')
138 def do_reconfigure_nrpe():
139 os_credentials_flag = 'config.changed.os-credentials'
140@@ -285,3 +290,9 @@ def parse_hooks():
141
142 # render configs again
143 do_reconfigure_nrpe()
144+
145+
146+def _set_keystone_error_workload_status(keystone_error):
147+ error_status_message = 'Failed to create endpoint checks due issue communicating with Keystone'
148+ hookenv.log('{}. Error:\n{}'.format(error_status_message, keystone_error), level=hookenv.ERROR)
149+ hookenv.status_set('blocked', keystone_error.workload_status)
150diff --git a/tests/unit/test_lib.py b/tests/unit/test_lib.py
151index effd5ea..e925ee2 100644
152--- a/tests/unit/test_lib.py
153+++ b/tests/unit/test_lib.py
154@@ -1,4 +1,9 @@
155+from unittest.mock import MagicMock
156+
157 import pytest
158+import keystoneauth1
159+
160+from lib_openstack_service_checks import OSCKeystoneServerError, OSCKeystoneClientError, OSCSslError
161
162
163 def test_openstackservicechecks_common_properties(openstackservicechecks):
164@@ -61,3 +66,41 @@ def test_get_rally_checks_context(skip_rally, result, openstackservicechecks):
165 expected = {comp: result[num]
166 for num, comp in enumerate('cinder glance nova neutron'.split())}
167 assert openstackservicechecks._get_rally_checks_context() == expected
168+
169+
170+@pytest.mark.parametrize('keystone_auth_exception,expected_raised_exception', [
171+ (keystoneauth1.exceptions.http.InternalServerError, OSCKeystoneServerError),
172+ (keystoneauth1.exceptions.connection.ConnectFailure, OSCKeystoneServerError),
173+ (keystoneauth1.exceptions.http.BadRequest, OSCKeystoneClientError),
174+ (keystoneauth1.exceptions.connection.SSLError, OSCSslError),
175+])
176+def test_keystone_endpoints_client_exceptions(
177+ keystone_auth_exception, expected_raised_exception, openstackservicechecks):
178+ mock_keystone_client = MagicMock()
179+ mock_keystone_client.endpoints.list.side_effect = keystone_auth_exception
180+ openstackservicechecks._keystone_client = mock_keystone_client
181+ try:
182+ openstackservicechecks.keystone_endpoints
183+ except Exception as e:
184+ assert isinstance(e, expected_raised_exception)
185+ else:
186+ assert False, 'Error should have been raised.'
187+
188+
189+@pytest.mark.parametrize('keystone_auth_exception,expected_raised_exception', [
190+ (keystoneauth1.exceptions.http.InternalServerError, OSCKeystoneServerError),
191+ (keystoneauth1.exceptions.connection.ConnectFailure, OSCKeystoneServerError),
192+ (keystoneauth1.exceptions.http.BadRequest, OSCKeystoneClientError),
193+ (keystoneauth1.exceptions.connection.SSLError, OSCSslError),
194+])
195+def test_keystone_services_client_exceptions(
196+ keystone_auth_exception, expected_raised_exception, openstackservicechecks):
197+ mock_keystone_client = MagicMock()
198+ mock_keystone_client.services.list.side_effect = keystone_auth_exception
199+ openstackservicechecks._keystone_client = mock_keystone_client
200+ try:
201+ openstackservicechecks.keystone_services
202+ except Exception as e:
203+ assert isinstance(e, expected_raised_exception)
204+ else:
205+ assert False, 'Error should have been raised.'

Subscribers

People subscribed via source and target branches