Merge ~ack/maas:1976516-scriptresult-update-3.2 into maas:3.2

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: ff1f55a7fb3e23e94fa1aeffb3083e035ec9dbe8
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:1976516-scriptresult-update-3.2
Merge into: maas:3.2
Diff against target: 299 lines (+134/-37)
5 files modified
src/metadataserver/api.py (+23/-19)
src/metadataserver/migrations/0030_scriptresult_script_link.py (+66/-0)
src/metadataserver/tests/test_api.py (+17/-0)
src/provisioningserver/drivers/pod/lxd.py (+6/-1)
src/provisioningserver/drivers/pod/tests/test_lxd.py (+22/-17)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Alberto Donato (community) Approve
Review via email: mp+423918@code.launchpad.net

Commit message

LP:1976516 link Script to ScriptResult when creating a new one

This was causing no script to be returned for LXD VM hosts that are not deployed by MAAS.
It also fixes commissioning data sent back from the refresh operation on LXD VM hosts to return data for both resources script (to match what regular commissioning does).

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

backport of 23bcfa88d from master to 3.2

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

LANDING
-b 1976516-scriptresult-update-3.2 lp:~ack/maas/+git/maas into -b 3.2 lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/12804/consoleText

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b 1976516-scriptresult-update-3.2 lp:~ack/maas/+git/maas into -b 3.2 lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: ff1f55a7fb3e23e94fa1aeffb3083e035ec9dbe8

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/metadataserver/api.py b/src/metadataserver/api.py
2index fce2220..5c4ad54 100644
3--- a/src/metadataserver/api.py
4+++ b/src/metadataserver/api.py
5@@ -287,19 +287,21 @@ def process_file(
6 )
7 if script_result is None:
8 # If the script_result_id doesn't exist or wasn't sent try to find the
9- # ScriptResult by script_name. Since ScriptResults can get their name
10- # from the Script they are linked to or its own script_name field we
11- # have to iterate over the list of script_results.
12- script_result_found = False
13- for script_result in script_set:
14- if script_result.name == script_name:
15- script_result_found = True
16- break
17+ # ScriptResult by script_name.
18+ script_result = script_set.scriptresult_set.filter(
19+ script_name=script_name
20+ ).first()
21
22 # If the ScriptResult wasn't found by id or name create an entry for
23 # it.
24- if not script_result_found:
25+ if not script_result:
26+ script_id = (
27+ Script.objects.filter(name=script_name)
28+ .values_list("id", flat=True)
29+ .first()
30+ )
31 script_result = ScriptResult.objects.create(
32+ script_id=script_id,
33 script_set=script_set,
34 script_name=script_name,
35 status=SCRIPT_STATUS.RUNNING,
36@@ -1318,7 +1320,7 @@ class MAASScriptsHandler(OperationsHandler):
37 anonymous = AnonMAASScriptsHandler
38
39 def _add_script_set_to_tar(
40- self, script_set, tar, prefix, mtime, include_finshed=False
41+ self, script_set, tar, prefix, mtime, include_finished=False
42 ):
43 if script_set is None:
44 return []
45@@ -1326,7 +1328,7 @@ class MAASScriptsHandler(OperationsHandler):
46 for script_result in script_set:
47 # Don't rerun Scripts which have already run.
48 if (
49- not include_finshed
50+ not include_finished
51 and script_result.status
52 not in SCRIPT_STATUS_RUNNING_OR_PENDING
53 ):
54@@ -1425,9 +1427,11 @@ class MAASScriptsHandler(OperationsHandler):
55 )
56 and node.current_commissioning_script_set is not None
57 ):
58+ script_set = node.current_commissioning_script_set
59 # Prefetch all the data we need.
60- qs = node.current_commissioning_script_set.scriptresult_set
61- qs = qs.select_related("script", "script__script")
62+ qs = script_set.scriptresult_set.select_related(
63+ "script", "script__script"
64+ )
65 # After the script runner finishes sending all commissioning
66 # results it redownloads the script tar. It does this in-case
67 # a commissioning script discovers hardware associated with
68@@ -1439,7 +1443,6 @@ class MAASScriptsHandler(OperationsHandler):
69 # data.
70 for script_result in qs:
71 if script_result.status != SCRIPT_STATUS.PENDING:
72- script_set = node.current_commissioning_script_set
73 script_set.select_for_hardware_scripts()
74 break
75 meta_data = self._add_script_set_to_tar(
76@@ -1447,9 +1450,9 @@ class MAASScriptsHandler(OperationsHandler):
77 tar,
78 "commissioning",
79 mtime,
80- include_finshed=node.status == NODE_STATUS.DEPLOYED,
81+ include_finished=node.status == NODE_STATUS.DEPLOYED,
82 )
83- if meta_data != []:
84+ if meta_data:
85 tar_meta_data["commissioning_scripts"] = sorted(
86 meta_data, key=itemgetter("name", "script_result_id")
87 )
88@@ -1457,12 +1460,13 @@ class MAASScriptsHandler(OperationsHandler):
89 # Always send testing scripts.
90 if node.current_testing_script_set is not None:
91 # prefetch all the data we need
92- qs = node.current_testing_script_set.scriptresult_set
93- qs = qs.select_related("script", "script__script")
94+ qs = node.current_testing_script_set.scriptresult_set.select_related(
95+ "script", "script__script"
96+ )
97 meta_data = self._add_script_set_to_tar(
98 qs, tar, "testing", mtime
99 )
100- if meta_data != []:
101+ if meta_data:
102 tar_meta_data["testing_scripts"] = sorted(
103 meta_data, key=itemgetter("name", "script_result_id")
104 )
105diff --git a/src/metadataserver/migrations/0030_scriptresult_script_link.py b/src/metadataserver/migrations/0030_scriptresult_script_link.py
106new file mode 100644
107index 0000000..a2827da
108--- /dev/null
109+++ b/src/metadataserver/migrations/0030_scriptresult_script_link.py
110@@ -0,0 +1,66 @@
111+# Generated by Django 2.2.12 on 2022-06-02 15:09
112+
113+from django.db import migrations
114+from django.db.models import F, OuterRef, Subquery
115+
116+
117+def scriptresult_fix_script_link(apps, schema_editor):
118+ """Link ScriptResult to Script with matching name, if any."""
119+ ScriptResult = apps.get_model("metadataserver", "ScriptResult")
120+ Script = apps.get_model("metadataserver", "Script")
121+ (
122+ ScriptResult.objects.filter(script__isnull=True)
123+ .annotate(
124+ new_script_id=Subquery(
125+ Script.objects.filter(name=OuterRef("script_name")).values(
126+ "id"
127+ )[:1]
128+ )
129+ )
130+ .update(script_id=F("new_script_id"))
131+ )
132+
133+
134+RUN_MACHINE_RESOURCES = "20-maas-03-machine-resources"
135+COMMISSIONING_OUTPUT_NAME = "50-maas-01-commissioning"
136+
137+
138+def add_run_machine_resources_scriptresult(apps, schema_editor):
139+ """Create missing script results for machine-resources output.
140+
141+ This duplicates the output from the commissioning output when the former
142+ script is not present (for LXD VM hosts).
143+ """
144+ ScriptResult = apps.get_model("metadataserver", "ScriptResult")
145+ Script = apps.get_model("metadataserver", "Script")
146+ run_resources_script = (
147+ Script.objects.prefetch_related("script")
148+ .filter(name=RUN_MACHINE_RESOURCES)
149+ .first()
150+ )
151+ if not run_resources_script:
152+ return
153+
154+ script_results = ScriptResult.objects.filter(
155+ script_name=COMMISSIONING_OUTPUT_NAME
156+ ).exclude(script_set__scriptresult__script_name=RUN_MACHINE_RESOURCES)
157+ for script_result in script_results:
158+ # create a new result with the same content, pointing to the right
159+ # script
160+ script_result.id = None
161+ script_result.script_name = RUN_MACHINE_RESOURCES
162+ script_result.script = run_resources_script
163+ script_result.script_version = run_resources_script.script
164+ script_result.save(force_insert=True)
165+
166+
167+class Migration(migrations.Migration):
168+
169+ dependencies = [
170+ ("metadataserver", "0029_scriptset_tags_cleanup"),
171+ ]
172+
173+ operations = [
174+ migrations.RunPython(scriptresult_fix_script_link),
175+ migrations.RunPython(add_run_machine_resources_scriptresult),
176+ ]
177diff --git a/src/metadataserver/tests/test_api.py b/src/metadataserver/tests/test_api.py
178index 8efb8b7..a020b2e 100644
179--- a/src/metadataserver/tests/test_api.py
180+++ b/src/metadataserver/tests/test_api.py
181@@ -629,6 +629,23 @@ class TestHelpers(MAASServerTestCase):
182 {"exit_status": request["exit_status"], "output": output}, value
183 )
184
185+ def test_creates_new_script_result_linked_to_script(self):
186+ results = {}
187+ script = factory.make_Script()
188+ script_set = factory.make_ScriptSet()
189+ output = factory.make_string()
190+ request = {"exit_status": random.randint(0, 255)}
191+
192+ process_file(results, script_set, script.name, output, request)
193+ script_result, value = list(results.items())[0]
194+
195+ self.assertEqual(script.name, script_result.name)
196+ self.assertEqual(script_result.script, script)
197+ self.assertEqual(SCRIPT_STATUS.RUNNING, script_result.status)
198+ self.assertDictEqual(
199+ {"exit_status": request["exit_status"], "output": output}, value
200+ )
201+
202 def test_uses_default_exit_status_when_undef(self):
203 results = {}
204 script_result = factory.make_ScriptResult(status=SCRIPT_STATUS.RUNNING)
205diff --git a/src/provisioningserver/drivers/pod/lxd.py b/src/provisioningserver/drivers/pod/lxd.py
206index 9c02b86..0265d4f 100644
207--- a/src/provisioningserver/drivers/pod/lxd.py
208+++ b/src/provisioningserver/drivers/pod/lxd.py
209@@ -44,6 +44,7 @@ from provisioningserver.logger import get_maas_logger
210 from provisioningserver.prometheus.metrics import PROMETHEUS_METRICS
211 from provisioningserver.refresh.node_info_scripts import (
212 COMMISSIONING_OUTPUT_NAME,
213+ RUN_MACHINE_RESOURCES,
214 )
215 from provisioningserver.rpc.exceptions import PodInvalidResources
216 from provisioningserver.utils import (
217@@ -427,7 +428,11 @@ class LXDPodDriver(PodDriver):
218 # /1.0/networks/<network>/state
219 "networks": _get_lxd_network_states(client),
220 }
221- return {COMMISSIONING_OUTPUT_NAME: resources}
222+ # return the output for both scripts to match what commissioning does
223+ return {
224+ RUN_MACHINE_RESOURCES: resources,
225+ COMMISSIONING_OUTPUT_NAME: resources,
226+ }
227
228 @threadDeferred
229 def compose(self, pod_id: int, context: dict, request: RequestedMachine):
230diff --git a/src/provisioningserver/drivers/pod/tests/test_lxd.py b/src/provisioningserver/drivers/pod/tests/test_lxd.py
231index c98abcb..45f0951 100644
232--- a/src/provisioningserver/drivers/pod/tests/test_lxd.py
233+++ b/src/provisioningserver/drivers/pod/tests/test_lxd.py
234@@ -33,6 +33,7 @@ from provisioningserver.drivers.pod import (
235 from provisioningserver.drivers.pod import lxd as lxd_module
236 from provisioningserver.refresh.node_info_scripts import (
237 COMMISSIONING_OUTPUT_NAME,
238+ RUN_MACHINE_RESOURCES,
239 )
240 from provisioningserver.rpc.exceptions import PodInvalidResources
241 from provisioningserver.testing.certificates import (
242@@ -1159,18 +1160,20 @@ class TestLXDPodDriver(MAASTestCase):
243 1, self.make_context()
244 )
245
246+ details = {
247+ **self.fake_lxd.host_info,
248+ "resources": self.fake_lxd.resources,
249+ "networks": {
250+ "eth0": {"hwaddr": "aa:bb:cc:dd:ee:ff"},
251+ "eth1": {"hwaddr": "ff:ee:dd:cc:bb:aa"},
252+ },
253+ }
254 self.assertEqual(
255+ commissioning_data,
256 {
257- COMMISSIONING_OUTPUT_NAME: {
258- **self.fake_lxd.host_info,
259- "resources": self.fake_lxd.resources,
260- "networks": {
261- "eth0": {"hwaddr": "aa:bb:cc:dd:ee:ff"},
262- "eth1": {"hwaddr": "ff:ee:dd:cc:bb:aa"},
263- },
264- }
265+ RUN_MACHINE_RESOURCES: details,
266+ COMMISSIONING_OUTPUT_NAME: details,
267 },
268- commissioning_data,
269 )
270
271 @inlineCallbacks
272@@ -1205,17 +1208,19 @@ class TestLXDPodDriver(MAASTestCase):
273 1, self.make_context()
274 )
275
276+ details = {
277+ **self.fake_lxd.host_info,
278+ "resources": self.fake_lxd.resources,
279+ "networks": {
280+ "eth2": {"hwaddr": "ee:dd:cc:bb:aa:ff"},
281+ },
282+ }
283 self.assertEqual(
284+ commissioning_data,
285 {
286- COMMISSIONING_OUTPUT_NAME: {
287- **self.fake_lxd.host_info,
288- "resources": self.fake_lxd.resources,
289- "networks": {
290- "eth2": {"hwaddr": "ee:dd:cc:bb:aa:ff"},
291- },
292- }
293+ RUN_MACHINE_RESOURCES: details,
294+ COMMISSIONING_OUTPUT_NAME: details,
295 },
296- commissioning_data,
297 )
298
299 @inlineCallbacks

Subscribers

People subscribed via source and target branches