Merge ~xavpaice/charm-openstack-service-checks/+git/charm-openstack-service-checks:ssl-nagios-checks into ~canonical-bootstack/charm-openstack-service-checks:master
- Git
- lp:~xavpaice/charm-openstack-service-checks/+git/charm-openstack-service-checks
- ssl-nagios-checks
- Merge into master
Status: | Merged |
---|---|
Approved by: | Xav Paice |
Approved revision: | 9b2192b8e8320f255264d7558c1544eabe8734a0 |
Merged at revision: | 7895a97e4ee052c44971576a691e4ce23d6969f2 |
Proposed branch: | ~xavpaice/charm-openstack-service-checks/+git/charm-openstack-service-checks:ssl-nagios-checks |
Merge into: | ~canonical-bootstack/charm-openstack-service-checks:master |
Diff against target: |
592 lines (+288/-92) 10 files modified
.gitignore (+10/-0) Makefile (+7/-5) README.md (+9/-5) config.yaml (+25/-0) files/plugins/check_nova_services.py (+7/-7) layer.yaml (+11/-1) reactive/service_checks.py (+173/-65) templates/nagios.novarc (+6/-6) test-requirements.txt (+9/-0) tox.ini (+31/-3) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Peter Sabaini (community) | Approve | ||
Joel Sing (community) | +1 | Approve | |
Alvaro Uria | Pending | ||
Andrea Ieri | Pending | ||
Review via email: mp+363689@code.launchpad.net |
This proposal supersedes a proposal from 2019-02-25.
Commit message
Add NRPE checks for all API endpoints in the Keystone catalog
This change implements the first stage of spec https:/
The intent is to add an NRPE check to the openstack-
Description of the change
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal | # |
Alvaro Uria (aluria) wrote : Posted in a previous version of this proposal | # |
Please find comments inline. Other than that, I tested it in the charmlab and it looks good to me.
Xav Paice (xavpaice) wrote : Posted in a previous version of this proposal | # |
Thanks for the review - couple of responses inline. Will update the actual change shortly.
Joel Sing (jsing) wrote : Posted in a previous version of this proposal | # |
Please add an actual commit message to this merge proposal - what is this change doing and why?
Readability/
Andrea Ieri (aieri) wrote : Posted in a previous version of this proposal | # |
A bunch of inline comments :)
Xav Paice (xavpaice) wrote : Posted in a previous version of this proposal | # |
Thanks for the detailed review(s). Fresh commit coming.
Couple of inline responses.
Joel Sing (jsing) : Posted in a previous version of this proposal | # |
Joel Sing (jsing) wrote : Posted in a previous version of this proposal | # |
LGTM for a readability/
Please ensure you get a peer approval before merging.
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal | # |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.
Xav Paice (xavpaice) wrote : Posted in a previous version of this proposal | # |
Review nits have been addressed, would appreciate review.
Joel Sing (jsing) wrote : Posted in a previous version of this proposal | # |
LGTM for development standards - please see a handful of comments inline, mostly around doc strings and internal consistency.
Joel Sing (jsing) wrote : Posted in a previous version of this proposal | # |
FWIW it also seems that peer review/approval is still needed.
Xav Paice (xavpaice) wrote : Posted in a previous version of this proposal | # |
peer review would be good if someone can please.
Re the formatting nits, thanks for pointing them out. For these particular ones, I'll consider adding another commit - but I do want to avoid growing the scope any further than it already has grown due to catching formatting issues. We should do a separate PEP8 compliance commit for the rest of the code, plus another to add unit test coverage.
Peter Sabaini (peter-sabaini) wrote : Posted in a previous version of this proposal | # |
Comments inline -- one tox.ini issue and some nitpickings
A more general suggestion would be to add some logging to the charm and the service checks to aid in troubleshooting (charm currently has little logging and service checks none). Service checks could maybe just use a syslog handler
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.
Xav Paice (xavpaice) wrote : | # |
I've addressed the latest round of review comments, and squashed commits to one to keep the history a bit cleaner.
Thanks Joel for the +1 earlier, all I've done here is address the nits and squash commits.
Re logging, that's a great plan for the next commit.
Joel Sing (jsing) wrote : | # |
> I've addressed the latest round of review comments, and squashed commits to
> one to keep the history a bit cleaner.
>
> Thanks Joel for the +1 earlier, all I've done here is address the nits and
> squash commits.
Looks like the merge proposal has been resubmitted, so I'll +1 again without further review on this basis. FWIW you can squash and force push without resubmitting, which will avoid this.
> Re logging, that's a great plan for the next commit.
Joel Sing (jsing) wrote : | # |
Reapproving without further review.
Peter Sabaini (peter-sabaini) wrote : | # |
lgtm, cheers
Xav Paice (xavpaice) wrote : | # |
got +1's from two folks, marking approved. Thanks!
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision 7895a97e4ee052c
Preview Diff
1 | diff --git a/.gitignore b/.gitignore | |||
2 | 0 | new file mode 100644 | 0 | new file mode 100644 |
3 | index 0000000..dccc0dc | |||
4 | --- /dev/null | |||
5 | +++ b/.gitignore | |||
6 | @@ -0,0 +1,10 @@ | |||
7 | 1 | *~ | ||
8 | 2 | /bin | ||
9 | 3 | debian/files | ||
10 | 4 | /pkg | ||
11 | 5 | *.pyc | ||
12 | 6 | __pycache__ | ||
13 | 7 | *.swp | ||
14 | 8 | .tox | ||
15 | 9 | .venv | ||
16 | 10 | .idea | ||
17 | diff --git a/Makefile b/Makefile | |||
18 | index a1ad3a5..27eeff6 100644 | |||
19 | --- a/Makefile | |||
20 | +++ b/Makefile | |||
21 | @@ -1,6 +1,6 @@ | |||
22 | 1 | #!/usr/bin/make | 1 | #!/usr/bin/make |
23 | 2 | 2 | ||
25 | 3 | all: lint unit_test | 3 | all: lint test |
26 | 4 | 4 | ||
27 | 5 | 5 | ||
28 | 6 | .PHONY: clean | 6 | .PHONY: clean |
29 | @@ -10,15 +10,17 @@ clean: | |||
30 | 10 | .PHONY: apt_prereqs | 10 | .PHONY: apt_prereqs |
31 | 11 | apt_prereqs: | 11 | apt_prereqs: |
32 | 12 | @# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip) | 12 | @# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip) |
34 | 13 | @which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox) | 13 | @which tox >/dev/null || (sudo apt-get install -y python3-pip && sudo pip3 install tox) |
35 | 14 | 14 | ||
36 | 15 | .PHONY: lint | 15 | .PHONY: lint |
37 | 16 | lint: apt_prereqs | 16 | lint: apt_prereqs |
40 | 17 | @tox --notest | 17 | @tox -e pep8 |
39 | 18 | @PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests) | ||
41 | 19 | @charm proof | 18 | @charm proof |
42 | 20 | 19 | ||
44 | 21 | .PHONY: unit_test | 20 | .PHONY: test |
45 | 22 | unit_test: apt_prereqs | 21 | unit_test: apt_prereqs |
46 | 23 | @echo Starting tests... | 22 | @echo Starting tests... |
47 | 24 | tox | 23 | tox |
48 | 24 | |||
49 | 25 | build: | ||
50 | 26 | charm build | ||
51 | diff --git a/README.md b/README.md | |||
52 | index 24a4b54..b3159af 100644 | |||
53 | --- a/README.md | |||
54 | +++ b/README.md | |||
55 | @@ -2,7 +2,7 @@ | |||
56 | 2 | 2 | ||
57 | 3 | This charm provides OpenStack services checks for Nagios | 3 | This charm provides OpenStack services checks for Nagios |
58 | 4 | 4 | ||
60 | 5 | # Build | 5 | # Build |
61 | 6 | The fully built charm needs the following source branch | 6 | The fully built charm needs the following source branch |
62 | 7 | * https://git.launchpad.net/~canonical-bootstack/bootstack-ops/+git/charm-openstack-services-checks | 7 | * https://git.launchpad.net/~canonical-bootstack/bootstack-ops/+git/charm-openstack-services-checks |
63 | 8 | 8 | ||
64 | @@ -29,16 +29,20 @@ Build the charm, and symlink for juju-1 compatibility | |||
65 | 29 | 29 | ||
66 | 30 | juju deploy local:xenial/openstack-services-checks | 30 | juju deploy local:xenial/openstack-services-checks |
67 | 31 | 31 | ||
71 | 32 | This charm supports relating to keystone, but keystone-credentials interface | 32 | This charm supports relating to keystone via the keystone-credentials |
72 | 33 | seems to be flaky, and hard to remove-relation, so the charm works around this | 33 | interface. If you do not wish to use this, you can supply your own credential |
73 | 34 | by adding 'os-credentials' setting (see setting description hints) | 34 | set for Openstack by adding 'os-credentials' setting (see setting description |
74 | 35 | hints) | ||
75 | 35 | 36 | ||
76 | 36 | juju set openstack-services-checks os-credentials=" ... " | 37 | juju set openstack-services-checks os-credentials=" ... " |
77 | 37 | |||
78 | 38 | juju add-relation openstack-services-checks nagios | 38 | juju add-relation openstack-services-checks nagios |
79 | 39 | 39 | ||
80 | 40 | With Keystone | 40 | With Keystone |
81 | 41 | 41 | ||
82 | 42 | juju add-relation openstack-services-checks:identity-credentials keystone:identity-credentials | 42 | juju add-relation openstack-services-checks:identity-credentials keystone:identity-credentials |
83 | 43 | 43 | ||
84 | 44 | If your OpenStack API endpoints have a common URL for the Admin, Public and | ||
85 | 45 | Internal addresses, you should consider disabling some endpoints which would be | ||
86 | 46 | duplicated otherwise, e.g. | ||
87 | 44 | 47 | ||
88 | 48 | juju config openstack-service-checks check_internal_urls=False check_admin_urls=False | ||
89 | diff --git a/config.yaml b/config.yaml | |||
90 | index e4e8783..b71208c 100644 | |||
91 | --- a/config.yaml | |||
92 | +++ b/config.yaml | |||
93 | @@ -48,3 +48,28 @@ options: | |||
94 | 48 | default: false | 48 | default: false |
95 | 49 | description: | | 49 | description: | |
96 | 50 | An option to specify whether you want Warning alerts in nagios for disabled nova-compute hosts. | 50 | An option to specify whether you want Warning alerts in nagios for disabled nova-compute hosts. |
97 | 51 | tls_warn_days: | ||
98 | 52 | type: int | ||
99 | 53 | default: 30 | ||
100 | 54 | description: | | ||
101 | 55 | Number of days left for the TLS certificate to expire before warning. | ||
102 | 56 | tls_crit_days: | ||
103 | 57 | type: int | ||
104 | 58 | default: 14 | ||
105 | 59 | description: | | ||
106 | 60 | Number of days left for the TLS certificate to expire before alerting Critical. | ||
107 | 61 | check_public_urls: | ||
108 | 62 | type: boolean | ||
109 | 63 | default: True | ||
110 | 64 | description: | | ||
111 | 65 | If true, create NRPE checks matching all 'public' URLs in the Keystone catalog. | ||
112 | 66 | check_internal_urls: | ||
113 | 67 | type: boolean | ||
114 | 68 | default: True | ||
115 | 69 | description: | | ||
116 | 70 | If true, create NRPE checks matching all 'internal' URLs in the Keystone catalog. | ||
117 | 71 | check_admin_urls: | ||
118 | 72 | type: boolean | ||
119 | 73 | default: True | ||
120 | 74 | description: | | ||
121 | 75 | If true, create NRPE checks matching all 'admin' URLs in the Keystone catalog. | ||
122 | diff --git a/files/plugins/check_nova_services.py b/files/plugins/check_nova_services.py | |||
123 | index 1279c6a..6498542 100755 | |||
124 | --- a/files/plugins/check_nova_services.py | |||
125 | +++ b/files/plugins/check_nova_services.py | |||
126 | @@ -43,11 +43,11 @@ def check_hosts_up(args, aggregate, hosts, services_compute): | |||
127 | 43 | local_msg.append("Host Aggregate {} has {} hosts alive".format( | 43 | local_msg.append("Host Aggregate {} has {} hosts alive".format( |
128 | 44 | aggregate, counts['ok'])) | 44 | aggregate, counts['ok'])) |
129 | 45 | nova_status = { | 45 | nova_status = { |
135 | 46 | 'agg_name': aggregate, | 46 | 'agg_name': aggregate, |
136 | 47 | 'msg_text': ", ".join(local_msg), | 47 | 'msg_text': ", ".join(local_msg), |
137 | 48 | 'critical': status_crit, | 48 | 'critical': status_crit, |
138 | 49 | 'warning': status_warn, | 49 | 'warning': status_warn, |
139 | 50 | } | 50 | } |
140 | 51 | return nova_status | 51 | return nova_status |
141 | 52 | 52 | ||
142 | 53 | 53 | ||
143 | @@ -103,8 +103,8 @@ if __name__ == '__main__': | |||
144 | 103 | command = ['/bin/bash', '-c', "source {} && env".format(args.env)] | 103 | command = ['/bin/bash', '-c', "source {} && env".format(args.env)] |
145 | 104 | proc = subprocess.Popen(command, stdout=subprocess.PIPE) | 104 | proc = subprocess.Popen(command, stdout=subprocess.PIPE) |
146 | 105 | for line in proc.stdout: | 105 | for line in proc.stdout: |
149 | 106 | (key, _, value) = line.partition("=") | 106 | (key, _, value) = line.partition(b'=') |
150 | 107 | os.environ[key] = value.rstrip() | 107 | os.environ[key.decode('utf-8')] = value.rstrip().decode('utf-8') |
151 | 108 | proc.communicate() | 108 | proc.communicate() |
152 | 109 | nova = os_client_config.session_client('compute', cloud='envvars') | 109 | nova = os_client_config.session_client('compute', cloud='envvars') |
153 | 110 | nagios_plugin.try_check(check_nova_services, args, nova) | 110 | nagios_plugin.try_check(check_nova_services, args, nova) |
154 | diff --git a/layer.yaml b/layer.yaml | |||
155 | index f364d90..72f0af5 100644 | |||
156 | --- a/layer.yaml | |||
157 | +++ b/layer.yaml | |||
158 | @@ -1,6 +1,16 @@ | |||
159 | 1 | includes: | 1 | includes: |
160 | 2 | - 'layer:apt' | ||
161 | 2 | - 'layer:basic' | 3 | - 'layer:basic' |
162 | 3 | - 'interface:nrpe-external-master' | ||
163 | 4 | - 'interface:keystone-credentials' | 4 | - 'interface:keystone-credentials' |
164 | 5 | - 'interface:nrpe-external-master' | ||
165 | 5 | ignore: ['.*.swp' ] | 6 | ignore: ['.*.swp' ] |
166 | 6 | repo: 'lp:~canonical-bootstack/+git/charm-openstack-service-checks' | 7 | repo: 'lp:~canonical-bootstack/+git/charm-openstack-service-checks' |
167 | 8 | options: | ||
168 | 9 | basic: | ||
169 | 10 | use_venv: true | ||
170 | 11 | apt: | ||
171 | 12 | packages: | ||
172 | 13 | - nagios-nrpe-server | ||
173 | 14 | - python3-keystoneclient | ||
174 | 15 | - python3-openstackclient | ||
175 | 16 | - python-openstackclient | ||
176 | diff --git a/reactive/service_checks.py b/reactive/service_checks.py | |||
177 | index 8341324..02a82d3 100644 | |||
178 | --- a/reactive/service_checks.py | |||
179 | +++ b/reactive/service_checks.py | |||
180 | @@ -17,112 +17,117 @@ from charmhelpers.core import ( | |||
181 | 17 | unitdata, | 17 | unitdata, |
182 | 18 | ) | 18 | ) |
183 | 19 | 19 | ||
184 | 20 | from charmhelpers.fetch import ( | ||
185 | 21 | apt_install, | ||
186 | 22 | apt_update, | ||
187 | 23 | ) | ||
188 | 24 | |||
189 | 25 | from charmhelpers.contrib.charmsupport.nrpe import NRPE | 20 | from charmhelpers.contrib.charmsupport.nrpe import NRPE |
190 | 21 | from urllib.parse import urlparse | ||
191 | 26 | 22 | ||
192 | 27 | config = hookenv.config() | 23 | config = hookenv.config() |
194 | 28 | install_packages = ['nagios-nrpe-server', 'python-openstackclient'] | 24 | NOVARC = '/var/lib/nagios/nagios.novarc' |
195 | 25 | PLUGINS_DIR = '/usr/local/lib/nagios/plugins/' | ||
196 | 29 | 26 | ||
197 | 30 | 27 | ||
198 | 31 | @when_not('os-service-checks.installed') | 28 | @when_not('os-service-checks.installed') |
199 | 32 | def install_service_checks(): | 29 | def install_service_checks(): |
203 | 33 | hookenv.status_set('maintenance', 'Installing software') | 30 | # Apt package installs are handled by the apt layer |
201 | 34 | apt_update() | ||
202 | 35 | apt_install(install_packages) | ||
204 | 36 | set_state('os-service-checks.installed') | 31 | set_state('os-service-checks.installed') |
205 | 37 | remove_state('os-service-checks.configured') | 32 | remove_state('os-service-checks.configured') |
206 | 38 | hookenv.status_set('active', 'Ready') | 33 | hookenv.status_set('active', 'Ready') |
207 | 39 | # setup openstack user | ||
208 | 40 | 34 | ||
209 | 41 | 35 | ||
210 | 42 | @when('identity-credentials.connected') | 36 | @when('identity-credentials.connected') |
212 | 43 | def configure_keystone_username(keystone): | 37 | def configure_ident_username(keystone): |
213 | 44 | username = 'nagios' | 38 | username = 'nagios' |
214 | 45 | keystone.request_credentials(username) | 39 | keystone.request_credentials(username) |
215 | 46 | 40 | ||
216 | 47 | 41 | ||
217 | 48 | @when('identity-credentials.available') | 42 | @when('identity-credentials.available') |
218 | 49 | def save_creds(keystone): | 43 | def save_creds(keystone): |
226 | 50 | creds = get_creds(keystone) | 44 | """ |
227 | 51 | unitdata.kv().set('keystone-relation-creds', creds) | 45 | Collect and save credentials from Keystone relation. |
228 | 52 | remove_state('os-service-checks.configured') | 46 | |
229 | 53 | 47 | Get credentials from the Keystone relation, | |
230 | 54 | 48 | reformat them into something the Keystone client | |
231 | 55 | def get_creds(keystone): | 49 | can use, and save them into the unitdata. |
232 | 56 | 50 | """ | |
233 | 51 | creds = { | ||
234 | 52 | 'username': keystone.credentials_username(), | ||
235 | 53 | 'password': keystone.credentials_password(), | ||
236 | 54 | 'region': keystone.region(), | ||
237 | 55 | } | ||
238 | 57 | if keystone.api_version() == '3': | 56 | if keystone.api_version() == '3': |
239 | 58 | api_url = "v3" | 57 | api_url = "v3" |
240 | 59 | try: | 58 | try: |
241 | 60 | domain = keystone.domain() | 59 | domain = keystone.domain() |
242 | 61 | except AttributeError: | 60 | except AttributeError: |
243 | 62 | domain = 'service_domain' | 61 | domain = 'service_domain' |
248 | 63 | creds = { | 62 | # keystone relation sends info back with funny names, fix here |
249 | 64 | 'credentials_username': keystone.credentials_username(), | 63 | creds.update({ |
250 | 65 | 'credentials_password': keystone.credentials_password(), | 64 | 'project_name': keystone.credentials_project(), |
247 | 66 | 'credentials_project': keystone.credentials_project(), | ||
251 | 67 | 'auth_version': '3', | 65 | 'auth_version': '3', |
256 | 68 | 'region': keystone.region(), | 66 | 'user_domain_name': domain, |
257 | 69 | 'credentials_user_domain': domain, | 67 | 'project_domain_name': domain |
258 | 70 | 'credentials_project_domain': domain | 68 | }) |
255 | 71 | } | ||
259 | 72 | else: | 69 | else: |
260 | 73 | api_url = "v2.0" | 70 | api_url = "v2.0" |
267 | 74 | creds = { | 71 | creds['tenant_name'] = keystone.credentials_project() |
262 | 75 | 'credentials_username': keystone.credentials_username(), | ||
263 | 76 | 'credentials_password': keystone.credentials_password(), | ||
264 | 77 | 'credentials_project': keystone.credentials_project(), | ||
265 | 78 | 'region': keystone.region(), | ||
266 | 79 | } | ||
268 | 80 | 72 | ||
269 | 81 | auth_url = "%s://%s:%s/%s" % (keystone.auth_protocol(), | 73 | auth_url = "%s://%s:%s/%s" % (keystone.auth_protocol(), |
270 | 82 | keystone.auth_host(), keystone.auth_port(), | 74 | keystone.auth_host(), keystone.auth_port(), |
271 | 83 | api_url) | 75 | api_url) |
272 | 84 | creds['auth_url'] = auth_url | 76 | creds['auth_url'] = auth_url |
274 | 85 | return creds | 77 | unitdata.kv().set('keystonecreds', creds) |
275 | 78 | remove_state('os-service-checks.configured') | ||
276 | 86 | 79 | ||
277 | 87 | 80 | ||
279 | 88 | # allow user to override credentials (and the need to be related to keystone) | 81 | # allow user to override credentials (and the need to be related to Keystone) |
280 | 89 | # with 'os-credentials' | 82 | # with 'os-credentials' |
281 | 90 | def get_credentials(): | 83 | def get_credentials(): |
291 | 91 | keystone_creds = config_flags_parser(config.get('os-credentials')) | 84 | """ |
292 | 92 | if keystone_creds: | 85 | Get credential info from either config or relation data. |
293 | 93 | if '/v3' in keystone_creds['auth_url']: | 86 | |
294 | 94 | creds = { | 87 | If config 'os-credentials' is set, return that info otherwise look for for a keystonecreds relation data. |
295 | 95 | 'credentials_username': keystone_creds['username'], | 88 | |
296 | 96 | 'credentials_password': keystone_creds['password'], | 89 | :return: dictionary of credential information for Keystone. |
297 | 97 | 'credentials_project': keystone_creds['credentials_project'], | 90 | """ |
298 | 98 | 'region': keystone_creds['region_name'], | 91 | ident_creds = config_flags_parser(config.get('os-credentials')) |
299 | 99 | 'auth_url': keystone_creds['auth_url'], | 92 | if ident_creds: |
300 | 93 | creds = { | ||
301 | 94 | 'username': ident_creds['username'], | ||
302 | 95 | 'password': ident_creds['password'], | ||
303 | 96 | 'region': ident_creds['region_name'], | ||
304 | 97 | 'auth_url': ident_creds['auth_url'], | ||
305 | 98 | } | ||
306 | 99 | if '/v3' in ident_creds['auth_url']: | ||
307 | 100 | creds.update({ | ||
308 | 101 | 'project_name': ident_creds['credentials_project'], | ||
309 | 100 | 'auth_version': '3', | 102 | 'auth_version': '3', |
313 | 101 | 'credentials_user_domain': keystone_creds['domain'], | 103 | 'user_domain_name': ident_creds['domain'], |
314 | 102 | 'credentials_project_domain': keystone_creds['domain'], | 104 | 'project_domain_name': ident_creds['domain'], |
315 | 103 | } | 105 | }) |
316 | 104 | else: | 106 | else: |
324 | 105 | creds = { | 107 | creds['tenant_name'] = ident_creds['credentials_project'] |
318 | 106 | 'credentials_username': keystone_creds['username'], | ||
319 | 107 | 'credentials_password': keystone_creds['password'], | ||
320 | 108 | 'credentials_project': keystone_creds['credentials_project'], | ||
321 | 109 | 'region': keystone_creds['region_name'], | ||
322 | 110 | 'auth_url': keystone_creds['auth_url'], | ||
323 | 111 | } | ||
325 | 112 | else: | 108 | else: |
326 | 113 | kv = unitdata.kv() | 109 | kv = unitdata.kv() |
328 | 114 | creds = kv.get('keystone-relation-creds') | 110 | creds = kv.get('keystonecreds') |
329 | 111 | old_creds = kv.get('keystone-relation-creds') | ||
330 | 112 | if old_creds and not creds: | ||
331 | 113 | # This set of creds needs an update to a newer format | ||
332 | 114 | creds['username'] = old_creds.pop('credentials_username') | ||
333 | 115 | creds['password'] = old_creds.pop('credentials_password') | ||
334 | 116 | creds['project_name'] = old_creds.pop('credentials_project') | ||
335 | 117 | creds['tenant_name'] = old_creds['project_name'] | ||
336 | 118 | creds['user_domain_name'] = old_creds.pop('credentials_user_domain', None) | ||
337 | 119 | creds['project_domain_name'] = old_creds.pop('credentials_project_domain', None) | ||
338 | 120 | kv.set('keystonecreds', creds) | ||
339 | 121 | kv.update(creds, 'keystonecreds') | ||
340 | 115 | return creds | 122 | return creds |
341 | 116 | 123 | ||
342 | 117 | 124 | ||
343 | 118 | def render_checks(): | 125 | def render_checks(): |
344 | 119 | nrpe = NRPE() | 126 | nrpe = NRPE() |
348 | 120 | plugins_dir = '/usr/local/lib/nagios/plugins/' | 127 | if not os.path.exists(PLUGINS_DIR): |
349 | 121 | if not os.path.exists(plugins_dir): | 128 | os.makedirs(PLUGINS_DIR) |
347 | 122 | os.makedirs(plugins_dir) | ||
350 | 123 | charm_file_dir = os.path.join(hookenv.charm_dir(), 'files') | 129 | charm_file_dir = os.path.join(hookenv.charm_dir(), 'files') |
351 | 124 | charm_plugin_dir = os.path.join(charm_file_dir, 'plugins') | 130 | charm_plugin_dir = os.path.join(charm_file_dir, 'plugins') |
352 | 125 | |||
353 | 126 | host.rsync( | 131 | host.rsync( |
354 | 127 | charm_plugin_dir, | 132 | charm_plugin_dir, |
355 | 128 | '/usr/local/lib/nagios/', | 133 | '/usr/local/lib/nagios/', |
356 | @@ -133,9 +138,8 @@ def render_checks(): | |||
357 | 133 | crit = config.get("nova_crit") | 138 | crit = config.get("nova_crit") |
358 | 134 | skip_disabled = config.get("skip-disabled") | 139 | skip_disabled = config.get("skip-disabled") |
359 | 135 | check_dns = config.get("check-dns") | 140 | check_dns = config.get("check-dns") |
363 | 136 | 141 | nova_check_command = os.path.join(PLUGINS_DIR, 'check_nova_services.py') | |
364 | 137 | check_command = plugins_dir + 'check_nova_services.py --warn ' \ | 142 | check_command = '{} --warn {} --crit {}'.format(nova_check_command, warn, crit) |
362 | 138 | + str(warn) + ' --crit ' + str(crit) | ||
365 | 139 | 143 | ||
366 | 140 | if skip_disabled: | 144 | if skip_disabled: |
367 | 141 | check_command = check_command + ' --skip-disabled' | 145 | check_command = check_command + ' --skip-disabled' |
368 | @@ -146,15 +150,18 @@ def render_checks(): | |||
369 | 146 | 150 | ||
370 | 147 | nrpe.add_check(shortname='neutron_agents', | 151 | nrpe.add_check(shortname='neutron_agents', |
371 | 148 | description='Check that enabled Neutron agents are up', | 152 | description='Check that enabled Neutron agents are up', |
373 | 149 | check_cmd=plugins_dir + 'check_neutron_agents.sh') | 153 | check_cmd=os.path.join(PLUGINS_DIR, 'check_neutron_agents.sh')) |
374 | 150 | 154 | ||
375 | 151 | if len(check_dns): | 155 | if len(check_dns): |
376 | 152 | nrpe.add_check(shortname='dns_multi', | 156 | nrpe.add_check(shortname='dns_multi', |
377 | 153 | description='Check DNS names are resolvable', | 157 | description='Check DNS names are resolvable', |
379 | 154 | check_cmd=plugins_dir + 'check_dns_multi.sh ' + ' '.join(check_dns.split())) | 158 | check_cmd=PLUGINS_DIR + 'check_dns_multi.sh ' + ' '.join(check_dns.split())) |
380 | 155 | else: | 159 | else: |
381 | 156 | nrpe.remove_check(shortname='dns_multi') | 160 | nrpe.remove_check(shortname='dns_multi') |
382 | 157 | 161 | ||
383 | 162 | endpoint_checks = create_endpoint_checks() | ||
384 | 163 | for check in endpoint_checks: | ||
385 | 164 | nrpe.add_check(**check) | ||
386 | 158 | nrpe.write() | 165 | nrpe.write() |
387 | 159 | 166 | ||
388 | 160 | 167 | ||
389 | @@ -171,8 +178,8 @@ def render_config(): | |||
390 | 171 | hookenv.log('render_config: No credentials yet, skipping') | 178 | hookenv.log('render_config: No credentials yet, skipping') |
391 | 172 | return | 179 | return |
392 | 173 | hookenv.log('render_config: Got credentials for username={}'.format( | 180 | hookenv.log('render_config: Got credentials for username={}'.format( |
395 | 174 | creds['credentials_username'])) | 181 | creds['username'])) |
396 | 175 | render('nagios.novarc', '/var/lib/nagios/nagios.novarc', creds, | 182 | render('nagios.novarc', NOVARC, creds, |
397 | 176 | owner='nagios', group='nagios') | 183 | owner='nagios', group='nagios') |
398 | 177 | render_checks() | 184 | render_checks() |
399 | 178 | if config.get('trusted_ssl_ca', None): | 185 | if config.get('trusted_ssl_ca', None): |
400 | @@ -198,3 +205,104 @@ def fix_ssl(): | |||
401 | 198 | with open(cert_file, 'w') as f: | 205 | with open(cert_file, 'w') as f: |
402 | 199 | print(cert_content, file=f) | 206 | print(cert_content, file=f) |
403 | 200 | subprocess.call(["/usr/sbin/update-ca-certificates"]) | 207 | subprocess.call(["/usr/sbin/update-ca-certificates"]) |
404 | 208 | |||
405 | 209 | |||
406 | 210 | def create_endpoint_checks(): | ||
407 | 211 | """ | ||
408 | 212 | Create an NRPE check for each Keystone catalog endpoint. | ||
409 | 213 | |||
410 | 214 | Read the Keystone catalog, and create a check for each endpoint listed. | ||
411 | 215 | If there is a healthcheck endpoint for the API, use that URL, otherwise check | ||
412 | 216 | the url '/'. | ||
413 | 217 | If SSL, add a check for the cert. | ||
414 | 218 | """ | ||
415 | 219 | # provide URLs that can be used for healthcheck for some services | ||
416 | 220 | # This also provides a nasty hack-ish way to add switches if we need | ||
417 | 221 | # for some services. | ||
418 | 222 | health_check_params = { | ||
419 | 223 | 'keystone': '/healthcheck', | ||
420 | 224 | 's3': '/healthcheck', | ||
421 | 225 | 'swift': '/healthcheck', | ||
422 | 226 | 'aodh': '/ -e Unauthorized -d x-openstack-request-id', | ||
423 | 227 | 'cinderv3': '/v3 -e Unauthorized -d x-openstack-request-id', | ||
424 | 228 | 'cinderv2': '/v2 -e Unauthorized -d x-openstack-request-id', | ||
425 | 229 | 'cinderv1': '/v1 -e Unauthorized -d x-openstack-request-id', | ||
426 | 230 | 'glance': '/healthcheck', | ||
427 | 231 | 'nova': '/healthcheck', | ||
428 | 232 | } | ||
429 | 233 | |||
430 | 234 | creds = get_credentials() | ||
431 | 235 | keystone_client = get_keystone_client(creds) | ||
432 | 236 | endpoints = keystone_client.endpoints.list() | ||
433 | 237 | services = [x for x in keystone_client.services.list() if x.enabled] | ||
434 | 238 | nrpe_checks = [] | ||
435 | 239 | for endpoint in endpoints: | ||
436 | 240 | endpoint.service_names = [x.name for x in services if x.id == endpoint.service_id] | ||
437 | 241 | service_name = endpoint.service_names[0] | ||
438 | 242 | endpoint.healthcheck_url = health_check_params.get(service_name, '/') | ||
439 | 243 | if config.get('check_{}_urls'.format(endpoint.interface)): | ||
440 | 244 | cmd_params = ['/usr/lib/nagios/plugins/check_http '] | ||
441 | 245 | check_url = urlparse(endpoint.url) | ||
442 | 246 | host_port = check_url.netloc.split(':') | ||
443 | 247 | cmd_params.append('-H {} -p {}'.format(host_port[0], host_port[1])) | ||
444 | 248 | cmd_params.append('-u {}'.format(endpoint.healthcheck_url)) | ||
445 | 249 | # if this is https, we want to add a check for cert expiry | ||
446 | 250 | # also need to tell check_http use use TLS | ||
447 | 251 | if check_url.scheme == 'https': | ||
448 | 252 | cmd_params.append('-S') | ||
449 | 253 | # Add an extra check for TLS cert expiry | ||
450 | 254 | cert_check = ['-C {},{}'.format( | ||
451 | 255 | config.get('tls_warn_days'), | ||
452 | 256 | config.get('tls_crit_days'))] | ||
453 | 257 | nrpe_checks.append({ | ||
454 | 258 | 'shortname': "{}_{}_cert".format( | ||
455 | 259 | service_name, | ||
456 | 260 | endpoint.interface), | ||
457 | 261 | 'description': 'Certificate expiry check for {} {}'.format( | ||
458 | 262 | service_name, | ||
459 | 263 | endpoint.interface), | ||
460 | 264 | 'check_cmd': ' '.join(cmd_params + cert_check) | ||
461 | 265 | }) | ||
462 | 266 | # Add the actual health check for the URL | ||
463 | 267 | nrpe_checks.append({ | ||
464 | 268 | 'shortname': "{}_{}".format( | ||
465 | 269 | service_name, | ||
466 | 270 | endpoint.interface), | ||
467 | 271 | 'description': 'Endpoint url check for {} {}'.format( | ||
468 | 272 | service_name, | ||
469 | 273 | endpoint.interface), | ||
470 | 274 | 'check_cmd': (' '.join(cmd_params))}) | ||
471 | 275 | return nrpe_checks | ||
472 | 276 | |||
473 | 277 | |||
474 | 278 | def get_keystone_client(creds): | ||
475 | 279 | """ | ||
476 | 280 | Import the appropriate Keystone client depending on API version. | ||
477 | 281 | |||
478 | 282 | Use credential info to determine the Keystone API version, and make a client session object that is to be | ||
479 | 283 | used for authenticated communication with Keystone. | ||
480 | 284 | |||
481 | 285 | :returns: a keystoneclient Client object | ||
482 | 286 | """ | ||
483 | 287 | from keystoneclient import session | ||
484 | 288 | if int(creds['auth_version']) >= 3: | ||
485 | 289 | from keystoneclient.v3 import client | ||
486 | 290 | from keystoneclient.auth.identity import v3 | ||
487 | 291 | auth_fields = ['auth_url', 'password', 'username', 'user_domain_name', | ||
488 | 292 | 'project_domain_name', 'project_name'] | ||
489 | 293 | auth_creds = {} | ||
490 | 294 | for key in auth_fields: | ||
491 | 295 | auth_creds[key] = creds[key] | ||
492 | 296 | auth = v3.Password(**auth_creds) | ||
493 | 297 | |||
494 | 298 | else: | ||
495 | 299 | from keystoneclient.v2_0 import client | ||
496 | 300 | from keystoneclient.auth.identity import v2 | ||
497 | 301 | auth_fields = ['auth_url', 'password', 'username', 'tenant_name'] | ||
498 | 302 | auth_creds = {} | ||
499 | 303 | for key in auth_fields: | ||
500 | 304 | auth_creds[key] = creds[key] | ||
501 | 305 | auth = v2.Password(**auth_creds) | ||
502 | 306 | |||
503 | 307 | sess = session.Session(auth=auth) | ||
504 | 308 | return client.Client(session=sess) | ||
505 | diff --git a/templates/nagios.novarc b/templates/nagios.novarc | |||
506 | index e4c7e68..27f1c2a 100644 | |||
507 | --- a/templates/nagios.novarc | |||
508 | +++ b/templates/nagios.novarc | |||
509 | @@ -1,7 +1,7 @@ | |||
514 | 1 | export OS_USERNAME={{ credentials_username }} | 1 | export OS_USERNAME={{ username }} |
515 | 2 | export OS_TENANT_NAME={{ credentials_project }} | 2 | export OS_TENANT_NAME={{ tenant_name }} |
516 | 3 | export OS_PROJECT_NAME={{ credentials_project }} | 3 | export OS_PROJECT_NAME={{ project_name }} |
517 | 4 | export OS_PASSWORD={{ credentials_password }} | 4 | export OS_PASSWORD={{ password }} |
518 | 5 | export OS_REGION_NAME={{ region }} | 5 | export OS_REGION_NAME={{ region }} |
519 | 6 | export OS_AUTH_URL={{ auth_url }} | 6 | export OS_AUTH_URL={{ auth_url }} |
520 | 7 | {%- if cacert %} | 7 | {%- if cacert %} |
521 | @@ -13,6 +13,6 @@ export HOME=${SNAP_COMMON} | |||
522 | 13 | {%- if auth_version %} | 13 | {%- if auth_version %} |
523 | 14 | export OS_IDENTITY_API_VERSION={{ auth_version }} | 14 | export OS_IDENTITY_API_VERSION={{ auth_version }} |
524 | 15 | export OS_AUTH_VERSION={{ auth_version }} | 15 | export OS_AUTH_VERSION={{ auth_version }} |
527 | 16 | export OS_USER_DOMAIN_NAME={{ credentials_user_domain }} | 16 | export OS_USER_DOMAIN_NAME={{ user_domain_name }} |
528 | 17 | export OS_PROJECT_DOMAIN_NAME={{ credentials_project_domain }} | 17 | export OS_PROJECT_DOMAIN_NAME={{ project_domain_name }} |
529 | 18 | {%- endif %} | 18 | {%- endif %} |
530 | diff --git a/test-requirements.txt b/test-requirements.txt | |||
531 | 19 | new file mode 100644 | 19 | new file mode 100644 |
532 | index 0000000..77ec3f5 | |||
533 | --- /dev/null | |||
534 | +++ b/test-requirements.txt | |||
535 | @@ -0,0 +1,9 @@ | |||
536 | 1 | # Lint and unit test requirements | ||
537 | 2 | flake8>=2.2.4,<=2.4.1 | ||
538 | 3 | os-testr>=0.4.1 | ||
539 | 4 | requests>=2.18.4 | ||
540 | 5 | charms.reactive | ||
541 | 6 | mock>=1.2 | ||
542 | 7 | nose>=1.3.7 | ||
543 | 8 | coverage>=3.6 | ||
544 | 9 | netifaces | ||
545 | diff --git a/tox.ini b/tox.ini | |||
546 | index 0b8b27a..f4560b5 100644 | |||
547 | --- a/tox.ini | |||
548 | +++ b/tox.ini | |||
549 | @@ -1,12 +1,40 @@ | |||
550 | 1 | [tox] | 1 | [tox] |
551 | 2 | skipsdist=True | 2 | skipsdist=True |
553 | 3 | envlist = py34, py35 | 3 | envlist = pep8 |
554 | 4 | skip_missing_interpreters = True | 4 | skip_missing_interpreters = True |
555 | 5 | 5 | ||
556 | 6 | [testenv] | 6 | [testenv] |
558 | 7 | commands = py.test -v | 7 | setenv = VIRTUAL_ENV={envdir} |
559 | 8 | PYTHONHASHSEED=0 | ||
560 | 9 | TERM=linux | ||
561 | 10 | LAYER_PATH={toxinidir}/layers | ||
562 | 11 | INTERFACE_PATH={toxinidir}/interfaces | ||
563 | 12 | JUJU_REPOSITORY={toxinidir}/build | ||
564 | 13 | passenv = http_proxy https_proxy | ||
565 | 14 | install_command = | ||
566 | 15 | pip install {opts} {packages} | ||
567 | 8 | deps = | 16 | deps = |
568 | 9 | -r{toxinidir}/requirements.txt | 17 | -r{toxinidir}/requirements.txt |
569 | 10 | 18 | ||
570 | 19 | [testenv:build] | ||
571 | 20 | basepython = python3 | ||
572 | 21 | commands = | ||
573 | 22 | charm-build --log-level DEBUG -o {toxinidir}/build src {posargs} | ||
574 | 23 | |||
575 | 24 | [testenv:py3] | ||
576 | 25 | basepython = python3 | ||
577 | 26 | deps = -r{toxinidir}/test-requirements.txt | ||
578 | 27 | commands = ostestr {posargs} | ||
579 | 28 | |||
580 | 29 | [testenv:pep8] | ||
581 | 30 | basepython = python3 | ||
582 | 31 | deps = -r{toxinidir}/test-requirements.txt | ||
583 | 32 | commands = flake8 {posargs} --max-complexity=10 --max-line-length=120 reactive files unit_tests | ||
584 | 33 | |||
585 | 34 | [testenv:venv] | ||
586 | 35 | basepython = python3 | ||
587 | 36 | commands = {posargs} | ||
588 | 37 | |||
589 | 11 | [flake8] | 38 | [flake8] |
591 | 12 | exclude=docs | 39 | # E402 ignore necessary for path append before sys module import in actions |
592 | 40 | ignore = E402 |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.