Merge ~jfguedez/charm-telegraf:backport-1891781-1892129 into charm-telegraf:stable/20.08

Proposed by Jose Guedez
Status: Merged
Approved by: James Hebden
Approved revision: 0806008b573c3b57ed2423157a5e52badafc3620
Merged at revision: 0806008b573c3b57ed2423157a5e52badafc3620
Proposed branch: ~jfguedez/charm-telegraf:backport-1891781-1892129
Merge into: charm-telegraf:stable/20.08
Diff against target: 275 lines (+129/-23)
4 files modified
src/files/telegraf_exec_metrics.py (+18/-7)
src/files/telegraf_sudoers (+1/-0)
src/reactive/telegraf.py (+35/-11)
src/tests/unit/test_telegraf.py (+75/-5)
Reviewer Review Type Date Requested Status
James Hebden (community) Approve
Celia Wang Approve
Review via email: mp+390128@code.launchpad.net

Commit message

Backport LP1891781/LP1892129

To post a comment you must log in.
Revision history for this message
Celia Wang (ziyiwang) wrote :

LGTM

review: Approve
Revision history for this message
James Hebden (ec0) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/files/telegraf_exec_metrics.py b/src/files/telegraf_exec_metrics.py
2index d9b3402..7f9bfc3 100755
3--- a/src/files/telegraf_exec_metrics.py
4+++ b/src/files/telegraf_exec_metrics.py
5@@ -313,6 +313,12 @@ class NetNSCmdMetric(CmdMetric):
6 return items
7
8
9+# Openvswitch metric collectors
10+# The OVS collectors use the "ovs_" prefix and should not be installed in a non-OVS box
11+# the deployment logic will skip them by including them in the "disabled_plugins" list
12+# more context in LP1891781
13+
14+
15 @register_metric
16 class OvsDumpFlowsCmdMetric(CmdMetric):
17 name = "ovs_dump_flows"
18@@ -342,15 +348,20 @@ class OvsDumpFlowsCmdMetric(CmdMetric):
19 """
20 flows = {}
21
22- ovs_cmd = "ovs-ofctl"
23-
24- if not shutil.which(ovs_cmd):
25- LOG.error('openvswitch command "%s" is not available, exit', ovs_cmd)
26+ ovs_vsctl = "ovs-vsctl"
27+ ovs_ofctl = "ovs-ofctl"
28
29- return flows
30+ for ovs_cmd in [ovs_vsctl, ovs_ofctl]:
31+ if not shutil.which(ovs_cmd):
32+ LOG.error(
33+ 'openvswitch command "%s" is not available, aborting', ovs_cmd
34+ )
35+ return flows
36
37- for bridge in ["br-int", "br-tun", "br-data"]:
38- cmd = ["sudo", ovs_cmd, "dump-flows", bridge]
39+ # query OVS for the list of bridges
40+ bridges = self.get_cmd_output(["sudo", ovs_vsctl, "list-br"]).split()
41+ for bridge in bridges:
42+ cmd = ["sudo", ovs_ofctl, "dump-flows", bridge]
43 output = self.get_cmd_output(cmd)
44 flows[bridge] = output
45
46diff --git a/src/files/telegraf_sudoers b/src/files/telegraf_sudoers
47index d1776b3..6be9f54 100644
48--- a/src/files/telegraf_sudoers
49+++ b/src/files/telegraf_sudoers
50@@ -1,4 +1,5 @@
51 Defaults:telegraf !requiretty
52
53 telegraf ALL = (root) NOPASSWD: /usr/bin/ovs-ofctl
54+telegraf ALL = (root) NOPASSWD: /usr/bin/ovs-vsctl
55 telegraf ALL = (root) NOPASSWD: /usr/bin/ovs-appctl
56diff --git a/src/reactive/telegraf.py b/src/reactive/telegraf.py
57index 4b72eab..058380c 100644
58--- a/src/reactive/telegraf.py
59+++ b/src/reactive/telegraf.py
60@@ -48,12 +48,15 @@ from charms.reactive import (
61 )
62 from charms.reactive.bus import get_states
63
64+from files.telegraf_exec_metrics import METRICS
65+
66 from jinja2 import Environment, FileSystemLoader, Template, exceptions
67
68 import netifaces
69
70 import yaml
71
72+
73 DEB_BASE_DIR = "/etc/telegraf"
74 SNAP_BASE_DIR = "/var/snap/telegraf/current"
75 SUDOERS_DIR = "/etc/sudoers.d"
76@@ -219,7 +222,6 @@ def get_base_inputs():
77 "python": sys.executable,
78 "files_dir": get_files_dir(),
79 "disabled_plugins": disabled_plugins,
80- "openvswitch": host.service_running("openvswitch-switch"),
81 "conntrack": conntrack,
82 }
83
84@@ -502,12 +504,43 @@ def configure_telegraf(): # noqa: C901
85
86 render_socket_listener_config(context=context)
87
88+ disabled_plugins = config["disabled_plugins"]
89+
90+ # handle plugin configuration for OVS
91+ if host.service_running("openvswitch-switch"):
92+ # add sudoers file for telegraf if openvswitch is running
93+ sudoers_filename = "telegraf_sudoers"
94+ src = os.path.join(get_files_dir(), sudoers_filename)
95+ dst = os.path.join(SUDOERS_DIR, sudoers_filename)
96+ shutil.copy(src, dst)
97+ os.chmod(dst, 0o640)
98+ else:
99+ # disable the OVS checks if the service is not curently running
100+ # no need to handle duplicates here, as those are handled during
101+ # template rendering later
102+ ovs_metric_names = [
103+ metric_name for metric_name in METRICS if metric_name.startswith("ovs_")
104+ ]
105+
106+ hookenv.log(
107+ "disabling the following metrics, since OVS is not available: {}".format(
108+ ovs_metric_names
109+ )
110+ )
111+
112+ ovs_disabled_plugins = ":".join(ovs_metric_names)
113+
114+ if disabled_plugins:
115+ disabled_plugins = disabled_plugins + ":" + ovs_disabled_plugins
116+ else:
117+ disabled_plugins = ovs_disabled_plugins
118+
119 telegraf_exec_metrics = os.path.join(get_files_dir(), "telegraf_exec_metrics.py")
120 cmd = [
121 telegraf_exec_metrics,
122 "--render-config-files",
123 "--disabled-metrics",
124- config["disabled_plugins"],
125+ disabled_plugins,
126 "--configs-dir",
127 get_configs_dir(), # unit test can monkeypatch this path
128 "--python",
129@@ -524,15 +557,6 @@ def configure_telegraf(): # noqa: C901
130 context=context,
131 )
132
133- # add sudoers file for telegraf if openvswitch is running
134-
135- if host.service_running("openvswitch-switch"):
136- sudoers_filename = "telegraf_sudoers"
137- src = os.path.join(get_files_dir(), sudoers_filename)
138- dst = os.path.join(SUDOERS_DIR, sudoers_filename)
139- shutil.copy(src, dst)
140- os.chmod(dst, 0o640)
141-
142 for service in [DEB_SERVICE, SNAP_SERVICE]:
143 if service == get_service():
144 host.service_resume(service)
145diff --git a/src/tests/unit/test_telegraf.py b/src/tests/unit/test_telegraf.py
146index 57e7e3c..8ef82ec 100644
147--- a/src/tests/unit/test_telegraf.py
148+++ b/src/tests/unit/test_telegraf.py
149@@ -20,6 +20,7 @@ import getpass
150 import grp
151 import json
152 import os
153+import shutil
154 import subprocess
155 import sys
156 import unittest
157@@ -27,6 +28,7 @@ from textwrap import dedent
158 from unittest import mock
159 from unittest.mock import MagicMock, call, patch
160
161+from charmhelpers.core import host
162 from charmhelpers.core.hookenv import Config
163 from charmhelpers.core.templating import render
164
165@@ -40,11 +42,18 @@ import pytest
166 import yaml
167
168 # Mock layer modules
169+apt = MagicMock()
170+layer = MagicMock()
171 promreg = MagicMock()
172+charms.apt = apt
173+charms.layer = layer
174 charms.promreg = promreg
175+sys.modules["charms.apt"] = apt
176+sys.modules["charms.layer"] = layer
177 sys.modules["charms.promreg"] = promreg
178
179 import reactive # noqa E402
180+from files.telegraf_exec_metrics import METRICS # noqa E402
181 from reactive import telegraf # noqa E402
182
183 UNIT_TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
184@@ -165,6 +174,12 @@ def persist_state():
185 helpers.data_changed("active_plugins", states)
186
187
188+def run_telegraf_exec_metrics(*args):
189+ script = os.path.join(telegraf.get_files_dir(), "telegraf_exec_metrics.py")
190+ cmd = [script] + list(args)
191+ assert 0 == subprocess.call(cmd)
192+
193+
194 # Tests
195
196
197@@ -350,11 +365,6 @@ def test_sadc_options_s_missing(monkeypatch):
198
199
200 def test_telegraf_exec_metrics(monkeypatch, temp_config_dir):
201- def run_telegraf_exec_metrics(*args):
202- script = os.path.join(telegraf.get_files_dir(), "telegraf_exec_metrics.py")
203- cmd = [script] + list(args)
204- assert 0 == subprocess.call(cmd)
205-
206 metrics = set(
207 ["sockstat", "sockstat6", "softnet_stat", "buddyinfo", "zoneinfo", "netns"]
208 )
209@@ -402,6 +412,66 @@ def test_telegraf_exec_metrics(monkeypatch, temp_config_dir):
210 assert os.path.isfile(os.path.join(configs_dir(), "{}.conf".format(metric)))
211
212
213+def test_telegraf_exec_metrics_enable_ovs(monkeypatch, config, temp_config_dir):
214+ config["disabled_plugins"] = "" # ensure no metrics are initially disabled
215+ monkeypatch.setattr(telegraf, "check_prometheus_port", lambda a, b: None)
216+ monkeypatch.setattr(host, "service_running", lambda svc: True)
217+ telegraf.configure_telegraf() # this will generate the check definitions
218+
219+ # get the names of all the OVS metrics
220+ ovs_metric_names = [
221+ metric_name for metric_name in METRICS if metric_name.startswith("ovs_")
222+ ]
223+
224+ # check that the check definitions for the OVS checks are in place
225+ for metric in ovs_metric_names:
226+ assert os.path.isfile(os.path.join(configs_dir(), "{}.conf".format(metric)))
227+
228+
229+def test_telegraf_exec_metrics_disable_ovs(monkeypatch, config):
230+ def mock_check_call(*args, **kwargs):
231+ disabled_metrics = args[0][3]
232+ assert disabled_metrics # this should not be empty
233+ for metric in disabled_metrics.split(":"):
234+ assert metric.startswith("ovs_")
235+
236+ config["disabled_plugins"] = "" # ensure no metrics are initially disabled
237+ monkeypatch.setattr(telegraf, "check_prometheus_port", lambda a, b: None)
238+ monkeypatch.setattr(host, "service_running", lambda svc: False)
239+ monkeypatch.setattr(subprocess, "check_call", mock_check_call)
240+ telegraf.configure_telegraf()
241+
242+
243+def test_ovs_dump_flows_cmd_metric(monkeypatch):
244+ def mock_check_output(*args, **kwargs):
245+ """Mock the subprocess calls to the OVS tools."""
246+ cmd = args[0][1]
247+ if cmd == "ovs-vsctl":
248+ return b"br-ex\n"
249+ elif (
250+ cmd == "ovs-ofctl" and args[0][3] == "br-ex"
251+ ): # sudo ovs-ofctl dump-flows br-ex
252+ return (
253+ b"NXST_FLOW reply (xid=0x4):\n"
254+ b' cookie=0x3711311ec90146a9, duration=247966.151s, table=0, n_packets=0, n_bytes=0, priority=2,in_port="phy-br-ex" actions=drop\n' # noqa E501
255+ b" cookie=0x3711311ec90146a9, duration=247966.175s, table=0, n_packets=2192, n_bytes=156148, priority=0 actions=NORMAL\n" # noqa E501
256+ )
257+ else:
258+ raise Exception("unexpected argument: {}".format(args))
259+
260+ monkeypatch.setattr(subprocess, "check_output", mock_check_output)
261+ monkeypatch.setattr(shutil, "which", lambda path: True)
262+
263+ from files.telegraf_exec_metrics import OvsDumpFlowsCmdMetric
264+
265+ metric = OvsDumpFlowsCmdMetric()
266+
267+ raw_output = metric.get_input_content()
268+ parsed_output = metric.parse(raw_output)
269+ expected_output = [{"bridge": "br-ex", "count": 2}]
270+ assert parsed_output == expected_output
271+
272+
273 def test_get_remote_unit_name_juju2(monkeypatch):
274 monkeypatch.undo()
275 monkeypatch.setitem(os.environ, "JUJU_PRINCIPAL_UNIT", "remote/0")

Subscribers

People subscribed via source and target branches

to all changes: