Merge ~afreiberger/charm-openstack-service-checks/+git/charm-openstack-service-checks:lint-20.10 into ~llama-charmers/charm-openstack-service-checks:master

Proposed by Drew Freiberger
Status: Merged
Merged at revision: f241e9d107b90302f6c3c80bdd4977e6b5727b5f
Proposed branch: ~afreiberger/charm-openstack-service-checks/+git/charm-openstack-service-checks:lint-20.10
Merge into: ~llama-charmers/charm-openstack-service-checks:master
Prerequisite: ~afreiberger/charm-openstack-service-checks/+git/charm-openstack-service-checks:blacken-20.08
Diff against target: 1193 lines (+212/-109)
19 files modified
src/actions/actions.py (+8/-5)
src/files/plugins/check_cinder_services.py (+7/-4)
src/files/plugins/check_contrail_analytics_alarms.py (+8/-3)
src/files/plugins/check_nova_services.py (+13/-7)
src/files/plugins/check_octavia.py (+15/-12)
src/files/plugins/check_rally.py (+13/-11)
src/files/run_rally.py (+5/-2)
src/lib/lib_openstack_service_checks.py (+38/-10)
src/reactive/openstack_service_checks.py (+15/-7)
src/tests/functional/conftest.py (+15/-18)
src/tests/functional/test_deploy.py (+9/-4)
src/tests/nagios_plugin/setup.py (+4/-2)
src/tests/unit/conftest.py (+9/-1)
src/tests/unit/test_check_cinder_services.py (+14/-6)
src/tests/unit/test_check_contrail_analytics_alarms.py (+7/-0)
src/tests/unit/test_check_nova_services.py (+11/-4)
src/tests/unit/test_check_octavia.py (+9/-1)
src/tests/unit/test_lib.py (+8/-3)
src/tox.ini (+4/-9)
Reviewer Review Type Date Requested Status
Alvaro Uria (community) Approve
Review via email: mp+392257@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Drew Freiberger (afreiberger) wrote :

Someone besides me, please run full tests on this before final approval/merging. unit/lint passing, and I've run functional to death on my environment.

You'll note that I punted D102 and D103s because I didn't want to waste any more release rotation time on helper library and test library document strings more than I already have. There are two files that would trigger on those checks and they can be future TODOs.

Revision history for this message
Drew Freiberger (afreiberger) :
Revision history for this message
Alvaro Uria (aluria) wrote :

I'm running into a weird issue. The deploy_openstack func test fails because app.status for neutron-api returns "blocked". However, "juju status -m functest-<uuid> neutron-api --format yaml" returns an app status "active".

I've been trying to troubleshoot it without success. Juju version is 2.8.2. I'm going to try to upgrade to 2.8.5 and test again.

Revision history for this message
Alvaro Uria (aluria) wrote :

Upgrading the controller to 2.8.5 made the neutron-api unit go from waiting to active as it was expected. I'm running all tests now under the upgraded controller.

Revision history for this message
Alvaro Uria (aluria) wrote :

