blob: 76e632ba72677ee2aac31b385e3dd25108424f72 [file] [log] [blame]
Iftekharul Islam99d199f2017-03-24 15:28:25 -05001/**
2 * Controller for power-operations
3 *
Iftekharul Islamcd789502017-04-19 14:37:55 -05004 * @module app/serverControl
Iftekharul Islam99d199f2017-03-24 15:28:25 -05005 * @exports powerOperationsController
6 * @name powerOperationsController
Iftekharul Islam99d199f2017-03-24 15:28:25 -05007 */
8
Andrew Geisslerba5e3f32018-05-24 10:58:00 -07009window.angular && (function(angular) {
10 'use strict';
Iftekharul Islam99d199f2017-03-24 15:28:25 -050011
Andrew Geisslerd27bb132018-05-24 11:07:27 -070012 angular.module('app.serverControl').controller('powerOperationsController', [
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050013 '$scope', 'APIUtils', 'dataService', 'Constants', '$interval', '$q',
Dixsie Wolmerse3681082019-06-21 13:48:06 -050014 'toastService', '$uibModal',
Andrew Geisslerd27bb132018-05-24 11:07:27 -070015 function(
Dixsie Wolmerse3681082019-06-21 13:48:06 -050016 $scope, APIUtils, dataService, Constants, $interval, $q, toastService,
17 $uibModal) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -070018 $scope.dataService = dataService;
Gunnar Mills6add8322018-09-05 15:16:12 -050019 $scope.loading = true;
Dixsie Wolmerse3681082019-06-21 13:48:06 -050020 $scope.oneTimeBootEnabled = false;
21 $scope.bootOverrideError = false;
22 $scope.bootSources = [];
23 $scope.boot = {};
24 $scope.defaultRebootSetting = 'warm-reboot';
25 $scope.defaultShutdownSetting = 'warm-shutdown';
26
27 $scope.activeModal;
Iftekharul Islama1d238f2018-02-26 12:29:45 -060028
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050029 // When a power operation is in progress, set to true,
30 // when a power operation completes (success/fail) set to false.
31 // This property is used to show/hide the 'in progress' message
32 // in markup.
33 $scope.operationPending = false;
Iftekharul Islam99d199f2017-03-24 15:28:25 -050034
Dixsie Wolmerse3681082019-06-21 13:48:06 -050035 const modalTemplate = require('./power-operations-modal.html');
36
37 const powerOperations =
38 {WARM_REBOOT: 0, COLD_REBOOT: 1, WARM_SHUTDOWN: 2, COLD_SHUTDOWN: 3};
39
Yoshie Muranakaafcfda72019-06-21 09:19:32 -050040 /**
41 * Checks the host status provided by the dataService using an
42 * interval timer
43 * @param {string} statusType : host status type to check for
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050044 * @param {number} timeout : timeout limit, defaults to 5 minutes
45 * @param {string} error : error message, defaults to 'Time out'
Yoshie Muranakaafcfda72019-06-21 09:19:32 -050046 * @returns {Promise} : returns a deferred promise that will be fulfilled
47 * if the status is met or be rejected if the timeout is reached
48 */
Dixsie Wolmerse3681082019-06-21 13:48:06 -050049 const checkHostStatus =
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050050 (statusType, timeout = 300000, error = 'Time out.') => {
51 const deferred = $q.defer();
52 const start = new Date();
53 const checkHostStatusInverval = $interval(() => {
54 let now = new Date();
55 let timePassed = now.getTime() - start.getTime();
56 if (timePassed > timeout) {
57 deferred.reject(error);
58 $interval.cancel(checkHostStatusInverval);
59 }
60 if (dataService.server_state === statusType) {
61 deferred.resolve();
62 $interval.cancel(checkHostStatusInverval);
63 }
64 }, Constants.POLL_INTERVALS.POWER_OP);
65 return deferred.promise;
66 };
Yoshie Muranakaafcfda72019-06-21 09:19:32 -050067
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050068 /**
69 * Initiate Orderly reboot
70 * Attempts to stop all software
71 */
Dixsie Wolmerse3681082019-06-21 13:48:06 -050072 const warmReboot = () => {
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050073 $scope.operationPending = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070074 dataService.setUnreachableState();
75 APIUtils.hostReboot()
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050076 .then(() => {
77 // Check for off state
78 return checkHostStatus(
79 Constants.HOST_STATE_TEXT.off, Constants.TIMEOUT.HOST_OFF,
80 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
Andrew Geisslerd27bb132018-05-24 11:07:27 -070081 })
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050082 .then(() => {
83 // Check for on state
84 return checkHostStatus(
85 Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
86 Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
Andrew Geisslerd27bb132018-05-24 11:07:27 -070087 })
Dixsie Wolmerse3681082019-06-21 13:48:06 -050088 .catch(error => {
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050089 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -060090 toastService.error(
91 Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050092 })
93 .finally(() => {
94 $scope.operationPending = false;
Dixsie Wolmerse3681082019-06-21 13:48:06 -050095 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -070096 };
97
Yoshie Muranaka5ff98782019-07-18 15:36:39 -050098 /**
99 * Initiate Immediate reboot
100 * Does not attempt to stop all software
101 */
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500102 const coldReboot = () => {
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500103 $scope.operationPending = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700104 dataService.setUnreachableState();
105 APIUtils.chassisPowerOff()
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500106 .then(() => {
107 // Check for off state
Yoshie Muranakaafcfda72019-06-21 09:19:32 -0500108 return checkHostStatus(
109 Constants.HOST_STATE_TEXT.off,
110 Constants.TIMEOUT.HOST_OFF_IMMEDIATE,
111 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700112 })
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500113 .then(() => {
Yoshie Muranakaafcfda72019-06-21 09:19:32 -0500114 return APIUtils.hostPowerOn();
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700115 })
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500116 .then(() => {
117 // Check for on state
Yoshie Muranakaafcfda72019-06-21 09:19:32 -0500118 return checkHostStatus(
119 Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
120 Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700121 })
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500122 .catch(error => {
Yoshie Muranakaafcfda72019-06-21 09:19:32 -0500123 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600124 toastService.error(
125 Constants.MESSAGES.POWER_OP.COLD_REBOOT_FAILED);
Yoshie Muranakaafcfda72019-06-21 09:19:32 -0500126 })
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500127 .finally(() => {
128 $scope.operationPending = false;
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500129 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700130 };
131
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500132 /**
133 * Initiate Orderly shutdown
134 * Attempts to stop all software
135 */
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500136 const orderlyShutdown = () => {
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500137 $scope.operationPending = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700138 dataService.setUnreachableState();
139 APIUtils.hostPowerOff()
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500140 .then(() => {
141 // Check for off state
142 return checkHostStatus(
143 Constants.HOST_STATE_TEXT.off, Constants.TIMEOUT.HOST_OFF,
144 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700145 })
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500146 .catch(error => {
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500147 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600148 toastService.error(
beccabroek92d13b62019-01-08 14:24:29 -0600149 Constants.MESSAGES.POWER_OP.ORDERLY_SHUTDOWN_FAILED);
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500150 })
151 .finally(() => {
152 $scope.operationPending = false;
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500153 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700154 };
155
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500156 /**
157 * Initiate Immediate shutdown
158 * Does not attempt to stop all software
159 */
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500160 const immediateShutdown = () => {
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500161 $scope.operationPending = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700162 dataService.setUnreachableState();
163 APIUtils.chassisPowerOff()
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500164 .then(() => {
165 // Check for off state
166 return checkHostStatus(
167 Constants.HOST_STATE_TEXT.off,
168 Constants.TIMEOUT.HOST_OFF_IMMEDIATE,
169 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700170 })
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500171 .then(() => {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700172 dataService.setPowerOffState();
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700173 })
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500174 .catch(error => {
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500175 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600176 toastService.error(
beccabroek92d13b62019-01-08 14:24:29 -0600177 Constants.MESSAGES.POWER_OP.IMMEDIATE_SHUTDOWN_FAILED);
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500178 })
179 .finally(() => {
180 $scope.operationPending = false;
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500181 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700182 };
Yoshie Muranaka5ff98782019-07-18 15:36:39 -0500183
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500184 /**
185 * Initiate Power on
186 */
187 $scope.powerOn = () => {
188 $scope.operationPending = true;
189 dataService.setUnreachableState();
190 APIUtils.hostPowerOn()
191 .then(() => {
192 // Check for on state
193 return checkHostStatus(
194 Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
195 Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
196 })
197 .catch(error => {
198 console.log(error);
199 toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
200 })
201 .finally(() => {
202 $scope.operationPending = false;
203 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700204 };
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500205
206 /*
207 * Power operations modal
208 */
209 const initPowerOperation = function(powerOperation) {
210 switch (powerOperation) {
211 case powerOperations.WARM_REBOOT:
212 warmReboot();
213 break;
214 case powerOperations.COLD_REBOOT:
215 coldReboot();
216 break;
217 case powerOperations.WARM_SHUTDOWN:
218 orderlyShutdown();
219 break;
220 case powerOperations.COLD_SHUTDOWN:
221 immediateShutdown();
222 break;
223 default:
224 // do nothing
225 }
226 };
227
228 const powerOperationModal = function() {
229 $uibModal
230 .open({
231 template: modalTemplate,
232 windowTopClass: 'uib-modal',
233 scope: $scope,
234 ariaLabelledBy: 'modal-operation'
235 })
236 .result
237 .then(function(activeModal) {
238 initPowerOperation(activeModal);
239 })
240 .finally(function() {
241 $scope.activeModal = undefined;
242 });
243 };
244
245 $scope.rebootConfirmModal = function() {
246 if ($scope.rebootForm.radioReboot.$modelValue == 'warm-reboot') {
247 $scope.activeModal = powerOperations.WARM_REBOOT;
248 } else if ($scope.rebootForm.radioReboot.$modelValue == 'cold-reboot') {
249 $scope.activeModal = powerOperations.COLD_REBOOT;
250 }
251 powerOperationModal();
252 };
253
254 $scope.shutdownConfirmModal = function() {
255 if ($scope.shutdownForm.radioShutdown.$modelValue == 'warm-shutdown') {
256 $scope.activeModal = powerOperations.WARM_SHUTDOWN;
257 } else if (
258 $scope.shutdownForm.radioShutdown.$modelValue == 'cold-shutdown') {
259 $scope.activeModal = powerOperations.COLD_SHUTDOWN;
260 }
261 powerOperationModal();
262 };
263
264 $scope.resetForm = function() {
265 $scope.boot = angular.copy($scope.originalBoot);
266 $scope.TPMToggle = angular.copy($scope.originalTPMToggle);
267 };
268
269 /*
270 * Get boot settings
271 */
272 const loadBootSettings = function() {
273 APIUtils.getBootOptions()
274 .then(function(response) {
275 const boot = response.Boot;
276 const BootSourceOverrideEnabled =
277 boot['BootSourceOverrideEnabled'];
278 const BootSourceOverrideTarget = boot['BootSourceOverrideTarget'];
279 const bootSourceValues =
280 boot['BootSourceOverrideTarget@Redfish.AllowableValues'];
281
282 $scope.bootSources = bootSourceValues;
283
284 $scope.boot = {
285 BootSourceOverrideEnabled: BootSourceOverrideEnabled,
286 BootSourceOverrideTarget: BootSourceOverrideTarget
287 };
288
289 if (BootSourceOverrideEnabled == 'Once') {
290 $scope.boot.oneTimeBootEnabled = true;
291 }
292
293 $scope.originalBoot = angular.copy($scope.boot);
294 })
295 .catch(function(error) {
296 $scope.bootOverrideError = true;
297 toastService.error('Unable to get boot override values.');
298 console.log(
299 'Error loading boot settings:', JSON.stringify(error));
300 });
301 $scope.loading = false;
302 };
303
304 /*
305 * Get TPM status
306 */
307 const loadTPMStatus = function() {
308 APIUtils.getTPMStatus()
309 .then(function(response) {
310 $scope.TPMToggle = response.data;
311 $scope.originalTPMToggle = angular.copy($scope.TPMToggle);
312 })
313 .catch(function(error) {
314 toastService.error('Unable to get TPM policy status.');
315 console.log('Error loading TPM status', JSON.stringify(error));
316 });
317 $scope.loading = false;
318 };
319
320 /*
321 * Save boot settings
322 */
323 $scope.saveBootSettings = function() {
324 if ($scope.hostBootSettings.bootSelected.$dirty ||
325 $scope.hostBootSettings.oneTime.$dirty) {
326 const data = {};
327 data.Boot = {};
328
329 let isOneTimeBoot = $scope.boot.oneTimeBootEnabled;
330 let overrideTarget = $scope.boot.BootSourceOverrideTarget || 'None';
331 let overrideEnabled = 'Disabled';
332
333 if (isOneTimeBoot) {
334 overrideEnabled = 'Once';
335 } else if (overrideTarget !== 'None') {
336 overrideEnabled = 'Continuous';
337 }
338
339 data.Boot.BootSourceOverrideEnabled = overrideEnabled;
340 data.Boot.BootSourceOverrideTarget = overrideTarget;
341
342 APIUtils.saveBootSettings(data).then(
343 function(response) {
344 $scope.originalBoot = angular.copy($scope.boot);
345 toastService.success('Successfully updated boot settings.');
346 },
347 function(error) {
348 toastService.error('Unable to save boot settings.');
349 console.log(JSON.stringify(error));
350 });
351 }
352 };
353
354 /*
355 * Save TPM required policy
356 */
357 $scope.saveTPMPolicy = function() {
358 if ($scope.hostBootSettings.toggle.$dirty) {
359 const tpmEnabled = $scope.TPMToggle.TPMEnable;
360
361 if (tpmEnabled === undefined) {
362 return;
363 }
364
365 APIUtils.saveTPMEnable(tpmEnabled)
366 .then(
367 function(response) {
368 $scope.originalTPMToggle = angular.copy($scope.TPMToggle);
369 toastService.success(
370 'Sucessfully updated TPM required policy.');
371 },
372 function(error) {
373 toastService.error('Unable to update TPM required policy.');
374 console.log(JSON.stringify(error));
375 });
376 }
377 };
378
Yoshie Muranaka5dac9e12019-09-23 12:01:36 -0700379 /**
380 * Callback when boot setting option changed
381 */
382 $scope.onChangeBootSetting = function() {
383 const bootSetting = $scope.hostBootSettings.bootSelected.$viewValue;
384 if (bootSetting === 'None') {
385 $scope.boot.oneTimeBootEnabled = false;
386 }
387 };
388
Dixsie Wolmerse3681082019-06-21 13:48:06 -0500389 /*
390 * Emitted every time the view is reloaded
391 */
392 $scope.$on('$viewContentLoaded', function() {
393 APIUtils.getLastPowerTime()
394 .then(
395 function(data) {
396 if (data.data == 0) {
397 $scope.powerTime = 'not available';
398 } else {
399 $scope.powerTime = data.data;
400 }
401 },
402 function(error) {
403 toastService.error(
404 'Unable to get last power operation time.');
405 console.log(JSON.stringify(error));
406 })
407 .finally(function() {
408 $scope.loading = false;
409 });
410
411 loadBootSettings();
412 loadTPMStatus();
413 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700414 }
415 ]);
Iftekharul Islam99d199f2017-03-24 15:28:25 -0500416})(angular);