Merge ~blake-rouse/maas:lp1723425 into maas:master
- Git
- lp:~blake-rouse/maas
- lp1723425
- Merge into master
Proposed by
Blake Rouse
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | 6351fe88d73b42a1fb9abe2f87c7ef8cad463c19 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~blake-rouse/maas:lp1723425 |
Merge into: | maas:master |
Diff against target: |
519 lines (+316/-8) 12 files modified
src/maasserver/api/scriptresults.py (+3/-0) src/maasserver/api/tests/test_scriptresults.py (+9/-0) src/maasserver/static/js/angular/directives/script_runtime.js (+57/-0) src/maasserver/static/js/angular/directives/tests/test_script_runtime.js (+89/-0) src/maasserver/static/partials/script-results-list.html (+8/-8) src/maasserver/views/combo.py (+1/-0) src/maasserver/websockets/handlers/node.py (+6/-0) src/maasserver/websockets/handlers/node_result.py (+6/-0) src/maasserver/websockets/handlers/tests/test_machine.py (+12/-0) src/maasserver/websockets/handlers/tests/test_node_result.py (+6/-0) src/metadataserver/models/scriptresult.py (+45/-0) src/metadataserver/models/tests/test_scriptresult.py (+74/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Lander | Approve | ||
Lee Trager (community) | Approve | ||
Mike Pontillo (community) | Approve | ||
Review via email: mp+333281@code.launchpad.net |
Commit message
Fixes LP: #1723425 - Add a maas-script-
Description of the change
To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote : | # |
New code looks good. (Of course, I trust you to fix the regressions in the test suite noted by the lander!)
review:
Approve
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b lp1723425 lp:~blake-rouse/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b lp1723425 lp:~blake-rouse/maas into -b master lp:~maas-committers/maas
STATUS: SUCCESS
COMMIT: 6351fe88d73b42a
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/api/scriptresults.py b/src/maasserver/api/scriptresults.py |
2 | index 80fc7f3..b11ba66 100644 |
3 | --- a/src/maasserver/api/scriptresults.py |
4 | +++ b/src/maasserver/api/scriptresults.py |
5 | @@ -210,6 +210,9 @@ class NodeScriptResultHandler(OperationsHandler): |
6 | 'started': fmt_time(script_result.started), |
7 | 'ended': fmt_time(script_result.ended), |
8 | 'runtime': script_result.runtime, |
9 | + 'starttime': script_result.starttime, |
10 | + 'endtime': script_result.endtime, |
11 | + 'estimated_runtime': script_result.estimated_runtime, |
12 | 'parameters': script_result.parameters, |
13 | 'script_id': script_result.script_id, |
14 | 'script_revision_id': script_result.script_version_id, |
15 | diff --git a/src/maasserver/api/tests/test_scriptresults.py b/src/maasserver/api/tests/test_scriptresults.py |
16 | index d80b68f..ee942a9 100644 |
17 | --- a/src/maasserver/api/tests/test_scriptresults.py |
18 | +++ b/src/maasserver/api/tests/test_scriptresults.py |
19 | @@ -269,6 +269,9 @@ class TestNodeScriptResultAPI(APITestCase.ForUser): |
20 | 'started': fmt_time(script_result.started), |
21 | 'ended': fmt_time(script_result.ended), |
22 | 'runtime': script_result.runtime, |
23 | + 'starttime': script_result.starttime, |
24 | + 'endtime': script_result.endtime, |
25 | + 'estimated_runtime': script_result.estimated_runtime, |
26 | 'parameters': script_result.parameters, |
27 | 'script_id': script_result.script_id, |
28 | 'script_revision_id': script_result.script_version_id, |
29 | @@ -315,6 +318,9 @@ class TestNodeScriptResultAPI(APITestCase.ForUser): |
30 | 'started': fmt_time(script_result.started), |
31 | 'ended': fmt_time(script_result.ended), |
32 | 'runtime': script_result.runtime, |
33 | + 'starttime': script_result.starttime, |
34 | + 'endtime': script_result.endtime, |
35 | + 'estimated_runtime': script_result.estimated_runtime, |
36 | 'parameters': script_result.parameters, |
37 | 'script_id': script_result.script_id, |
38 | 'script_revision_id': script_result.script_version_id, |
39 | @@ -378,6 +384,9 @@ class TestNodeScriptResultAPI(APITestCase.ForUser): |
40 | 'started': fmt_time(script_result.started), |
41 | 'ended': fmt_time(script_result.ended), |
42 | 'runtime': script_result.runtime, |
43 | + 'starttime': script_result.starttime, |
44 | + 'endtime': script_result.endtime, |
45 | + 'estimated_runtime': script_result.estimated_runtime, |
46 | 'parameters': script_result.parameters, |
47 | 'script_id': script_result.script_id, |
48 | 'script_revision_id': script_result.script_version_id, |
49 | diff --git a/src/maasserver/static/js/angular/directives/script_runtime.js b/src/maasserver/static/js/angular/directives/script_runtime.js |
50 | new file mode 100644 |
51 | index 0000000..3a198c3 |
52 | --- /dev/null |
53 | +++ b/src/maasserver/static/js/angular/directives/script_runtime.js |
54 | @@ -0,0 +1,57 @@ |
55 | +/* Copyright 2017 Canonical Ltd. This software is licensed under the |
56 | + * GNU Affero General Public License version 3 (see the file LICENSE). |
57 | + * |
58 | + * Script runtime counter directive. |
59 | + */ |
60 | + |
61 | +angular.module('MAAS').run(['$templateCache', function ($templateCache) { |
62 | + // Inject the script_runtime.html into the template cache. |
63 | + $templateCache.put('directive/templates/script_runtime.html', [ |
64 | + '<span data-ng-if="(scriptStatus === 1 || scriptStatus === 7) &&', |
65 | + " estimatedRunTime !== 'Unknown'" + '">{{counter}} of ', |
66 | + '~{{estimatedRunTime}}</span>', |
67 | + '<span data-ng-if="(scriptStatus === 1 || scriptStatus === 7) &&', |
68 | + " estimatedRunTime == 'Unknown'" + '">{{counter}}</span>', |
69 | + '<span data-ng-if="scriptStatus === 0 && estimatedRunTime !== ', |
70 | + "'Unknown'" + '">~{{estimatedRunTime}}</span>', |
71 | + '<span data-ng-if="scriptStatus !== 0 && scriptStatus !== 1 ', |
72 | + '&& scriptStatus !== 7">{{runTime}}</span>' |
73 | + ].join('')); |
74 | +}]); |
75 | + |
76 | +angular.module('MAAS').directive('maasScriptRunTime', function() { |
77 | + return { |
78 | + restrict: "A", |
79 | + require: ["startTime", "runTime", "estimatedRunTime", "scriptStatus"], |
80 | + scope: { |
81 | + startTime: '=', |
82 | + runTime: '@', |
83 | + estimatedRunTime: '@', |
84 | + scriptStatus: '=' |
85 | + }, |
86 | + templateUrl: 'directive/templates/script_runtime.html', |
87 | + controller: function($scope, $interval) { |
88 | + $scope.counter = "0:00:00"; |
89 | + |
90 | + function incrementCounter() { |
91 | + if(($scope.scriptStatus === 1 || $scope.scriptStatus === 7) && |
92 | + $scope.startTime) { |
93 | + var date = new Date(null); |
94 | + date.setSeconds((Date.now()/1000) - $scope.startTime); |
95 | + $scope.counter = date.toISOString().substr(11, 8); |
96 | + if($scope.counter.indexOf('00:') === 0) { |
97 | + $scope.counter = $scope.counter.substr(1); |
98 | + } |
99 | + } |
100 | + } |
101 | + |
102 | + // Update the counter on init, start the interval and stop it when |
103 | + // the directive is destroyed. |
104 | + incrementCounter(); |
105 | + var promise = $interval(incrementCounter, 1000); |
106 | + $scope.$on('$destroy', function() { |
107 | + $interval.cancel(promise); |
108 | + }); |
109 | + } |
110 | + }; |
111 | +}); |
112 | diff --git a/src/maasserver/static/js/angular/directives/tests/test_script_runtime.js b/src/maasserver/static/js/angular/directives/tests/test_script_runtime.js |
113 | new file mode 100644 |
114 | index 0000000..5839f3f |
115 | --- /dev/null |
116 | +++ b/src/maasserver/static/js/angular/directives/tests/test_script_runtime.js |
117 | @@ -0,0 +1,89 @@ |
118 | +/* Copyright 2017 Canonical Ltd. This software is licensed under the |
119 | + * GNU Affero General Public License version 3 (see the file LICENSE). |
120 | + * |
121 | + * Unit tests for script runtime directive. |
122 | + */ |
123 | + |
124 | +describe("maasScriptRunTime", function() { |
125 | + |
126 | + // Load the MAAS module. |
127 | + beforeEach(module("MAAS")); |
128 | + |
129 | + // Create a new scope before each test. |
130 | + var $scope, $interval; |
131 | + beforeEach(inject(function($rootScope, $injector) { |
132 | + $interval = $injector.get('$interval'); |
133 | + $scope = $rootScope.$new(); |
134 | + $scope.startTime = null; |
135 | + $scope.runTime = null; |
136 | + $scope.estimatedRunTime = null; |
137 | + $scope.scriptStatus = null; |
138 | + })); |
139 | + |
140 | + // Return the compiled directive. |
141 | + function compileDirective( |
142 | + startTime, runTime, estimatedRunTime, scriptStatus) { |
143 | + var directive; |
144 | + var html = '<span data-maas-script-run-time="script-runtime" ' + |
145 | + 'data-start-time="' + startTime + '" data-run-time="' + |
146 | + runTime + '" data-estimated-run-time="' + estimatedRunTime + |
147 | + '" data-script-status="' + scriptStatus + '"></span>'; |
148 | + |
149 | + // Compile the directive. |
150 | + inject(function($compile) { |
151 | + directive = $compile(html)($scope); |
152 | + }); |
153 | + |
154 | + // Perform the digest cycle to finish the compile. |
155 | + $scope.$digest(); |
156 | + return directive; |
157 | + } |
158 | + |
159 | + it('should have span element', function () { |
160 | + var startTime = Date.now() / 1000; |
161 | + var runTime = '0:00:30'; |
162 | + var estimatedRunTime = '0:00:35'; |
163 | + var scriptStatus = 7; |
164 | + var directive = compileDirective( |
165 | + startTime, runTime, estimatedRunTime, scriptStatus); |
166 | + var spanElement = directive.find('span'); |
167 | + expect(spanElement).toBeDefined(); |
168 | + expect(spanElement.text()).toEqual('0:00:00 of ~' + estimatedRunTime); |
169 | + }); |
170 | + |
171 | + it('should have applied template', function () { |
172 | + var startTime = Date.now() / 1000; |
173 | + var runTime = '0:00:30'; |
174 | + var estimatedRunTime = '0:00:35'; |
175 | + var scriptStatus = 7; |
176 | + var directive = compileDirective( |
177 | + startTime, runTime, estimatedRunTime, scriptStatus); |
178 | + expect(directive.html()).not.toEqual(''); |
179 | + }); |
180 | + |
181 | + it('should counter based on passed time', function () { |
182 | + var startTime = (Date.now() / 1000) - 5; // 5 seconds |
183 | + var runTime = '0:00:30'; |
184 | + var estimatedRunTime = '0:00:35'; |
185 | + var scriptStatus = 7; |
186 | + var directive = compileDirective( |
187 | + startTime, runTime, estimatedRunTime, scriptStatus); |
188 | + var spanElement = directive.find('span'); |
189 | + expect(spanElement).toBeDefined(); |
190 | + expect(spanElement.text()).toEqual('0:00:05 of ~' + estimatedRunTime); |
191 | + }); |
192 | + |
193 | + it('counter updated based on passed time not $interval', function () { |
194 | + var startTime = (Date.now() / 1000) - 5; // 5 seconds |
195 | + var runTime = '0:00:30'; |
196 | + var estimatedRunTime = '0:00:35'; |
197 | + var scriptStatus = 7; |
198 | + var directive = compileDirective( |
199 | + startTime, runTime, estimatedRunTime, scriptStatus); |
200 | + // Flush should not cause the passed time to change. |
201 | + $interval.flush(1000); |
202 | + var spanElement = directive.find('span'); |
203 | + expect(spanElement).toBeDefined(); |
204 | + expect(spanElement.text()).toEqual('0:00:05 of ~' + estimatedRunTime); |
205 | + }); |
206 | +}); |
207 | diff --git a/src/maasserver/static/partials/script-results-list.html b/src/maasserver/static/partials/script-results-list.html |
208 | index 974e44f..4fd4130 100644 |
209 | --- a/src/maasserver/static/partials/script-results-list.html |
210 | +++ b/src/maasserver/static/partials/script-results-list.html |
211 | @@ -13,9 +13,9 @@ |
212 | <div class="table__header table-col--2 u-padding--left-none"></div> |
213 | <div class="table__header table-col--24">Name</div> |
214 | <div class="table__header table-col--24">Tags</div> |
215 | - <div class="table__header table-col--10">Runtime</div> |
216 | + <div class="table__header table-col--15">Runtime</div> |
217 | <div class="table__header table-col--20">Date</div> |
218 | - <div class="table__header table-col--15">Result</div> |
219 | + <div class="table__header table-col--10">Result</div> |
220 | <div class="table__header table-col--5 u-align--right">Actions</div> |
221 | </div> |
222 | </header> |
223 | @@ -30,9 +30,9 @@ |
224 | <button class="icon u-margin--top-tiny u-float--right" data-ng-class="{'icon--open': !result.showing_results, 'icon--close': result.showing_results}" data-ng-if="!result.showing_history"></button> |
225 | </div> |
226 | <div class="table__data table-col--24" aria-label="Tags"><span data-ng-hide="result.showing_history">{$ result.tags $}</span></div> |
227 | - <div class="table__data table-col--10" aria-label="Runtime"><span data-ng-hide="result.showing_history">{$ result.runtime $}</span></div> |
228 | + <div class="table__data table-col--15" aria-label="Runtime"><span data-ng-hide="result.showing_history" data-maas-script-run-time="script-runtime" data-start-time="result.starttime" data-run-time="{{result.runtime}}" data-estimated-run-time="{{result.estimated_runtime}}" data-script-status="result.status"></span></div> |
229 | <div class="table__data table-col--20" aria-label="Date"><span data-ng-hide="result.showing_history">{$ result.updated $}</span></div> |
230 | - <div class="table__data table-col--15" aria-label="Status"> |
231 | + <div class="table__data table-col--10" aria-label="Status"> |
232 | <span data-ng-hide="result.showing_history"> |
233 | <!-- Only link to the testing result when we've received it. This is indicated with status 2(passed), 3(failed), 4(timedout), 6(degraded), 8(failed installing)--> |
234 | {$ result.status_name $} <a data-ng-if="result.status === 2 || result.status === 3 || result.status === 4 || result.status === 6 || result.status === 8" href="#/node/{$ type_name $}/{$ node.system_id $}/{$ section.area $}/{$ result.id $}">View log</a> |
235 | @@ -69,11 +69,11 @@ |
236 | <div class="table__data table-col--2 u-padding--left-none" aria-label="Status"> |
237 | <span data-maas-script-status="script-status" data-script-status="item.status"></span> |
238 | </div> |
239 | - <div class="table__data table-col--20" aria-label="Name">{$ result.name $}</div> |
240 | - <div class="table__data table-col--25" aria-label="Tags">{$ result.tags $}</div> |
241 | - <div class="table__data table-col--10" aria-label="Runtime">{$ item.runtime $}</div> |
242 | + <div class="table__data table-col--24" aria-label="Name">{$ result.name $}</div> |
243 | + <div class="table__data table-col--24" aria-label="Tags">{$ result.tags $}</div> |
244 | + <div class="table__data table-col--15" aria-label="Runtime"><span data-maas-script-run-time="script-runtime" data-start-time="item.starttime" data-run-time="{{item.runtime}}" data-estimated-run-time="{{item.estimated_runtime}}" data-script-status="item.status"></span></div> |
245 | <div class="table__data table-col--20" aria-label="Date">{$ item.updated $}</div> |
246 | - <div class="table__data table-col--23" aria-label="Status"> |
247 | + <div class="table__data table-col--10" aria-label="Status"> |
248 | <!-- Only link to the testing result when we've received it. This is indicated with status 2(passed), 3(failed), 4(timedout), 6(degraded), 8(failed installing)--> |
249 | {$ item.status_name $} <a data-ng-if="item.status === 2 || item.status === 3 || item.status === 4 || item.status === 6 || item.status === 8" href="#/node/{$ type_name $}/{$ node.system_id $}/{$ section.area $}/{$ item.id $}">View log</a> |
250 | </div> |
251 | diff --git a/src/maasserver/views/combo.py b/src/maasserver/views/combo.py |
252 | index 6eb2fd9..e8fc6e1 100755 |
253 | --- a/src/maasserver/views/combo.py |
254 | +++ b/src/maasserver/views/combo.py |
255 | @@ -128,6 +128,7 @@ MERGE_VIEWS = { |
256 | "js/angular/directives/release_name.js", |
257 | "js/angular/directives/release_options.js", |
258 | "js/angular/directives/script_results_list.js", |
259 | + "js/angular/directives/script_runtime.js", |
260 | "js/angular/directives/script_select.js", |
261 | "js/angular/directives/script_status.js", |
262 | "js/angular/directives/ssh_keys.js", |
263 | diff --git a/src/maasserver/websockets/handlers/node.py b/src/maasserver/websockets/handlers/node.py |
264 | index 7b719a4..95c70b7 100644 |
265 | --- a/src/maasserver/websockets/handlers/node.py |
266 | +++ b/src/maasserver/websockets/handlers/node.py |
267 | @@ -605,6 +605,9 @@ class NodeHandler(TimestampedModelHandler): |
268 | 'started': dehydrate_datetime(script_result.started), |
269 | 'ended': dehydrate_datetime(script_result.ended), |
270 | 'runtime': script_result.runtime, |
271 | + 'starttime': script_result.starttime, |
272 | + 'endtime': script_result.endtime, |
273 | + 'estimated_runtime': script_result.estimated_runtime, |
274 | }) |
275 | if (script_result.stderr != b'' and |
276 | script_set.result_type != RESULT_TYPE.TESTING): |
277 | @@ -621,6 +624,9 @@ class NodeHandler(TimestampedModelHandler): |
278 | 'started': dehydrate_datetime(script_result.started), |
279 | 'ended': dehydrate_datetime(script_result.ended), |
280 | 'runtime': script_result.runtime, |
281 | + 'starttime': script_result.starttime, |
282 | + 'endtime': script_result.endtime, |
283 | + 'estimated_runtime': script_result.estimated_runtime, |
284 | }) |
285 | return sorted(ret, key=lambda i: i['name']) |
286 | |
287 | diff --git a/src/maasserver/websockets/handlers/node_result.py b/src/maasserver/websockets/handlers/node_result.py |
288 | index a405d06..e6e516e 100644 |
289 | --- a/src/maasserver/websockets/handlers/node_result.py |
290 | +++ b/src/maasserver/websockets/handlers/node_result.py |
291 | @@ -76,6 +76,9 @@ class NodeResultHandler(TimestampedModelHandler): |
292 | data["name"] = obj.name |
293 | data["status_name"] = obj.status_name |
294 | data["runtime"] = obj.runtime |
295 | + data["starttime"] = obj.starttime |
296 | + data["endtime"] = obj.endtime |
297 | + data["estimated_runtime"] = obj.estimated_runtime |
298 | data["result_type"] = obj.script_set.result_type |
299 | if obj.script is not None: |
300 | data["hardware_type"] = obj.script.hardware_type |
301 | @@ -92,6 +95,9 @@ class NodeResultHandler(TimestampedModelHandler): |
302 | "status": history.status, |
303 | "status_name": history.status_name, |
304 | "runtime": history.runtime, |
305 | + "starttime": history.starttime, |
306 | + "endtime": history.endtime, |
307 | + "estimated_runtime": history.estimated_runtime, |
308 | } for history in obj.history |
309 | ] |
310 | try: |
311 | diff --git a/src/maasserver/websockets/handlers/tests/test_machine.py b/src/maasserver/websockets/handlers/tests/test_machine.py |
312 | index edebbea..5080cfa 100644 |
313 | --- a/src/maasserver/websockets/handlers/tests/test_machine.py |
314 | +++ b/src/maasserver/websockets/handlers/tests/test_machine.py |
315 | @@ -1277,6 +1277,9 @@ class TestMachineHandler(MAASServerTestCase): |
316 | 'started': dehydrate_datetime(script_result.started), |
317 | 'ended': dehydrate_datetime(script_result.ended), |
318 | 'runtime': script_result.runtime, |
319 | + 'starttime': script_result.starttime, |
320 | + 'endtime': script_result.endtime, |
321 | + 'estimated_runtime': script_result.estimated_runtime, |
322 | }, |
323 | { |
324 | 'id': script_result.id, |
325 | @@ -1292,6 +1295,9 @@ class TestMachineHandler(MAASServerTestCase): |
326 | 'started': dehydrate_datetime(script_result.started), |
327 | 'ended': dehydrate_datetime(script_result.ended), |
328 | 'runtime': script_result.runtime, |
329 | + 'starttime': script_result.starttime, |
330 | + 'endtime': script_result.endtime, |
331 | + 'estimated_runtime': script_result.estimated_runtime, |
332 | }], handler.dehydrate_script_set(script_set)) |
333 | |
334 | def test_dehydrate_script_set_returns_output_if_stdout_empty(self): |
335 | @@ -1318,6 +1324,9 @@ class TestMachineHandler(MAASServerTestCase): |
336 | 'started': dehydrate_datetime(script_result.started), |
337 | 'ended': dehydrate_datetime(script_result.ended), |
338 | 'runtime': script_result.runtime, |
339 | + 'starttime': script_result.starttime, |
340 | + 'endtime': script_result.endtime, |
341 | + 'estimated_runtime': script_result.estimated_runtime, |
342 | }, handler.dehydrate_script_set(script_result.script_set)[0]) |
343 | |
344 | def test_dehydrate_script_set_returns_combined_for_testing(self): |
345 | @@ -1341,6 +1350,9 @@ class TestMachineHandler(MAASServerTestCase): |
346 | 'started': dehydrate_datetime(script_result.started), |
347 | 'ended': dehydrate_datetime(script_result.ended), |
348 | 'runtime': script_result.runtime, |
349 | + 'starttime': script_result.starttime, |
350 | + 'endtime': script_result.endtime, |
351 | + 'estimated_runtime': script_result.estimated_runtime, |
352 | }, handler.dehydrate_script_set(script_result.script_set)[0]) |
353 | |
354 | def test_dehydrate_script_set_status(self): |
355 | diff --git a/src/maasserver/websockets/handlers/tests/test_node_result.py b/src/maasserver/websockets/handlers/tests/test_node_result.py |
356 | index 0c3013e..5b3fc37 100644 |
357 | --- a/src/maasserver/websockets/handlers/tests/test_node_result.py |
358 | +++ b/src/maasserver/websockets/handlers/tests/test_node_result.py |
359 | @@ -42,6 +42,9 @@ class TestNodeResultHandler(MAASServerTestCase): |
360 | "started": dehydrate_datetime(script_result.started), |
361 | "ended": dehydrate_datetime(script_result.ended), |
362 | "runtime": script_result.runtime, |
363 | + "starttime": script_result.starttime, |
364 | + "endtime": script_result.endtime, |
365 | + "estimated_runtime": script_result.estimated_runtime, |
366 | "name": script_result.name, |
367 | "result_type": script_result.script_set.result_type, |
368 | "hardware_type": script_result.script.hardware_type, |
369 | @@ -52,6 +55,9 @@ class TestNodeResultHandler(MAASServerTestCase): |
370 | "status": history.status, |
371 | "status_name": history.status_name, |
372 | "runtime": history.runtime, |
373 | + "starttime": history.starttime, |
374 | + "endtime": history.endtime, |
375 | + "estimated_runtime": history.estimated_runtime, |
376 | } for history in script_result.history], |
377 | "results": [{ |
378 | "name": key, |
379 | diff --git a/src/metadataserver/models/scriptresult.py b/src/metadataserver/models/scriptresult.py |
380 | index 7620ddd..dccda0a 100644 |
381 | --- a/src/metadataserver/models/scriptresult.py |
382 | +++ b/src/metadataserver/models/scriptresult.py |
383 | @@ -117,6 +117,51 @@ class ScriptResult(CleanSave, TimestampedModel): |
384 | else: |
385 | return '' |
386 | |
387 | + @property |
388 | + def starttime(self): |
389 | + if self.started is not None: |
390 | + return self.started.timestamp() |
391 | + else: |
392 | + return '' |
393 | + |
394 | + @property |
395 | + def endtime(self): |
396 | + if self.ended is not None: |
397 | + return self.ended.timestamp() |
398 | + else: |
399 | + return '' |
400 | + |
401 | + @property |
402 | + def estimated_runtime(self): |
403 | + # If there is a runtime the script has completed, no need to calculate |
404 | + # an estimate. |
405 | + if self.runtime != '': |
406 | + return self.runtime |
407 | + runtime = None |
408 | + # Get an estimated runtime from previous runs. |
409 | + for script_result in self.history: |
410 | + # Only look at passed results when calculating an estimated |
411 | + # runtime. Failed results may take longer or shorter than |
412 | + # average. Don't use self.history.filter for this as the now |
413 | + # cached history list may be used elsewhere. |
414 | + if script_result.status != SCRIPT_STATUS.PASSED: |
415 | + continue |
416 | + previous_runtime = script_result.ended - script_result.started |
417 | + if runtime is None: |
418 | + runtime = previous_runtime |
419 | + else: |
420 | + runtime += previous_runtime |
421 | + runtime = runtime / 2 |
422 | + if runtime is None: |
423 | + if self.script is not None and self.script.timeout != timedelta(0): |
424 | + # If there were no previous runs use the script's timeout. |
425 | + return str(self.script.timeout - timedelta( |
426 | + microseconds=self.script.timeout.microseconds)) |
427 | + else: |
428 | + return 'Unknown' |
429 | + else: |
430 | + return str(runtime - timedelta(microseconds=runtime.microseconds)) |
431 | + |
432 | def __str__(self): |
433 | return "%s/%s" % (self.script_set.node.system_id, self.name) |
434 | |
435 | diff --git a/src/metadataserver/models/tests/test_scriptresult.py b/src/metadataserver/models/tests/test_scriptresult.py |
436 | index ed8f41e..65f4fc7 100644 |
437 | --- a/src/metadataserver/models/tests/test_scriptresult.py |
438 | +++ b/src/metadataserver/models/tests/test_scriptresult.py |
439 | @@ -417,6 +417,80 @@ class TestScriptResult(MAASServerTestCase): |
440 | script_result = factory.make_ScriptResult(status=SCRIPT_STATUS.PENDING) |
441 | self.assertEquals('', script_result.runtime) |
442 | |
443 | + def test_get_starttime(self): |
444 | + now = datetime.now() |
445 | + script_result = factory.make_ScriptResult( |
446 | + status=SCRIPT_STATUS.PASSED, |
447 | + started=now, ended=now) |
448 | + self.assertEquals(now.timestamp(), script_result.starttime) |
449 | + |
450 | + def test_get_starttime_None(self): |
451 | + script_result = factory.make_ScriptResult( |
452 | + status=SCRIPT_STATUS.PENDING) |
453 | + self.assertEquals('', script_result.starttime) |
454 | + |
455 | + def test_get_endtime(self): |
456 | + now = datetime.now() |
457 | + script_result = factory.make_ScriptResult( |
458 | + status=SCRIPT_STATUS.PASSED, |
459 | + started=now, ended=now) |
460 | + self.assertEquals(now.timestamp(), script_result.endtime) |
461 | + |
462 | + def test_get_endtime_None(self): |
463 | + script_result = factory.make_ScriptResult( |
464 | + status=SCRIPT_STATUS.PENDING) |
465 | + self.assertEquals('', script_result.endtime) |
466 | + |
467 | + def test_estimated_runtime_returns_set_runtime(self): |
468 | + now = datetime.now() |
469 | + script_result = factory.make_ScriptResult( |
470 | + status=SCRIPT_STATUS.PENDING, |
471 | + started=now, ended=(now + factory.make_timedelta())) |
472 | + self.assertEquals( |
473 | + script_result.runtime, script_result.estimated_runtime) |
474 | + |
475 | + def test_estimated_runtime_returns_average_of_previous(self): |
476 | + script = factory.make_Script() |
477 | + script_set = factory.make_ScriptSet() |
478 | + old_results = [ |
479 | + factory.make_ScriptResult( |
480 | + status=SCRIPT_STATUS.PASSED, |
481 | + script=script, script_set=script_set) |
482 | + for _ in range(10) |
483 | + ] |
484 | + factory.make_ScriptResult( |
485 | + status=SCRIPT_STATUS.FAILED, |
486 | + script=script, script_set=script_set) |
487 | + average_runtime = (old_results[9].ended - old_results[9].started) |
488 | + for result in reversed(old_results[:-1]): |
489 | + average_runtime += result.ended - result.started |
490 | + average_runtime = average_runtime / 2 |
491 | + now = datetime.now() |
492 | + script_result = factory.make_ScriptResult( |
493 | + status=SCRIPT_STATUS.RUNNING, started=now, |
494 | + script=script, script_set=script_set) |
495 | + expected = str( |
496 | + average_runtime - timedelta( |
497 | + microseconds=average_runtime.microseconds)) |
498 | + self.assertEquals( |
499 | + expected, script_result.estimated_runtime) |
500 | + |
501 | + def test_estimated_runtime_uses_timeout(self): |
502 | + now = datetime.now() |
503 | + script_result = factory.make_ScriptResult( |
504 | + status=SCRIPT_STATUS.RUNNING, started=now) |
505 | + expected = str(script_result.script.timeout - timedelta( |
506 | + microseconds=script_result.script.timeout.microseconds)) |
507 | + self.assertEquals(expected, script_result.estimated_runtime) |
508 | + |
509 | + def test_estimated_runtime_returns_Unknown(self): |
510 | + now = datetime.now() |
511 | + script_result = factory.make_ScriptResult( |
512 | + status=SCRIPT_STATUS.RUNNING, started=now) |
513 | + script_result.script.timeout = timedelta(0) |
514 | + script_result.script.save() |
515 | + self.assertEquals("Unknown", script_result.estimated_runtime) |
516 | + |
517 | def test_read_results(self): |
518 | results = { |
519 | 'status': random.choice( |
UNIT TESTS
-b lp1723425 lp:~blake-rouse/maas into -b master lp:~maas-committers/maas
STATUS: FAILED maas-ci- jenkins. internal: 8080/job/ maas/job/ branch- tester/ 662/console 6db6548ec1ada82 526f2e9186
LOG: http://
COMMIT: a55e2f46eeba4ca