Merge ~exsdev/juju-verify:lp1916231-neutron-gateway-juju-verifier-plugin into ~canonical-solutions-engineering/juju-verify:master
- Git
- lp:~exsdev/juju-verify
- lp1916231-neutron-gateway-juju-verifier-plugin
- Merge into master
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) |
Related bugs: |
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:/
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
🤖 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.
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
A CI job is currently in progress. A follow up comment will be added when it completes.
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
FAILED: Continuous integration, rev:ddf29dc6f86
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:/
https:/
Executed test runs:
FAILURE: https:/
None: https:/
Click here to trigger a rebuild:
https:/
- f244ee1... by Edin S
-
NGW: fix lint issues including typing information
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
A CI job is currently in progress. A follow up comment will be added when it completes.
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
FAILED: Continuous integration, rev:f244ee183c3
https:/
Executed test runs:
FAILURE: https:/
None: https:/
Click here to trigger a rebuild:
https:/
- 8c7ff94... by Edin S
-
NGW: fix issues raised by pylint
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
A CI job is currently in progress. A follow up comment will be added when it completes.
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
FAILED: Continuous integration, rev:8c7ff946926
https:/
Executed test runs:
FAILURE: https:/
None: https:/
Click here to trigger a rebuild:
https:/
- 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
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
A CI job is currently in progress. A follow up comment will be added when it completes.
🤖 prod-jenkaas-bootstack (prod-jenkaas-bootstack) wrote : | # |
FAILED: Continuous integration, rev:83d72cb74ee
https:/
Executed test runs:
FAILURE: https:/
None: https:/
Click here to trigger a rebuild:
https:/
Edin S (exsdev) wrote : | # |
NeutronGateway.
Robert Gildein (rgildein) : | # |
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.
Martin Kalcok (martin-kalcok) wrote : | # |
juju-verify development was moved to https:/
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
1 | diff --git a/juju_verify/utils/unit.py b/juju_verify/utils/unit.py |
2 | index 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 | |
18 | diff --git a/juju_verify/verifiers/__init__.py b/juju_verify/verifiers/__init__.py |
19 | index 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 | |
38 | diff --git a/juju_verify/verifiers/neutron_gateway.py b/juju_verify/verifiers/neutron_gateway.py |
39 | new file mode 100644 |
40 | index 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")) |
203 | diff --git a/tests/unit/verifiers/test_neutron_gateway.py b/tests/unit/verifiers/test_neutron_gateway.py |
204 | new file mode 100644 |
205 | index 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 |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.