Merge ~adam-collard/maas-ci/+git/system-tests:debug-after-giving-up-waiting into ~maas-committers/maas-ci/+git/system-tests:master

Proposed by Adam Collard
Status: Merged
Merged at revision: c891f1efc5e77d55ca96eff269f2154a1728e022
Proposed branch: ~adam-collard/maas-ci/+git/system-tests:debug-after-giving-up-waiting
Merge into: ~maas-committers/maas-ci/+git/system-tests:master
Diff against target: 164 lines (+63/-11)
3 files modified
systemtests/api.py (+4/-1)
systemtests/tests/test_admin.py (+10/-4)
systemtests/utils.py (+49/-6)
Reviewer Review Type Date Requested Status
Diego Mascialino (community) Approve
Alberto Donato (community) Approve
Review via email: mp+407685@code.launchpad.net

Commit message

Get debug information after we give up waiting for machines

To post a comment you must log in.
Revision history for this message
Alberto Donato (ack) wrote :

nice, +1

small nit inline

review: Approve
42fe55c... by Adam Collard

ack's feedback

Revision history for this message
Diego Mascialino (dmascialino) wrote :

I like this feature. Two comments inline

Revision history for this message
Adam Collard (adam-collard) :
Revision history for this message
Diego Mascialino (dmascialino) wrote :

looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/systemtests/api.py b/systemtests/api.py
2index 18c9b52..6a55cae 100644
3--- a/systemtests/api.py
4+++ b/systemtests/api.py
5@@ -2,7 +2,7 @@ import json
6 import time
7 from logging import getLogger
8
9-from .utils import wait_for_machines
10+from .utils import filter_just_hostname, wait_for_machines
11
12 LOG = getLogger("systemtests.api")
13
14@@ -144,6 +144,7 @@ class AuthenticatedAPIClient:
15 wait_for_machines(
16 self,
17 [{"hostname": machine["hostname"], "status": "ready"}],
18+ filter_just_hostname,
19 timeout=5 * 60,
20 delay=10,
21 )
22@@ -185,6 +186,7 @@ class AuthenticatedAPIClient:
23 wait_for_machines(
24 self,
25 [{"hostname": machine["hostname"], "status": "rescue_mode"}],
26+ filter_just_hostname,
27 timeout=20 * 60,
28 delay=30,
29 )
30@@ -195,6 +197,7 @@ class AuthenticatedAPIClient:
31 wait_for_machines(
32 self,
33 [{"hostname": machine["hostname"], "status": next_status}],
34+ filter_just_hostname,
35 timeout=20 * 60,
36 delay=30,
37 )
38diff --git a/systemtests/tests/test_admin.py b/systemtests/tests/test_admin.py
39index c70a35e..01653c8 100644
40--- a/systemtests/tests/test_admin.py
41+++ b/systemtests/tests/test_admin.py
42@@ -1,6 +1,6 @@
43 import pytest
44
45-from ..utils import ssh_execute_command, wait_for_machines
46+from ..utils import filter_just_mac, ssh_execute_command, wait_for_machines
47
48
49 @pytest.mark.usefixtures("authenticated_admin")
50@@ -89,7 +89,9 @@ class TestMachines:
51 waiting_for.append(
52 {"mac_address": power_config["mac_address"], "status": "new"}
53 )
54- wait_for_machines(authenticated_admin, waiting_for, timeout=20 * 60)
55+ wait_for_machines(
56+ authenticated_admin, waiting_for, filter_just_mac, timeout=20 * 60
57+ )
58
59 @pytest.mark.dependency(name="commission", depends=["enlist"])
60 def test_commission(self, authenticated_admin, config):
61@@ -101,7 +103,9 @@ class TestMachines:
62 authenticated_admin.commission_machine(machine)
63 waiting_for.append({"mac_address": mac_address, "status": "ready"})
64
65- wait_for_machines(authenticated_admin, waiting_for, timeout=20 * 60)
66+ wait_for_machines(
67+ authenticated_admin, waiting_for, filter_just_mac, timeout=20 * 60
68+ )
69
70 @pytest.mark.dependency(name="deploy", depends=["commission"])
71 def test_deploy(self, authenticated_admin, config, ssh_key, testlog):
72@@ -113,7 +117,9 @@ class TestMachines:
73 authenticated_admin.deploy_machine(machine, osystem=osystem)
74 waiting_for.append({"mac_address": mac_address, "status": "deployed"})
75
76- wait_for_machines(authenticated_admin, waiting_for, timeout=20 * 60)
77+ wait_for_machines(
78+ authenticated_admin, waiting_for, filter_just_mac, timeout=20 * 60
79+ )
80
81 leave_machine_deployed = True
82 for machine_name, power_config in config["machines"]["hardware"].items():
83diff --git a/systemtests/utils.py b/systemtests/utils.py
84index 125b1c0..fe6bf93 100644
85--- a/systemtests/utils.py
86+++ b/systemtests/utils.py
87@@ -6,6 +6,26 @@ import paramiko
88 from retry.api import retry_call
89
90
91+class UnexpectedMachineStatus(Exception):
92+ """Raised when we run out of time waiting for machines to be in a given state.
93+
94+ Will report on the desired state, the time we waited for and debug outputs.
95+ """
96+
97+ def __init__(self, filters, elapsed_time, debug_outputs):
98+ self._filters = filters
99+ self._elapsed_time = elapsed_time
100+ self._debug_outputs = debug_outputs
101+
102+ def __str__(self):
103+ return f"""\
104+Machines not found for: {self._filters} after {self._elapsed_time:.01f} seconds.
105+
106+Debug information:
107+{self._debug_outputs}
108+"""
109+
110+
111 def randomstring(length=10):
112 """Return a random string."""
113 return "".join(random.choice(string.ascii_lowercase) for _ in range(length))
114@@ -29,8 +49,27 @@ def retries(timeout=30, delay=1):
115 break
116
117
118-def wait_for_machines(api_client, select_params, timeout=10 * 60, delay=30):
119- """Blocks execution until all select_params are satisfied."""
120+def filter_just_mac(params):
121+ return {"mac_address": params["mac_address"]}
122+
123+
124+def filter_just_hostname(params):
125+ return {"hostname": params["hostname"]}
126+
127+
128+def wait_for_machines(
129+ api_client, select_params, filter_for_debug, timeout=10 * 60, delay=30
130+):
131+ """Blocks execution until all select_params are satisfied.
132+
133+ `filter_for_debug` is called for each filter in the select_params
134+ that doesn't match after `timeout` seconds with `delay` seconds
135+ sleep between each attempt.
136+
137+ The results of the filters after calling `filter_for_debug` are
138+ used to debug what's going on with the machines that didn't match
139+ the given state.
140+ """
141 select_params = select_params.copy() # I dont want to modify the extern values
142
143 for elapsed, _ in retries(timeout, delay):
144@@ -40,13 +79,17 @@ def wait_for_machines(api_client, select_params, timeout=10 * 60, delay=30):
145 if not select_params:
146 break
147
148- assert (
149- select_params == []
150- ), f"Machines not found for this params: {select_params} after {elapsed:.01f} seconds"
151+ if select_params:
152+ # We've timed out waiting for machines to match the filter
153+ debug_outputs = []
154+ for filters in select_params:
155+ debug_filter = filter_for_debug(filters)
156+ debug_outputs.append(api_client.list_machines(**debug_filter))
157+ raise UnexpectedMachineStatus(select_params, elapsed, debug_outputs)
158
159
160 def ssh_execute_command(machine, username, pkey, command):
161- """ Connect to machine, execute command and return stdout."""
162+ """Connect to machine, execute command and return stdout."""
163 client = paramiko.SSHClient()
164 client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
165

Subscribers

People subscribed via source and target branches

to all changes: