Merge ~xavpaice/charm-telegraf:lint-20.08 into charm-telegraf:master

Proposed by Xav Paice
Status: Merged
Approved by: Alvaro Uria
Approved revision: b9e75f78db2428389d614f412f6dc0baca255fce
Merged at revision: 5b7391d840a618ab91dc1597864bb29138a04d19
Proposed branch: ~xavpaice/charm-telegraf:lint-20.08
Merge into: charm-telegraf:master
Prerequisite: ~xavpaice/charm-telegraf:blacken-20.08
Diff against target: 1281 lines (+226/-112)
12 files modified
src/files/telegraf_exec_metrics.py (+69/-34)
src/hooks/relations/apache-website/requires.py (+1/-3)
src/hooks/relations/statistics/requires.py (+2/-3)
src/hooks/relations/telegraf-exec/requires.py (+7/-3)
src/reactive/__init__.py (+1/-0)
src/reactive/telegraf.py (+82/-31)
src/tests/functional/tests/test_telegraf.py (+19/-6)
src/tests/unit/__init__.py (+2/-0)
src/tests/unit/test_mysql.py (+2/-1)
src/tests/unit/test_postgresql.py (+4/-4)
src/tests/unit/test_telegraf.py (+35/-26)
src/tox.ini (+2/-1)
Reviewer Review Type Date Requested Status
Alvaro Uria (community) Approve
James Troup (community) Approve
Canonical IS Reviewers Pending
Review via email: mp+388546@code.launchpad.net

Commit message

Extra Linting completed for 20.08 charm release

To post a comment you must log in.
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
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Revision history for this message
James Troup (elmo) wrote :

LGTM

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

+1 to this and the other 2 related MPs (makefile and blacken)

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

Change successfully merged at revision 5b7391d840a618ab91dc1597864bb29138a04d19

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/files/telegraf_exec_metrics.py b/src/files/telegraf_exec_metrics.py
2index f03e23a..d9b3402 100755
3--- a/src/files/telegraf_exec_metrics.py
4+++ b/src/files/telegraf_exec_metrics.py
5@@ -1,29 +1,32 @@
6 #!/usr/bin/env python3
7-import os
8-import re
9-import sys
10+import argparse
11 import json
12 import logging
13-import argparse
14-import subprocess
15+import os
16+import re
17 import shutil
18+import subprocess
19+import sys
20 from collections import OrderedDict
21 from textwrap import dedent
22
23-from insights.tests import context_wrap
24 from insights.parsers.softnet_stat import SoftNetStats
25-
26+from insights.tests import context_wrap
27
28 LOG = logging.getLogger()
29
30 METRICS = OrderedDict()
31
32
33-def register_metric(MetricClass):
34- """A decorator to create a metric class instance and register it"""
35- metric = MetricClass()
36+def register_metric(metricclass):
37+ """Create metric class instance.
38+
39+ Decorator that creates a metric class and registers it.
40+ """
41+ metric = metricclass()
42 METRICS[metric.name] = metric
43- return MetricClass
44+
45+ return metricclass
46
47
48 class FileMetric:
49@@ -46,18 +49,22 @@ class FileMetric:
50 LOG.info("rendering config to %s", config_path)
51 script = os.path.abspath(__file__)
52 output = self.config_template.format(
53- python=python or sys.executable, script=script, name=self.name,
54+ python=python or sys.executable, script=script, name=self.name
55 )
56 LOG.debug(output)
57+
58 if not dry_run:
59 with open(config_path, mode="w", encoding="utf8") as f:
60 f.write(output)
61+
62 return output
63
64 def remove_config(self, configs_dir, dry_run=False):
65 config_path = self.config_path(configs_dir)
66+
67 if os.path.isfile(config_path):
68 LOG.info("removing %s", config_path)
69+
70 if not dry_run:
71 os.remove(config_path)
72 else:
73@@ -78,6 +85,7 @@ class SockStatFileMetric(FileMetric):
74
75 def parse(self, content):
76 results = {}
77+
78 for line in content.splitlines():
79 LOG.debug("parsing line: %s", line)
80 # line example: TCP: inuse 62 orphan 0 tw 5 alloc 222 mem 20
81@@ -86,9 +94,11 @@ class SockStatFileMetric(FileMetric):
82 count = len(items)
83 assert count % 2 == 0
84 group = {}
85+
86 for i in range(0, count, 2):
87 group[items[i]] = int(items[i + 1])
88 results[key.strip()] = group
89+
90 return results
91
92
93@@ -175,18 +185,22 @@ class BuddyinfoFileMetric(FileMetric):
94 re_line = re.compile(
95 r"Node\s+(?P<node>\d+).*zone\s+(?P<zone>\w+)\s+(?P<pages>.*)"
96 )
97+
98 for line in content.splitlines():
99 LOG.debug(line)
100 match = re_line.match(line)
101+
102 if match:
103 data = match.groupdict()
104 LOG.debug(data)
105 pages = data["pages"].split()
106+
107 if not human_sizes:
108 human_sizes = [
109 self._get_human_size(page_size * 2 ** i)
110 for i, _ in enumerate(pages)
111 ]
112+
113 for k, v in zip(human_sizes, pages):
114 items.append(
115 {
116@@ -196,6 +210,7 @@ class BuddyinfoFileMetric(FileMetric):
117 "count": int(v),
118 }
119 )
120+
121 return items
122
123
124@@ -228,14 +243,17 @@ class ZoneinfoFileMetric(FileMetric):
125 re.MULTILINE + re.DOTALL,
126 )
127 items = []
128+
129 for match in pattern.finditer(content):
130 data = match.groupdict()
131 LOG.debug(data)
132 # convert str to int
133+
134 for k, v in data.items():
135 if v.isdigit():
136 data[k] = int(v)
137 items.append(data)
138+
139 return items
140
141
142@@ -246,11 +264,14 @@ class CmdMetric(FileMetric):
143 def get_cmd_output(self, cmd, is_json=False):
144 LOG.debug("running cmd: %s", " ".join(cmd))
145 output = subprocess.check_output(cmd).decode("utf8").strip()
146+
147 return json.loads(output) if is_json else output
148
149 def get_input_content(self):
150 if self.input_file:
151- # if provided, still read cmd output from a file, helpful for debuging/testing.
152+ # if provided, still read cmd output from a file, helpful
153+ # for debuging/testing.
154+
155 return super().get_input_content()
156 else:
157 return self.get_cmd_output(self.cmd)
158@@ -277,17 +298,18 @@ class NetNSCmdMetric(CmdMetric):
159 items = []
160 # find openstack/neutron netns list pattern
161 pattern = re.compile(r"^([\w-]+)\s+\(id:\s+(\d+)\)", flags=re.M)
162+
163 for match in pattern.finditer(content):
164 items.append(
165- {"namespace": match.group(1), "id": match.group(2), "state": 1,}
166+ {"namespace": match.group(1), "id": match.group(2), "state": 1}
167 )
168
169 # no re matches, should be in simple list format
170+
171 if not items:
172 for line in content.splitlines():
173- items.append(
174- {"namespace": line.strip(), "state": 1,}
175- )
176+ items.append({"namespace": line.strip(), "state": 1})
177+
178 return items
179
180
181@@ -321,8 +343,10 @@ class OvsDumpFlowsCmdMetric(CmdMetric):
182 flows = {}
183
184 ovs_cmd = "ovs-ofctl"
185+
186 if not shutil.which(ovs_cmd):
187 LOG.error('openvswitch command "%s" is not available, exit', ovs_cmd)
188+
189 return flows
190
191 for bridge in ["br-int", "br-tun", "br-data"]:
192@@ -335,7 +359,6 @@ class OvsDumpFlowsCmdMetric(CmdMetric):
193 def parse(self, input_content):
194 """Parse input content for each bridge and convert to a json list.
195
196-
197 Example final json result:
198
199 [
200@@ -356,12 +379,14 @@ class OvsDumpFlowsCmdMetric(CmdMetric):
201 # input_content here is a dict with cmd output for each bridge for this metric
202 flows = input_content
203 items = []
204+
205 for bridge, output in flows.items():
206 # when invoked via python (or redirected) it includes an extra line
207 # like 'NXST_FLOW reply (xid=0x4):'
208 # so need to substract one
209 lines = output.splitlines()
210 items.append({"bridge": bridge, "count": len(lines) - 1})
211+
212 return items
213
214
215@@ -381,8 +406,9 @@ class OvsDpCtlCmdMetric(CmdMetric):
216 )
217
218 def parse_fields(self, text):
219- """
220- parse lines following pattern to a dictionary,
221+ """Parse lines following pattern to a dictionary.
222+
223+ Parse lines following pattern to a dictionary,
224 converting int/floats as relevant
225 'hit:706 missed:456 lost:0'
226 """
227@@ -396,23 +422,26 @@ class OvsDpCtlCmdMetric(CmdMetric):
228 # parse lines of key/values into a dict
229 # hit:706 missed:489 lost:1
230 data = {}
231+
232 for datapair in text.split():
233 k, v = datapair.split(":")
234 # do nothing if the line has question marks (no data), e.g.:
235 # RX packets:0 errors:? dropped:? overruns:? frame:?
236+
237 if "?" in v:
238 continue
239 # reuse field parsing logic by recursion, no nested dicts
240 data[k] = self.parse_fields(v)
241+
242 return data
243+
244 return text
245
246 def parse(self, input_content):
247- """
248- parse the output of `ovs-appctl dpctl/show -s`
249- """
250+ """Parse the output of `ovs-appctl dpctl/show -s`."""
251 result = []
252 datapath_entry = None
253+
254 for line in input_content.splitlines():
255 if line[0].isalnum(): # datapath header, other lines start w/space or tab
256 if datapath_entry: # store the current one before creating a new one
257@@ -422,9 +451,10 @@ class OvsDpCtlCmdMetric(CmdMetric):
258 "port": "",
259 "portname": "",
260 }
261+
262 continue
263
264- label, rest = (l.strip() for l in line.split(":", maxsplit=1))
265+ label, rest = (item.strip() for item in line.split(":", maxsplit=1))
266
267 if label.startswith("port"):
268 # complete previous entry, and initialise a new one for the new port
269@@ -434,12 +464,14 @@ class OvsDpCtlCmdMetric(CmdMetric):
270 }
271 datapath_entry["port"] = label.split()[-1]
272 datapath_entry["portname"] = rest
273+
274 continue
275
276 if datapath_entry["port"]: # parsing lines corresponding to a port block
277 if "X packets" in label:
278 # parse the RX|TX packets line using a prefix
279 prefix, data = line.strip().split(maxsplit=1)
280+
281 for k, v in self.parse_fields(data).items():
282 datapath_entry["{}_{}".format(prefix, k)] = v
283 else: # parse the non-packet lines (collisions/bytes)
284@@ -449,12 +481,14 @@ class OvsDpCtlCmdMetric(CmdMetric):
285 # use RX/TX as a prefix by removing the space
286 data = line.replace(" bytes", "_bytes")
287 datapath_entry.update(self.parse_fields(data))
288+
289 continue
290
291 # all other cases, are datapairs or metrics
292 datapath_entry[label] = self.parse_fields(rest.rstrip())
293
294 # handle the end of the output
295+
296 if datapath_entry:
297 result.append(datapath_entry)
298
299@@ -462,17 +496,18 @@ class OvsDpCtlCmdMetric(CmdMetric):
300
301
302 def render_config_files(configs_dir, python, disabled_metrics="", dry_run=False):
303- """Render config files for any metrics not disabled, and remove any disabled"""
304-
305+ """Render config files for any metrics not disabled, and remove any disabled."""
306 disabled_metrics_set = (
307 set(disabled_metrics.split(":")) if disabled_metrics else set([])
308 )
309+
310 for metric in disabled_metrics_set:
311 try:
312 METRICS[metric].remove_config(configs_dir, dry_run=dry_run)
313 except KeyError:
314 LOG.warning("metric name %s not in %s", metric, ",".join(METRICS))
315 enabled_metrics_set = set(METRICS.keys()) - disabled_metrics_set
316+
317 for metric in enabled_metrics_set:
318 METRICS[metric].render_config(configs_dir, python, dry_run=dry_run)
319
320@@ -484,10 +519,7 @@ def main():
321 )
322 choices = list(METRICS) # sorted dict
323 argparser.add_argument(
324- "--metric",
325- choices=choices,
326- default=choices[0],
327- help="which metric to execuate",
328+ "--metric", choices=choices, default=choices[0], help="which metric to execuate"
329 )
330 argparser.add_argument(
331 "-f",
332@@ -499,7 +531,8 @@ def main():
333 "--render-config-files",
334 dest="render_config_files",
335 action="store_true",
336- help="render config files for all metrics, use --disabled-metrics to exclude specific ones",
337+ help="render config files for all metrics, use --disabled-metrics to "
338+ "exclude specific ones",
339 )
340 argparser.add_argument(
341 "--disabled-metrics",
342@@ -511,7 +544,8 @@ def main():
343 "--python",
344 dest="python",
345 default=sys.executable,
346- help="python interperter path in config file to execute this script, required for venv",
347+ help="python interperter path in config file to execute this script, "
348+ "required for venv",
349 )
350 # useful for testing
351 argparser.add_argument(
352@@ -521,10 +555,10 @@ def main():
353 help="telegraf configs dir for exec metrics",
354 )
355 argparser.add_argument(
356- "--dry-run", action="store_true", help="dry run without real actions",
357+ "--dry-run", action="store_true", help="dry run without real actions"
358 )
359 argparser.add_argument(
360- "-v", "--verbose", action="store_true", help="be verbose in log",
361+ "-v", "--verbose", action="store_true", help="be verbose in log"
362 )
363
364 cli = argparser.parse_args()
365@@ -542,6 +576,7 @@ def main():
366 )
367 else:
368 metric = METRICS[cli.metric]
369+
370 if cli.input_file:
371 metric.input_file = cli.input_file
372 output = metric.parse(metric.get_input_content())
373diff --git a/src/hooks/relations/apache-website/requires.py b/src/hooks/relations/apache-website/requires.py
374index ecb663a..861fb8f 100644
375--- a/src/hooks/relations/apache-website/requires.py
376+++ b/src/hooks/relations/apache-website/requires.py
377@@ -14,9 +14,7 @@
378 # You should have received a copy of the GNU General Public License
379 # along with this program. If not, see <http://www.gnu.org/licenses/>.
380
381-from charms.reactive import hook
382-from charms.reactive import RelationBase
383-from charms.reactive import scopes
384+from charms.reactive import RelationBase, hook, scopes
385
386
387 class WSGIRequires(RelationBase):
388diff --git a/src/hooks/relations/statistics/requires.py b/src/hooks/relations/statistics/requires.py
389index 4aafb2a..702db6f 100644
390--- a/src/hooks/relations/statistics/requires.py
391+++ b/src/hooks/relations/statistics/requires.py
392@@ -14,9 +14,7 @@
393 # You should have received a copy of the GNU General Public License
394 # along with this program. If not, see <http://www.gnu.org/licenses/>.
395
396-from charms.reactive import hook
397-from charms.reactive import RelationBase
398-from charms.reactive import scopes
399+from charms.reactive import RelationBase, hook, scopes
400
401
402 class WSGIRequires(RelationBase):
403@@ -26,6 +24,7 @@ class WSGIRequires(RelationBase):
404 def changed(self):
405 conv = self.conversation()
406 conv.set_state("{relation_name}.connected")
407+
408 if conv.get_remote("enabled"):
409 # this unit's conversation has a port, so
410 # it is part of the set of available units
411diff --git a/src/hooks/relations/telegraf-exec/requires.py b/src/hooks/relations/telegraf-exec/requires.py
412index 42bb783..453203b 100644
413--- a/src/hooks/relations/telegraf-exec/requires.py
414+++ b/src/hooks/relations/telegraf-exec/requires.py
415@@ -16,9 +16,7 @@
416
417 import json
418
419-from charms.reactive import hook
420-from charms.reactive import RelationBase
421-from charms.reactive import scopes
422+from charms.reactive import RelationBase, hook, scopes
423
424
425 class ExecRequires(RelationBase):
426@@ -29,6 +27,7 @@ class ExecRequires(RelationBase):
427 conv = self.conversation()
428 conv.set_state("{relation_name}.connected")
429 commands_json_dict = conv.get_remote("commands") # list of commands to run
430+
431 if commands_json_dict is not None and json.loads(commands_json_dict):
432 conv.set_state("{relation_name}.available")
433
434@@ -40,12 +39,16 @@ class ExecRequires(RelationBase):
435
436 def commands(self):
437 cmds = []
438+
439 for conv in self.conversations():
440 commands_json_dict = conv.get_remote("commands") # list of commands dicts
441+
442 for cmd_info in json.loads(commands_json_dict):
443 commands = cmd_info.pop("commands", []) # list of commands
444+
445 if commands is None and "command" in cmd_info:
446 commands = [cmd_info.pop("command")]
447+
448 if not commands:
449 continue
450 data_format = cmd_info.pop("data_format") # json, graphite, influx
451@@ -57,4 +60,5 @@ class ExecRequires(RelationBase):
452 cmd["run_on_this_unit"] = cmd_info.pop("run_on_this_unit", True)
453 cmd.update(cmd_info)
454 cmds.append(cmd)
455+
456 return cmds
457diff --git a/src/reactive/__init__.py b/src/reactive/__init__.py
458index e69de29..82e3dfc 100644
459--- a/src/reactive/__init__.py
460+++ b/src/reactive/__init__.py
461@@ -0,0 +1 @@
462+"""Reactive package."""
463diff --git a/src/reactive/telegraf.py b/src/reactive/telegraf.py
464index ba47505..4b72eab 100644
465--- a/src/reactive/telegraf.py
466+++ b/src/reactive/telegraf.py
467@@ -16,40 +16,43 @@
468
469 import base64
470 import binascii
471-from distutils.version import LooseVersion
472 import io
473 import json
474 import os
475+import re
476 import shutil
477 import socket
478+import subprocess
479 import sys
480 import time
481-import yaml
482-import re
483-import netifaces
484-import subprocess
485+from distutils.version import LooseVersion
486+
487+from charmhelpers import context
488+from charmhelpers.contrib.charmsupport import nrpe
489+from charmhelpers.core import hookenv, host, unitdata
490+from charmhelpers.core.host import is_container
491+from charmhelpers.core.templating import render
492
493+import charms.promreg
494 from charms import apt
495 from charms.layer import snap
496 from charms.reactive import (
497+ clear_flag,
498 endpoint_from_flag,
499 helpers,
500 hook,
501- when,
502- when_not,
503 set_flag,
504- clear_flag,
505 toggle_flag,
506+ when,
507+ when_not,
508 )
509 from charms.reactive.bus import get_states
510
511-from charmhelpers import context
512-from charmhelpers.core import hookenv, host, unitdata
513-from charmhelpers.core.templating import render
514-from charmhelpers.core.host import is_container
515-from charmhelpers.contrib.charmsupport import nrpe
516-import charms.promreg
517-from jinja2 import Template, Environment, FileSystemLoader, exceptions
518+from jinja2 import Environment, FileSystemLoader, Template, exceptions
519+
520+import netifaces
521+
522+import yaml
523
524 DEB_BASE_DIR = "/etc/telegraf"
525 SNAP_BASE_DIR = "/var/snap/telegraf/current"
526@@ -75,6 +78,7 @@ class InvalidInstallMethod(Exception):
527
528 def get_install_method():
529 config = hookenv.config()
530+
531 if config["install_method"] in ["deb", "snap"]:
532 return config["install_method"]
533 else:
534@@ -87,6 +91,7 @@ def get_install_method():
535
536 def get_base_dir():
537 config = hookenv.config()
538+
539 if config["install_method"] == "deb":
540 return DEB_BASE_DIR
541 elif config["install_method"] == "snap":
542@@ -101,6 +106,7 @@ def get_base_dir():
543
544 def get_service():
545 config = hookenv.config()
546+
547 if config["install_method"] == "deb":
548 return DEB_SERVICE
549 elif config["install_method"] == "snap":
550@@ -139,13 +145,16 @@ def list_config_files():
551 config_files = [get_main_config_path()]
552 # only include config files for configured plugins
553 current_states = get_states()
554+
555 for plugin in list_supported_plugins():
556 if "plugins.{}.configured".format(plugin) in current_states.keys():
557 config_path = "{}/{}.conf".format(get_configs_dir(), plugin)
558 config_files.append(config_path)
559+
560 if "extra_plugins.configured" in current_states.keys():
561 config_files.append("{}/extra_plugins.conf".format(get_configs_dir()))
562 config_files.append("{}/socket_listener.conf".format(get_configs_dir()))
563+
564 return config_files
565
566
567@@ -153,33 +162,40 @@ def get_hostname_label():
568 config = hookenv.config()
569 hostname_fmt = config["hostname"]
570 unit = get_remote_unit_name().replace("/", "-") # / is invalid in labels.
571+
572 if hostname_fmt == "UNIT_NAME": # Deprecated
573 return unit
574 env = os.environ
575 model = env.get("JUJU_ENV_NAME") or env.get("JUJU_MODEL_NAME", "")
576 uuid = env.get("JUJU_ENV_UUID") or env.get("JUJU_MODEL_UUID", "")
577 syshost = socket.gethostname()
578+
579 return hostname_fmt.format(unit=unit, model=model, uuid=uuid, host=syshost)
580
581
582 def get_remote_unit_name():
583 unit = hookenv.principal_unit()
584+
585 if unit:
586 # Note(aluria): use Juju env var available since 2017
587+
588 return unit
589 else:
590 # Note(aluria): lookup all available IPv4/IPv6 addresses (except lo)
591 ip_addresses = set()
592+
593 for iface in netifaces.interfaces():
594 if iface == "lo":
595 continue
596 ip_addrs = netifaces.ifaddresses(iface)
597+
598 for iface_type in ip_addrs:
599 if iface_type in (netifaces.AF_INET, netifaces.AF_INET6):
600 for addrs in ip_addrs[iface_type]:
601 ip_addresses.add(addrs["addr"])
602
603 # Note(aluria): and try to match them against rel['private-address']
604+
605 for rel_type in hookenv.metadata()["requires"].keys():
606 for rel in hookenv.relations_of_type(rel_type):
607 if rel["private-address"] in ip_addresses:
608@@ -188,6 +204,7 @@ def get_remote_unit_name():
609
610 def get_base_inputs():
611 """Make a structure for rendering the base_inputs template.
612+
613 Returns a dict of items for the template.
614 """
615 extra_options = get_extra_options()
616@@ -195,6 +212,7 @@ def get_base_inputs():
617 config = hookenv.config()
618 str_disabled_plugins = config["disabled_plugins"]
619 disabled_plugins = str_disabled_plugins.split(":") if str_disabled_plugins else []
620+
621 return {
622 "extra_options": extra_options["inputs"],
623 "bcache": is_bcache(),
624@@ -221,20 +239,25 @@ def get_extra_options():
625 # to raw json
626 json_vals = {}
627 # kind level
628+
629 for k, v in extra_options.items():
630 json_vals[k] = {}
631 # plugins level
632+
633 for plugin, values in v.items():
634 json_vals[k][plugin] = {}
635 # inner plugin (aka key:value)
636+
637 for key, val in values.items():
638 if key in ("tagpass", "tagdrop"):
639 # this is a tagpass/drop, we need to go deeper
640 json_vals[k][plugin][key] = {}
641+
642 for tag, tagvalue in val.items():
643 json_vals[k][plugin][key][tag] = json.dumps(tagvalue)
644 else:
645 json_vals[k][plugin][key] = json.dumps(val)
646+
647 return json_vals
648
649
650@@ -323,13 +346,17 @@ def render_socket_listener_config(context):
651 def get_sysstat_config_with_sadc_xall(content):
652 """Get updated sysstat config content with `-S XALL` in `SADC_OPTIONS`.
653
654- `/etc/sysstat/systat` consists of a sequence of shell variable assignments used to configure sysstat logging.
655+ `/etc/sysstat/systat` consists of a sequence of shell variable assignments
656+ used to configure sysstat logging.
657
658 Check the original config content.
659+
660 If change needed, make the change and return new config content.
661+
662 If no change, return None.
663 """
664- # if SADC_OPTIONS already exists with `-S XALL` in value, no need to change, return None
665+ # if SADC_OPTIONS already exists with `-S XALL` in value, no need to change,
666+ # return None
667 if re.search(r'^SADC_OPTIONS=".*-S\s+XALL.*"', content, flags=re.M):
668 return None
669
670@@ -365,6 +392,7 @@ def update_sysstat_config_with_sdac_xall(path="/etc/sysstat/sysstat"):
671 if os.path.isfile(path):
672 with open(path, mode="r", encoding="utf8") as f:
673 new_text = get_sysstat_config_with_sadc_xall(f.read())
674+
675 if new_text:
676 hookenv.log("updating {} to ensure `-S XALL` in SADC_OPTIONS".format(path))
677 with open(path, mode="w", encoding="utf8") as f:
678@@ -386,18 +414,23 @@ def configure_telegraf(): # noqa: C901
679 "blocked",
680 "Wrong install_method provided: {!r}".format(config["install_method"]),
681 )
682+
683 return
684+
685 if get_remote_unit_name() is None:
686 hookenv.status_set("waiting", "Waiting for juju-info relation")
687 # if UNIT_NAME in hostname config and relation not yet available,
688 # make telegraf unable to start to not get weird metrics names
689+
690 if os.path.exists(config_path):
691 os.unlink(config_path)
692+
693 return
694
695 inputs = config.get("inputs_config", "")
696 outputs = config.get("outputs_config", "")
697 # just for the migration out of base64
698+
699 if inputs:
700 try:
701 inputs = base64.b64decode(inputs.encode("utf-8"), validate=True).decode(
702@@ -406,6 +439,7 @@ def configure_telegraf(): # noqa: C901
703 except binascii.Error:
704 # not bas64, probably already up to date configs
705 pass
706+
707 if outputs:
708 try:
709 outputs = base64.b64decode(outputs.encode("utf-8"), validate=True).decode(
710@@ -415,6 +449,7 @@ def configure_telegraf(): # noqa: C901
711 # not bas64, probably already up to date configs
712 pass
713 tags = []
714+
715 if config["tags"]:
716 for tag in config["tags"].split(","):
717 key, value = tag.split("=")
718@@ -424,11 +459,13 @@ def configure_telegraf(): # noqa: C901
719 tags.append('juju_unit = "{}"'.format(get_remote_unit_name().replace("/", "-")))
720 tags.append('juju_model = "{}"'.format(hookenv.model_name()))
721 context["tags"] = tags
722+
723 if inputs:
724 context["inputs"] = inputs
725 else:
726 # use base inputs from charm templates
727 context["inputs"] = render_base_inputs()
728+
729 if outputs:
730 context["outputs"] = outputs
731 else:
732@@ -438,12 +475,13 @@ def configure_telegraf(): # noqa: C901
733 context["hostname"] = get_hostname_label()
734
735 logfile_path = os.path.normpath(context["logfile"])
736+
737 if (
738 context["logfile"]
739- and not logfile_path.startswith("/var/log/")
740- and not (
741+ and not logfile_path.startswith("/var/log/") # noqa W503
742+ and not ( # noqa W503
743 config["install_method"] == "snap"
744- and logfile_path.startswith("/var/snap/telegraf/common/")
745+ and logfile_path.startswith("/var/snap/telegraf/common/") # noqa W503
746 )
747 ):
748 # only allow logging in /var/log, syslog, or /var/snap/telegraf/common
749@@ -487,6 +525,7 @@ def configure_telegraf(): # noqa: C901
750 )
751
752 # add sudoers file for telegraf if openvswitch is running
753+
754 if host.service_running("openvswitch-switch"):
755 sudoers_filename = "telegraf_sudoers"
756 src = os.path.join(get_files_dir(), sudoers_filename)
757@@ -508,6 +547,7 @@ def configure_telegraf(): # noqa: C901
758
759 set_flag("telegraf.configured")
760 set_flag("telegraf.needs_reload")
761+
762 if config["install_method"] == "deb":
763 set_flag("telegraf.apt.configured")
764 else:
765@@ -525,12 +565,15 @@ def install_telegraf():
766 hookenv.status_set(
767 "blocked", "Wrong install_method provided. Expected either 'deb' or 'snap'."
768 )
769+
770 return
771+
772 if install_method == "deb":
773 try:
774 snap.remove("telegraf")
775 except Exception:
776- # the snap may already be absent, or snaps may not even be supported in this environment
777+ # the snap may already be absent, or snaps may not even be supported
778+ # in this environment
779 pass
780 apt.queue_install(["telegraf"])
781 elif install_method == "snap":
782@@ -538,6 +581,7 @@ def install_telegraf():
783 config = hookenv.config()
784 snap_channel = config.get("snap_channel")
785 snap.install("telegraf", channel=snap_channel, classic=True)
786+
787 if install_method:
788 set_flag("telegraf.installed")
789
790@@ -572,16 +616,19 @@ def upgrade_charm():
791 @when("config.changed")
792 def handle_config_changes():
793 config = hookenv.config()
794+
795 if config.changed("extra_options"):
796 for plugin in list_supported_plugins():
797 clear_flag("plugins.{}.configured".format(plugin))
798 # if something else changed, let's reconfigure telegraf itself just in case
799+
800 if config.changed("extra_plugins"):
801 clear_flag("extra_plugins.configured")
802+
803 if (
804 config.changed("install_method")
805- or config.changed("snap_channel")
806- or config.changed("install_sources")
807+ or config.changed("snap_channel") # noqa W503
808+ or config.changed("install_sources") # noqa W503
809 ):
810 clear_flag("telegraf.installed")
811 clear_flag("telegraf.configured")
812@@ -595,6 +642,7 @@ def handle_config_changes():
813 def configure_extra_plugins():
814 config = hookenv.config()
815 plugins = config["extra_plugins"]
816+
817 if plugins:
818 config_path = "{}/extra_plugins.conf".format(get_configs_dir())
819 host.write_file(config_path, plugins.encode("utf-8"))
820@@ -894,7 +942,7 @@ def redis_input(redis):
821 servers = ["tcp://{{ host }}:{{ port }}"]
822 # Until https://github.com/influxdata/telegraf/issues/5036 is fixed
823 fielddrop = ["aof_last_bgrewrite_status","aof_last_write_status","maxmemory_policy","rdb_last_bgsave_status","used_memory_dataset_perc","used_memory_peak_perc"]
824-"""
825+""" # noqa E501 (inline template)
826 config_path = "{}/{}.conf".format(get_configs_dir(), "redis")
827
828 rels = hookenv.relations_of_type("redis")
829@@ -1146,7 +1194,8 @@ def prometheus_client(prometheus):
830
831
832 def convert_days(time_string):
833- """
834+ """Convert string time descript to days.
835+
836 Function to convert strings like 2w or 14d to a sting containing the number
837 of days.
838
839@@ -1302,8 +1351,8 @@ def start_or_restart():
840 active_plugins_changed = helpers.data_changed("active_plugins", states or "")
841 if (
842 not host.service_running(service)
843- or config_files_changed
844- or active_plugins_changed
845+ or config_files_changed # noqa W503
846+ or active_plugins_changed # noqa W503
847 ):
848 hookenv.log("Restarting telegraf")
849 host.service_restart(service)
850@@ -1328,8 +1377,9 @@ def start_or_restart():
851
852
853 def is_bcache():
854- """
855- return true if bcache is present, and this is not a container
856+ """Determine if this is a container.
857+
858+ return true if bcache is present, and this is not a container.
859 """
860 container = is_container()
861 return os.path.exists("/sys/fs/bcache") and not container
862@@ -1352,9 +1402,10 @@ def update_status():
863 @when_not("telegraf.nagios-setup.complete")
864 def configure_nagios(nagios):
865 """Configure nagios process check.
866- the flag 'telegraf.nagios-setup.complete' is reset at the moment config is
867- changed, so this should make sure that updates are handled."""
868
869+ The flag 'telegraf.nagios-setup.complete' is reset at the moment config is
870+ changed, so this should make sure that updates are handled.
871+ """
872 # Use charmhelpers to handle the configuration of nrpe
873 hostname = nrpe.get_nagios_hostname()
874 nrpe_setup = nrpe.NRPE(hostname=hostname, primary=False)
875diff --git a/src/tests/functional/tests/test_telegraf.py b/src/tests/functional/tests/test_telegraf.py
876index 1b80661..3c4354d 100644
877--- a/src/tests/functional/tests/test_telegraf.py
878+++ b/src/tests/functional/tests/test_telegraf.py
879@@ -21,9 +21,8 @@ import unittest
880
881 import requests
882
883-from zaza.utilities import juju
884 from zaza import model
885-
886+from zaza.utilities import juju
887
888 DEFAULT_HTTPGET_TIMEOUT = 10
889 DEFAULT_RETRIES = 12
890@@ -58,21 +57,24 @@ class TestTelegraf(BaseTelegrafTest):
891 model.get_units(app)
892 for app in juju.get_principle_applications(self.application_name)
893 )
894+
895 for unit in it.chain.from_iterable(principal_units):
896 if not unit.public_address:
897 continue
898 url = "http://{}:{}/metrics".format(
899 unit.public_address, DEFAULT_TELEGRAF_EXPORTER_PORT
900 )
901+
902 for retry in range(DEFAULT_RETRIES):
903 resp = requests.get(url, timeout=DEFAULT_HTTPGET_TIMEOUT)
904 self.assertEqual(resp.status_code, 200)
905
906 if (
907 unit.name.startswith("postgresql/")
908- and "postgresql_blks_hit" not in resp.text
909+ and "postgresql_blks_hit" not in resp.text # noqa W503
910 ) or "cpu_usage_idle" not in resp.text:
911 time.sleep(DEFAULT_RETRIES_TIMEOUT)
912+
913 continue
914
915 logging.info("test_service: Unit {} is reachable".format(unit.name))
916@@ -90,6 +92,7 @@ class TestTelegraf(BaseTelegrafTest):
917 r"^zoneinfo_",
918 r"^processes_",
919 ]
920+
921 for re_pattern in re_patterns:
922 self.check_re_pattern(re_pattern, text)
923
924@@ -104,12 +107,14 @@ class TestTelegraf(BaseTelegrafTest):
925 install_method = model.get_application_config("telegraf")["install_method"][
926 "value"
927 ]
928+
929 if install_method == "deb":
930 telegraf_conf = DEB_TELEGRAF_CONF
931 else:
932 telegraf_conf = SNAP_TELEGRAF_CONF
933 cmd = "cat {}".format(telegraf_conf)
934 response = model.run_on_unit(self.lead_unit_name, cmd)
935+
936 if response["Code"] != "0":
937 self.fail(
938 "test_02_telegraf_logfile: could not read file {}".format(telegraf_conf)
939@@ -118,6 +123,7 @@ class TestTelegraf(BaseTelegrafTest):
940 for line in response["Stdout"].splitlines():
941 if line.strip() == 'logfile = "/var/log/telegraf/telegraf.log"':
942 logging.info("test_02_telegraf_logfile: logfile config parameter found")
943+
944 return
945
946 self.fail(
947@@ -127,25 +133,32 @@ class TestTelegraf(BaseTelegrafTest):
948 )
949
950 def test_03_system_service(self):
951- """Check that the right service is running, e.g. either the deb's or the snap's."""
952+ """Check that the right service is running.
953+
954+ e.g. either the deb's or the snap's.
955+ """
956 install_method = model.get_application_config("telegraf")["install_method"][
957 "value"
958 ]
959 services = {"deb": "telegraf", "snap": "snap.telegraf.telegraf"}
960+
961 for method in services.keys():
962 service = services[method]
963 cmd = "service {} status".format(service)
964 response = model.run_on_unit(self.lead_unit_name, cmd)
965+
966 if install_method == method and response["Code"] != "0":
967 self.fail(
968- "test_03_system_service: service {} should be running on {} but is not. "
969+ "test_03_system_service: service {} should be running "
970+ "on {} but is not. "
971 "install_method is {}.".format(
972 service, self.lead_unit_name, install_method
973 )
974 )
975 elif install_method != method and response["Code"] == "0":
976 self.fail(
977- "test_03_system_service: service {} is running on {} but shouldn't. "
978+ "test_03_system_service: service {} is running "
979+ "on {} but shouldn't. "
980 "install_method is {}.".format(
981 service, self.lead_unit_name, install_method
982 )
983diff --git a/src/tests/unit/__init__.py b/src/tests/unit/__init__.py
984index 28e9795..5f22a2b 100644
985--- a/src/tests/unit/__init__.py
986+++ b/src/tests/unit/__init__.py
987@@ -1,3 +1,5 @@
988+"""Unit test module."""
989+
990 import sys
991
992 sys.path.append(".")
993diff --git a/src/tests/unit/test_mysql.py b/src/tests/unit/test_mysql.py
994index 27f206d..9015600 100644
995--- a/src/tests/unit/test_mysql.py
996+++ b/src/tests/unit/test_mysql.py
997@@ -22,10 +22,11 @@ from unittest.mock import ANY, MagicMock, patch, sentinel
998 # Mock layer modules
999 import charms
1000
1001+import reactive
1002+
1003 promreg = MagicMock()
1004 charms.promreg = promreg
1005 sys.modules["charms.promreg"] = promreg
1006-import reactive
1007
1008
1009 class TestMySQL(unittest.TestCase):
1010diff --git a/src/tests/unit/test_postgresql.py b/src/tests/unit/test_postgresql.py
1011index 9192ea4..c011b32 100644
1012--- a/src/tests/unit/test_postgresql.py
1013+++ b/src/tests/unit/test_postgresql.py
1014@@ -17,7 +17,7 @@
1015 import os
1016 import sys
1017 import unittest
1018-from unittest.mock import ANY, call, MagicMock, patch, sentinel
1019+from unittest.mock import ANY, MagicMock, call, patch, sentinel
1020
1021 # Mock layer modules
1022 import charms
1023@@ -32,8 +32,8 @@ sys.modules["charms.apt"] = apt
1024 sys.modules["charms.layer"] = layer
1025 sys.modules["charms.promreg"] = promreg
1026
1027-import reactive
1028-from reactive import telegraf
1029+import reactive # noqa E402
1030+from reactive import telegraf # noqa E402
1031
1032
1033 class TestPostgreSQL(unittest.TestCase):
1034@@ -123,7 +123,7 @@ class TestPostgreSQL(unittest.TestCase):
1035 get_base_dir.return_value = "/etc/telegraf"
1036
1037 reactive.telegraf.render_postgresql_tmpl(
1038- [{"replica": "master", "conn_str": "my_conn_str", "server": "my_server"},]
1039+ [{"replica": "master", "conn_str": "my_conn_str", "server": "my_server"}]
1040 )
1041
1042 write_file.assert_called_once_with(
1043diff --git a/src/tests/unit/test_telegraf.py b/src/tests/unit/test_telegraf.py
1044index 1825fa6..57e7e3c 100644
1045--- a/src/tests/unit/test_telegraf.py
1046+++ b/src/tests/unit/test_telegraf.py
1047@@ -14,36 +14,38 @@
1048 # You should have received a copy of the GNU General Public License
1049 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1050
1051-"""actions.py tests"""
1052+"""Tests for actions.py."""
1053 import base64
1054-import os
1055 import getpass
1056 import grp
1057 import json
1058-import sys
1059+import os
1060 import subprocess
1061+import sys
1062 import unittest
1063 from textwrap import dedent
1064-from unittest.mock import patch, MagicMock, call
1065 from unittest import mock
1066+from unittest.mock import MagicMock, call, patch
1067
1068-import yaml
1069-import pytest
1070-import py
1071-
1072-from charms.reactive import bus, helpers, RelationBase, set_flag
1073 from charmhelpers.core.hookenv import Config
1074 from charmhelpers.core.templating import render
1075
1076-# Mock layer modules
1077 import charms
1078+from charms.reactive import RelationBase, bus, helpers, set_flag
1079+
1080+import py
1081
1082+import pytest
1083+
1084+import yaml
1085+
1086+# Mock layer modules
1087 promreg = MagicMock()
1088 charms.promreg = promreg
1089 sys.modules["charms.promreg"] = promreg
1090
1091-import reactive
1092-from reactive import telegraf
1093+import reactive # noqa E402
1094+from reactive import telegraf # noqa E402
1095
1096 UNIT_TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
1097 UNIT_TESTS_DATA_DIR = os.path.join(UNIT_TESTS_DIR, "data")
1098@@ -71,6 +73,7 @@ def setup(monkeypatch, tmpdir):
1099 # fix the args to use non-root, this is for callers that pass
1100 # owner/group as positional arguments like
1101 # charmhelpers.core.templating.render
1102+
1103 if len(a) > 2:
1104 if a[2] == "root" and a[3] == "root":
1105 # make all files writable by owner, as we need don't run as root
1106@@ -80,6 +83,7 @@ def setup(monkeypatch, tmpdir):
1107 kw["group"] = group
1108 # make all files writable by owner, as we need don't run as root
1109 kw["perms"] = 0o744
1110+
1111 return orig_write_file(*a, **kw)
1112
1113 monkeypatch.setattr(telegraf.host, "write_file", intercept_write_file)
1114@@ -94,6 +98,7 @@ def cleanup(request):
1115 unitdata._KV = None
1116 # rm unit-state.db file
1117 unit_state_db = os.path.join(telegraf.hookenv.charm_dir(), ".unit-state.db")
1118+
1119 if os.path.exists(unit_state_db):
1120 os.unlink(unit_state_db)
1121
1122@@ -134,6 +139,7 @@ def config(monkeypatch, temp_charm_dir):
1123 data = dict((k, v["default"]) for k, v in raw_config["options"].items())
1124 config = Config(data)
1125 monkeypatch.setattr(telegraf.hookenv, "config", lambda: config)
1126+
1127 return config
1128
1129
1130@@ -147,13 +153,14 @@ def configs_dir():
1131
1132
1133 def persist_state():
1134- """Fake persistent state by calling helpers that modify unitdata.kv"""
1135+ """Fake persistent state by calling helpers that modify unitdata.kv."""
1136 states = [
1137 k
1138 for k in bus.get_states().keys()
1139 if k.startswith("plugins") or k.startswith("extra_plugins")
1140 ]
1141 helpers.any_file_changed(telegraf.list_config_files())
1142+
1143 if states:
1144 helpers.data_changed("active_plugins", states)
1145
1146@@ -169,7 +176,7 @@ def check_sysstat_config(original, expected):
1147
1148
1149 def test_sadc_options_correct(monkeypatch):
1150- """If SADC_OPTIONS is already correct, should return None"""
1151+ """If SADC_OPTIONS is already correct, should return None."""
1152 original = dedent(
1153 """
1154 # some comment
1155@@ -183,7 +190,7 @@ def test_sadc_options_correct(monkeypatch):
1156
1157
1158 def test_sadc_options_correct_included(monkeypatch):
1159- """If SADC_OPTIONS already includes `-S XALL`, should return None"""
1160+ """If SADC_OPTIONS already includes `-S XALL`, should return None."""
1161 original = dedent(
1162 """
1163 # some comment
1164@@ -197,7 +204,7 @@ def test_sadc_options_correct_included(monkeypatch):
1165
1166
1167 def test_sadc_options_non_exist(monkeypatch):
1168- """If SADC_OPTIONS doesn't exist, should just append"""
1169+ """If SADC_OPTIONS doesn't exist, should just append."""
1170 original = dedent(
1171 """
1172 # some comment
1173@@ -217,7 +224,7 @@ def test_sadc_options_non_exist(monkeypatch):
1174
1175
1176 def test_sadc_options_commented(monkeypatch):
1177- """If SADC_OPTIONS exists but commented, should ignore and append"""
1178+ """If SADC_OPTIONS exists but commented, should ignore and append."""
1179 original = dedent(
1180 """
1181 # some comment
1182@@ -243,7 +250,7 @@ def test_sadc_options_commented(monkeypatch):
1183
1184
1185 def test_sadc_options_incorrect(monkeypatch):
1186- """If SADC_OPTIONS exists but not XALL, should replace"""
1187+ """If SADC_OPTIONS exists but not XALL, should replace."""
1188 original = dedent(
1189 """
1190 # some comment
1191@@ -267,7 +274,7 @@ def test_sadc_options_incorrect(monkeypatch):
1192
1193
1194 def test_sadc_options_incorrect_included(monkeypatch):
1195- """If SADC_OPTIONS exists but not XALL, should replace"""
1196+ """If SADC_OPTIONS exists but not XALL, should replace."""
1197 original = dedent(
1198 """
1199 # some comment
1200@@ -291,7 +298,7 @@ def test_sadc_options_incorrect_included(monkeypatch):
1201
1202
1203 def test_sadc_options_commented_line_not_touched(monkeypatch):
1204- """If SADC_OPTIONS exists but has no -S, should append"""
1205+ """If SADC_OPTIONS exists but has no -S, should append."""
1206 original = dedent(
1207 """
1208 # some comment
1209@@ -318,7 +325,7 @@ def test_sadc_options_commented_line_not_touched(monkeypatch):
1210
1211
1212 def test_sadc_options_s_missing(monkeypatch):
1213- """If SADC_OPTIONS exists but has no -S, should append"""
1214+ """If SADC_OPTIONS exists but has no -S, should append."""
1215 original = dedent(
1216 """
1217 # some comment
1218@@ -351,6 +358,7 @@ def test_telegraf_exec_metrics(monkeypatch, temp_config_dir):
1219 metrics = set(
1220 ["sockstat", "sockstat6", "softnet_stat", "buddyinfo", "zoneinfo", "netns"]
1221 )
1222+
1223 for metric in metrics:
1224 run_telegraf_exec_metrics("--metric", metric)
1225
1226@@ -370,9 +378,8 @@ def test_telegraf_exec_metrics(monkeypatch, temp_config_dir):
1227 )
1228
1229 # ensure config files exists after render
1230- run_telegraf_exec_metrics(
1231- "--render-config-files", "--configs-dir", configs_dir(),
1232- )
1233+ run_telegraf_exec_metrics("--render-config-files", "--configs-dir", configs_dir())
1234+
1235 for metric in metrics:
1236 assert os.path.isfile(os.path.join(configs_dir(), "{}.conf".format(metric)))
1237
1238@@ -386,9 +393,11 @@ def test_telegraf_exec_metrics(monkeypatch, temp_config_dir):
1239 ":".join(disabled_metrics),
1240 )
1241 # config files for disabled metrics should be removed.
1242+
1243 for metric in disabled_metrics:
1244 assert not os.path.isfile(os.path.join(configs_dir(), "{}.conf".format(metric)))
1245 # config files for other metrics should still be there
1246+
1247 for metric in metrics - disabled_metrics:
1248 assert os.path.isfile(os.path.join(configs_dir(), "{}.conf".format(metric)))
1249
1250@@ -619,7 +628,7 @@ inputs:
1251
1252
1253 def test_base_inputs_disabled_plugins(config):
1254- """Check disabled_plugins option is working for builtin inputs"""
1255+ """Check disabled_plugins option is working for builtin inputs."""
1256 config["disabled_plugins"] = "cpu:disk"
1257 content = telegraf.render_base_inputs()
1258
1259@@ -1246,7 +1255,7 @@ class TestGrafanaDashboard(unittest.TestCase):
1260 variable_end_string=">>",
1261 )
1262 mock_grafana.register_dashboard.assert_called_once_with(
1263- name=telegraf.GRAFANA_DASHBOARD_NAME, dashboard=mock_dashboard_dict,
1264+ name=telegraf.GRAFANA_DASHBOARD_NAME, dashboard=mock_dashboard_dict
1265 )
1266 mock_set_flag.assert_called_once_with("grafana.configured")
1267
1268diff --git a/src/tox.ini b/src/tox.ini
1269index 40c62d6..4977737 100644
1270--- a/src/tox.ini
1271+++ b/src/tox.ini
1272@@ -42,7 +42,8 @@ exclude =
1273 charmhelpers,
1274 mod,
1275 .build
1276-ignore = I100, D101, D102, D103, I201, D205, W504, D400, D401, D403, D209, D100, E403, I101, E501, N803, E226, E128, D200, E741, D202, E261, E402, D104, W503, E231 # TODO
1277+#ignore = I100, D101, D102, D103, I201, D205, W504, D400, D401, D403, D209, D100, E403, I101, E501, N803, E226, E128, D200, E741, D202, E261, E402, D104, W503, E231 # TODO
1278+ignore = D100, D101, D102, D103
1279
1280 max-line-length = 88
1281 max-complexity = 10

Subscribers

People subscribed via source and target branches

to all changes: