Merge ~exsdev/juju-verify:lp1916231-neutron-gateway-juju-verifier-plugin into ~canonical-solutions-engineering/juju-verify:master

Proposed by Edin S
Status: Rejected
Rejected by: Martin Kalcok
Proposed branch: ~exsdev/juju-verify:lp1916231-neutron-gateway-juju-verifier-plugin
Merge into: ~canonical-solutions-engineering/juju-verify:master
Diff against target: 518 lines (+477/-0)
4 files modified
juju_verify/utils/unit.py (+6/-0)
juju_verify/verifiers/__init__.py (+2/-0)
juju_verify/verifiers/neutron_gateway.py (+159/-0)
tests/unit/verifiers/test_neutron_gateway.py (+310/-0)
Reviewer Review Type Date Requested Status
Martin Kalcok (community) Needs Fixing
Robert Gildein Needs Fixing
🤖 prod-jenkaas-bootstack (community) continuous-integration Needs Fixing
Review via email: mp+399961@code.launchpad.net

Commit message

Add Juju verify plugin for Neutron Gateway

Description of the change

Fixes LP#1916231

The other half of the required code (the required Charm actions) can be found here: https://review.opendev.org/c/openstack/charm-neutron-gateway/+/782896

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
🤖 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 :

FAILED: Continuous integration, rev:ddf29dc6f8628857a266a8223d32915d657e12b8

No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want jenkins to rebuild you need to trigger it yourself):
https://code.launchpad.net/~exsdev/juju-verify/+git/juju-verify/+merge/399961/+edit-commit-message

https://jenkins.canonical.com/bootstack/job/juju-verify-test/50/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/bootstack/job/generic-make-test/71/
    None: https://jenkins.canonical.com/bootstack/job/lp-update-mp/50/

Click here to trigger a rebuild:
https://jenkins.canonical.com/bootstack/job/juju-verify-test/50//rebuild

review: Needs Fixing (continuous-integration)
f244ee1... by Edin S

NGW: fix lint issues including typing information

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)
8c7ff94... by Edin S

NGW: fix issues raised by pylint

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)
b3f7b69... by Edin S

NGW: Add test for verifying reboot/shutdown

bc56476... by Edin S

NGW: Add test for getting unit's hostname

83d72cb... by Edin S

NGW: Add test for getting a unit's agent resource list

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
Edin S (exsdev) wrote :

NeutronGateway.get_all_ngw_units() currently doesn't have a unit test for it, but is well-suited for functional tests.

Revision history for this message
Robert Gildein (rgildein) :
review: Needs Fixing
Revision history for this message
Martin Kalcok (martin-kalcok) wrote :

I have few inline suggestions here, otherwise it looks good, thanks.

Regarding the code coverage, I think it's important that we keep 100% coverage in unit tests unless there's a really good reason why something is not unit-testable. Anyway, that's not high priority and can be done by someone else if you won't manage to finish it by the end of your rotation.

review: Needs Fixing
Revision history for this message
Martin Kalcok (martin-kalcok) wrote :

juju-verify development was moved to https://github.com/canonical/juju-verify

Unmerged commits

83d72cb... by Edin S

NGW: Add test for getting a unit's agent resource list

bc56476... by Edin S

NGW: Add test for getting unit's hostname

b3f7b69... by Edin S

NGW: Add test for verifying reboot/shutdown

8c7ff94... by Edin S

NGW: fix issues raised by pylint

f244ee1... by Edin S

NGW: fix lint issues including typing information

ddf29dc... by Edin S

Add NGW tests for warning about manual failover of HA routers

ff315e1... by Edin S

Add NGW tests for determining redundancy of resources

20f6288... by Edin S

Add NGW tests for generating list of resources that will remain online

ebd233b... by Edin S

Add NGW tests for generating list of resources to be shutdown

e1e6433... by Edin S

