Merge ~ltrager/maas:2.3_1722607_2 into maas:2.3

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: c0f6a305f32b20c96de1804bacdc1642a9fdffaa
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:2.3_1722607_2
Merge into: maas:2.3
Prerequisite: ~ltrager/maas:2.3_1722607_1
Diff against target: 949 lines (+436/-308)
7 files modified
src/maasserver/static/js/angular/controllers/node_details.js (+0/-125)
src/maasserver/static/js/angular/controllers/node_results.js (+141/-11)
src/maasserver/static/js/angular/controllers/tests/test_node_details.js (+1/-145)
src/maasserver/static/js/angular/controllers/tests/test_node_results.js (+230/-0)
src/maasserver/static/js/angular/factories/nodes.js (+13/-1)
src/maasserver/static/js/angular/factories/tests/test_nodes.js (+32/-0)
src/maasserver/static/partials/node-details.html (+19/-26)
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+368110@code.launchpad.net

Commit message

LP: #1722607 - 2/2 Stop sending logs with node object over the websocket.

Update the UI to pull summary data from NodesHandler and installation
data from the NodeResultsHandler. There are no UI changes with this
branch.

Backport of 3d16280 for LP: #1830365

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) 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/src/maasserver/static/js/angular/controllers/node_details.js b/src/maasserver/static/js/angular/controllers/node_details.js
2index 1b6bc0e..fa3ab7e 100644
3--- a/src/maasserver/static/js/angular/controllers/node_details.js
4+++ b/src/maasserver/static/js/angular/controllers/node_details.js
5@@ -111,13 +111,6 @@ angular.module('MAAS').controller('NodeDetailsController', [
6 limit: 10
7 };
8
9- // Machine output section.
10- $scope.machine_output = {
11- viewable: false,
12- selectedView: null,
13- views: []
14- };
15-
16 // Updates the page title.
17 function updateTitle() {
18 if($scope.node && $scope.node.fqdn) {
19@@ -244,68 +237,6 @@ angular.module('MAAS').controller('NodeDetailsController', [
20 }
21 }
22
23- // Updates the machine output section.
24- function updateMachineOutput() {
25- // Set if it should even be viewable.
26- $scope.machine_output.viewable = (
27- angular.isString($scope.node.summary_xml) ||
28- angular.isString($scope.node.summary_yaml) ||
29- (angular.isArray($scope.node.installation_results) &&
30- $scope.node.installation_results.length > 0));
31-
32- // Grab the selected view name, so it can be kept the same if
33- // possible.
34- var viewName = null;
35- if(angular.isObject($scope.machine_output.selectedView)) {
36- viewName = $scope.machine_output.selectedView.name;
37- }
38-
39- // If the viewName is empty, then a default one was not selected.
40- // We want the installation output to be the default if possible.
41- if(!angular.isString(viewName)) {
42- viewName = "installation";
43- }
44-
45- // Setup the views that are viewable.
46- $scope.machine_output.views = [];
47- if(angular.isString($scope.node.summary_yaml)) {
48- $scope.machine_output.views.push({
49- name: "summary_yaml",
50- title: "Machine output (YAML)"
51- });
52- }
53- if(angular.isString($scope.node.summary_xml)) {
54- $scope.machine_output.views.push({
55- name: "summary_xml",
56- title: "Machine output (XML)"
57- });
58- }
59- if(angular.isArray($scope.node.installation_results) &&
60- $scope.node.installation_results.length > 0) {
61- $scope.machine_output.views.push({
62- name: "installation",
63- title: "Installation output"
64- });
65- }
66-
67- // Set the selected view to its previous value or to the first
68- // entry in the views list.
69- var selectedView = null;
70- angular.forEach($scope.machine_output.views, function(view) {
71- if(view.name === viewName) {
72- selectedView = view;
73- }
74- });
75- if(angular.isObject(selectedView)) {
76- $scope.machine_output.selectedView = selectedView;
77- } else if ($scope.machine_output.views.length > 0) {
78- $scope.machine_output.selectedView =
79- $scope.machine_output.views[0];
80- } else {
81- $scope.machine_output.selectedView = null;
82- }
83- }
84-
85 // Update the devices array on the scope based on the device children
86 // on the node.
87 function updateDevices() {
88@@ -395,13 +326,6 @@ angular.module('MAAS').controller('NodeDetailsController', [
89
90 // Update the services when the services list is updated.
91 $scope.$watch("node.service_ids", updateServices);
92-
93- // Update the machine output view when summary, commissioning, or
94- // installation results are updated on the node.
95- $scope.$watch("node.summary_xml", updateMachineOutput);
96- $scope.$watch("node.summary_yaml", updateMachineOutput);
97- $scope.$watch("node.commissioning_results", updateMachineOutput);
98- $scope.$watch("node.installation_results", updateMachineOutput);
99 }
100
101 // Called when the node has been loaded.
102@@ -411,7 +335,6 @@ angular.module('MAAS').controller('NodeDetailsController', [
103
104 updateTitle();
105 updateSummary();
106- updateMachineOutput();
107 updateServices();
108 startWatching();
109
110@@ -987,54 +910,6 @@ angular.module('MAAS').controller('NodeDetailsController', [
111 }
112 };
113
114- // Return the commissioning summary output data.
115- $scope.getSummaryData = function(type) {
116- // Can be called by angular before the node is set in the scope,
117- // in that case return blank string. It will be called once the
118- // node is set to get the correct information.
119- if(!angular.isObject($scope.node)) {
120- return "";
121- }
122- return $scope.node["summary_" + type];
123- };
124-
125- // Return the installation log data.
126- $scope.getInstallationData = function() {
127- // Can be called by angular before the node is set in the scope,
128- // in that case return blank string. It will be called once the
129- // node is set to get the correct information.
130- if(!angular.isObject($scope.node)) {
131- return "";
132- }
133- // It is possible for the node to have multiple installation
134- // results, but it is unused. Only one installation result will
135- // exists for a node. Grab the first one in the array.
136- var results = $scope.node.installation_results;
137- if(!angular.isArray(results) ||
138- results.length === 0 || results[0].output === "") {
139- switch($scope.node.installation_status) {
140- case 0:
141- return "System is booting...";
142- case 1:
143- return "Installation has begun!";
144- case 2:
145- return ("Installation has succeeded but no " +
146- "output was given.");
147- case 3:
148- return ("Installation has failed and no output was " +
149- "given.");
150- case 4:
151- return "Installation failed after 40 minutes.";
152- case 5:
153- return "Installation was aborted.";
154- default:
155- return "";
156- }
157- } else {
158- return results[0].output;
159- }
160- };
161-
162 // true if power error prevents the provided action
163 $scope.hasActionPowerError = function(actionName) {
164 if(!$scope.hasPowerError()) {
165diff --git a/src/maasserver/static/js/angular/controllers/node_results.js b/src/maasserver/static/js/angular/controllers/node_results.js
166index f3399d9..101e500 100644
167--- a/src/maasserver/static/js/angular/controllers/node_results.js
168+++ b/src/maasserver/static/js/angular/controllers/node_results.js
169@@ -11,39 +11,93 @@ angular.module('MAAS').controller('NodeResultsController', [
170 NodeResultsManagerFactory, ManagerHelperService, ErrorService) {
171
172 // NodeResultsManager that is loaded once the node is loaded.
173- var nodeResultsManager = null;
174+ $scope.nodeResultsManager = null;
175 // References to manager data used in scope.
176 $scope.commissioning_results = null;
177 $scope.testing_results = null;
178 $scope.installation_results = null;
179 $scope.results = null;
180
181+ // List of logs available.
182+ $scope.logs = {
183+ option: null,
184+ availableOptions: []
185+ };
186+ // Log content being displayed.
187+ $scope.logOutput = 'Loading...';
188+
189 // Initial values.
190 $scope.loaded = false;
191 $scope.resultsLoaded = false;
192 $scope.node = null;
193
194+ function updateLogs() {
195+ var i;
196+ var option;
197+ var had_installation = $scope.logs.availableOptions.length === 3;
198+ $scope.logs.availableOptions.length = 0;
199+ // XXX ltrager 2017-12-01 - Only show the current installation log
200+ // if the machine is deploying, deployed, or failed deploying. The
201+ // logs page needs to be redesigned to show previous installation
202+ // results.
203+ if($scope.installation_results &&
204+ $scope.installation_results.length > 0 && (
205+ $scope.node.status_code === 6 ||
206+ $scope.node.status_code === 9 ||
207+ $scope.node.status_code === 11)) {
208+ $scope.logs.availableOptions.push({
209+ 'title': 'Installation output',
210+ 'id': $scope.installation_results[0].id
211+ });
212+ }
213+ $scope.logs.availableOptions.push({
214+ 'title': 'Machine output (YAML)',
215+ 'id': 'summary_yaml'
216+ });
217+ $scope.logs.availableOptions.push({
218+ 'title': 'Machine output (XML)',
219+ 'id': 'summary_xml'
220+ });
221+ if(!had_installation &&
222+ $scope.logs.availableOptions.length === 3) {
223+ // A new installation log has appeared, show it.
224+ $scope.logs.option = $scope.logs.availableOptions[0];
225+ }else if(!$scope.selectedLog || (
226+ had_installation && $scope.logs.length === 2)) {
227+ // No longer in a deployed state.
228+ $scope.logs.option = $scope.logs.availableOptions[0];
229+ }
230+ }
231+
232 // Called once the node has loaded.
233 function nodeLoaded(node) {
234 $scope.node = node;
235 $scope.loaded = true;
236 // Get the NodeResultsManager and load it.
237- nodeResultsManager = NodeResultsManagerFactory.getManager(
238+ $scope.nodeResultsManager = NodeResultsManagerFactory.getManager(
239 node, $scope.section.area);
240- nodeResultsManager.loadItems().then(function() {
241+ $scope.nodeResultsManager.loadItems().then(function() {
242 $scope.commissioning_results =
243- nodeResultsManager.commissioning_results;
244- $scope.testing_results = nodeResultsManager.testing_results;
245+ $scope.nodeResultsManager.commissioning_results;
246+ $scope.testing_results =
247+ $scope.nodeResultsManager.testing_results;
248 $scope.installation_results =
249- nodeResultsManager.installation_results;
250- $scope.results = nodeResultsManager.results;
251+ $scope.nodeResultsManager.installation_results;
252+ $scope.results = $scope.nodeResultsManager.results;
253+ // Only load and monitor logs when on the logs page.
254+ if($scope.section.area === "logs") {
255+ updateLogs();
256+ $scope.$watch("installation_results", updateLogs, true);
257+ $scope.$watch(
258+ "installation_results", $scope.updateLogOutput, true);
259+ }
260 $scope.resultsLoaded = true;
261 });
262 }
263
264 if($routeParams.type === 'controller') {
265 $scope.nodesManager = ControllersManager;
266- }else{
267+ } else {
268 $scope.nodesManager = MachinesManager;
269 }
270 // Load nodes manager.
271@@ -55,7 +109,7 @@ angular.module('MAAS').controller('NodeResultsController', [
272 if(angular.isObject(activeNode) &&
273 activeNode.system_id === $routeParams.system_id) {
274 nodeLoaded(activeNode);
275- }else{
276+ } else {
277 $scope.nodesManager.setActiveItem(
278 $routeParams.system_id).then(function(node) {
279 nodeLoaded(node);
280@@ -65,12 +119,88 @@ angular.module('MAAS').controller('NodeResultsController', [
281 }
282 });
283
284+ $scope.updateLogOutput = function() {
285+ $scope.logOutput = "Loading...";
286+ if(!$scope.node) {
287+ return;
288+ }else if($scope.logs.option.id === 'summary_xml') {
289+ $scope.nodesManager.getSummaryXML($scope.node).then(
290+ function(output) {
291+ $scope.logOutput = output;
292+ });
293+ }else if($scope.logs.option.id === 'summary_yaml') {
294+ $scope.nodesManager.getSummaryYAML($scope.node).then(
295+ function(output) {
296+ $scope.logOutput = output;
297+ });
298+ } else {
299+ var result = null;
300+ var i, j;
301+ // Find the installation result to be displayed.
302+ for(i = 0; i < $scope.installation_results.length; i++) {
303+ var hlist = $scope.installation_results[i].history_list;
304+ for(j = 0; j < hlist.length; j++) {
305+ if(hlist[j].id === $scope.logs.option.id) {
306+ result = hlist[j];
307+ break;
308+ }
309+ }
310+ if(result) {
311+ break;
312+ }
313+ }
314+ switch(result.status) {
315+ case 0:
316+ $scope.logOutput = "System is booting...";
317+ break;
318+ case 1:
319+ $scope.logOutput = "Installation has begun!";
320+ break;
321+ case 2:
322+ $scope.nodeResultsManager.get_result_data(
323+ result.id, 'combined').then(function(output) {
324+ if(output === '') {
325+ $scope.logOutput =
326+ "Installation has succeeded but " +
327+ "no output was given.";
328+ } else {
329+ $scope.logOutput = output;
330+ }
331+ });
332+ break;
333+ case 3:
334+ $scope.nodeResultsManager.get_result_data(
335+ result.id, 'combined').then(function(output) {
336+ if(output === '') {
337+ $scope.logOutput =
338+ "Installation has failed and no " +
339+ "output was given.";
340+ } else {
341+ $scope.logOutput = output;
342+ }
343+ });
344+ break;
345+ case 4:
346+ $scope.logOutput =
347+ "Installation failed after 40 minutes.";
348+ break;
349+ case 5:
350+ $scope.logOutput = "Installation was aborted.";
351+ break;
352+ default:
353+ $scope.logOutput = "BUG: Unknown log status " +
354+ result.status;
355+ break;
356+ }
357+ }
358+ };
359+
360 // Destroy the NodeResultsManager when the scope is destroyed. This is
361 // so the client will not recieve any more notifications about results
362 // from this node.
363 $scope.$on("$destroy", function() {
364- if(angular.isObject(nodeResultsManager)) {
365- nodeResultsManager.destroy();
366+ if(angular.isObject($scope.nodeResultsManager)) {
367+ $scope.nodeResultsManager.destroy();
368 }
369 });
370 }]);
371diff --git a/src/maasserver/static/js/angular/controllers/tests/test_node_details.js b/src/maasserver/static/js/angular/controllers/tests/test_node_details.js
372index 00b3c0b..1947106 100644
373--- a/src/maasserver/static/js/angular/controllers/tests/test_node_details.js
374+++ b/src/maasserver/static/js/angular/controllers/tests/test_node_details.js
375@@ -253,15 +253,6 @@ describe("NodeDetailsController", function() {
376 });
377 });
378
379- it("sets initial values for machine output section", function() {
380- var controller = makeController();
381- expect($scope.machine_output).toEqual({
382- viewable: false,
383- selectedView: null,
384- views: []
385- });
386- });
387-
388 it("sets initial area to routeParams value", function() {
389 $routeParams.area = makeName("area");
390 var controller = makeController();
391@@ -452,71 +443,6 @@ describe("NodeDetailsController", function() {
392 expect($scope.power.editing).toBe(false);
393 });
394
395- it("machine output not visible if all required data missing", function() {
396- var controller = makeControllerResolveSetActiveItem();
397- expect($scope.machine_output.viewable).toBe(false);
398- });
399-
400- it("machine output visible if summary_xml and summary_yaml", function() {
401- node.summary_xml = node.summary_yaml = "summary";
402- var controller = makeControllerResolveSetActiveItem();
403- expect($scope.machine_output.viewable).toBe(true);
404- });
405-
406- it("machine output visible if installation_results", function() {
407- node.installation_results.push({});
408- var controller = makeControllerResolveSetActiveItem();
409- expect($scope.machine_output.viewable).toBe(true);
410- });
411-
412- it("machine output not visible if installation_results not an array",
413- function() {
414- node.installation_results = undefined;
415- var controller = makeControllerResolveSetActiveItem();
416- expect($scope.machine_output.viewable).toBe(false);
417- });
418-
419- it("machine output summary view available if summary_xml",
420- function() {
421- node.summary_xml = "summary_xml";
422- var controller = makeControllerResolveSetActiveItem();
423- expect($scope.machine_output.views).toEqual([{
424- name: "summary_xml",
425- title: "Machine output (XML)"
426- }]);
427- });
428-
429- it("machine output summary view available if summary_yaml",
430- function() {
431- node.summary_yaml = "summary_yaml";
432- var controller = makeControllerResolveSetActiveItem();
433- expect($scope.machine_output.views).toEqual([{
434- name: "summary_yaml",
435- title: "Machine output (YAML)"
436- }]);
437- });
438-
439- it("machine output installation view available if installation_results",
440- function() {
441- node.installation_results.push({});
442- var controller = makeControllerResolveSetActiveItem();
443- expect($scope.machine_output.views).toEqual([{
444- name: "installation",
445- title: "Installation output"
446- }]);
447- });
448-
449- it("machine output install view is always selected first if possible",
450- function() {
451- node.summary_xml = node.summary_yaml = "summary";
452- node.installation_results.push({});
453- var controller = makeControllerResolveSetActiveItem();
454- expect($scope.machine_output.selectedView).toEqual({
455- name: "installation",
456- title: "Installation output"
457- });
458- });
459-
460 it("starts watching once setActiveItem resolves", function() {
461 var setActiveDefer = $q.defer();
462 spyOn(MachinesManager, "setActiveItem").and.returnValue(
463@@ -553,11 +479,7 @@ describe("NodeDetailsController", function() {
464 "node.zone.id",
465 "node.power_type",
466 "node.power_parameters",
467- "node.service_ids",
468- "node.summary_xml",
469- "node.summary_yaml",
470- "node.commissioning_results",
471- "node.installation_results"
472+ "node.service_ids"
473 ]);
474 expect(watchCollections).toEqual([
475 $scope.summary.architecture.options,
476@@ -2180,72 +2102,6 @@ describe("NodeDetailsController", function() {
477 });
478 });
479
480- describe("getSummaryData", function() {
481-
482- it("returns blank string if node is null", function() {
483- var controller = makeController();
484- expect($scope.getSummaryData()).toBe("");
485- });
486-
487- it("returns summary_xml", function() {
488- var controller = makeController();
489- $scope.node = makeNode();
490- var summary_xml = {};
491- $scope.node.summary_xml = summary_xml;
492- expect($scope.getSummaryData("xml")).toBe(summary_xml);
493- });
494-
495- it("returns summary_yaml when summaryType equal yaml", function() {
496- var controller = makeController();
497- $scope.node = makeNode();
498- var summary_yaml = {};
499- $scope.node.summary_yaml = summary_yaml;
500- expect($scope.getSummaryData("yaml")).toBe(summary_yaml);
501- });
502- });
503-
504- describe("getInstallationData", function() {
505-
506- it("returns blank string if node is null", function() {
507- var controller = makeController();
508- expect($scope.getInstallationData()).toBe("");
509- });
510-
511- it("returns blank string if installation results not an array",
512- function() {
513- var controller = makeController();
514- $scope.node = makeNode();
515- $scope.node.installation_results = undefined;
516- expect($scope.getInstallationData()).toBe("");
517- });
518-
519- it("returns blank string if no installation results", function() {
520- var controller = makeController();
521- $scope.node = makeNode();
522- expect($scope.getInstallationData()).toBe("");
523- });
524-
525- it("returns first installation result data", function() {
526- var controller = makeController();
527- $scope.node = makeNode();
528- var install_result = {};
529- $scope.node.installation_results.push({
530- output: install_result
531- });
532- $scope.node.installation_results.push({
533- output: {}
534- });
535- expect($scope.getInstallationData()).toBe(install_result);
536- });
537-
538- it("returns status message when no output and status", function() {
539- var controller = makeController();
540- $scope.node = makeNode();
541- $scope.node.installation_status = makeInteger(0, 5);
542- expect($scope.getInstallationData()).not.toBe("");
543- });
544- });
545-
546 describe("getServiceClass", function() {
547
548 it("returns 'none' if null", function() {
549diff --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
550index b677ae5..f2f44d7 100644
551--- a/src/maasserver/static/js/angular/controllers/tests/test_node_results.js
552+++ b/src/maasserver/static/js/angular/controllers/tests/test_node_results.js
553@@ -49,6 +49,35 @@ describe("NodeResultsController", function() {
554 return node;
555 }
556
557+ // Make a result.
558+ function makeResult(type, status) {
559+ if(type === null) {
560+ type = makeInteger(0, 3);
561+ }
562+ if(status === null) {
563+ status = makeInteger(0, 8);
564+ }
565+ var id = makeInteger(0, 1000);
566+ var result = {
567+ id: id,
568+ name: makeName("name"),
569+ type: type,
570+ status: status,
571+ history_list: [{
572+ id: id,
573+ status: status
574+ }]
575+ };
576+ var i;
577+ for(i = 0; i < 3; i++) {
578+ result.history_list.push({
579+ id: makeInteger(0, 1000),
580+ status: makeInteger(0, 8)
581+ });
582+ }
583+ return result;
584+ }
585+
586 // Makes the NodeResultsController
587 function makeController(loadManagerDefer) {
588 var loadManager = spyOn(ManagerHelperService, "loadManager");
589@@ -88,6 +117,9 @@ describe("NodeResultsController", function() {
590 expect($scope.testing_results).toBeNull();
591 expect($scope.installation_results).toBeNull();
592 expect($scope.results).toBeNull();
593+ expect($scope.logs.option).toBeNull();
594+ expect($scope.logs.availableOptions).toEqual([]);
595+ expect($scope.logOutput).toEqual("Loading...");
596 expect($scope.loaded).toBe(false);
597 expect($scope.resultsLoaded).toBe(false);
598 expect($scope.node).toBeNull();
599@@ -101,6 +133,9 @@ describe("NodeResultsController", function() {
600 expect($scope.testing_results).toBeNull();
601 expect($scope.installation_results).toBeNull();
602 expect($scope.results).toBeNull();
603+ expect($scope.logs.option).toBeNull();
604+ expect($scope.logs.availableOptions).toEqual([]);
605+ expect($scope.logOutput).toEqual("Loading...");
606 expect($scope.loaded).toBe(false);
607 expect($scope.resultsLoaded).toBe(false);
608 expect($scope.node).toBeNull();
609@@ -203,4 +238,199 @@ describe("NodeResultsController", function() {
610 $rootScope.$digest();
611 expect($scope.resultsLoaded).toBe(true);
612 });
613+
614+ describe("updateLogs", function() {
615+ it("only runs on logs page", function() {
616+ var defer = $q.defer();
617+ var controller = makeController(defer);
618+ MachinesManager._activeItem = node;
619+ var manager = NodeResultsManagerFactory.getManager(node);
620+ var loadDefer = $q.defer();
621+
622+ defer.resolve();
623+ $rootScope.$digest();
624+ loadDefer.resolve();
625+ $rootScope.$digest();
626+ expect($scope.logs.availableOptions).toEqual([]);
627+ });
628+
629+ it("loads summary", function() {
630+ var defer = $q.defer();
631+ var controller = makeController(defer);
632+ $scope.section = {area: "logs"};
633+ MachinesManager._activeItem = node;
634+ webSocket.returnData.push(makeFakeResponse([]));
635+ var manager = NodeResultsManagerFactory.getManager(node);
636+
637+ defer.resolve();
638+ $rootScope.$digest();
639+ var expectFunc;
640+ expectFunc = function() {
641+ if($scope.resultsLoaded) {
642+ expect($scope.logs.availableOptions).toEqual([
643+ {
644+ title: 'Machine output (YAML)',
645+ id: 'summary_yaml'
646+ },
647+ {
648+ title: 'Machine output (XML)',
649+ id: 'summary_xml'
650+ }
651+ ]);
652+ expect($scope.logs.option).toEqual({
653+ title: 'Machine output (YAML)',
654+ id: 'summary_yaml'
655+ });
656+ }else{
657+ setTimeout(expectFunc);
658+ }
659+ };
660+ setTimeout(expectFunc);
661+ });
662+ });
663+
664+ describe("updateLogOutput", function() {
665+ it("sets to loading when no node", function() {
666+ var controller = makeController();
667+ $scope.updateLogOutput();
668+ expect($scope.logOutput).toEqual("Loading...");
669+ });
670+
671+ it("sets summary xml", function() {
672+ var defer = $q.defer();
673+ var controller = makeController(defer);
674+ MachinesManager._activeItem = node;
675+ var managerDefer = $q.defer();
676+ $scope.logs = {option: {id: "summary_xml"}};
677+ spyOn(MachinesManager, "getSummaryXML").and.returnValue(
678+ managerDefer.promise);
679+
680+ defer.resolve();
681+ $rootScope.$digest();
682+ managerDefer.resolve();
683+ $rootScope.$digest();
684+
685+ $scope.updateLogOutput();
686+ expect(MachinesManager.getSummaryXML).toHaveBeenCalledWith(node);
687+ });
688+
689+ it("sets summary yaml", function() {
690+ var defer = $q.defer();
691+ var controller = makeController(defer);
692+ MachinesManager._activeItem = node;
693+ var managerDefer = $q.defer();
694+ $scope.logs = {option: {id: "summary_yaml"}};
695+ spyOn(MachinesManager, "getSummaryYAML").and.returnValue(
696+ managerDefer.promise);
697+
698+ defer.resolve();
699+ $rootScope.$digest();
700+ managerDefer.resolve();
701+ $rootScope.$digest();
702+
703+ $scope.updateLogOutput();
704+ expect(MachinesManager.getSummaryYAML).toHaveBeenCalledWith(node);
705+ });
706+
707+ it("sets system booting", function() {
708+ var controller = makeController();
709+ var installation_result = makeResult(1, 0);
710+ $scope.installation_results = [installation_result];
711+ $scope.node = node;
712+ $scope.logs = {option: {id: installation_result.id}};
713+
714+ $scope.updateLogOutput();
715+ expect($scope.logOutput).toEqual("System is booting...");
716+ });
717+
718+ it("sets installation has begun", function() {
719+ var controller = makeController();
720+ var installation_result = makeResult(1, 1);
721+ $scope.installation_results = [installation_result];
722+ $scope.node = node;
723+ $scope.logs = {option: {id: installation_result.id}};
724+
725+ $scope.updateLogOutput();
726+ expect($scope.logOutput).toEqual("Installation has begun!");
727+ });
728+
729+ it("sets installation output succeeded", function() {
730+ var defer = $q.defer();
731+ var controller = makeController(defer);
732+ var installation_result = makeResult(1, 2);
733+ MachinesManager._activeItem = node;
734+ var manager = NodeResultsManagerFactory.getManager(node);
735+ var managerDefer = $q.defer();
736+ spyOn(manager, "get_result_data").and.returnValue(
737+ managerDefer.promise);
738+
739+ defer.resolve();
740+ $rootScope.$digest();
741+ managerDefer.resolve();
742+ $rootScope.$digest();
743+
744+ $scope.installation_results = [installation_result];
745+ $scope.logs = {option: {id: installation_result.id}};
746+ $scope.updateLogOutput();
747+ expect(manager.get_result_data).toHaveBeenCalledWith(
748+ installation_result.id, 'combined');
749+ });
750+
751+ it("sets installation output failed", function() {
752+ var defer = $q.defer();
753+ var controller = makeController(defer);
754+ var installation_result = makeResult(1, 3);
755+ MachinesManager._activeItem = node;
756+ var manager = NodeResultsManagerFactory.getManager(node);
757+ var managerDefer = $q.defer();
758+ spyOn(manager, "get_result_data").and.returnValue(
759+ managerDefer.promise);
760+
761+ defer.resolve();
762+ $rootScope.$digest();
763+ managerDefer.resolve();
764+ $rootScope.$digest();
765+
766+ $scope.installation_results = [installation_result];
767+ $scope.logs = {option: {id: installation_result.id}};
768+ $scope.updateLogOutput();
769+ expect(manager.get_result_data).toHaveBeenCalledWith(
770+ installation_result.id, 'combined');
771+ });
772+
773+ it("sets timed out", function() {
774+ var controller = makeController();
775+ var installation_result = makeResult(1, 4);
776+ $scope.installation_results = [installation_result];
777+ $scope.node = node;
778+ $scope.logs = {option: {id: installation_result.id}};
779+
780+ $scope.updateLogOutput();
781+ expect($scope.logOutput).toEqual(
782+ "Installation failed after 40 minutes.");
783+ });
784+
785+ it("sets installation aborted", function() {
786+ var controller = makeController();
787+ var installation_result = makeResult(1, 5);
788+ $scope.installation_results = [installation_result];
789+ $scope.node = node;
790+ $scope.logs = {option: {id: installation_result.id}};
791+
792+ $scope.updateLogOutput();
793+ expect($scope.logOutput).toEqual("Installation was aborted.");
794+ });
795+
796+ it("sets unknown status", function() {
797+ var controller = makeController();
798+ var installation_result = makeResult(1, makeInteger(6, 100));
799+ $scope.installation_results = [installation_result];
800+ $scope.node = node;
801+ $scope.logs = {option: {id: installation_result.id}};
802+
803+ $scope.updateLogOutput();
804+ expect($scope.logOutput).toEqual(
805+ "BUG: Unknown log status " + installation_result.status);
806+ });
807+ });
808 });
809diff --git a/src/maasserver/static/js/angular/factories/nodes.js b/src/maasserver/static/js/angular/factories/nodes.js
810index e1406e8..1b4c0de 100644
811--- a/src/maasserver/static/js/angular/factories/nodes.js
812+++ b/src/maasserver/static/js/angular/factories/nodes.js
813@@ -1,4 +1,4 @@
814-/* Copyright 2015-2016 Canonical Ltd. This software is licensed under the
815+/* Copyright 2015-2017 Canonical Ltd. This software is licensed under the
816 * GNU Affero General Public License version 3 (see the file LICENSE).
817 *
818 * MAAS Nodes Manager
819@@ -337,5 +337,17 @@ angular.module('MAAS').service(
820 this._handler + ".set_boot_disk", params);
821 };
822
823+ NodesManager.prototype.getSummaryXML = function(node) {
824+ return RegionConnection.callMethod(
825+ this._handler + ".get_summary_xml",
826+ {"system_id": node.system_id});
827+ };
828+
829+ NodesManager.prototype.getSummaryYAML = function(node) {
830+ return RegionConnection.callMethod(
831+ this._handler + ".get_summary_yaml",
832+ {"system_id": node.system_id});
833+ };
834+
835 return NodesManager;
836 }]);
837diff --git a/src/maasserver/static/js/angular/factories/tests/test_nodes.js b/src/maasserver/static/js/angular/factories/tests/test_nodes.js
838index 39dce06..77ce066 100644
839--- a/src/maasserver/static/js/angular/factories/tests/test_nodes.js
840+++ b/src/maasserver/static/js/angular/factories/tests/test_nodes.js
841@@ -826,4 +826,36 @@ describe("NodesManager", function() {
842 });
843 });
844 });
845+
846+ describe("getSummaryXML", function() {
847+
848+ it("calls machine.get_summary_xml", function(done) {
849+ var fakemachine = makemachine();
850+ var summary_xml = makeName("summary_xml");
851+ webSocket.returnData.push(makeFakeResponse(summary_xml));
852+ MachinesManager.getSummaryXML(fakemachine).then(function(output) {
853+ var sentObject = angular.fromJson(webSocket.sentData[0]);
854+ expect(sentObject.method).toBe("machine.get_summary_xml");
855+ expect(sentObject.params.system_id).toBe(fakemachine.system_id);
856+ expect(output).toBe(summary_xml);
857+ done();
858+ });
859+ });
860+ });
861+
862+ describe("getSummaryYAML", function() {
863+
864+ it("calls machine.get_summary_yaml", function(done) {
865+ var fakemachine = makemachine();
866+ var summary_yaml = makeName("summary_yaml");
867+ webSocket.returnData.push(makeFakeResponse(summary_yaml));
868+ MachinesManager.getSummaryYAML(fakemachine).then(function(output) {
869+ var sentObject = angular.fromJson(webSocket.sentData[0]);
870+ expect(sentObject.method).toBe("machine.get_summary_yaml");
871+ expect(sentObject.params.system_id).toBe(fakemachine.system_id);
872+ expect(output).toBe(summary_yaml);
873+ done();
874+ });
875+ });
876+ });
877 });
878diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html
879index 9e4c16e..22dda29 100755
880--- a/src/maasserver/static/partials/node-details.html
881+++ b/src/maasserver/static/partials/node-details.html
882@@ -219,9 +219,9 @@
883 <button role="tab" class="page-navigation__link" data-ng-if="node.testing_script_count > 0"
884 data-ng-class="{ 'is-active': section.area === 'testing'}"
885 data-ng-click="section.area = 'testing'"><span data-maas-script-status="script-status" data-script-status="node.testing_status"></span> Hardware tests</button>
886- <button role="tab" class="page-navigation__link" data-ng-if="machine_output.viewable"
887+ <button role="tab" class="page-navigation__link" data-ng-if="node.has_logs"
888 data-ng-class="{ 'is-active': section.area === 'logs'}"
889- data-ng-click="section.area = 'logs'"><span data-maas-script-status="script-status" data-script-status="node.installation_status" data-ng-if="node.installation_results.length > 0"></span> Logs</button>
890+ data-ng-click="section.area = 'logs'"><span data-maas-script-status="script-status" data-script-status="node.installation_status" data-ng-if="node.installation_status !== -1"></span> Logs</button>
891 <button role="tab" class="page-navigation__link" data-ng-if="!isDevice"
892 data-ng-class="{ 'is-active': section.area === 'events'}"
893 data-ng-click="section.area = 'events'">Events</button>
894@@ -257,7 +257,7 @@
895 </div>
896 <div class="p-notification--warning twelve-col" data-ng-if="showFailedTestWarning()">
897 <p class="p-notification__response">
898- <span class="p-notification__status">Warning:</span>Machine has failed tests; use with caution!</p>
899+ <span class="p-notification__status">Warning:</span>Machine has failed tests; use with caution!</p>
900 </div>
901 <div class="twelve-col u-equal-height" data-ng-if="isDevice">
902 <div class="four-col action-card action-card--positionable u-border--solid">
903@@ -2393,30 +2393,23 @@
904 </div>
905 </section>
906 <section class="row" data-ng-if="section.area === 'logs'">
907- <div class="wrapper--inner">
908- <div class="twelve-col">
909- <div data-maas-cta="machine_output.views"
910- data-ng-model="machine_output.selectedView">
911- </div>
912- </div>
913- </div>
914- <div class="wrapper--inner" data-ng-if="machine_output.selectedView.name == 'summary_yaml'">
915- <div class="twelve-col">
916- <pre class="code-block code-block--numbering code-block--terminal" data-maas-code-lines="getSummaryData('yaml')">
917- </pre>
918- </div>
919- </div>
920- <div class="wrapper--inner" data-ng-if="machine_output.selectedView.name == 'summary_xml'">
921- <div class="twelve-col">
922- <pre class="code-block code-block--numbering code-block--terminal" data-maas-code-lines="getSummaryData('xml')">
923- </pre>
924- </div>
925- </div>
926- <div class="wrapper--inner" data-ng-if="machine_output.selectedView.name == 'installation'">
927- <div class="twelve-col">
928- <pre class="code-block code-block--numbering code-block--terminal" data-maas-code-lines="getInstallationData()"></pre>
929+ <div data-ng-controller="NodeResultsController">
930+ <div class="twelve-col" data-ng-if="!resultsLoaded">
931+ <p class="u-text--loading"><i class="icon icon--loading u-animation--spin"></i>&nbsp;&nbsp;Loading...</p>
932+ </div>
933+ <div data-ng-if="resultsLoaded">
934+ <div class="wrapper--inner">
935+ <div class="twelve-col">
936+ <div data-maas-cta="logs.availableOptions" data-ng-model="logs.option" data-ng-change="updateLogOutput()">
937+ </div>
938+ </div>
939+ <div class="wrapper--inner">
940+ <div class="twelve-col">
941+ <pre class="code-block code-block--numbering code-block--terminal" data-maas-code-lines="logOutput"></pre>
942+ </div>
943+ </div>
944+ </div>
945 </div>
946- </div>
947 </section>
948 <section class="row" data-ng-if="section.area === 'commissioning'">
949 <div data-maas-script-results-list="script-results"></div>

Subscribers

People subscribed via source and target branches