lgtm

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/actions/actions.py b/src/actions/actions.py
2index 1e679b6..f165aaa 100755
3--- a/src/actions/actions.py
4+++ b/src/actions/actions.py
5@@ -12,28 +12,30 @@
6 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7 # See the License for the specific language governing permissions and
8 # limitations under the License.
9+"""Define charm actions."""
10
11 import os
12 import sys
13+from traceback import format_exc
14+
15 import charmhelpers.core.hookenv as hookenv
16 import charmhelpers.core.unitdata as unitdata
17-from traceback import format_exc
18
19 # Load modules from $CHARM_DIR/lib
20 sys.path.append("lib")
21
22 import charms.reactive # NOQA: E402
23 from charms.layer import basic # NOQA: E402
24+from charms.reactive.flags import clear_flag # NOQA: E402
25
26 basic.bootstrap_charm_deps()
27 basic.init_config_states()
28
29-from charms.reactive.flags import clear_flag # NOQA: E402
30-
31
32 def refresh_endpoint_checks(*args):
33- """Clear the openstack-service-checks.endpoints.configured flag
34- so that next time update-status runs, the Keystone catalog is re-read
35+ """Clear the openstack-service-checks.endpoints.configured flag.
36+
37+ Ensures next time update-status runs, the Keystone catalog is re-read
38 and nrpe checks refreshed.
39 """
40 clear_flag("openstack-service-checks.endpoints.configured")
41@@ -47,6 +49,7 @@ ACTIONS = {
42
43
44 def main(args):
45+ """Parse the action hook and call the related function."""
46 action_name = os.path.basename(args[0])
47 try:
48 action = ACTIONS[action_name]
49diff --git a/src/files/plugins/check_cinder_services.py b/src/files/plugins/check_cinder_services.py
50index 004b1be..5939db7 100755
51--- a/src/files/plugins/check_cinder_services.py
52+++ b/src/files/plugins/check_cinder_services.py
53@@ -1,14 +1,17 @@
54 #!/usr/bin/env python3
55+"""Perform cinder services nagios checks."""
56
57 import argparse
58-import nagios_plugin3
59 import os
60-import os_client_config
61 import subprocess
62
63+import nagios_plugin3
64+
65+import os_client_config
66+
67
68 def check_status(service):
69- """Checks attributes of services and reports issues (or OK message).
70+ """Check attributes of services and reports issues (or OK message).
71
72 Attributes are (among others):
73 - binary: cinder-volume, cinder-scheduler
74@@ -28,7 +31,7 @@ def check_status(service):
75
76
77 def check_cinder_services(args, cinder):
78- """Retrieves list of services and returns appropriate nagios return code."""
79+ """Retrieve list of services and returns appropriate nagios return code."""
80 services = cinder.get("/os-services").json()["services"]
81 if not services:
82 output = "UNKNOWN: No cinder services found"
83diff --git a/src/files/plugins/check_contrail_analytics_alarms.py b/src/files/plugins/check_contrail_analytics_alarms.py
84index aa12a3e..feae07a 100755
85--- a/src/files/plugins/check_contrail_analytics_alarms.py
86+++ b/src/files/plugins/check_contrail_analytics_alarms.py
87@@ -1,17 +1,20 @@
88 #!/usr/bin/env python3
89+"""Perform Contrail Analytics nagios checks."""
90
91 import argparse
92 import collections
93 import datetime
94 import ipaddress
95 import os
96-import os_client_config
97 import re
98-import requests
99 import subprocess
100
101 import nagios_plugin3
102
103+import os_client_config
104+
105+import requests
106+
107 DEFAULT_IGNORED = r""
108 Alarm = collections.namedtuple("Alarm", "ts, desc")
109
110@@ -111,7 +114,7 @@ def check_contrail_alarms(contrail_vip, token, **kwargs):
111
112
113 def load_os_envvars(args):
114- # grab environment vars
115+ """Process environment variables."""
116 command = ["/bin/bash", "-c", "source {} && env".format(args.env)]
117 proc = subprocess.Popen(command, stdout=subprocess.PIPE)
118 for line in proc.stdout:
119@@ -121,6 +124,7 @@ def load_os_envvars(args):
120
121
122 def validate_ipv4(ipv4_addr):
123+ """Test if IPv4 address is valid."""
124 try:
125 ipaddress.IPv4Address(ipv4_addr)
126 except ipaddress.AddressValueError:
127@@ -130,6 +134,7 @@ def validate_ipv4(ipv4_addr):
128
129
130 def main():
131+ """Define main routine, process CLI args, and run checks."""
132 parser = argparse.ArgumentParser(description="Check Contrail alarms")
133 parser.add_argument(
134 "--env",
135diff --git a/src/files/plugins/check_nova_services.py b/src/files/plugins/check_nova_services.py
136index 5da5955..4ac1588 100755
137--- a/src/files/plugins/check_nova_services.py
138+++ b/src/files/plugins/check_nova_services.py
139@@ -1,18 +1,23 @@
140 #!/usr/bin/env python3
141+"""Define nagios check to determine if openstack compute services are impacted."""
142
143 import argparse
144-import nagios_plugin3
145 import os
146-import os_client_config
147 import subprocess
148
149+import nagios_plugin3
150+
151+import os_client_config
152+
153
154 def check_hosts_up(args, aggregate, hosts, services_compute):
155- # function to check an agg
156- # in: list of hosts
157- # in: services_compute
158- # in: args
159- # out: dict, msg_text, status
160+ """Check that aggregates have a minimum number of hosts active.
161+
162+ :in: list of hosts
163+ :in: services_compute
164+ :in: args
165+ :return: dict, msg_text, status
166+ """
167 status_crit = False
168 status_warn = False
169 counts = {"down": 0, "disabled": 0, "ok": 0}
170@@ -52,6 +57,7 @@ def check_hosts_up(args, aggregate, hosts, services_compute):
171
172
173 def check_nova_services(args, nova):
174+ """Define nagios service checks for nova services."""
175 aggregates = nova.get("/os-aggregates").json()["aggregates"]
176 services = nova.get("/os-services").json()["services"]
177 services_compute = [x for x in services if x["binary"] == "nova-compute"]
178diff --git a/src/files/plugins/check_octavia.py b/src/files/plugins/check_octavia.py
179index c7e4d02..c0081fd 100755
180--- a/src/files/plugins/check_octavia.py
181+++ b/src/files/plugins/check_octavia.py
182@@ -1,13 +1,14 @@
183 #!/usr/bin/env python3
184+"""Define nagios checks for octavia services."""
185
186 import argparse
187 import collections
188-from datetime import datetime, timedelta
189 import json
190 import os
191 import re
192 import subprocess
193 import sys
194+from datetime import datetime, timedelta
195
196 import openstack
197
198@@ -28,9 +29,7 @@ NAGIOS_STATUS = {
199
200
201 def filter_checks(alarms, ignored=DEFAULT_IGNORED):
202- """
203- Reduce all checks down to an overall check based on the highest level
204- not ignored
205+ """Reduce results down to an overall check based on the highest level not ignored.
206
207 :param List[Tuple] alarms: list of alarms (lvl, message)
208 :param str ignored: regular expression of messages to ignore
209@@ -59,7 +58,7 @@ def filter_checks(alarms, ignored=DEFAULT_IGNORED):
210
211
212 def nagios_exit(args, results):
213- # parse ignored list
214+ """Filter ignored checks and ensure proper nagios check return code."""
215 unique = sorted(filter(None, set(args.ignored.split(","))))
216 ignored_re = r"|".join("(?:{})".format(_) for _ in unique)
217
218@@ -71,8 +70,7 @@ def nagios_exit(args, results):
219
220
221 def check_loadbalancers(connection):
222- """check loadbalancers status."""
223-
224+ """Check loadbalancers status."""
225 lb_mgr = connection.load_balancer
226 lb_all = lb_mgr.load_balancers()
227
228@@ -128,7 +126,7 @@ def check_loadbalancers(connection):
229
230
231 def check_pools(connection):
232- """check pools status."""
233+ """Check pools status."""
234 lb_mgr = connection.load_balancer
235 pools_all = lb_mgr.pools()
236
237@@ -171,8 +169,7 @@ def check_pools(connection):
238
239
240 def check_amphorae(connection):
241- """check amphorae status."""
242-
243+ """Check amphorae status."""
244 lb_mgr = connection.load_balancer
245
246 resp = lb_mgr.get("/v2/octavia/amphorae")
247@@ -210,6 +207,7 @@ def check_amphorae(connection):
248
249
250 def check_image(connection, tag, days):
251+ """Check that there is an image with the proper octavia image tag."""
252 img_mgr = connection.image
253 images = list(img_mgr.images(tag=tag))
254
255@@ -245,8 +243,12 @@ def check_image(connection, tag, days):
256
257
258 def process_checks(args):
259- # use closure to make all checks have same signature
260- # so we can handle them in same way
261+ """Process all octavia checks in a standardized manner.
262+
263+ Use closure to make all checks have same signature
264+ so we can handle them in same way
265+ """
266+
267 def _check_image(_connection):
268 return check_image(_connection, args.amp_image_tag, args.amp_image_days)
269
270@@ -262,6 +264,7 @@ def process_checks(args):
271
272
273 def main():
274+ """Define main routine, parse CLI args, and run checks."""
275 parser = argparse.ArgumentParser(
276 description="Check Octavia status",
277 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
278diff --git a/src/files/plugins/check_rally.py b/src/files/plugins/check_rally.py
279index fac835b..73dbf8f 100755
280--- a/src/files/plugins/check_rally.py
281+++ b/src/files/plugins/check_rally.py
282@@ -1,10 +1,12 @@
283 #!/usr/bin/env python3
284+"""Perform rally testing for nagios alerting."""
285+
286 import collections
287+import json
288 import os
289 import re
290 import sys
291
292-import json
293
294 # ie. {0} tempest.test.test1 ... success
295 TEMPEST_TEST_RE = r"{\d+} [.\w]+ ... (\w+)"
296@@ -12,6 +14,7 @@ INPUT_FILE = "/home/nagiososc/rally.status"
297
298
299 def print_results(results):
300+ """Display results of the checks."""
301 status_message = collections.defaultdict(lambda: "UNKNOWN") # 3
302 status_message.update(
303 {
304@@ -65,22 +68,21 @@ def print_results(results):
305 break
306
307 if len(summary) > 0:
308- print(
309- "{}: ".format(nagios_status)
310- + ", ".join(
311- [
312- "{}[{}]".format(status_msg, summary[status_msg])
313- for status_msg in sorted(
314- summary, key=lambda status: summary[status], reverse=True
315- )
316- ]
317- )
318+ details = ", ".join(
319+ [
320+ "{}[{}]".format(status_msg, summary[status_msg])
321+ for status_msg in sorted(
322+ summary, key=lambda status: summary[status], reverse=True
323+ )
324+ ]
325 )
326+ print("{}: {}".format(nagios_status, details))
327 print("\n".join(output))
328 return rc
329
330
331 def main(results_filename):
332+ """Define main routine, parse arguments, run checks."""
333 if not os.path.exists(results_filename):
334 print("UNKNOWN: {} does not exist".format(results_filename))
335 return 3
336diff --git a/src/files/run_rally.py b/src/files/run_rally.py
337index 3f0802e..4543e64 100755
338--- a/src/files/run_rally.py
339+++ b/src/files/run_rally.py
340@@ -1,11 +1,12 @@
341 #!/usr/bin/env python3
342+"""Run rally tests."""
343+
344 import datetime
345 import json
346 import os
347+import shutil
348 import subprocess
349 import sys
350-
351-import shutil
352 import tempfile
353
354 OUTPUT_FILE = "/home/nagiososc/rally.status"
355@@ -13,6 +14,7 @@ HISTORY_FOLDER = "/home/nagiososc/rallystatuses"
356
357
358 def get_backup_output_filename():
359+ """Create path and define backup filename based on date."""
360 if not os.path.isdir(HISTORY_FOLDER):
361 os.mkdir(HISTORY_FOLDER, mode=0o755)
362
363@@ -56,6 +58,7 @@ def _load_envvars(novarc="/var/lib/nagios/nagios.novarc"):
364
365
366 def main(testfile="/home/nagiososc/ostests.txt"):
367+ """Perform rally test main routine."""
368 if not _load_envvars():
369 print("UNKNOWN: could not load OS_ envvars")
370 sys.exit(3)
371diff --git a/src/lib/lib_openstack_service_checks.py b/src/lib/lib_openstack_service_checks.py
372index 405120d..85ed3eb 100644
373--- a/src/lib/lib_openstack_service_checks.py
374+++ b/src/lib/lib_openstack_service_checks.py
375@@ -1,4 +1,7 @@
376+"""Helper library for openstack-service-checks charm."""
377+
378 import collections
379+import configparser
380 import glob
381 import os
382 import pwd
383@@ -6,31 +9,41 @@ import re
384 import subprocess
385 from urllib.parse import urlparse
386
387-import configparser
388
389 from charmhelpers import fetch
390-from charmhelpers.core.templating import render
391+from charmhelpers.contrib.charmsupport.nrpe import NRPE
392 from charmhelpers.contrib.openstack.utils import config_flags_parser
393 from charmhelpers.core import hookenv, host, unitdata
394-from charmhelpers.contrib.charmsupport.nrpe import NRPE
395+from charmhelpers.core.templating import render
396+
397 from charms.reactive import any_file_changed
398+
399 import keystoneauth1
400+
401 from keystoneclient import session
402
403
404 class OSCCredentialsError(Exception):
405+ """Define OSCCredentialError exception."""
406+
407 pass
408
409
410 class OSCKeystoneError(Exception):
411+ """Define OSCKeystoneError exception."""
412+
413 @property
414 def workload_status(self):
415+ """Implement workload_status method from Exception class."""
416 raise NotImplementedError
417
418
419 class OSCKeystoneServerError(OSCKeystoneError):
420+ """Define OSCKeystoneServerError exception."""
421+
422 @property
423 def workload_status(self):
424+ """Implement workload_status method from Exception class."""
425 return (
426 "Keystone server error was encountered trying to list keystone "
427 "resources. Check keystone server health. "
428@@ -39,8 +52,11 @@ class OSCKeystoneServerError(OSCKeystoneError):
429
430
431 class OSCKeystoneClientError(OSCKeystoneError):
432+ """Define OSCKeystoneClientError exception."""
433+
434 @property
435 def workload_status(self):
436+ """Implement workload_status method from Exception class."""
437 return (
438 "Keystone client request error was encountered trying to "
439 "keystone resources. Check keystone auth creds and url."
440@@ -49,8 +65,11 @@ class OSCKeystoneClientError(OSCKeystoneError):
441
442
443 class OSCSslError(OSCKeystoneError):
444+ """Define OSCSslError exception."""
445+
446 @property
447 def workload_status(self):
448+ """Implement workload_status method from Exception class."""
449 return (
450 "SSL error was encountered when requesting Keystone for "
451 "resource list. Check trusted_ssl_ca config option. "
452@@ -59,22 +78,27 @@ class OSCSslError(OSCKeystoneError):
453
454
455 class OSCHelper:
456+ """Define OSCHelper object."""
457+
458 def __init__(self):
459+ """Initialize charm configs and null keystone client into Helper object."""
460 self.charm_config = hookenv.config()
461 self._keystone_client = None
462
463 def store_keystone_credentials(self, creds):
464- """store keystone credentials"""
465+ """Store keystone credentials."""
466 kv = unitdata.kv()
467 kv.set("keystonecreds", creds)
468 kv.set("rallyinstalled", False)
469
470 @property
471 def novarc(self):
472+ """Define path to novarc config file for checks."""
473 return "/var/lib/nagios/nagios.novarc"
474
475 @property
476 def contrail_analytics_vip(self):
477+ """Expose the contrail_analytics_vip charm config value."""
478 return self.charm_config["contrail_analytics_vip"]
479
480 @property
481@@ -179,7 +203,7 @@ class OSCHelper:
482 return creds
483
484 def get_keystone_credentials(self):
485- """Retrieve keystone credentials from either config or relation data
486+ """Retrieve keystone credentials from either config or relation data.
487
488 If config 'os-crendentials' is set, return that info otherwise look
489 for a keystonecreds relation data'
490@@ -356,7 +380,9 @@ class OSCHelper:
491 self.create_endpoint_checks()
492
493 def _split_url(self, netloc, scheme):
494- """http(s)://host:port or http(s)://host will return a host and a port
495+ """Split URL and return host and port tuple.
496+
497+ http(s)://host:port or http(s)://host will return a host and a port
498
499 Even if a port is not specified, this helper will return a host and a port
500 (guessing it from the protocol used, if needed)
501@@ -709,7 +735,9 @@ class OSCHelper:
502 config.write(fd)
503
504 def reconfigure_tempest(self):
505- """Expects an external network already configured, and enables cinder tests
506+ """Enable cinder tests.
507+
508+ Expects an external network already configured
509
510 Sample:
511 RALLY_VERIFIER=7b9d06ef-e651-4da3-a56b-ecac67c595c5
512@@ -717,8 +745,8 @@ class OSCHelper:
513 RALLY_DEPLOYMENT=a75657c6-9eea-4f00-9117-2580fe056a80
514 RALLY_ENV=a75657c6-9eea-4f00-9117-2580fe056a80
515 """
516- RALLY_CONF = ["/home", self._rallyuser, "snap", "fcbtest", "current", ".rally"]
517- rally_globalconfig = os.path.join(*RALLY_CONF, "globals")
518+ rally_conf = ["/home", self._rallyuser, "snap", "fcbtest", "current", ".rally"]
519+ rally_globalconfig = os.path.join(*rally_conf, "globals")
520 if not os.path.isfile(rally_globalconfig):
521 return False
522
523@@ -730,7 +758,7 @@ class OSCHelper:
524 uuids[key] = value
525
526 tempest_path = os.path.join(
527- *RALLY_CONF,
528+ *rally_conf,
529 "verification",
530 "verifier-{RALLY_VERIFIER}".format(**uuids),
531 "for-deployment-{RALLY_DEPLOYMENT}".format(**uuids),
532diff --git a/src/reactive/openstack_service_checks.py b/src/reactive/openstack_service_checks.py
533index ede8c79..9c99652 100644
534--- a/src/reactive/openstack_service_checks.py
535+++ b/src/reactive/openstack_service_checks.py
536@@ -1,6 +1,7 @@
537 # -*- coding: us-ascii -*-
538 """
539-install_osc +------------------------> render_config+--------------> do_restart
540+install_osc +------------------------> render_config+--------------> do_restart.
541+
542 ^ + ^
543 conf_kst_user+---> save_creds +-----------+ v |
544 create_endpoints +-----+
545@@ -19,6 +20,7 @@ import base64
546 import subprocess
547
548 from charmhelpers.core import hookenv, host, unitdata
549+
550 from charms.reactive import (
551 any_flags_set,
552 clear_flag,
553@@ -29,8 +31,8 @@ from charms.reactive import (
554 )
555
556 from lib_openstack_service_checks import (
557- OSCHelper,
558 OSCCredentialsError,
559+ OSCHelper,
560 OSCKeystoneError,
561 )
562
563@@ -40,13 +42,14 @@ helper = OSCHelper()
564
565 @when("config.changed")
566 def config_changed():
567+ """Clear configured flag to trigger update of configs."""
568 clear_flag("openstack-service-checks.configured")
569
570
571 @when_not("openstack-service-checks.installed")
572 @when("nrpe-external-master.available")
573 def install_openstack_service_checks():
574- """Entry point to start configuring the unit
575+ """Start configuring the unit.
576
577 Triggered if related to the nrpe-external-master relation.
578 Some relation data can be initialized if the application is related to
579@@ -59,7 +62,7 @@ def install_openstack_service_checks():
580 @when_not("identity-credentials.available")
581 @when("identity-credentials.connected")
582 def configure_ident_username(keystone):
583- """Requests a user to the Identity Service"""
584+ """Request a user to be created by the Identity Service."""
585 username = "nagios"
586 keystone.request_credentials(username)
587 clear_flag("openstack-service-checks.stored-creds")
588@@ -125,7 +128,7 @@ def update_keystone_store():
589
590
591 def get_credentials():
592- """Get credential info from either config or relation data
593+ """Get credential info from either config or relation data.
594
595 If config 'os-credentials' is set, return it. Otherwise look for a
596 keystonecreds relation data.
597@@ -146,7 +149,7 @@ def get_credentials():
598 @when("openstack-service-checks.installed")
599 @when_not("openstack-service-checks.configured")
600 def render_config():
601- """Render nrpe checks from the templates
602+ """Render nrpe checks from the templates.
603
604 This code is only triggered after the nrpe relation is set. If a relation
605 with keystone is later set, it will be re-triggered. On the other hand,
606@@ -227,12 +230,14 @@ def configure_nrpe_endpoints():
607
608 @when("identity-notifications.available.updated")
609 def endpoints_changed():
610+ """Clear configured flag if endpoints are updated."""
611 clear_flag("openstack-service-checks.endpoints.configured")
612
613
614 @when("openstack-service-checks.configured")
615 @when_not("openstack-service-checks.started")
616 def do_restart():
617+ """Restart services when configuration has changed and not started."""
618 hookenv.log("Reloading nagios-nrpe-server")
619 host.service_restart("nagios-nrpe-server")
620 set_flag("openstack-service-checks.started")
621@@ -241,11 +246,13 @@ def do_restart():
622 @when("openstack-service-checks.started")
623 @when("openstack-service-checks.endpoints.configured")
624 def set_active():
625+ """Update unit status to active."""
626 hookenv.status_set("active", "Unit is ready")
627
628
629 @when("nrpe-external-master.available")
630 def do_reconfigure_nrpe():
631+ """Trigger NRPE relation reconfiguration."""
632 os_credentials_flag = "config.changed.os-credentials"
633 flags = [
634 "config.changed.check_{}_urls".format(interface)
635@@ -270,7 +277,7 @@ def do_reconfigure_nrpe():
636
637 @when_not("nrpe-external-master.available")
638 def missing_nrpe():
639- """Avoid a user action to be missed or overwritten by another hook"""
640+ """Set a blocked status if awaiting nrpe relation."""
641 if hookenv.hook_name() != "update-status":
642 hookenv.status_set("blocked", "Missing relations: nrpe")
643
644@@ -278,6 +285,7 @@ def missing_nrpe():
645 @when("openstack-service-checks.installed")
646 @when("nrpe-external-master.available")
647 def parse_hooks():
648+ """Catch upgrade-charm, update kv stores, and trigger reconfig."""
649 if hookenv.hook_name() == "upgrade-charm":
650 # Check if creds storage needs to be migrated
651 # Old key: keystone-relation-creds
652diff --git a/src/tests/functional/conftest.py b/src/tests/functional/conftest.py
653index 948059b..5d19e09 100644
654--- a/src/tests/functional/conftest.py
655+++ b/src/tests/functional/conftest.py
656@@ -1,5 +1,4 @@
657-"""
658-Reusable pytest fixtures for functional testing
659+"""Initialize reusable pytest fixtures for functional testing.
660
661 Environment variables
662 ---------------------
663@@ -11,14 +10,16 @@ if set, the testing model won't be torn down at the end of the testing session
664 import asyncio
665 import json
666 import os
667+import subprocess
668 import uuid
669
670-import pytest
671-import subprocess
672 import juju
673 from juju.controller import Controller
674 from juju.errors import JujuError
675
676+import pytest
677+
678+
679 STAT_CMD = """python3 - <<EOF
680 import json
681 import os
682@@ -39,8 +40,7 @@ EOF
683
684 @pytest.fixture(scope="module")
685 def event_loop():
686- """Override the default pytest event loop to allow for fixtures using a
687- broader scope"""
688+ """Override default pytest event loop to allow for fixtures using broader scope."""
689 loop = asyncio.get_event_loop_policy().new_event_loop()
690 asyncio.set_event_loop(loop)
691 loop.set_debug(True)
692@@ -51,7 +51,7 @@ def event_loop():
693
694 @pytest.fixture(scope="module")
695 async def controller():
696- """Connect to the current controller"""
697+ """Connect to the current controller."""
698 _controller = Controller()
699 await _controller.connect_current()
700 yield _controller
701@@ -60,7 +60,7 @@ async def controller():
702
703 @pytest.fixture(scope="module")
704 async def model(controller):
705- """This model lives only for the duration of the test"""
706+ """Create model which lives only for the duration of the test."""
707 model_name = os.getenv("PYTEST_MODEL")
708 if model_name is None:
709 model_name = "functest-{}".format(str(uuid.uuid4())[-12:])
710@@ -88,7 +88,7 @@ async def model(controller):
711
712 @pytest.fixture
713 async def get_app(model):
714- """Returns the application requested"""
715+ """Return the application requested."""
716
717 async def _get_app(name):
718 try:
719@@ -101,7 +101,7 @@ async def get_app(model):
720
721 @pytest.fixture
722 async def get_unit(model):
723- """Returns the requested <app_name>/<unit_number> unit"""
724+ """Return the requested <app_name>/<unit_number> unit."""
725
726 async def _get_unit(name):
727 try:
728@@ -115,7 +115,7 @@ async def get_unit(model):
729
730 @pytest.fixture
731 async def get_entity(get_unit, get_app):
732- """Returns a unit or an application"""
733+ """Return a unit or an application."""
734
735 async def _get_entity(name):
736 try:
737@@ -131,8 +131,7 @@ async def get_entity(get_unit, get_app):
738
739 @pytest.fixture
740 async def run_command(get_unit):
741- """
742- Runs a command on a unit.
743+ """Run a command on a unit.
744
745 :param cmd: Command to be run
746 :param target: Unit object or unit name string
747@@ -148,8 +147,7 @@ async def run_command(get_unit):
748
749 @pytest.fixture
750 async def file_stat(run_command):
751- """
752- Runs stat on a file
753+ """Run stat on a file.
754
755 :param path: File path
756 :param target: Unit object or unit name string
757@@ -168,8 +166,7 @@ async def file_stat(run_command):
758
759 @pytest.fixture
760 async def file_contents(run_command):
761- """
762- Returns the contents of a file
763+ """Return the contents of a file.
764
765 :param path: File path
766 :param target: Unit object or unit name string
767@@ -185,7 +182,7 @@ async def file_contents(run_command):
768
769 @pytest.fixture
770 async def reconfigure_app(get_app, model):
771- """Applies a different config to the requested app"""
772+ """Apply a different config to the requested app."""
773
774 async def _reconfigure_app(cfg, target):
775 application = (
776diff --git a/src/tests/functional/test_deploy.py b/src/tests/functional/test_deploy.py
777index e7d5782..4656232 100644
778--- a/src/tests/functional/test_deploy.py
779+++ b/src/tests/functional/test_deploy.py
780@@ -1,3 +1,5 @@
781+"""Test deployment of openstack-service-checks charm."""
782+
783 import asyncio
784 import os
785
786@@ -53,7 +55,10 @@ class ActionFailed(Exception):
787
788
789 class Agent:
790+ """Define the pytest model management agent."""
791+
792 def __init__(self, unit):
793+ """Initialize the unit the agent should act on."""
794 self.unit = unit
795
796 async def _act(self, action, **kwargs):
797@@ -158,7 +163,7 @@ async def deploy_app(request, deploy_openstack, model):
798 # Starts a deploy for each series
799 if (
800 apps["nrpe"] in model.applications
801- and apps["openstack-service-checks"] in model.applications
802+ and apps["openstack-service-checks"] in model.applications # noqa:W503
803 ):
804 yield model.applications[apps["openstack-service-checks"]]
805 return
806@@ -473,8 +478,8 @@ async def test_openstackservicechecks_invalid_keystone_workload_status(
807
808 # Wait for osc app to block with expected workload-status
809 await agent.block_until(lambda: agent.status("blocked"))
810- assert (
811- agent.unit.workload_status_message
812- == "Keystone server error was encountered trying to list keystone "
813+ expected_msg = (
814+ "Keystone server error was encountered trying to list keystone "
815 "resources. Check keystone server health. View juju logs for more info."
816 )
817+ assert agent.unit.workload_status_message == expected_msg
818diff --git a/src/tests/nagios_plugin/setup.py b/src/tests/nagios_plugin/setup.py
819index 90f14d5..83d86e9 100644
820--- a/src/tests/nagios_plugin/setup.py
821+++ b/src/tests/nagios_plugin/setup.py
822@@ -1,7 +1,9 @@
823-from setuptools import setup
824-from tempfile import TemporaryDirectory
825+"""Setup nagios_plugin3 module for unittesting."""
826 import os
827 import urllib.request
828+from tempfile import TemporaryDirectory
829+
830+from setuptools import setup
831
832
833 with TemporaryDirectory() as tempd:
834diff --git a/src/tests/unit/conftest.py b/src/tests/unit/conftest.py
835index e379711..f144c2b 100644
836--- a/src/tests/unit/conftest.py
837+++ b/src/tests/unit/conftest.py
838@@ -1,5 +1,7 @@
839-import unittest.mock as mock
840+"""Initialize the pytest unittesting environment."""
841+
842 import sys
843+import unittest.mock as mock
844 from os.path import abspath, dirname, join
845
846 import pytest
847@@ -15,6 +17,7 @@ sys.path.append(CHECKS_DIR)
848 # and import layer in lib_openstack_service_checks
849 @pytest.fixture
850 def mock_layers(monkeypatch):
851+ """Mock charms.layer."""
852 import sys
853
854 sys.modules["charms.layer"] = mock.Mock()
855@@ -34,6 +37,7 @@ def mock_layers(monkeypatch):
856
857 @pytest.fixture
858 def mock_hookenv_config(monkeypatch):
859+ """Mock hookenv.config."""
860 import yaml
861
862 def mock_config():
863@@ -53,6 +57,7 @@ def mock_hookenv_config(monkeypatch):
864
865 @pytest.fixture
866 def mock_remote_unit(monkeypatch):
867+ """Mock remote_unit as unit-mock/0."""
868 monkeypatch.setattr(
869 "lib_openstack_service_checks.hookenv.remote_unit", lambda: "unit-mock/0"
870 )
871@@ -60,6 +65,7 @@ def mock_remote_unit(monkeypatch):
872
873 @pytest.fixture
874 def mock_charm_dir(monkeypatch):
875+ """Mock hookenv.charm_dir."""
876 monkeypatch.setattr(
877 "lib_openstack_service_checks.hookenv.charm_dir", lambda: "/mock/charm/dir"
878 )
879@@ -67,6 +73,7 @@ def mock_charm_dir(monkeypatch):
880
881 @pytest.fixture
882 def mock_unitdata_keystonecreds(monkeypatch):
883+ """Mock keystone credentials from unitdata.kv."""
884 creds = {
885 "keystonecreds": {
886 "username": "nagios",
887@@ -82,6 +89,7 @@ def mock_unitdata_keystonecreds(monkeypatch):
888
889 @pytest.fixture
890 def openstackservicechecks(tmpdir, mock_hookenv_config, mock_charm_dir, monkeypatch):
891+ """Mock the OSCHelper library bits."""
892 from lib_openstack_service_checks import OSCHelper
893
894 helper = OSCHelper()
895diff --git a/src/tests/unit/test_check_cinder_services.py b/src/tests/unit/test_check_cinder_services.py
896index 3428dd6..588e58d 100644
897--- a/src/tests/unit/test_check_cinder_services.py
898+++ b/src/tests/unit/test_check_cinder_services.py
899@@ -1,8 +1,11 @@
900-import pytest
901-import nagios_plugin3
902+"""Test Cinder Service Checks."""
903
904 import check_cinder_services
905
906+import nagios_plugin3
907+
908+import pytest
909+
910
911 @pytest.mark.parametrize(
912 "state,status,result",
913@@ -14,6 +17,7 @@ import check_cinder_services
914 ],
915 )
916 def test_check_status(state, status, result):
917+ """Ensure status of cinder check is green."""
918 cinder_service = {
919 "binary": "cinder-scheduler",
920 "disabled_reason": None,
921@@ -58,15 +62,17 @@ def test_check_status(state, status, result):
922 ],
923 )
924 def test_check_cinder_services(state1, is_skip_disabled, status2, status3, result):
925+ """Test several cinder status inputs and expected nagios returns."""
926+
927 class _TestArgs(object):
928 skip_disabled = is_skip_disabled
929
930 class _TestCinder(object):
931- def get(cls, name):
932+ def get(self, name):
933 return _TestCinderJson()
934
935 class _TestCinderJson(object):
936- def json(cls):
937+ def json(self):
938 return {
939 "services": [
940 {
941@@ -131,12 +137,14 @@ def test_check_cinder_services(state1, is_skip_disabled, status2, status3, resul
942
943
944 def test_check_cinder_services_unknown():
945+ """Test that cinder checks return UNKNOWN when no services found."""
946+
947 class _TestCinder(object):
948- def get(cls, name):
949+ def get(self, name):
950 return _TestCinderJson()
951
952 class _TestCinderJson(object):
953- def json(cls):
954+ def json(self):
955 return {"services": []}
956
957 args, cinder = None, _TestCinder()
958diff --git a/src/tests/unit/test_check_contrail_analytics_alarms.py b/src/tests/unit/test_check_contrail_analytics_alarms.py
959index 663814a..a057c60 100644
960--- a/src/tests/unit/test_check_contrail_analytics_alarms.py
961+++ b/src/tests/unit/test_check_contrail_analytics_alarms.py
962@@ -1,3 +1,5 @@
963+"""Test contrail analytics nagios check script."""
964+
965 import json
966 from os.path import abspath, dirname, join
967
968@@ -7,6 +9,7 @@ TEST_DIR = dirname(abspath(__file__))
969
970
971 def test_parse_contrail_alarms():
972+ """Test defined failure input provides expected alerts."""
973 with open(join(TEST_DIR, "contrail_alert_data.json")) as f:
974 data = json.load(f)
975 parsed = check_contrail_analytics_alarms.parse_contrail_alarms(data)
976@@ -30,6 +33,7 @@ CRITICAL: vrouter{compute-7.maas, sev=1, ts[2020-07-03 18:30:32.481386]} Vrouter
977
978
979 def test_parse_contrail_alarms_filter_vrouter_control_9():
980+ """Test that alerts are ignorable with proper configs."""
981 with open(join(TEST_DIR, "contrail_alert_data.json")) as f:
982 data = json.load(f)
983 ignored_re = r"(?:vrouter)|(?:control-9)"
984@@ -48,6 +52,7 @@ CRITICAL: control-node{control-7-contrail-rmq, sev=1, ts[2020-06-25 18:29:24.377
985
986
987 def test_parse_contrail_alarms_filter_critical():
988+ """Test that we can ignore critical alerts by pattern."""
989 with open(join(TEST_DIR, "contrail_alert_data.json")) as f:
990 data = json.load(f)
991 ignored_re = r"(?:CRITICAL)"
992@@ -64,6 +69,7 @@ WARNING: control-node{control-8-contrail-rmq, sev=0, ts[2020-06-25 18:29:23.6848
993
994
995 def test_parse_contrail_alarms_all_ignored():
996+ """Test that we get okay response if ignoring all crit/warn."""
997 with open(join(TEST_DIR, "contrail_alert_data.json")) as f:
998 data = json.load(f)
999 ignored_re = r"(?:CRITICAL)|(?:WARNING)"
1000@@ -79,6 +85,7 @@ OK: total_alarms[11], unacked_or_sev_gt_0[10], total_ignored[11], ignoring r'(?:
1001
1002
1003 def test_parse_contrail_alarms_no_alarms():
1004+ """Test that no alarms results in green check response."""
1005 ignored_re = r""
1006 parsed = check_contrail_analytics_alarms.parse_contrail_alarms(
1007 {}, ignored=ignored_re
1008diff --git a/src/tests/unit/test_check_nova_services.py b/src/tests/unit/test_check_nova_services.py
1009index 816ec73..5358376 100644
1010--- a/src/tests/unit/test_check_nova_services.py
1011+++ b/src/tests/unit/test_check_nova_services.py
1012@@ -1,8 +1,11 @@
1013-import pytest
1014-import nagios_plugin3
1015+"""Test nova-services-check nagios plugin."""
1016
1017 import check_nova_services
1018
1019+import nagios_plugin3
1020+
1021+import pytest
1022+
1023
1024 @pytest.mark.parametrize(
1025 "is_skip_disabled,num_nodes",
1026@@ -14,6 +17,8 @@ import check_nova_services
1027 ],
1028 )
1029 def test_check_hosts_up(is_skip_disabled, num_nodes):
1030+ """Test aggregate host availability alerts."""
1031+
1032 class _TestArgs(object):
1033 warn = 2
1034 crit = 1
1035@@ -61,17 +66,19 @@ def test_check_hosts_up(is_skip_disabled, num_nodes):
1036
1037 @pytest.mark.parametrize("is_skip_disabled", [True, False])
1038 def test_check_nova_services(is_skip_disabled, monkeypatch):
1039+ """Check expected results of nova service checks."""
1040+
1041 class _TestArgs(object):
1042 warn = 2
1043 crit = 1
1044 skip_disabled = is_skip_disabled
1045
1046 class _TestNova(object):
1047- def get(cls, name):
1048+ def get(self, name):
1049 return _TestNovaJson()
1050
1051 class _TestNovaJson(object):
1052- def json(cls):
1053+ def json(self):
1054 return {
1055 "aggregates": [],
1056 "services": [
1057diff --git a/src/tests/unit/test_check_octavia.py b/src/tests/unit/test_check_octavia.py
1058index 5f871e8..2001efb 100644
1059--- a/src/tests/unit/test_check_octavia.py
1060+++ b/src/tests/unit/test_check_octavia.py
1061@@ -1,15 +1,19 @@
1062-from datetime import datetime, timedelta
1063+"""Test octavia nagios check script."""
1064+
1065 import json
1066 import unittest.mock as mock
1067+from datetime import datetime, timedelta
1068 from uuid import uuid4
1069
1070 import check_octavia
1071+
1072 import pytest
1073
1074
1075 @mock.patch("check_octavia.openstack.connect")
1076 @pytest.mark.parametrize("check", ["loadbalancers", "pools", "amphorae", "image"])
1077 def test_stable_alarms(connect, check):
1078+ """Test for expected green status."""
1079 args = mock.MagicMock()
1080 args.ignored = r""
1081 args.check = check
1082@@ -39,6 +43,7 @@ OK: total_alarms[0], total_crit[0], total_ignored[0], ignoring r''
1083
1084 @mock.patch("check_octavia.openstack.connect")
1085 def test_no_images_is_ignorable(connect):
1086+ """Test that we can ignore when no images exist."""
1087 args = mock.MagicMock()
1088 args.ignored = "none exist"
1089 args.check = "image"
1090@@ -59,6 +64,7 @@ OK: total_alarms[1], total_crit[1], total_ignored[1], ignoring r'(?:none exist)'
1091
1092 @mock.patch("check_octavia.openstack.connect")
1093 def test_no_images(connect):
1094+ """Test alerting status when no images exist."""
1095 args = mock.MagicMock()
1096 args.ignored = r""
1097 args.check = "image"
1098@@ -80,6 +86,7 @@ Octavia requires image with tag octavia to create amphora, but none exist
1099
1100 @mock.patch("check_octavia.openstack.connect")
1101 def test_no_active_images(connect):
1102+ """Test alerting status when the octavia amphora image is not active."""
1103 args = mock.MagicMock()
1104 args.ignored = r""
1105 args.check = "image"
1106@@ -108,6 +115,7 @@ Octavia requires image with tag octavia to create amphora, but none are active:
1107
1108 @mock.patch("check_octavia.openstack.connect")
1109 def test_no_fresh_images(connect):
1110+ """Test alerting for stale octavia amphora images."""
1111 args = mock.MagicMock()
1112 args.ignored = r""
1113 args.check = "image"
1114diff --git a/src/tests/unit/test_lib.py b/src/tests/unit/test_lib.py
1115index 674285d..1fcfca6 100644
1116--- a/src/tests/unit/test_lib.py
1117+++ b/src/tests/unit/test_lib.py
1118@@ -1,17 +1,20 @@
1119+"""Test helper library functions."""
1120+
1121 from unittest.mock import MagicMock
1122
1123-import pytest
1124 import keystoneauth1
1125
1126 from lib_openstack_service_checks import (
1127- OSCKeystoneServerError,
1128 OSCKeystoneClientError,
1129+ OSCKeystoneServerError,
1130 OSCSslError,
1131 )
1132
1133+import pytest
1134+
1135
1136 def test_openstackservicechecks_common_properties(openstackservicechecks):
1137- """Verify the most common properties from the class or default config.yaml"""
1138+ """Verify the most common properties from the class or default config.yaml."""
1139 assert isinstance(openstackservicechecks.charm_config, dict)
1140 assert openstackservicechecks.check_dns == ""
1141 assert openstackservicechecks.contrail_analytics_vip == ""
1142@@ -93,6 +96,7 @@ def test_openstackservicechecks_get_keystone_credentials_oscredentials(
1143 ],
1144 )
1145 def test_get_rally_checks_context(skip_rally, result, openstackservicechecks):
1146+ """Check that rally config context configuration works as expected."""
1147 openstackservicechecks.charm_config["skip-rally"] = skip_rally
1148 expected = {
1149 comp: result[num]
1150@@ -114,6 +118,7 @@ def test_get_rally_checks_context(skip_rally, result, openstackservicechecks):
1151 def test_keystone_client_exceptions(
1152 keystone_auth_exception, expected_raised_exception, openstackservicechecks, source
1153 ):
1154+ """Test OSC exceptions."""
1155 mock_keystone_client = MagicMock()
1156 getattr(mock_keystone_client, source).list.side_effect = keystone_auth_exception
1157 openstackservicechecks._keystone_client = mock_keystone_client
1158diff --git a/src/tox.ini b/src/tox.ini
1159index e00666c..75b9a23 100644
1160--- a/src/tox.ini
1161+++ b/src/tox.ini
1162@@ -29,18 +29,15 @@ commands =
1163 deps =
1164 black
1165 flake8
1166-#TODO flake8-docstrings
1167-#TODO flake8-import-order
1168+ flake8-docstrings
1169+ flake8-import-order
1170 pep8-naming
1171 flake8-colors
1172
1173 [flake8]
1174 ignore =
1175- N805 #TODO first argument of a method should be named 'self'
1176- E123 #TODO closing bracket does not match indentation of opening bracket's line
1177- W504 #TODO line break after binary operator
1178- W503 #TODO line break before binary operator
1179- N806 #TODO variable 'RALLY_CONF' in function should be lowercase
1180+ D102 #TODO Missing docstring in public method
1181+ D103 #TODO Missing docstring in public function
1182 exclude =
1183 .git,
1184 __pycache__,
1185@@ -65,8 +62,6 @@ commands =
1186 --cov=reactive \
1187 --cov=actions \
1188 --cov=files \
1189- --cov=hooks \
1190- --cov=src \
1191 --cov-report=term \
1192 --cov-report=annotate:report/annotated \
1193 --cov-report=html:report/html

Subscribers

People subscribed via source and target branches