Merge ~xavpaice/charm-nrpe:lp1937888 into charm-nrpe:master

Proposed by Xav Paice
Status: Merged
Approved by: James Troup
Approved revision: 4344ba9b531ddeabd017b53ebaa468900679315c
Merged at revision: 74ca861645689f8eeeeebea7175d7c4b7a35f68d
Proposed branch: ~xavpaice/charm-nrpe:lp1937888
Merge into: charm-nrpe:master
Diff against target: 269 lines (+115/-56)
4 files modified
hooks/nrpe_helpers.py (+29/-42)
tests/functional/tests/bundles/focal.yaml (+1/-0)
tests/functional/tests/tests.yaml (+3/-1)
tests/unit/test_nrpe_helpers.py (+82/-13)
Reviewer Review Type Date Requested Status
🤖 prod-jenkaas-bootstack (community) continuous-integration Approve
James Troup (community) Needs Fixing
BootStack Reviewers Pending
Review via email: mp+408524@code.launchpad.net

Commit message

Bind NRPE to local address

    In cases where we specify to use public address and/or Juju returns a
    public address regardless of the setting, we cannot use that address for
    binding the NRPE service.

    This change ensures get_local_ingress_address returns the local address
    for binding the nrpe service, and if configured to do so returns the
    public address for relation data and NRPE host exports.

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
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
James Troup (elmo) wrote :

See comment inline.

review: Needs Fixing
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

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

I've rewritten the change and added some tests.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :
review: Approve (continuous-integration)
Revision history for this message
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote :

A CI job is currently in progress. A follow up comment will be added when it completes.

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

Change successfully merged at revision 74ca861645689f8eeeeebea7175d7c4b7a35f68d

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/hooks/nrpe_helpers.py b/hooks/nrpe_helpers.py
2index 8384d76..b4f40b9 100644
3--- a/hooks/nrpe_helpers.py
4+++ b/hooks/nrpe_helpers.py
5@@ -77,42 +77,42 @@ class Monitors(dict):
6 return mons
7
8
9-def get_local_ingress_address(binding):
10+def get_ingress_address(binding, external=False):
11 """Get ingress IP address for a binding.
12
13- binding - e.g. 'monitors'
14+ Returns a local IP address for incoming requests to NRPE.
15+
16+ :param binding: name of the binding, e.g. 'monitors'
17+ :param external: bool, if True return the public address if charm config requests
18+ otherwise return the local address which would be used for incoming
19+ nrpe requests.
20 """
21 # using network-get to retrieve the address details if available.
22 hookenv.log("Getting ingress IP address for binding %s" % binding)
23+ if hookenv.config("nagios_address_type").lower() == "public" and external:
24+ return hookenv.unit_get("public-address")
25+
26+ ip_address = None
27 try:
28 network_info = hookenv.network_get(binding)
29 if network_info is not None and "ingress-addresses" in network_info:
30- hookenv.log("Using ingress-addresses")
31- # workaround lp#1897261
32 try:
33 ip_address = network_info["bind-addresses"][0]["addresses"][0][
34 "address"
35 ]
36+ hookenv.log("Using ingress-addresses, found %s" % ip_address)
37 except KeyError:
38- # ignore KeyError and populate ip_address per old method
39- ip_address = None
40- if ip_address not in network_info["ingress-addresses"]:
41- ip_address = network_info["ingress-addresses"][0]
42- hookenv.log(ip_address)
43- return ip_address
44- except (NotImplementedError, FileNotFoundError):
45- # We'll fallthrough to the Pre 2.3 code below.
46- pass
47-
48- # Pre 2.3 output
49- try:
50- ip_address = hookenv.network_get_primary_address(binding)
51- hookenv.log("Using primary-addresses")
52- except NotImplementedError:
53- # pre Juju 2.0
54- ip_address = hookenv.unit_private_ip()
55- hookenv.log("Using unit_private_ip")
56- hookenv.log(ip_address)
57+ hookenv.log("Using primary-addresses")
58+ ip_address = hookenv.network_get_primary_address(binding)
59+
60+ except (NotImplementedError, FileNotFoundError) as e:
61+ hookenv.log(
62+ "Unable to determine inbound IP address for binding {} with {}".format(
63+ binding, e
64+ ),
65+ level=hookenv.ERROR,
66+ )
67+
68 return ip_address
69
70
71@@ -200,7 +200,8 @@ class MonitorsRelation(helpers.RelationContext):
72
73 def provide_data(self):
74 """Return relation info."""
75- address = get_local_ingress_address("monitors")
76+ # get the address to send to Nagios for host definition
77+ address = get_ingress_address("monitors", external=True)
78
79 relation_info = {
80 "target-id": self.principal_relation.nagios_hostname(),
81@@ -299,24 +300,10 @@ class NagiosInfo(dict):
82 )
83 self["nagios_hostname"] = self.principal_relation.nagios_hostname()
84
85- address = None
86- if hookenv.config("nagios_address_type").lower() == "public":
87- address = hookenv.unit_get("public-address")
88- elif hookenv.config("nagios_master") != "None":
89- # Try to work out the correct interface/IP. We can't use both
90- # network-get nor 'unit-get private-address' because both can
91- # return the wrong IP on systems with more than one interface
92- # (LP: #1736050).
93- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
94- s.connect((hookenv.config("nagios_master").split(",")[0], 80))
95- address = s.getsockname()[0]
96- s.close()
97- # Fallback to unit-get private-address
98- if not address:
99- address = hookenv.unit_get("private-address")
100-
101- self["nagios_ipaddress"] = address
102- self["nrpe_ipaddress"] = get_local_ingress_address("monitors")
103+ # export_host.cfg.tmpl host definition for Nagios
104+ self["nagios_ipaddress"] = get_ingress_address("monitors", external=True)
105+ # Address configured for NRPE to listen on
106+ self["nrpe_ipaddress"] = get_ingress_address("monitors")
107
108 self["dont_blame_nrpe"] = "1" if hookenv.config("dont_blame_nrpe") else "0"
109 self["debug"] = "1" if hookenv.config("debug") else "0"
110diff --git a/tests/functional/tests/bundles/focal.yaml b/tests/functional/tests/bundles/focal.yaml
111index 2689a08..33bea11 100644
112--- a/tests/functional/tests/bundles/focal.yaml
113+++ b/tests/functional/tests/bundles/focal.yaml
114@@ -3,6 +3,7 @@ applications:
115 rabbitmq-server:
116 charm: cs:rabbitmq-server
117 num_units: 1
118+ constraints: root-disk=8G
119 container:
120 charm: cs:ubuntu
121 num_units: 1
122diff --git a/tests/functional/tests/tests.yaml b/tests/functional/tests/tests.yaml
123index 8034f51..a661597 100644
124--- a/tests/functional/tests/tests.yaml
125+++ b/tests/functional/tests/tests.yaml
126@@ -1,7 +1,9 @@
127 charm_name: nrpe
128 gate_bundles:
129- - xenial
130+ - focal
131 - bionic
132+ - xenial
133+smoke_bundles:
134 - focal
135 tests:
136 - tests.nrpe_tests.TestNrpe
137diff --git a/tests/unit/test_nrpe_helpers.py b/tests/unit/test_nrpe_helpers.py
138index fc17454..949eb92 100644
139--- a/tests/unit/test_nrpe_helpers.py
140+++ b/tests/unit/test_nrpe_helpers.py
141@@ -4,6 +4,7 @@ from unittest import mock
142
143 import netifaces
144
145+import nrpe_helpers
146 from nrpe_helpers import match_cidr_to_ifaces
147
148
149@@ -11,39 +12,107 @@ class TestMatchCidrToIfaces(unittest.TestCase):
150 """Test match_cidr_to_ifaces helper function."""
151
152 mock_iface_ip_data = {
153- 'lo': '127.0.0.1',
154- 'eno1': '10.0.0.0',
155- 'eno2': '10.1.0.0',
156- 'fan-252': '252.0.0.1',
157+ "lo": "127.0.0.1",
158+ "eno1": "10.0.0.0",
159+ "eno2": "10.1.0.0",
160+ "fan-252": "252.0.0.1",
161 }
162
163 def test_single_dev_match(self):
164 """Test single interface match."""
165- self._run_mocked_test('10.0.0.0/16', ['eno1'])
166+ self._run_mocked_test("10.0.0.0/16", ["eno1"])
167
168 def test_multi_dev_match(self):
169 """Test multiple interface match."""
170- self._run_mocked_test('10.0.0.0/8', ['eno1', 'eno2'])
171+ self._run_mocked_test("10.0.0.0/8", ["eno1", "eno2"])
172
173 def test_no_dev_match(self):
174 """Test no interface match."""
175- self._run_mocked_test('192.168.0.0/16', [])
176+ self._run_mocked_test("192.168.0.0/16", [])
177
178 def test_cidr_with_host_bits_set(self):
179 """Test invalid CIDR input (e.g. "eno1")."""
180 with self.assertRaises(Exception):
181- match_cidr_to_ifaces('10.1.2.3/8') # Should be 10.0.0.0/8
182+ match_cidr_to_ifaces("10.1.2.3/8") # Should be 10.0.0.0/8
183
184 def test_iface_passed_in_as_cidr(self):
185 """Test invalid CIDR input (e.g. "eno1")."""
186 with self.assertRaises(Exception):
187- match_cidr_to_ifaces('eno1')
188+ match_cidr_to_ifaces("eno1")
189
190- @mock.patch('netifaces.ifaddresses')
191- @mock.patch('netifaces.interfaces')
192+ @mock.patch("netifaces.ifaddresses")
193+ @mock.patch("netifaces.interfaces")
194 def _run_mocked_test(self, cidr, matches, ifaces_mock, addrs_mock):
195 iface_ip_tuples = list(self.mock_iface_ip_data.items())
196 ifaces_mock.return_value = [t[0] for t in iface_ip_tuples]
197- addrs_mock.side_effect = [{netifaces.AF_INET: [{'addr': t[1]}]} for t in
198- iface_ip_tuples]
199+ addrs_mock.side_effect = [
200+ {netifaces.AF_INET: [{"addr": t[1]}]} for t in iface_ip_tuples
201+ ]
202 self.assertEqual(match_cidr_to_ifaces(cidr), matches)
203+
204+
205+class TestIngressAddress(unittest.TestCase):
206+ """Test functions to provide a suitable ingress address."""
207+
208+ @mock.patch("nrpe_helpers.hookenv.config")
209+ @mock.patch("nrpe_helpers.hookenv.network_get")
210+ def test_get_bind_address(self, mock_network_get, mock_config):
211+ """Prove we get a local IP address for interface binding."""
212+ mock_config.return_value = "private"
213+ mock_network_get.return_value = {
214+ "bind-addresses": [
215+ {
216+ "mac-address": "06:f1:3a:74:ad:fe",
217+ "interface-name": "ens5",
218+ "addresses": [
219+ {
220+ "hostname": "",
221+ "address": "172.31.29.247",
222+ "cidr": "172.31.16.0/20",
223+ }
224+ ],
225+ "macaddress": "06:f1:3a:74:ad:fe",
226+ "interfacename": "ens5",
227+ }
228+ ],
229+ "egress-subnets": ["3.8.134.119/32"],
230+ "ingress-addresses": ["3.8.134.119"],
231+ }
232+ self.assertEqual(nrpe_helpers.get_ingress_address("mockbinding"),
233+ "172.31.29.247")
234+
235+ @mock.patch("nrpe_helpers.hookenv.config")
236+ @mock.patch("nrpe_helpers.hookenv.network_get")
237+ def test_get_private_address(self, mock_network_get, mock_config):
238+ """Prove we get a local IP address for Nagios relation."""
239+ mock_config.return_value = "private"
240+ mock_network_get.return_value = {
241+ "bind-addresses": [
242+ {
243+ "mac-address": "06:f1:3a:74:ad:fe",
244+ "interface-name": "ens5",
245+ "addresses": [
246+ {
247+ "hostname": "",
248+ "address": "172.31.29.247",
249+ "cidr": "172.31.16.0/20",
250+ }
251+ ],
252+ "macaddress": "06:f1:3a:74:ad:fe",
253+ "interfacename": "ens5",
254+ }
255+ ],
256+ "egress-subnets": ["3.8.134.119/32"],
257+ "ingress-addresses": ["3.8.134.119"],
258+ }
259+ self.assertEqual(nrpe_helpers.get_ingress_address("mockbinding", external=True),
260+ "172.31.29.247")
261+
262+ @mock.patch("nrpe_helpers.hookenv.config")
263+ @mock.patch("nrpe_helpers.hookenv.unit_get")
264+ def test_get_public_address(self, mock_unit_get, mock_config):
265+ """Prove we get a public IP address for Nagios relation."""
266+ mock_config.return_value = "public"
267+ mock_unit_get.return_value = "1.2.3.4"
268+ self.assertEqual(nrpe_helpers.get_ingress_address("mockbinding", external=True),
269+ "1.2.3.4")

Subscribers

People subscribed via source and target branches

to all changes: