Merge ~afreiberger/charm-openstack-service-checks/+git/charm-openstack-service-checks:lint-20.10 into ~llama-charmers/charm-openstack-service-checks:master
- Git
- lp:~afreiberger/charm-openstack-service-checks/+git/charm-openstack-service-checks
- lint-20.10
- Merge into 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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alvaro Uria (community) | Approve | ||
Review via email: mp+392257@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Drew Freiberger (afreiberger) wrote : | # |
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.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/actions/actions.py b/src/actions/actions.py |
2 | index 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] |
49 | diff --git a/src/files/plugins/check_cinder_services.py b/src/files/plugins/check_cinder_services.py |
50 | index 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" |
83 | diff --git a/src/files/plugins/check_contrail_analytics_alarms.py b/src/files/plugins/check_contrail_analytics_alarms.py |
84 | index 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", |
135 | diff --git a/src/files/plugins/check_nova_services.py b/src/files/plugins/check_nova_services.py |
136 | index 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"] |
178 | diff --git a/src/files/plugins/check_octavia.py b/src/files/plugins/check_octavia.py |
179 | index 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, |
278 | diff --git a/src/files/plugins/check_rally.py b/src/files/plugins/check_rally.py |
279 | index 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 |
336 | diff --git a/src/files/run_rally.py b/src/files/run_rally.py |
337 | index 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) |
371 | diff --git a/src/lib/lib_openstack_service_checks.py b/src/lib/lib_openstack_service_checks.py |
372 | index 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), |
532 | diff --git a/src/reactive/openstack_service_checks.py b/src/reactive/openstack_service_checks.py |
533 | index 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 |
652 | diff --git a/src/tests/functional/conftest.py b/src/tests/functional/conftest.py |
653 | index 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 = ( |
776 | diff --git a/src/tests/functional/test_deploy.py b/src/tests/functional/test_deploy.py |
777 | index 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 |
818 | diff --git a/src/tests/nagios_plugin/setup.py b/src/tests/nagios_plugin/setup.py |
819 | index 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: |
834 | diff --git a/src/tests/unit/conftest.py b/src/tests/unit/conftest.py |
835 | index 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() |
895 | diff --git a/src/tests/unit/test_check_cinder_services.py b/src/tests/unit/test_check_cinder_services.py |
896 | index 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() |
958 | diff --git a/src/tests/unit/test_check_contrail_analytics_alarms.py b/src/tests/unit/test_check_contrail_analytics_alarms.py |
959 | index 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 |
1008 | diff --git a/src/tests/unit/test_check_nova_services.py b/src/tests/unit/test_check_nova_services.py |
1009 | index 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": [ |
1057 | diff --git a/src/tests/unit/test_check_octavia.py b/src/tests/unit/test_check_octavia.py |
1058 | index 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" |
1114 | diff --git a/src/tests/unit/test_lib.py b/src/tests/unit/test_lib.py |
1115 | index 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 |
1158 | diff --git a/src/tox.ini b/src/tox.ini |
1159 | index 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 |
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.