Merge ~xavpaice/charm-openstack-service-checks/+git/charm-openstack-service-checks:ssl-nagios-checks into ~canonical-bootstack/charm-openstack-service-checks:master

Proposed by Xav Paice
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)
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://code.launchpad.net/~xavpaice/bootstack-charm-specs/+git/bootstack-charm-specs/+ref/ssl-nagios-checks

The intent is to add an NRPE check to the openstack-services-checks unit, checking each endpoint found in the Keystone catalog using a healthcheck URL if known, otherwise the root URL found in the catalog. If the service is using TLS, then the expiry date of the certificate is also checked.

To post a comment you must log in.
Revision history for this message
🤖 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.

Revision history for this message
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.

review: Needs Fixing
Revision history for this message
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.

Revision history for this message
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/standards review comments inline (mostly consistency related).

Revision history for this message
Andrea Ieri (aieri) wrote : Posted in a previous version of this proposal

A bunch of inline comments :)

review: Needs Fixing
Revision history for this message
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.

Revision history for this message
Joel Sing (jsing) : Posted in a previous version of this proposal
Revision history for this message
Joel Sing (jsing) wrote : Posted in a previous version of this proposal

LGTM for a readability/standards review - couple of minor comments inline.

Please ensure you get a peer approval before merging.

review: Approve (+1)
Revision history for this message
🤖 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.

Revision history for this message
Xav Paice (xavpaice) wrote : Posted in a previous version of this proposal

Review nits have been addressed, would appreciate review.

Revision history for this message
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.

review: Approve (+1)
Revision history for this message
Joel Sing (jsing) wrote : Posted in a previous version of this proposal

FWIW it also seems that peer review/approval is still needed.

Revision history for this message
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.

Revision history for this message
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

review: Needs Fixing
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
Joel Sing (jsing) wrote :

Reapproving without further review.

review: Approve (+1)
Revision history for this message
Peter Sabaini (peter-sabaini) wrote :

lgtm, cheers

review: Approve
Revision history for this message
Xav Paice (xavpaice) wrote :

got +1's from two folks, marking approved. Thanks!

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Change successfully merged at revision 7895a97e4ee052c44971576a691e4ce23d6969f2

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/.gitignore b/.gitignore
0new file mode 1006440new file mode 100644
index 0000000..dccc0dc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
1*~
2/bin
3debian/files
4/pkg
5*.pyc
6__pycache__
7*.swp
8.tox
9.venv
10.idea
diff --git a/Makefile b/Makefile
index a1ad3a5..27eeff6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
1#!/usr/bin/make1#!/usr/bin/make
22
3all: lint unit_test3all: lint test
44
55
6.PHONY: clean6.PHONY: clean
@@ -10,15 +10,17 @@ clean:
10.PHONY: apt_prereqs10.PHONY: apt_prereqs
11apt_prereqs:11apt_prereqs:
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)
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)
1414
15.PHONY: lint15.PHONY: lint
16lint: apt_prereqs16lint: apt_prereqs
17 @tox --notest17 @tox -e pep8
18 @PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests)
19 @charm proof18 @charm proof
2019
21.PHONY: unit_test20.PHONY: test
22unit_test: apt_prereqs21unit_test: apt_prereqs
23 @echo Starting tests...22 @echo Starting tests...
24 tox23 tox
24
25build:
26 charm build
diff --git a/README.md b/README.md
index 24a4b54..b3159af 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
22
3This charm provides OpenStack services checks for Nagios3This charm provides OpenStack services checks for Nagios
44
5 # Build5# Build
6The fully built charm needs the following source branch6The fully built charm needs the following source branch
7* https://git.launchpad.net/~canonical-bootstack/bootstack-ops/+git/charm-openstack-services-checks7* https://git.launchpad.net/~canonical-bootstack/bootstack-ops/+git/charm-openstack-services-checks
88
@@ -29,16 +29,20 @@ Build the charm, and symlink for juju-1 compatibility
2929
30 juju deploy local:xenial/openstack-services-checks30 juju deploy local:xenial/openstack-services-checks
3131
32This charm supports relating to keystone, but keystone-credentials interface32This charm supports relating to keystone via the keystone-credentials
33seems to be flaky, and hard to remove-relation, so the charm works around this33interface. If you do not wish to use this, you can supply your own credential
34by adding 'os-credentials' setting (see setting description hints)34set for Openstack by adding 'os-credentials' setting (see setting description
35hints)
3536
36 juju set openstack-services-checks os-credentials=" ... "37 juju set openstack-services-checks os-credentials=" ... "
37
38 juju add-relation openstack-services-checks nagios38 juju add-relation openstack-services-checks nagios
3939
40With Keystone40With Keystone
4141
42 juju add-relation openstack-services-checks:identity-credentials keystone:identity-credentials42 juju add-relation openstack-services-checks:identity-credentials keystone:identity-credentials
4343
44If your OpenStack API endpoints have a common URL for the Admin, Public and
45Internal addresses, you should consider disabling some endpoints which would be
46duplicated otherwise, e.g.
4447
48 juju config openstack-service-checks check_internal_urls=False check_admin_urls=False
diff --git a/config.yaml b/config.yaml
index e4e8783..b71208c 100644
--- a/config.yaml
+++ b/config.yaml
@@ -48,3 +48,28 @@ options:
48 default: false48 default: false
49 description: |49 description: |
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.
51 tls_warn_days:
52 type: int
53 default: 30
54 description: |
55 Number of days left for the TLS certificate to expire before warning.
56 tls_crit_days:
57 type: int
58 default: 14
59 description: |
60 Number of days left for the TLS certificate to expire before alerting Critical.
61 check_public_urls:
62 type: boolean
63 default: True
64 description: |
65 If true, create NRPE checks matching all 'public' URLs in the Keystone catalog.
66 check_internal_urls:
67 type: boolean
68 default: True
69 description: |
70 If true, create NRPE checks matching all 'internal' URLs in the Keystone catalog.
71 check_admin_urls:
72 type: boolean
73 default: True
74 description: |
75 If true, create NRPE checks matching all 'admin' URLs in the Keystone catalog.
diff --git a/files/plugins/check_nova_services.py b/files/plugins/check_nova_services.py
index 1279c6a..6498542 100755
--- a/files/plugins/check_nova_services.py
+++ b/files/plugins/check_nova_services.py
@@ -43,11 +43,11 @@ def check_hosts_up(args, aggregate, hosts, services_compute):
43 local_msg.append("Host Aggregate {} has {} hosts alive".format(43 local_msg.append("Host Aggregate {} has {} hosts alive".format(
44 aggregate, counts['ok']))44 aggregate, counts['ok']))
45 nova_status = {45 nova_status = {
46 'agg_name': aggregate,46 'agg_name': aggregate,
47 'msg_text': ", ".join(local_msg),47 'msg_text': ", ".join(local_msg),
48 'critical': status_crit,48 'critical': status_crit,
49 'warning': status_warn,49 'warning': status_warn,
50 }50 }
51 return nova_status51 return nova_status
5252
5353
@@ -103,8 +103,8 @@ if __name__ == '__main__':
103 command = ['/bin/bash', '-c', "source {} && env".format(args.env)]103 command = ['/bin/bash', '-c', "source {} && env".format(args.env)]
104 proc = subprocess.Popen(command, stdout=subprocess.PIPE)104 proc = subprocess.Popen(command, stdout=subprocess.PIPE)
105 for line in proc.stdout:105 for line in proc.stdout:
106 (key, _, value) = line.partition("=")106 (key, _, value) = line.partition(b'=')
107 os.environ[key] = value.rstrip()107 os.environ[key.decode('utf-8')] = value.rstrip().decode('utf-8')
108 proc.communicate()108 proc.communicate()
109 nova = os_client_config.session_client('compute', cloud='envvars')109 nova = os_client_config.session_client('compute', cloud='envvars')
110 nagios_plugin.try_check(check_nova_services, args, nova)110 nagios_plugin.try_check(check_nova_services, args, nova)
diff --git a/layer.yaml b/layer.yaml
index f364d90..72f0af5 100644
--- a/layer.yaml
+++ b/layer.yaml
@@ -1,6 +1,16 @@
1includes:1includes:
2 - 'layer:apt'
2 - 'layer:basic'3 - 'layer:basic'
3 - 'interface:nrpe-external-master'
4 - 'interface:keystone-credentials'4 - 'interface:keystone-credentials'
5 - 'interface:nrpe-external-master'
5ignore: ['.*.swp' ]6ignore: ['.*.swp' ]
6repo: 'lp:~canonical-bootstack/+git/charm-openstack-service-checks'7repo: 'lp:~canonical-bootstack/+git/charm-openstack-service-checks'
8options:
9 basic:
10 use_venv: true
11 apt:
12 packages:
13 - nagios-nrpe-server
14 - python3-keystoneclient
15 - python3-openstackclient
16 - python-openstackclient
diff --git a/reactive/service_checks.py b/reactive/service_checks.py
index 8341324..02a82d3 100644
--- a/reactive/service_checks.py
+++ b/reactive/service_checks.py
@@ -17,112 +17,117 @@ from charmhelpers.core import (
17 unitdata,17 unitdata,
18)18)
1919
20from charmhelpers.fetch import (
21 apt_install,
22 apt_update,
23)
24
25from charmhelpers.contrib.charmsupport.nrpe import NRPE20from charmhelpers.contrib.charmsupport.nrpe import NRPE
21from urllib.parse import urlparse
2622
27config = hookenv.config()23config = hookenv.config()
28install_packages = ['nagios-nrpe-server', 'python-openstackclient']24NOVARC = '/var/lib/nagios/nagios.novarc'
25PLUGINS_DIR = '/usr/local/lib/nagios/plugins/'
2926
3027
31@when_not('os-service-checks.installed')28@when_not('os-service-checks.installed')
32def install_service_checks():29def install_service_checks():
33 hookenv.status_set('maintenance', 'Installing software')30 # Apt package installs are handled by the apt layer
34 apt_update()
35 apt_install(install_packages)
36 set_state('os-service-checks.installed')31 set_state('os-service-checks.installed')
37 remove_state('os-service-checks.configured')32 remove_state('os-service-checks.configured')
38 hookenv.status_set('active', 'Ready')33 hookenv.status_set('active', 'Ready')
39# setup openstack user
4034
4135
42@when('identity-credentials.connected')36@when('identity-credentials.connected')
43def configure_keystone_username(keystone):37def configure_ident_username(keystone):
44 username = 'nagios'38 username = 'nagios'
45 keystone.request_credentials(username)39 keystone.request_credentials(username)
4640
4741
48@when('identity-credentials.available')42@when('identity-credentials.available')
49def save_creds(keystone):43def save_creds(keystone):
50 creds = get_creds(keystone)44 """
51 unitdata.kv().set('keystone-relation-creds', creds)45 Collect and save credentials from Keystone relation.
52 remove_state('os-service-checks.configured')46
5347 Get credentials from the Keystone relation,
5448 reformat them into something the Keystone client
55def get_creds(keystone):49 can use, and save them into the unitdata.
5650 """
51 creds = {
52 'username': keystone.credentials_username(),
53 'password': keystone.credentials_password(),
54 'region': keystone.region(),
55 }
57 if keystone.api_version() == '3':56 if keystone.api_version() == '3':
58 api_url = "v3"57 api_url = "v3"
59 try:58 try:
60 domain = keystone.domain()59 domain = keystone.domain()
61 except AttributeError:60 except AttributeError:
62 domain = 'service_domain'61 domain = 'service_domain'
63 creds = {62 # keystone relation sends info back with funny names, fix here
64 'credentials_username': keystone.credentials_username(),63 creds.update({
65 'credentials_password': keystone.credentials_password(),64 'project_name': keystone.credentials_project(),
66 'credentials_project': keystone.credentials_project(),
67 'auth_version': '3',65 'auth_version': '3',
68 'region': keystone.region(),66 'user_domain_name': domain,
69 'credentials_user_domain': domain,67 'project_domain_name': domain
70 'credentials_project_domain': domain68 })
71 }
72 else:69 else:
73 api_url = "v2.0"70 api_url = "v2.0"
74 creds = {71 creds['tenant_name'] = keystone.credentials_project()
75 'credentials_username': keystone.credentials_username(),
76 'credentials_password': keystone.credentials_password(),
77 'credentials_project': keystone.credentials_project(),
78 'region': keystone.region(),
79 }
8072
81 auth_url = "%s://%s:%s/%s" % (keystone.auth_protocol(),73 auth_url = "%s://%s:%s/%s" % (keystone.auth_protocol(),
82 keystone.auth_host(), keystone.auth_port(),74 keystone.auth_host(), keystone.auth_port(),
83 api_url)75 api_url)
84 creds['auth_url'] = auth_url76 creds['auth_url'] = auth_url
85 return creds77 unitdata.kv().set('keystonecreds', creds)
78 remove_state('os-service-checks.configured')
8679
8780
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)
89# with 'os-credentials'82# with 'os-credentials'
90def get_credentials():83def get_credentials():
91 keystone_creds = config_flags_parser(config.get('os-credentials'))84 """
92 if keystone_creds:85 Get credential info from either config or relation data.
93 if '/v3' in keystone_creds['auth_url']:86
94 creds = {87 If config 'os-credentials' is set, return that info otherwise look for for a keystonecreds relation data.
95 'credentials_username': keystone_creds['username'],88
96 'credentials_password': keystone_creds['password'],89 :return: dictionary of credential information for Keystone.
97 'credentials_project': keystone_creds['credentials_project'],90 """
98 'region': keystone_creds['region_name'],91 ident_creds = config_flags_parser(config.get('os-credentials'))
99 'auth_url': keystone_creds['auth_url'],92 if ident_creds:
93 creds = {
94 'username': ident_creds['username'],
95 'password': ident_creds['password'],
96 'region': ident_creds['region_name'],
97 'auth_url': ident_creds['auth_url'],
98 }
99 if '/v3' in ident_creds['auth_url']:
100 creds.update({
101 'project_name': ident_creds['credentials_project'],
100 'auth_version': '3',102 'auth_version': '3',
101 'credentials_user_domain': keystone_creds['domain'],103 'user_domain_name': ident_creds['domain'],
102 'credentials_project_domain': keystone_creds['domain'],104 'project_domain_name': ident_creds['domain'],
103 }105 })
104 else:106 else:
105 creds = {107 creds['tenant_name'] = ident_creds['credentials_project']
106 'credentials_username': keystone_creds['username'],
107 'credentials_password': keystone_creds['password'],
108 'credentials_project': keystone_creds['credentials_project'],
109 'region': keystone_creds['region_name'],
110 'auth_url': keystone_creds['auth_url'],
111 }
112 else:108 else:
113 kv = unitdata.kv()109 kv = unitdata.kv()
114 creds = kv.get('keystone-relation-creds')110 creds = kv.get('keystonecreds')
111 old_creds = kv.get('keystone-relation-creds')
112 if old_creds and not creds:
113 # This set of creds needs an update to a newer format
114 creds['username'] = old_creds.pop('credentials_username')
115 creds['password'] = old_creds.pop('credentials_password')
116 creds['project_name'] = old_creds.pop('credentials_project')
117 creds['tenant_name'] = old_creds['project_name']
118 creds['user_domain_name'] = old_creds.pop('credentials_user_domain', None)
119 creds['project_domain_name'] = old_creds.pop('credentials_project_domain', None)
120 kv.set('keystonecreds', creds)
121 kv.update(creds, 'keystonecreds')
115 return creds122 return creds
116123
117124
118def render_checks():125def render_checks():
119 nrpe = NRPE()126 nrpe = NRPE()
120 plugins_dir = '/usr/local/lib/nagios/plugins/'127 if not os.path.exists(PLUGINS_DIR):
121 if not os.path.exists(plugins_dir):128 os.makedirs(PLUGINS_DIR)
122 os.makedirs(plugins_dir)
123 charm_file_dir = os.path.join(hookenv.charm_dir(), 'files')129 charm_file_dir = os.path.join(hookenv.charm_dir(), 'files')
124 charm_plugin_dir = os.path.join(charm_file_dir, 'plugins')130 charm_plugin_dir = os.path.join(charm_file_dir, 'plugins')
125
126 host.rsync(131 host.rsync(
127 charm_plugin_dir,132 charm_plugin_dir,
128 '/usr/local/lib/nagios/',133 '/usr/local/lib/nagios/',
@@ -133,9 +138,8 @@ def render_checks():
133 crit = config.get("nova_crit")138 crit = config.get("nova_crit")
134 skip_disabled = config.get("skip-disabled")139 skip_disabled = config.get("skip-disabled")
135 check_dns = config.get("check-dns")140 check_dns = config.get("check-dns")
136141 nova_check_command = os.path.join(PLUGINS_DIR, 'check_nova_services.py')
137 check_command = plugins_dir + 'check_nova_services.py --warn ' \142 check_command = '{} --warn {} --crit {}'.format(nova_check_command, warn, crit)
138 + str(warn) + ' --crit ' + str(crit)
139143
140 if skip_disabled:144 if skip_disabled:
141 check_command = check_command + ' --skip-disabled'145 check_command = check_command + ' --skip-disabled'
@@ -146,15 +150,18 @@ def render_checks():
146150
147 nrpe.add_check(shortname='neutron_agents',151 nrpe.add_check(shortname='neutron_agents',
148 description='Check that enabled Neutron agents are up',152 description='Check that enabled Neutron agents are up',
149 check_cmd=plugins_dir + 'check_neutron_agents.sh')153 check_cmd=os.path.join(PLUGINS_DIR, 'check_neutron_agents.sh'))
150154
151 if len(check_dns):155 if len(check_dns):
152 nrpe.add_check(shortname='dns_multi',156 nrpe.add_check(shortname='dns_multi',
153 description='Check DNS names are resolvable',157 description='Check DNS names are resolvable',
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()))
155 else:159 else:
156 nrpe.remove_check(shortname='dns_multi')160 nrpe.remove_check(shortname='dns_multi')
157161
162 endpoint_checks = create_endpoint_checks()
163 for check in endpoint_checks:
164 nrpe.add_check(**check)
158 nrpe.write()165 nrpe.write()
159166
160167
@@ -171,8 +178,8 @@ def render_config():
171 hookenv.log('render_config: No credentials yet, skipping')178 hookenv.log('render_config: No credentials yet, skipping')
172 return179 return
173 hookenv.log('render_config: Got credentials for username={}'.format(180 hookenv.log('render_config: Got credentials for username={}'.format(
174 creds['credentials_username']))181 creds['username']))
175 render('nagios.novarc', '/var/lib/nagios/nagios.novarc', creds,182 render('nagios.novarc', NOVARC, creds,
176 owner='nagios', group='nagios')183 owner='nagios', group='nagios')
177 render_checks()184 render_checks()
178 if config.get('trusted_ssl_ca', None):185 if config.get('trusted_ssl_ca', None):
@@ -198,3 +205,104 @@ def fix_ssl():
198 with open(cert_file, 'w') as f:205 with open(cert_file, 'w') as f:
199 print(cert_content, file=f)206 print(cert_content, file=f)
200 subprocess.call(["/usr/sbin/update-ca-certificates"])207 subprocess.call(["/usr/sbin/update-ca-certificates"])
208
209
210def create_endpoint_checks():
211 """
212 Create an NRPE check for each Keystone catalog endpoint.
213
214 Read the Keystone catalog, and create a check for each endpoint listed.
215 If there is a healthcheck endpoint for the API, use that URL, otherwise check
216 the url '/'.
217 If SSL, add a check for the cert.
218 """
219 # provide URLs that can be used for healthcheck for some services
220 # This also provides a nasty hack-ish way to add switches if we need
221 # for some services.
222 health_check_params = {
223 'keystone': '/healthcheck',
224 's3': '/healthcheck',
225 'swift': '/healthcheck',
226 'aodh': '/ -e Unauthorized -d x-openstack-request-id',
227 'cinderv3': '/v3 -e Unauthorized -d x-openstack-request-id',
228 'cinderv2': '/v2 -e Unauthorized -d x-openstack-request-id',
229 'cinderv1': '/v1 -e Unauthorized -d x-openstack-request-id',
230 'glance': '/healthcheck',
231 'nova': '/healthcheck',
232 }
233
234 creds = get_credentials()
235 keystone_client = get_keystone_client(creds)
236 endpoints = keystone_client.endpoints.list()
237 services = [x for x in keystone_client.services.list() if x.enabled]
238 nrpe_checks = []
239 for endpoint in endpoints:
240 endpoint.service_names = [x.name for x in services if x.id == endpoint.service_id]
241 service_name = endpoint.service_names[0]
242 endpoint.healthcheck_url = health_check_params.get(service_name, '/')
243 if config.get('check_{}_urls'.format(endpoint.interface)):
244 cmd_params = ['/usr/lib/nagios/plugins/check_http ']
245 check_url = urlparse(endpoint.url)
246 host_port = check_url.netloc.split(':')
247 cmd_params.append('-H {} -p {}'.format(host_port[0], host_port[1]))
248 cmd_params.append('-u {}'.format(endpoint.healthcheck_url))
249 # if this is https, we want to add a check for cert expiry
250 # also need to tell check_http use use TLS
251 if check_url.scheme == 'https':
252 cmd_params.append('-S')
253 # Add an extra check for TLS cert expiry
254 cert_check = ['-C {},{}'.format(
255 config.get('tls_warn_days'),
256 config.get('tls_crit_days'))]
257 nrpe_checks.append({
258 'shortname': "{}_{}_cert".format(
259 service_name,
260 endpoint.interface),
261 'description': 'Certificate expiry check for {} {}'.format(
262 service_name,
263 endpoint.interface),
264 'check_cmd': ' '.join(cmd_params + cert_check)
265 })
266 # Add the actual health check for the URL
267 nrpe_checks.append({
268 'shortname': "{}_{}".format(
269 service_name,
270 endpoint.interface),
271 'description': 'Endpoint url check for {} {}'.format(
272 service_name,
273 endpoint.interface),
274 'check_cmd': (' '.join(cmd_params))})
275 return nrpe_checks
276
277
278def get_keystone_client(creds):
279 """
280 Import the appropriate Keystone client depending on API version.
281
282 Use credential info to determine the Keystone API version, and make a client session object that is to be
283 used for authenticated communication with Keystone.
284
285 :returns: a keystoneclient Client object
286 """
287 from keystoneclient import session
288 if int(creds['auth_version']) >= 3:
289 from keystoneclient.v3 import client
290 from keystoneclient.auth.identity import v3
291 auth_fields = ['auth_url', 'password', 'username', 'user_domain_name',
292 'project_domain_name', 'project_name']
293 auth_creds = {}
294 for key in auth_fields:
295 auth_creds[key] = creds[key]
296 auth = v3.Password(**auth_creds)
297
298 else:
299 from keystoneclient.v2_0 import client
300 from keystoneclient.auth.identity import v2
301 auth_fields = ['auth_url', 'password', 'username', 'tenant_name']
302 auth_creds = {}
303 for key in auth_fields:
304 auth_creds[key] = creds[key]
305 auth = v2.Password(**auth_creds)
306
307 sess = session.Session(auth=auth)
308 return client.Client(session=sess)
diff --git a/templates/nagios.novarc b/templates/nagios.novarc
index e4c7e68..27f1c2a 100644
--- a/templates/nagios.novarc
+++ b/templates/nagios.novarc
@@ -1,7 +1,7 @@
1export OS_USERNAME={{ credentials_username }}1export OS_USERNAME={{ username }}
2export OS_TENANT_NAME={{ credentials_project }}2export OS_TENANT_NAME={{ tenant_name }}
3export OS_PROJECT_NAME={{ credentials_project }}3export OS_PROJECT_NAME={{ project_name }}
4export OS_PASSWORD={{ credentials_password }}4export OS_PASSWORD={{ password }}
5export OS_REGION_NAME={{ region }}5export OS_REGION_NAME={{ region }}
6export OS_AUTH_URL={{ auth_url }}6export OS_AUTH_URL={{ auth_url }}
7{%- if cacert %}7{%- if cacert %}
@@ -13,6 +13,6 @@ export HOME=${SNAP_COMMON}
13{%- if auth_version %}13{%- if auth_version %}
14export OS_IDENTITY_API_VERSION={{ auth_version }}14export OS_IDENTITY_API_VERSION={{ auth_version }}
15export OS_AUTH_VERSION={{ auth_version }}15export OS_AUTH_VERSION={{ auth_version }}
16export OS_USER_DOMAIN_NAME={{ credentials_user_domain }}16export OS_USER_DOMAIN_NAME={{ user_domain_name }}
17export OS_PROJECT_DOMAIN_NAME={{ credentials_project_domain }}17export OS_PROJECT_DOMAIN_NAME={{ project_domain_name }}
18{%- endif %}18{%- endif %}
diff --git a/test-requirements.txt b/test-requirements.txt
19new file mode 10064419new file mode 100644
index 0000000..77ec3f5
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1,9 @@
1# Lint and unit test requirements
2flake8>=2.2.4,<=2.4.1
3os-testr>=0.4.1
4requests>=2.18.4
5charms.reactive
6mock>=1.2
7nose>=1.3.7
8coverage>=3.6
9netifaces
diff --git a/tox.ini b/tox.ini
index 0b8b27a..f4560b5 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,12 +1,40 @@
1[tox]1[tox]
2skipsdist=True2skipsdist=True
3envlist = py34, py353envlist = pep8
4skip_missing_interpreters = True4skip_missing_interpreters = True
55
6[testenv]6[testenv]
7commands = py.test -v7setenv = VIRTUAL_ENV={envdir}
8 PYTHONHASHSEED=0
9 TERM=linux
10 LAYER_PATH={toxinidir}/layers
11 INTERFACE_PATH={toxinidir}/interfaces
12 JUJU_REPOSITORY={toxinidir}/build
13passenv = http_proxy https_proxy
14install_command =
15 pip install {opts} {packages}
8deps =16deps =
9 -r{toxinidir}/requirements.txt17 -r{toxinidir}/requirements.txt
1018
19[testenv:build]
20basepython = python3
21commands =
22 charm-build --log-level DEBUG -o {toxinidir}/build src {posargs}
23
24[testenv:py3]
25basepython = python3
26deps = -r{toxinidir}/test-requirements.txt
27commands = ostestr {posargs}
28
29[testenv:pep8]
30basepython = python3
31deps = -r{toxinidir}/test-requirements.txt
32commands = flake8 {posargs} --max-complexity=10 --max-line-length=120 reactive files unit_tests
33
34[testenv:venv]
35basepython = python3
36commands = {posargs}
37
11[flake8]38[flake8]
12exclude=docs39# E402 ignore necessary for path append before sys module import in actions
40ignore = E402

Subscribers

People subscribed via source and target branches