Add NGW tests for getting resource list

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/juju_verify/utils/unit.py b/juju_verify/utils/unit.py
2index 05a9785..f672b20 100644
3--- a/juju_verify/utils/unit.py
4+++ b/juju_verify/utils/unit.py
5@@ -64,6 +64,12 @@ def run_action_on_units(
6 return result_map
7
8
9+def run_action_on_unit(unit: Unit, action: str, **params: str) -> Any:
10+ """Run Juju action on single unit, returning the result."""
11+ results = run_action_on_units([unit], action=action, **params)
12+ return results[unit.entity_id]
13+
14+
15 def parse_charm_name(charm_url: str) -> str:
16 """Parse charm name from full charm url.
17
18diff --git a/juju_verify/verifiers/__init__.py b/juju_verify/verifiers/__init__.py
19index 4400e0f..3908db2 100644
20--- a/juju_verify/verifiers/__init__.py
21+++ b/juju_verify/verifiers/__init__.py
22@@ -28,6 +28,7 @@ from juju_verify.verifiers.base import BaseVerifier
23 from juju_verify.verifiers.ceph import CephOsd
24 from juju_verify.verifiers.nova_compute import NovaCompute
25 from juju_verify.verifiers.result import Result
26+from juju_verify.verifiers.neutron_gateway import NeutronGateway
27
28 logger = logging.getLogger(__name__)
29
30@@ -35,6 +36,7 @@ logger = logging.getLogger(__name__)
31 SUPPORTED_CHARMS = {
32 'nova-compute': NovaCompute,
33 'ceph-osd': CephOsd,
34+ 'neutron-gateway': NeutronGateway,
35 }
36
37
38diff --git a/juju_verify/verifiers/neutron_gateway.py b/juju_verify/verifiers/neutron_gateway.py
39new file mode 100644
40index 0000000..d818ef0
41--- /dev/null
42+++ b/juju_verify/verifiers/neutron_gateway.py
43@@ -0,0 +1,159 @@
44+# Copyright 2021 Canonical Limited.
45+#
46+# This file is part of juju-verify.
47+#
48+# juju-verify is free software: you can redistribute it and/or modify it under
49+# the terms of the GNU General Public License as published by the Free Software
50+# Foundation, either version 3 of the License, or (at your option) any later
51+# version.
52+#
53+# juju-verify is distributed in the hope that it will be useful, but WITHOUT
54+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
55+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
56+# details.
57+#
58+# You should have received a copy of the GNU General Public License along with
59+# this program. If not, see https://www.gnu.org/licenses/.
60+"""neutron-gateway verification."""
61+import json
62+from typing import Dict, List
63+
64+from juju.unit import Unit
65+
66+from juju_verify.verifiers.base import BaseVerifier, Result
67+from juju_verify.verifiers.result import aggregate_results
68+from juju_verify.utils.action import data_from_action
69+from juju_verify.utils.unit import run_action_on_unit
70+
71+
72+def get_unit_hostname(unit: Unit) -> str:
73+ """Return name of the host on which the unit is running."""
74+ get_hostname_action = run_action_on_unit(unit, "node-name")
75+ hostname = data_from_action(get_hostname_action, "node-name")
76+ return hostname
77+
78+
79+def get_unit_resource_list(unit: Unit, get_resource_action_name: str) -> List[dict]:
80+ """Given a get resource action, return the relevant resources on the unit."""
81+ get_resource_action = run_action_on_unit(unit, get_resource_action_name)
82+ action_name_res = NeutronGateway.action_name_result_map[get_resource_action_name]
83+ resource_list_json = data_from_action(get_resource_action, action_name_res)
84+ resource_list = json.loads(resource_list_json)
85+ return resource_list
86+
87+
88+class NeutronGateway(BaseVerifier):
89+ """Implementation of verification checks for the neutron-gateway charm."""
90+
91+ NAME = 'neutron-gateway'
92+ action_name_result_map = {"get-status-routers": "router-list",
93+ "get-status-dhcp": "dhcp-networks",
94+ "get-status-lb": "load-balancers"}
95+ action_name_failure_string_map = {"get-status-routers": ("The following routers are "
96+ "non-redundant: {}."),
97+ "get-status-dhcp": ("The following DHCP networks "
98+ "are non-redundant: {}"),
99+ "get-status-lb": ("The following LBaasV2 LBs were "
100+ "found: {}. LBaasV2 does not "
101+ "offer HA.")}
102+
103+ def __init__(self, units: List[Unit]) -> None:
104+ """Neutron Gateway verifier constructor."""
105+ super().__init__(units)
106+ self.cache_action_resource_list_map: Dict[str, List] = {}
107+
108+ def get_all_ngw_units(self) -> List[Unit]:
109+ """Get all neutron-gateway units, including those not being shutdown."""
110+ application_name = NeutronGateway.NAME
111+ for rel in self.model.relations:
112+ if rel.matches("{}:cluster".format(application_name)):
113+ application = rel.applications.pop()
114+ all_ngw_units = application.units
115+ return all_ngw_units
116+
117+ def get_resource_list(self, get_resource_action_name: str) -> List[dict]:
118+ """Given a get resource action, return matching resources from all units."""
119+ try:
120+ return self.cache_action_resource_list_map[get_resource_action_name]
121+ except KeyError:
122+ pass
123+
124+ resource_list = []
125+ shutdown_hostname_list = [get_unit_hostname(unit) for unit in self.units]
126+
127+ for unit in self.get_all_ngw_units():
128+ hostname = get_unit_hostname(unit)
129+ host_resource_list = get_unit_resource_list(unit,
130+ get_resource_action_name)
131+
132+ # add host metadata to resource
133+ for resource in host_resource_list:
134+ resource["host"] = hostname
135+ resource["juju-entity-id"] = unit.entity_id
136+ resource["shutdown"] = False
137+
138+ if hostname in shutdown_hostname_list:
139+ resource["shutdown"] = True
140+
141+ resource_list.append(resource)
142+
143+ self.cache_action_resource_list_map[get_resource_action_name] = resource_list
144+ return resource_list
145+
146+ def get_shutdown_resource_list(self, get_resource_action_name: str) -> List[dict]:
147+ """Return a list of resources matching action that are going to be shutdown."""
148+ res_list = self.get_resource_list(get_resource_action_name)
149+ return [r for r in res_list if r["shutdown"] and r["status"] == "ACTIVE"]
150+
151+ def get_online_resource_list(self, get_resource_action_name: str) -> List[dict]:
152+ """Return a list of resources matching action, that will remain online."""
153+ res_list = self.get_resource_list(get_resource_action_name)
154+ return [r for r in res_list if not r["shutdown"] and r["status"] == "ACTIVE"]
155+
156+ def check_non_redundant_resource(self, action_name: str) -> Result:
157+ """Check that there are no non-redundant resources matching the resource type."""
158+ result = Result(True)
159+ shutdown_resource_list = self.get_shutdown_resource_list(action_name)
160+ redundant_resource_list = self.get_online_resource_list(action_name)
161+
162+ shutdown_resource_set = set(r["id"] for r in shutdown_resource_list)
163+ redundant_resource_set = set(r["id"] for r in redundant_resource_list)
164+ non_redundant_list = shutdown_resource_set - redundant_resource_set
165+ if non_redundant_list:
166+ result.success = False
167+ failure_string = NeutronGateway.action_name_failure_string_map[action_name]
168+ result.reason = failure_string.format(", ".join(non_redundant_list))
169+ return result
170+
171+ def warn_router_ha(self) -> Result:
172+ """Warn that HA routers should be manually failed over."""
173+ action_name = "get-status-routers"
174+ result = Result(True)
175+ shutdown_resource_list = self.get_shutdown_resource_list(action_name)
176+
177+ router_failover_err_list = []
178+ for router in shutdown_resource_list:
179+ if router["ha"]:
180+ _id = router["id"]
181+ entity_id = router["juju-entity-id"]
182+ host = router["host"]
183+ error_string = "{} (on {}, hostname: {})".format(_id, entity_id, host)
184+ router_failover_err_list.append(error_string)
185+
186+ if router_failover_err_list:
187+ error_string = ("It's recommended that you manually failover the following "
188+ "routers: {}")
189+ result.reason = error_string.format(", ".join(router_failover_err_list))
190+
191+ return result
192+
193+ def verify_reboot(self) -> Result:
194+ """Verify that it's safe to reboot selected neutron-gateway units."""
195+ return self.verify_shutdown()
196+
197+ def verify_shutdown(self) -> Result:
198+ """Verify that it's safe to shutdown selected neutron-gateway units."""
199+ return aggregate_results(self.warn_router_ha(),
200+ self.check_non_redundant_resource("get-status-routers"),
201+ self.check_non_redundant_resource("get-status-dhcp"),
202+ self.check_non_redundant_resource("get-status-lb"))
203diff --git a/tests/unit/verifiers/test_neutron_gateway.py b/tests/unit/verifiers/test_neutron_gateway.py
204new file mode 100644
205index 0000000..4aeb48d
206--- /dev/null
207+++ b/tests/unit/verifiers/test_neutron_gateway.py
208@@ -0,0 +1,310 @@
209+# Copyright 2021 Canonical Limited.
210+#
211+# This file is part of juju-verify.
212+#
213+# juju-verify is free software: you can redistribute it and/or modify it under
214+# the terms of the GNU General Public License as published by the Free Software
215+# Foundation, either version 3 of the License, or (at your option) any later
216+# version.
217+#
218+# juju-verify is distributed in the hope that it will be useful, but WITHOUT
219+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
220+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
221+# details.
222+#
223+# You should have received a copy of the GNU General Public License along with
224+# this program. If not, see https://www.gnu.org/licenses/.
225+"""NeutronGateway verifier class test suite."""
226+import json
227+from unittest import mock
228+from unittest.mock import MagicMock
229+
230+from juju.unit import Unit
231+
232+from juju_verify.verifiers import NeutronGateway
233+from juju_verify.verifiers.neutron_gateway import get_unit_hostname
234+from juju_verify.verifiers.neutron_gateway import get_unit_resource_list
235+
236+all_ngw_units = []
237+for i in range(3):
238+ ngw = MagicMock()
239+ ngw.entity_id = "neutron-gateway/{}".format(i)
240+ all_ngw_units.append(ngw)
241+
242+mock_data = [
243+ {
244+ "host": "host0",
245+ "shutdown": True,
246+ "unit": all_ngw_units[0],
247+ "routers": [{"id": "router0", "ha": False, "status": "ACTIVE"},
248+ {"id": "router1", "ha": False, "status": "ACTIVE"}]
249+ },
250+ {
251+ "host": "host1",
252+ "shutdown": False,
253+ "unit": all_ngw_units[1],
254+ "routers": [{"id": "router2", "ha": False, "status": "ACTIVE"}],
255+ },
256+ {
257+ "host": "host2",
258+ "shutdown": False,
259+ "unit": all_ngw_units[2],
260+ "routers": [{"id": "router3", "ha": False, "status": "ACTIVE"},
261+ {"id": "router4", "ha": False, "status": "ACTIVE"}],
262+ },
263+]
264+
265+all_ngw_host_names = [h["host"] for h in mock_data]
266+
267+model = MagicMock()
268+
269+
270+def get_ngw_verifier():
271+ """Get new NeutronGateway verifier (used for applying changes in shutdown list)."""
272+ return NeutronGateway([Unit(h["unit"].entity_id, model)
273+ for h in mock_data if h["shutdown"]])
274+
275+
276+def get_resource_lists():
277+ """Get all routers in mock data."""
278+ return [h["routers"] for h in mock_data]
279+
280+
281+def get_shutdown_host_name_list():
282+ """Get all hostnames of all hosts being shutdown."""
283+ return [h["host"] for h in mock_data if h["shutdown"]]
284+
285+
286+def set_router_status(routerid, status):
287+ """Set status of given router id in mock data."""
288+ for host in mock_data:
289+ for router in host["routers"]:
290+ if router["id"] == routerid:
291+ router["status"] = status
292+
293+
294+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_resource_list")
295+@mock.patch("juju_verify.verifiers.neutron_gateway.NeutronGateway.get_all_ngw_units")
296+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_hostname")
297+def test_get_resource_list(mock_get_unit_hostname,
298+ mock_get_all_ngw_units,
299+ mock_get_unit_resource_list):
300+ """Test list of resources returned by get_resource_list."""
301+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
302+ all_ngw_host_names)
303+ mock_get_all_ngw_units.return_value = all_ngw_units
304+ mock_get_unit_resource_list.side_effect = get_resource_lists()
305+
306+ ngw_verifier = get_ngw_verifier()
307+ router_list = ngw_verifier.get_resource_list("get-status-routers")
308+
309+ router_count = 0
310+ for host in mock_data:
311+ router_count += len(host["routers"])
312+ assert len(router_list) == router_count
313+
314+
315+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_resource_list")
316+@mock.patch("juju_verify.verifiers.neutron_gateway.NeutronGateway.get_all_ngw_units")
317+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_hostname")
318+def test_get_shutdown_resource_list(mock_get_unit_hostname,
319+ mock_get_all_ngw_units,
320+ mock_get_unit_resource_list):
321+ """Test validity of list of resources to be shutdown."""
322+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
323+ all_ngw_host_names)
324+ mock_get_all_ngw_units.return_value = all_ngw_units
325+ mock_get_unit_resource_list.side_effect = get_resource_lists()
326+
327+ ngw_verifier = get_ngw_verifier()
328+
329+ router_shutdown_count = 0
330+ for host in mock_data:
331+ if host["shutdown"]:
332+ router_shutdown_count += len(host["routers"])
333+
334+ shutdown_routers = ngw_verifier.get_shutdown_resource_list("get-status-routers")
335+ assert len(shutdown_routers) == router_shutdown_count
336+
337+ # test that inactive resources are not being listed as being shutdown
338+ set_router_status("router0", "NOTACTIVE")
339+
340+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
341+ all_ngw_host_names)
342+ mock_get_all_ngw_units.return_value = all_ngw_units
343+ mock_get_unit_resource_list.side_effect = get_resource_lists()
344+
345+ shutdown_routers = ngw_verifier.get_shutdown_resource_list("get-status-routers")
346+ assert len(shutdown_routers) == router_shutdown_count - 1
347+
348+ # set router0 back to active
349+ set_router_status("router0", "ACTIVE")
350+
351+
352+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_resource_list")
353+@mock.patch("juju_verify.verifiers.neutron_gateway.NeutronGateway.get_all_ngw_units")
354+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_hostname")
355+def test_get_online_resource_list(mock_get_unit_hostname,
356+ mock_get_all_ngw_units,
357+ mock_get_unit_resource_list):
358+ """Test validity of resources that will remain online."""
359+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
360+ all_ngw_host_names)
361+ mock_get_all_ngw_units.return_value = all_ngw_units
362+ mock_get_unit_resource_list.side_effect = get_resource_lists()
363+
364+ ngw_verifier = get_ngw_verifier()
365+
366+ router_online_count = 0
367+ for host in mock_data:
368+ if not host["shutdown"]:
369+ router_online_count += len(host["routers"])
370+
371+ online_routers = ngw_verifier.get_online_resource_list("get-status-routers")
372+ assert len(online_routers) == router_online_count
373+
374+ # test that NOT ACTIVE resources are not being listed as online/available
375+ set_router_status("router2", "NOTACTIVE")
376+
377+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
378+ all_ngw_host_names)
379+ mock_get_all_ngw_units.return_value = all_ngw_units
380+ mock_get_unit_resource_list.side_effect = get_resource_lists()
381+
382+ online_routers = ngw_verifier.get_online_resource_list("get-status-routers")
383+ assert len(online_routers) == router_online_count - 1
384+
385+ # set router2 back to active
386+ set_router_status("router2", "ACTIVE")
387+
388+
389+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_resource_list")
390+@mock.patch("juju_verify.verifiers.neutron_gateway.NeutronGateway.get_all_ngw_units")
391+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_hostname")
392+def test_check_non_redundant_resource(mock_get_unit_hostname,
393+ mock_get_all_ngw_units,
394+ mock_get_unit_resource_list):
395+ """Test validity of list of resources determined to not be redundant."""
396+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
397+ all_ngw_host_names)
398+ mock_get_all_ngw_units.return_value = all_ngw_units
399+ mock_get_unit_resource_list.side_effect = get_resource_lists()
400+
401+ ngw_verifier = get_ngw_verifier()
402+
403+ # host0 being shutdown, with no redundancy for its routers (router0, router1)
404+ result = ngw_verifier.check_non_redundant_resource("get-status-routers")
405+ assert result.success is False
406+
407+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
408+ all_ngw_host_names)
409+ mock_get_all_ngw_units.return_value = all_ngw_units
410+ mock_get_unit_resource_list.side_effect = get_resource_lists()
411+
412+ ngw_verifier = get_ngw_verifier()
413+
414+ # add redundancy (but not HA) for router0, router1 onto non-shutdown hosts
415+ mock_data[1]["routers"].append({"id": "router0", "ha": False, "status": "ACTIVE"})
416+ mock_data[2]["routers"].append({"id": "router1", "ha": False, "status": "ACTIVE"})
417+ mock_get_unit_resource_list.side_effect = get_resource_lists()
418+ result = ngw_verifier.check_non_redundant_resource("get-status-routers")
419+ assert result.success
420+
421+ # test setting redundant redundant router0 to NOTACTIVE will result in failure
422+ mock_data[1]["routers"][-1]["status"] = "NOTACTIVE"
423+ mock_get_unit_resource_list.side_effect = get_resource_lists()
424+ result = ngw_verifier.check_non_redundant_resource("get-status-routers")
425+ assert result.success is False
426+
427+ # test shutdown host1, which will take down the redundant router0
428+ mock_data[1]["shutdown"] = True
429+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
430+ all_ngw_host_names)
431+ mock_get_all_ngw_units.return_value = all_ngw_units
432+ mock_get_unit_resource_list.side_effect = get_resource_lists()
433+
434+ ngw_verifier = get_ngw_verifier()
435+ result = ngw_verifier.check_non_redundant_resource("get-status-routers")
436+ assert result.success is False
437+
438+ # reset shutdown for next tests
439+ mock_data[1]["shutdown"] = False
440+
441+
442+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_resource_list")
443+@mock.patch("juju_verify.verifiers.neutron_gateway.NeutronGateway.get_all_ngw_units")
444+@mock.patch("juju_verify.verifiers.neutron_gateway.get_unit_hostname")
445+def test_warn_router_ha(mock_get_unit_hostname,
446+ mock_get_all_ngw_units,
447+ mock_get_unit_resource_list):
448+ """Test existence of warning messages to manually failover HA routers when found."""
449+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
450+ all_ngw_host_names)
451+ mock_get_all_ngw_units.return_value = all_ngw_units
452+ mock_get_unit_resource_list.side_effect = get_resource_lists()
453+
454+ ngw_verifier = get_ngw_verifier()
455+
456+ result = ngw_verifier.warn_router_ha()
457+ # no HA to failover, lack of redundancy is detected by check_non_redundant_resource
458+ assert result.reason == ""
459+
460+ # Find router0 set it to HA
461+ for host in mock_data:
462+ for router in host["routers"]:
463+ if router["id"] == "router0":
464+ router["ha"] = True
465+
466+ mock_get_unit_hostname.side_effect = (get_shutdown_host_name_list() +
467+ all_ngw_host_names)
468+ mock_get_all_ngw_units.return_value = all_ngw_units
469+ mock_get_unit_resource_list.side_effect = get_resource_lists()
470+
471+ ngw_verifier = get_ngw_verifier()
472+
473+ result = ngw_verifier.warn_router_ha()
474+ # router is in HA, given instructions to failover
475+ assert result.reason
476+
477+
478+@mock.patch("juju_verify.verifiers.neutron_gateway.NeutronGateway.check_non_redundant_resource") # noqa: E501 pylint: disable=C0301
479+@mock.patch("juju_verify.verifiers.neutron_gateway.NeutronGateway.warn_router_ha")
480+@mock.patch("juju_verify.verifiers.neutron_gateway.aggregate_results")
481+def test_verify_reboot_shutdown(mock_aggregate_results,
482+ mock_warn_router_ha,
483+ mock_check_non_redundant_resource):
484+ """Test that reboot/shutdown call appropriate checks."""
485+ ngw_verifier = get_ngw_verifier()
486+ ngw_verifier.verify_reboot()
487+ assert mock_check_non_redundant_resource.call_count == 3
488+ mock_warn_router_ha.assert_called_once()
489+ mock_aggregate_results.assert_called_once()
490+
491+ mock_check_non_redundant_resource.reset_mock()
492+ mock_warn_router_ha.reset_mock()
493+ mock_aggregate_results.reset_mock()
494+
495+ ngw_verifier.verify_shutdown()
496+ mock_warn_router_ha.assert_called_once()
497+ assert mock_check_non_redundant_resource.call_count == 3
498+ mock_aggregate_results.assert_called_once()
499+
500+
501+@mock.patch("juju_verify.verifiers.neutron_gateway.run_action_on_unit")
502+@mock.patch("juju_verify.verifiers.neutron_gateway.data_from_action")
503+def test_get_unit_hostname(mock_data_from_action, mock_run_action_on_unit):
504+ """Test getting remote Unit's hostname."""
505+ get_unit_hostname(all_ngw_units[0])
506+ mock_run_action_on_unit.assert_called_once()
507+ mock_data_from_action.assert_called_once()
508+
509+
510+@mock.patch("juju_verify.verifiers.neutron_gateway.run_action_on_unit")
511+@mock.patch("juju_verify.verifiers.neutron_gateway.data_from_action")
512+def test_get_unit_resource_list(mock_data_from_action, mock_run_action_on_unit):
513+ """Test Neutron agent resources are retrieved via Juju actions."""
514+ resource = {"routers": [{"id": "r1"}]}
515+ mock_data_from_action.return_value = json.dumps(resource)
516+ resource_list = get_unit_resource_list(all_ngw_units[0], "get-status-routers")
517+ mock_run_action_on_unit.assert_called_once()
518+ assert resource == resource_list

Subscribers

People subscribed via source and target branches