Merge ~ltrager/maas:2.3_1752754 into maas:2.3
- Git
- lp:~ltrager/maas
- 2.3_1752754
- Merge into 2.3
Proposed by
Lee Trager
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Lee Trager | ||||||||
Approved revision: | 2bd80569e4a6e5c9bd25eaaf4f70a68a701bdc5c | ||||||||
Merge reported by: | MAAS Lander | ||||||||
Merged at revision: | not available | ||||||||
Proposed branch: | ~ltrager/maas:2.3_1752754 | ||||||||
Merge into: | maas:2.3 | ||||||||
Prerequisite: | ~ltrager/maas:2.3_fix_scriptresult_updates_on_trigger | ||||||||
Diff against target: |
415 lines (+189/-40) 9 files modified
src/maasserver/static/js/angular/controllers/node_results.js (+21/-10) src/maasserver/static/js/angular/controllers/tests/test_node_results.js (+46/-0) src/maasserver/static/js/angular/factories/node_results.js (+10/-1) src/maasserver/static/js/angular/factories/tests/test_node_results.js (+22/-1) src/maasserver/static/partials/script-results-list.html (+7/-1) src/maasserver/websockets/handlers/node_result.py (+21/-12) src/maasserver/websockets/handlers/tests/test_node_result.py (+32/-11) src/metadataserver/models/scriptresult.py (+4/-2) src/metadataserver/models/tests/test_scriptresult.py (+26/-2) |
||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Review via email: mp+368114@code.launchpad.net |
Description of the change
To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b 2.3_1752754 lp:~ltrager/maas/+git/maas into -b 2.3 lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
~ltrager/maas:2.3_1752754
updated
- 2bd8056... by Lee Trager
-
Merge branch '2.3' into 2.3_1752754
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/static/js/angular/controllers/node_results.js b/src/maasserver/static/js/angular/controllers/node_results.js | |||
2 | index 101e500..59f3f6f 100644 | |||
3 | --- a/src/maasserver/static/js/angular/controllers/node_results.js | |||
4 | +++ b/src/maasserver/static/js/angular/controllers/node_results.js | |||
5 | @@ -1,4 +1,4 @@ | |||
7 | 1 | /* Copyright 2017 Canonical Ltd. This software is licensed under the | 1 | /* Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
8 | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). |
9 | 3 | * | 3 | * |
10 | 4 | * MAAS Node Results Controller | 4 | * MAAS Node Results Controller |
11 | @@ -135,17 +135,12 @@ angular.module('MAAS').controller('NodeResultsController', [ | |||
12 | 135 | }); | 135 | }); |
13 | 136 | } else { | 136 | } else { |
14 | 137 | var result = null; | 137 | var result = null; |
16 | 138 | var i, j; | 138 | var i; |
17 | 139 | // Find the installation result to be displayed. | 139 | // Find the installation result to be displayed. |
18 | 140 | for(i = 0; i < $scope.installation_results.length; i++) { | 140 | for(i = 0; i < $scope.installation_results.length; i++) { |
27 | 141 | var hlist = $scope.installation_results[i].history_list; | 141 | if($scope.installation_results[i].id === |
28 | 142 | for(j = 0; j < hlist.length; j++) { | 142 | $scope.logs.option.id) { |
29 | 143 | if(hlist[j].id === $scope.logs.option.id) { | 143 | result = $scope.installation_results[i]; |
22 | 144 | result = hlist[j]; | ||
23 | 145 | break; | ||
24 | 146 | } | ||
25 | 147 | } | ||
26 | 148 | if(result) { | ||
30 | 149 | break; | 144 | break; |
31 | 150 | } | 145 | } |
32 | 151 | } | 146 | } |
33 | @@ -195,6 +190,22 @@ angular.module('MAAS').controller('NodeResultsController', [ | |||
34 | 195 | } | 190 | } |
35 | 196 | }; | 191 | }; |
36 | 197 | 192 | ||
37 | 193 | $scope.loadHistory = function(result) { | ||
38 | 194 | result.showing_results = false; | ||
39 | 195 | // History has already been loaded, no need to request it. | ||
40 | 196 | if(angular.isArray(result.history_list)) { | ||
41 | 197 | result.showing_history = true; | ||
42 | 198 | return; | ||
43 | 199 | } | ||
44 | 200 | result.loading_history = true; | ||
45 | 201 | $scope.nodeResultsManager.get_history(result.id).then( | ||
46 | 202 | function(history) { | ||
47 | 203 | result.history_list = history; | ||
48 | 204 | result.loading_history = false; | ||
49 | 205 | result.showing_history = true; | ||
50 | 206 | }); | ||
51 | 207 | }; | ||
52 | 208 | |||
53 | 198 | // Destroy the NodeResultsManager when the scope is destroyed. This is | 209 | // Destroy the NodeResultsManager when the scope is destroyed. This is |
54 | 199 | // so the client will not recieve any more notifications about results | 210 | // so the client will not recieve any more notifications about results |
55 | 200 | // from this node. | 211 | // from this node. |
56 | diff --git a/src/maasserver/static/js/angular/controllers/tests/test_node_results.js b/src/maasserver/static/js/angular/controllers/tests/test_node_results.js | |||
57 | index f2f44d7..fbeb5a1 100644 | |||
58 | --- a/src/maasserver/static/js/angular/controllers/tests/test_node_results.js | |||
59 | +++ b/src/maasserver/static/js/angular/controllers/tests/test_node_results.js | |||
60 | @@ -433,4 +433,50 @@ describe("NodeResultsController", function() { | |||
61 | 433 | "BUG: Unknown log status " + installation_result.status); | 433 | "BUG: Unknown log status " + installation_result.status); |
62 | 434 | }); | 434 | }); |
63 | 435 | }); | 435 | }); |
64 | 436 | |||
65 | 437 | describe("loadHistory", function() { | ||
66 | 438 | it("loads results", function() { | ||
67 | 439 | var defer = $q.defer(); | ||
68 | 440 | var controller = makeController(); | ||
69 | 441 | var result = { | ||
70 | 442 | id: makeInteger(0, 100) | ||
71 | 443 | }; | ||
72 | 444 | var history_list = [ | ||
73 | 445 | {id: makeInteger(0, 100)}, | ||
74 | 446 | {id: makeInteger(0, 100)}, | ||
75 | 447 | {id: makeInteger(0, 100)} | ||
76 | 448 | ]; | ||
77 | 449 | $scope.node = node; | ||
78 | 450 | $scope.nodeResultsManager = NodeResultsManagerFactory.getManager( | ||
79 | 451 | $scope.node); | ||
80 | 452 | spyOn($scope.nodeResultsManager, "get_history").and.returnValue( | ||
81 | 453 | defer.promise); | ||
82 | 454 | |||
83 | 455 | $scope.loadHistory(result); | ||
84 | 456 | defer.resolve(history_list); | ||
85 | 457 | $rootScope.$digest(); | ||
86 | 458 | |||
87 | 459 | expect(result.history_list).toBe(history_list); | ||
88 | 460 | expect(result.loading_history).toBe(false); | ||
89 | 461 | expect(result.showing_history).toBe(true); | ||
90 | 462 | }); | ||
91 | 463 | |||
92 | 464 | it("doesnt reload", function() { | ||
93 | 465 | var controller = makeController(); | ||
94 | 466 | var result = { | ||
95 | 467 | id: makeInteger(0, 100), | ||
96 | 468 | history_list: [{id: makeInteger(0, 100)}] | ||
97 | 469 | }; | ||
98 | 470 | $scope.node = node; | ||
99 | 471 | $scope.nodeResultsManager = NodeResultsManagerFactory.getManager( | ||
100 | 472 | $scope.node); | ||
101 | 473 | spyOn($scope.nodeResultsManager, "get_history"); | ||
102 | 474 | |||
103 | 475 | $scope.loadHistory(result); | ||
104 | 476 | |||
105 | 477 | expect(result.showing_history).toBe(true); | ||
106 | 478 | expect( | ||
107 | 479 | $scope.nodeResultsManager.get_history).not.toHaveBeenCalled(); | ||
108 | 480 | }); | ||
109 | 481 | }); | ||
110 | 436 | }); | 482 | }); |
111 | diff --git a/src/maasserver/static/js/angular/factories/node_results.js b/src/maasserver/static/js/angular/factories/node_results.js | |||
112 | index 1a0a5de..94a52f7 100644 | |||
113 | --- a/src/maasserver/static/js/angular/factories/node_results.js | |||
114 | +++ b/src/maasserver/static/js/angular/factories/node_results.js | |||
115 | @@ -1,4 +1,4 @@ | |||
117 | 1 | /* Copyright 2017 Canonical Ltd. This software is licensed under the | 1 | /* Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
118 | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). |
119 | 3 | * | 3 | * |
120 | 4 | * MAAS NodeResultsManager Manager | 4 | * MAAS NodeResultsManager Manager |
121 | @@ -266,6 +266,15 @@ angular.module('MAAS').factory( | |||
122 | 266 | return RegionConnection.callMethod(method, params); | 266 | return RegionConnection.callMethod(method, params); |
123 | 267 | }; | 267 | }; |
124 | 268 | 268 | ||
125 | 269 | // Get historic data. | ||
126 | 270 | NodeResultsManager.prototype.get_history = function(script_id) { | ||
127 | 271 | var method = this._handler + ".get_history"; | ||
128 | 272 | var params = { | ||
129 | 273 | id: script_id, | ||
130 | 274 | }; | ||
131 | 275 | return RegionConnection.callMethod(method, params); | ||
132 | 276 | }; | ||
133 | 277 | |||
134 | 269 | // Factory that holds all created NodeResultsManagers. | 278 | // Factory that holds all created NodeResultsManagers. |
135 | 270 | function NodeResultsManagerFactory() { | 279 | function NodeResultsManagerFactory() { |
136 | 271 | // Holds a list of all NodeResultsManagers that have been created. | 280 | // Holds a list of all NodeResultsManagers that have been created. |
137 | diff --git a/src/maasserver/static/js/angular/factories/tests/test_node_results.js b/src/maasserver/static/js/angular/factories/tests/test_node_results.js | |||
138 | index 859dda3..2508969 100644 | |||
139 | --- a/src/maasserver/static/js/angular/factories/tests/test_node_results.js | |||
140 | +++ b/src/maasserver/static/js/angular/factories/tests/test_node_results.js | |||
141 | @@ -1,4 +1,4 @@ | |||
143 | 1 | /* Copyright 2017 Canonical Ltd. This software is licensed under the | 1 | /* Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
144 | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). |
145 | 3 | * | 3 | * |
146 | 4 | * Unit tests for NodeResultsManagerFactory. | 4 | * Unit tests for NodeResultsManagerFactory. |
147 | @@ -532,6 +532,27 @@ describe("NodeResultsManagerFactory", function() { | |||
148 | 532 | }); | 532 | }); |
149 | 533 | }); | 533 | }); |
150 | 534 | 534 | ||
151 | 535 | describe("get_history", function() { | ||
152 | 536 | |||
153 | 537 | it("calls NodeResultHandler.get_history", function(done) { | ||
154 | 538 | var node = makenode(); | ||
155 | 539 | var output = [{ | ||
156 | 540 | id: makeInteger(0, 100), | ||
157 | 541 | name: makeName("output"), | ||
158 | 542 | status: makeInteger(0, 100) | ||
159 | 543 | }]; | ||
160 | 544 | var id = makeInteger(0, 100); | ||
161 | 545 | webSocket.returnData.push(makeFakeResponse(output)); | ||
162 | 546 | NodeResultsManager = NodeResultsManagerFactory.getManager(node); | ||
163 | 547 | NodeResultsManager.get_history(id).then(function() { | ||
164 | 548 | var sentObject = angular.fromJson(webSocket.sentData[0]); | ||
165 | 549 | expect(sentObject.method).toBe("noderesult.get_history"); | ||
166 | 550 | expect(sentObject.params.id).toEqual(id); | ||
167 | 551 | done(); | ||
168 | 552 | }); | ||
169 | 553 | }); | ||
170 | 554 | }); | ||
171 | 555 | |||
172 | 535 | describe("destroyManager", function() { | 556 | describe("destroyManager", function() { |
173 | 536 | 557 | ||
174 | 537 | it("removes manager from _managers", function() { | 558 | it("removes manager from _managers", function() { |
175 | diff --git a/src/maasserver/static/partials/script-results-list.html b/src/maasserver/static/partials/script-results-list.html | |||
176 | index 913978c..67ca3ab 100644 | |||
177 | --- a/src/maasserver/static/partials/script-results-list.html | |||
178 | +++ b/src/maasserver/static/partials/script-results-list.html | |||
179 | @@ -44,7 +44,7 @@ | |||
180 | 44 | <div class="table__controls-menu ng-hide" role="menu" data-ng-show="isToggled"> | 44 | <div class="table__controls-menu ng-hide" role="menu" data-ng-show="isToggled"> |
181 | 45 | <button class="table__controls-action" aria-label="View metrics" data-ng-if="!result.showing_results" data-ng-click="toggleMenu(); result.showing_history = false; result.showing_results = true">View metrics</button> | 45 | <button class="table__controls-action" aria-label="View metrics" data-ng-if="!result.showing_results" data-ng-click="toggleMenu(); result.showing_history = false; result.showing_results = true">View metrics</button> |
182 | 46 | <button class="table__controls-action" aria-label="Hide metrics" data-ng-if="result.showing_results" data-ng-click="toggleMenu(); result.showing_history = false; result.showing_results = false">Hide metrics</button> | 46 | <button class="table__controls-action" aria-label="Hide metrics" data-ng-if="result.showing_results" data-ng-click="toggleMenu(); result.showing_history = false; result.showing_results = false">Hide metrics</button> |
184 | 47 | <button class="table__controls-action" aria-label="View previous {$ result.result_section $}" data-ng-if="!result.showing_history" data-ng-click="toggleMenu(); result.showing_results = false; result.showing_history = true">View previous {$ result.result_section $}</button> | 47 | <button class="table__controls-action" aria-label="View previous {$ result.result_section $}" data-ng-if="!result.showing_history" data-ng-click="toggleMenu(); loadHistory(result)">View previous {$ result.result_section $}</button> |
185 | 48 | <button class="table__controls-action" aria-label="Hide previous {$ result.result_section $}" data-ng-if="result.showing_history" data-ng-click="toggleMenu(); result.showing_results = false; result.showing_history = false">Hide previous {$ result.result_section $}</button> | 48 | <button class="table__controls-action" aria-label="Hide previous {$ result.result_section $}" data-ng-if="result.showing_history" data-ng-click="toggleMenu(); result.showing_results = false; result.showing_history = false">Hide previous {$ result.result_section $}</button> |
186 | 49 | </div> | 49 | </div> |
187 | 50 | </div> | 50 | </div> |
188 | @@ -60,6 +60,11 @@ | |||
189 | 60 | </div> | 60 | </div> |
190 | 61 | </div> | 61 | </div> |
191 | 62 | 62 | ||
192 | 63 | <div class="p-table-expanding__panel col-12" aria-label="loading history" data-ng-if="result.loading_history"> | ||
193 | 64 | <div class="col-12"> | ||
194 | 65 | <p class="u-text--loading"><i class="p-icon--spinner u-animation--spin"></i> Loading...</p> | ||
195 | 66 | </div> | ||
196 | 67 | </div> | ||
197 | 63 | <div class="table__dropdown" aria-label="history" data-ng-if="result.showing_history"> | 68 | <div class="table__dropdown" aria-label="history" data-ng-if="result.showing_history"> |
198 | 64 | <div class="table__row is-active"> | 69 | <div class="table__row is-active"> |
199 | 65 | <div class="table__data table-col--100"> | 70 | <div class="table__data table-col--100"> |
200 | @@ -83,6 +88,7 @@ | |||
201 | 83 | <p class="u-align--center u-margin--bottom"> | 88 | <p class="u-align--center u-margin--bottom"> |
202 | 84 | <button class="button--secondary button--inline" data-ng-click="result.showing_history = false">Hide previous {$ result.result_section $}</button> | 89 | <button class="button--secondary button--inline" data-ng-click="result.showing_history = false">Hide previous {$ result.result_section $}</button> |
203 | 85 | </p> | 90 | </p> |
204 | 91 | |||
205 | 86 | </div> | 92 | </div> |
206 | 87 | </div> | 93 | </div> |
207 | 88 | </div> | 94 | </div> |
208 | diff --git a/src/maasserver/websockets/handlers/node_result.py b/src/maasserver/websockets/handlers/node_result.py | |||
209 | index e6e516e..cf04e58 100644 | |||
210 | --- a/src/maasserver/websockets/handlers/node_result.py | |||
211 | +++ b/src/maasserver/websockets/handlers/node_result.py | |||
212 | @@ -32,6 +32,7 @@ class NodeResultHandler(TimestampedModelHandler): | |||
213 | 32 | 'clear', | 32 | 'clear', |
214 | 33 | 'get', | 33 | 'get', |
215 | 34 | 'get_result_data', | 34 | 'get_result_data', |
216 | 35 | 'get_history', | ||
217 | 35 | 'list', | 36 | 'list', |
218 | 36 | ] | 37 | ] |
219 | 37 | listen_channels = ['scriptresult'] | 38 | listen_channels = ['scriptresult'] |
220 | @@ -88,18 +89,6 @@ class NodeResultHandler(TimestampedModelHandler): | |||
221 | 88 | # Script object. | 89 | # Script object. |
222 | 89 | data["hardware_type"] = HARDWARE_TYPE.NODE | 90 | data["hardware_type"] = HARDWARE_TYPE.NODE |
223 | 90 | data["tags"] = 'commissioning' | 91 | data["tags"] = 'commissioning' |
224 | 91 | data["history_list"] = [ | ||
225 | 92 | { | ||
226 | 93 | "id": history.id, | ||
227 | 94 | "updated": dehydrate_datetime(history.updated), | ||
228 | 95 | "status": history.status, | ||
229 | 96 | "status_name": history.status_name, | ||
230 | 97 | "runtime": history.runtime, | ||
231 | 98 | "starttime": history.starttime, | ||
232 | 99 | "endtime": history.endtime, | ||
233 | 100 | "estimated_runtime": history.estimated_runtime, | ||
234 | 101 | } for history in obj.history | ||
235 | 102 | ] | ||
236 | 103 | try: | 92 | try: |
237 | 104 | results = obj.read_results() | 93 | results = obj.read_results() |
238 | 105 | except ValidationError as e: | 94 | except ValidationError as e: |
239 | @@ -213,6 +202,26 @@ class NodeResultHandler(TimestampedModelHandler): | |||
240 | 213 | else: | 202 | else: |
241 | 214 | return "Unknown data_type %s" % data_type | 203 | return "Unknown data_type %s" % data_type |
242 | 215 | 204 | ||
243 | 205 | def get_history(self, params): | ||
244 | 206 | """Return a list of historic results.""" | ||
245 | 207 | id = params.get('id') | ||
246 | 208 | script_result = ScriptResult.objects.filter(id=id).only( | ||
247 | 209 | 'script_id', 'script_set_id', 'physical_blockdevice_id', | ||
248 | 210 | 'script_name').first() | ||
249 | 211 | history_qs = script_result.history.only( | ||
250 | 212 | 'id', 'updated', 'status', 'started', 'ended', 'script_id', | ||
251 | 213 | 'script_name', 'script_set_id', 'physical_blockdevice_id') | ||
252 | 214 | return [{ | ||
253 | 215 | 'id': history.id, | ||
254 | 216 | 'updated': dehydrate_datetime(history.updated), | ||
255 | 217 | 'status': history.status, | ||
256 | 218 | 'status_name': history.status_name, | ||
257 | 219 | 'runtime': history.runtime, | ||
258 | 220 | 'starttime': history.starttime, | ||
259 | 221 | 'endtime': history.endtime, | ||
260 | 222 | 'estimated_runtime': history.estimated_runtime, | ||
261 | 223 | } for history in history_qs] | ||
262 | 224 | |||
263 | 216 | def clear(self, params): | 225 | def clear(self, params): |
264 | 217 | """Clears the current node for events. | 226 | """Clears the current node for events. |
265 | 218 | 227 | ||
266 | diff --git a/src/maasserver/websockets/handlers/tests/test_node_result.py b/src/maasserver/websockets/handlers/tests/test_node_result.py | |||
267 | index 5b3fc37..a8fe7fe 100644 | |||
268 | --- a/src/maasserver/websockets/handlers/tests/test_node_result.py | |||
269 | +++ b/src/maasserver/websockets/handlers/tests/test_node_result.py | |||
270 | @@ -1,4 +1,4 @@ | |||
272 | 1 | # Copyright 2017 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
273 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
274 | 3 | 3 | ||
275 | 4 | """Tests for `maasserver.websockets.handlers.node_result`""" | 4 | """Tests for `maasserver.websockets.handlers.node_result`""" |
276 | @@ -16,6 +16,7 @@ from maasserver.websockets.base import ( | |||
277 | 16 | HandlerPKError, | 16 | HandlerPKError, |
278 | 17 | ) | 17 | ) |
279 | 18 | from maasserver.websockets.handlers.node_result import NodeResultHandler | 18 | from maasserver.websockets.handlers.node_result import NodeResultHandler |
280 | 19 | from maastesting.djangotestcase import CountQueries | ||
281 | 19 | from metadataserver.enum import ( | 20 | from metadataserver.enum import ( |
282 | 20 | HARDWARE_TYPE, | 21 | HARDWARE_TYPE, |
283 | 21 | HARDWARE_TYPE_CHOICES, | 22 | HARDWARE_TYPE_CHOICES, |
284 | @@ -49,16 +50,6 @@ class TestNodeResultHandler(MAASServerTestCase): | |||
285 | 49 | "result_type": script_result.script_set.result_type, | 50 | "result_type": script_result.script_set.result_type, |
286 | 50 | "hardware_type": script_result.script.hardware_type, | 51 | "hardware_type": script_result.script.hardware_type, |
287 | 51 | "tags": ", ".join(script_result.script.tags), | 52 | "tags": ", ".join(script_result.script.tags), |
288 | 52 | "history_list": [{ | ||
289 | 53 | "id": history.id, | ||
290 | 54 | "updated": dehydrate_datetime(history.updated), | ||
291 | 55 | "status": history.status, | ||
292 | 56 | "status_name": history.status_name, | ||
293 | 57 | "runtime": history.runtime, | ||
294 | 58 | "starttime": history.starttime, | ||
295 | 59 | "endtime": history.endtime, | ||
296 | 60 | "estimated_runtime": history.estimated_runtime, | ||
297 | 61 | } for history in script_result.history], | ||
298 | 62 | "results": [{ | 53 | "results": [{ |
299 | 63 | "name": key, | 54 | "name": key, |
300 | 64 | "title": key, | 55 | "title": key, |
301 | @@ -333,6 +324,36 @@ class TestNodeResultHandler(MAASServerTestCase): | |||
302 | 333 | 'data_type': unknown_data_type, | 324 | 'data_type': unknown_data_type, |
303 | 334 | })) | 325 | })) |
304 | 335 | 326 | ||
305 | 327 | def test_get_history(self): | ||
306 | 328 | user = factory.make_User() | ||
307 | 329 | handler = NodeResultHandler(user, {}) | ||
308 | 330 | node = factory.make_Node(owner=user) | ||
309 | 331 | script = factory.make_Script() | ||
310 | 332 | script_results = [] | ||
311 | 333 | for _ in range(10): | ||
312 | 334 | script_set = factory.make_ScriptSet(node=node) | ||
313 | 335 | script_results.append(factory.make_ScriptResult( | ||
314 | 336 | script=script, script_set=script_set, | ||
315 | 337 | status=SCRIPT_STATUS.PASSED)) | ||
316 | 338 | latest_script_result = script_results[-1] | ||
317 | 339 | script_results = sorted( | ||
318 | 340 | script_results, key=lambda i: i.id, reverse=True) | ||
319 | 341 | queries = CountQueries() | ||
320 | 342 | with queries: | ||
321 | 343 | ret = handler.get_history({'id': latest_script_result.id}) | ||
322 | 344 | self.assertEqual(4, queries.num_queries) | ||
323 | 345 | for script_result, out in zip(script_results, ret): | ||
324 | 346 | self.assertDictEqual({ | ||
325 | 347 | 'id': script_result.id, | ||
326 | 348 | 'updated': dehydrate_datetime(script_result.updated), | ||
327 | 349 | 'status': script_result.status, | ||
328 | 350 | 'status_name': script_result.status_name, | ||
329 | 351 | 'runtime': script_result.runtime, | ||
330 | 352 | 'starttime': script_result.starttime, | ||
331 | 353 | 'endtime': script_result.endtime, | ||
332 | 354 | 'estimated_runtime': script_result.estimated_runtime, | ||
333 | 355 | }, out) | ||
334 | 356 | |||
335 | 336 | def test_clear_removes_system_id_from_cache(self): | 357 | def test_clear_removes_system_id_from_cache(self): |
336 | 337 | user = factory.make_User() | 358 | user = factory.make_User() |
337 | 338 | handler = NodeResultHandler(user, {}) | 359 | handler = NodeResultHandler(user, {}) |
338 | diff --git a/src/metadataserver/models/scriptresult.py b/src/metadataserver/models/scriptresult.py | |||
339 | index 741c6db..7f0ea1b 100644 | |||
340 | --- a/src/metadataserver/models/scriptresult.py | |||
341 | +++ b/src/metadataserver/models/scriptresult.py | |||
342 | @@ -1,4 +1,4 @@ | |||
344 | 1 | # Copyright 2017 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
345 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
346 | 3 | __all__ = [ | 3 | __all__ = [ |
347 | 4 | 'ScriptResult', | 4 | 'ScriptResult', |
348 | @@ -139,7 +139,9 @@ class ScriptResult(CleanSave, TimestampedModel): | |||
349 | 139 | return self.runtime | 139 | return self.runtime |
350 | 140 | runtime = None | 140 | runtime = None |
351 | 141 | # Get an estimated runtime from previous runs. | 141 | # Get an estimated runtime from previous runs. |
353 | 142 | for script_result in self.history: | 142 | for script_result in self.history.only( |
354 | 143 | 'status', 'started', 'ended', 'script_id', 'script_name', | ||
355 | 144 | 'script_set_id', 'physical_blockdevice_id'): | ||
356 | 143 | # Only look at passed results when calculating an estimated | 145 | # Only look at passed results when calculating an estimated |
357 | 144 | # runtime. Failed results may take longer or shorter than | 146 | # runtime. Failed results may take longer or shorter than |
358 | 145 | # average. Don't use self.history.filter for this as the now | 147 | # average. Don't use self.history.filter for this as the now |
359 | diff --git a/src/metadataserver/models/tests/test_scriptresult.py b/src/metadataserver/models/tests/test_scriptresult.py | |||
360 | index 11269d9..5930fae 100644 | |||
361 | --- a/src/metadataserver/models/tests/test_scriptresult.py | |||
362 | +++ b/src/metadataserver/models/tests/test_scriptresult.py | |||
363 | @@ -1,4 +1,4 @@ | |||
365 | 1 | # Copyright 2017 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2017-2018 Canonical Ltd. This software is licensed under the |
366 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
367 | 3 | 3 | ||
368 | 4 | __all__ = [] | 4 | __all__ = [] |
369 | @@ -19,6 +19,7 @@ from maasserver.models import ( | |||
370 | 19 | from maasserver.testing.factory import factory | 19 | from maasserver.testing.factory import factory |
371 | 20 | from maasserver.testing.testcase import MAASServerTestCase | 20 | from maasserver.testing.testcase import MAASServerTestCase |
372 | 21 | from maasserver.utils.orm import reload_object | 21 | from maasserver.utils.orm import reload_object |
373 | 22 | from maastesting.djangotestcase import CountQueries | ||
374 | 22 | from maastesting.matchers import ( | 23 | from maastesting.matchers import ( |
375 | 23 | DocTestMatches, | 24 | DocTestMatches, |
376 | 24 | MockCalledOnceWith, | 25 | MockCalledOnceWith, |
377 | @@ -28,7 +29,10 @@ from metadataserver.enum import ( | |||
378 | 28 | SCRIPT_STATUS, | 29 | SCRIPT_STATUS, |
379 | 29 | SCRIPT_STATUS_CHOICES, | 30 | SCRIPT_STATUS_CHOICES, |
380 | 30 | ) | 31 | ) |
382 | 31 | from metadataserver.models import scriptresult as scriptresult_module | 32 | from metadataserver.models import ( |
383 | 33 | ScriptResult, | ||
384 | 34 | scriptresult as scriptresult_module, | ||
385 | 35 | ) | ||
386 | 32 | from provisioningserver.events import EVENT_TYPES | 36 | from provisioningserver.events import EVENT_TYPES |
387 | 33 | import yaml | 37 | import yaml |
388 | 34 | 38 | ||
389 | @@ -624,6 +628,26 @@ class TestScriptResult(MAASServerTestCase): | |||
390 | 624 | script_result = script_results[-1] | 628 | script_result = script_results[-1] |
391 | 625 | self.assertItemsEqual(script_results, script_result.history) | 629 | self.assertItemsEqual(script_results, script_result.history) |
392 | 626 | 630 | ||
393 | 631 | def test_history_query_count(self): | ||
394 | 632 | script_name = factory.make_name('script_name') | ||
395 | 633 | script_set = factory.make_ScriptSet() | ||
396 | 634 | script_results = [ | ||
397 | 635 | factory.make_ScriptResult( | ||
398 | 636 | script_name=script_name, script_set=script_set) | ||
399 | 637 | for _ in range(10) | ||
400 | 638 | ] | ||
401 | 639 | queries_one = CountQueries() | ||
402 | 640 | script_result_one = ScriptResult.objects.get(id=script_results[0].id) | ||
403 | 641 | with queries_one: | ||
404 | 642 | script_result_one.history | ||
405 | 643 | queries_many = CountQueries() | ||
406 | 644 | script_result_many = ScriptResult.objects.get( | ||
407 | 645 | id=script_results[-1].id) | ||
408 | 646 | with queries_many: | ||
409 | 647 | script_result_many.history | ||
410 | 648 | self.assertEquals(1, queries_one.num_queries) | ||
411 | 649 | self.assertEquals(1, queries_many.num_queries) | ||
412 | 650 | |||
413 | 627 | def test_history_storage_device(self): | 651 | def test_history_storage_device(self): |
414 | 628 | # Regression test for LP: #1721524 | 652 | # Regression test for LP: #1721524 |
415 | 629 | script = factory.make_Script() | 653 | script = factory.make_Script() |
Looks good