Refactor power operations
Refactored power operations to use checkHostStatus function
that checks host_status property provided by dataService,
instead of polling for host/chassis status.
Added property to check when a power operation is in progress.
This property will show/hide the in progress message and make
other operations unavailable until the operation completes.
We were previously checking whether the server_state property
was set to 'Unreachable', which enables the Power on button in
the middle of a reboot, since the server_state changes to 'Off'
during reboot.
- Removed unused $timeout service
Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I234749a9a875c7370b52bd23ed74d6e9617cf5e2
diff --git a/app/common/services/api-utils.js b/app/common/services/api-utils.js
index 5a28294..0b1aa46 100644
--- a/app/common/services/api-utils.js
+++ b/app/common/services/api-utils.js
@@ -26,31 +26,10 @@
var SERVICE = {
API_CREDENTIALS: Constants.API_CREDENTIALS,
API_RESPONSE: Constants.API_RESPONSE,
- CHASSIS_POWER_STATE: Constants.CHASSIS_POWER_STATE,
HOST_STATE_TEXT: Constants.HOST_STATE,
LED_STATE: Constants.LED_STATE,
LED_STATE_TEXT: Constants.LED_STATE_TEXT,
HOST_SESSION_STORAGE_KEY: Constants.API_CREDENTIALS.host_storage_key,
- getChassisState: function() {
- var deferred = $q.defer();
- $http({
- method: 'GET',
- url: DataService.getHost() +
- '/xyz/openbmc_project/state/chassis0/attr/CurrentPowerState',
- withCredentials: true
- })
- .then(
- function(response) {
- var json = JSON.stringify(response.data);
- var content = JSON.parse(json);
- deferred.resolve(content.data);
- },
- function(error) {
- console.log(error);
- deferred.reject(error);
- });
- return deferred.promise;
- },
validIPV4IP: function(ip) {
// Checks for [0-255].[0-255].[0-255].[0-255]
return ip.match(
diff --git a/app/common/services/constants.js b/app/common/services/constants.js
index dabbc77..ae82e76 100644
--- a/app/common/services/constants.js
+++ b/app/common/services/constants.js
@@ -31,12 +31,6 @@
'location': '/redfish/v1/AccountService/LDAP/Certificates'
}
],
- CHASSIS_POWER_STATE: {
- on: 'On',
- on_code: 'xyz.openbmc_project.State.Chassis.PowerState.On',
- off: 'Off',
- off_code: 'xyz.openbmc_project.State.Chassis.PowerState.Off'
- },
HOST_STATE_TEXT: {
on: 'Running',
on_code: 'xyz.openbmc_project.State.Host.HostState.Running',
@@ -104,15 +98,12 @@
TIMEOUT: {
ACTIVATION: 1000 * 60 * 10, // 10 mins
DOWNLOAD_IMAGE: 1000 * 60 * 2, // 2 mins
- CHASSIS_OFF: 1000 * 60 * 5, // 5 mins
HOST_ON: 1000 * 60 * 5, // 5 mins
HOST_OFF: 1000 * 60 * 5, // 5 mins
HOST_OFF_IMMEDIATE: 1000 * 60 * 2 // 2 mins
},
MESSAGES: {
POLL: {
- CHASSIS_OFF_TIMEOUT:
- 'Time out. Chassis did not reach power off state in allotted time.',
HOST_ON_TIMEOUT:
'Time out. System did not reach Running state in allotted time.',
HOST_OFF_TIMEOUT:
diff --git a/app/server-control/controllers/power-operations-controller.html b/app/server-control/controllers/power-operations-controller.html
index 2fcb95b..ddf8bda 100644
--- a/app/server-control/controllers/power-operations-controller.html
+++ b/app/server-control/controllers/power-operations-controller.html
@@ -17,38 +17,42 @@
<div class="row column">
<h3 class="subhead">Select a power operation</h3>
</div>
- <span class="inactive-message" ng-show="dataService.server_state == 'Unreachable'">There are no power operations to display while a power operation is in progress. When complete, any new power operations will be displayed here.</span>
- <!-- Power on displays only when server is shutdown -->
- <div class="row column power-option" ng-hide="dataService.server_state == 'Running' || dataService.server_state == 'Quiesced' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || confirm || dataService.loading, transitionAll: confirm}">
- <button id="power__power-on" class="btn btn-secondary" ng-click="powerOn()" role="button" ng-disabled="dataService.server_unreachable">
- <icon file="icon-power.svg"></icon>Power on
- </button>
- <p class="inline">Attempts to power on the server</p>
+ <div ng-if="operationPending">
+ <span class="inactive-message">There are no power operations to display while a power operation is in progress. When complete, any new power operations will be displayed here.</span>
</div>
- <!-- Power reboot/shutdown options : when server is off all of these are hidden. When one option is selected, the others are disabled. -->
- <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmWarmReboot) || dataService.loading, transitionAll: confirm && confirmWarmReboot}">
- <button id="power__warm-boot" class="btn btn-secondary" ng-click="warmRebootConfirm()" role="button" ng-disabled="dataService.server_unreachable">
- <icon file="icon-restart.svg"></icon>Warm reboot</button>
- <p class="inline">Attempts to perform an orderly shutdown before restarting the server</p>
- <confirm title="warm reboot" confirm="confirmWarmReboot" ng-show="confirmWarmReboot" callback="warmReboot"></confirm>
- </div>
- <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmColdReboot) || dataService.loading, transitionAll: confirm && confirmColdReboot}">
- <button id="power__cold-boot" class="btn btn-secondary" ng-click="coldRebootConfirm()" role="button" ng-disabled="dataService.server_unreachable">
- <icon file="icon-restart.svg"></icon>Cold reboot</button>
- <p class="inline">Shuts down the server immediately, then restarts it</p>
- <confirm title="cold reboot" confirm="confirmColdReboot" ng-show="confirmColdReboot" cancel="coldbootCancel" callback="coldReboot"></confirm>
- </div>
- <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmOrderlyShutdown) || dataService.loading, transitionAll: confirm && confirmOrderlyShutdown}">
- <button id="power__soft-shutdown" class="btn btn-secondary" ng-click="orderlyShutdownConfirm()" role="button" ng-disabled="dataService.server_unreachable">
- <icon file="icon-power.svg"></icon>Orderly shutdown</button>
- <p class="inline">Attempts to stop all software on the server before removing power</p>
- <confirm title="orderly shutdown" confirm="confirmOrderlyShutdown" ng-show="confirmOrderlyShutdown" cancel="orderlyShutdownCancel" callback="orderlyShutdown"></confirm>
- </div>
- <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmImmediateShutdown) || dataService.loading, transitionAll: confirm && confirmImmediateShutdown}">
- <button id="power__hard-shutdown" class="btn btn-secondary" ng-click="immediateShutdownConfirm()" role="button" ng-disabled="dataService.server_unreachable">
- <icon file="icon-power.svg"></icon>Immediate shutdown</button>
- <p class="inline">Removes power from the server without waiting for software to stop</p>
- <confirm title="immediate shutdown" confirm="confirmImmediateShutdown" ng-show="confirmImmediateShutdown" cancel="immediatelyShutdownCancel" callback="immediateShutdown"></confirm>
+ <div ng-if="!operationPending">
+ <!-- Power on displays only when server is shutdown -->
+ <div class="row column power-option" ng-hide="dataService.server_state == 'Running' || dataService.server_state == 'Quiesced' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || confirm || dataService.loading, transitionAll: confirm}">
+ <button id="power__power-on" class="btn btn-secondary" ng-click="powerOn()" role="button" ng-disabled="dataService.server_unreachable">
+ <icon file="icon-power.svg"></icon>Power on
+ </button>
+ <p class="inline">Attempts to power on the server</p>
+ </div>
+ <!-- Power reboot/shutdown options : when server is off all of these are hidden. When one option is selected, the others are disabled. -->
+ <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmWarmReboot) || dataService.loading, transitionAll: confirm && confirmWarmReboot}">
+ <button id="power__warm-boot" class="btn btn-secondary" ng-click="warmRebootConfirm()" role="button" ng-disabled="dataService.server_unreachable">
+ <icon file="icon-restart.svg"></icon>Warm reboot</button>
+ <p class="inline">Attempts to perform an orderly shutdown before restarting the server</p>
+ <confirm title="warm reboot" confirm="confirmWarmReboot" ng-show="confirmWarmReboot" callback="warmReboot"></confirm>
+ </div>
+ <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmColdReboot) || dataService.loading, transitionAll: confirm && confirmColdReboot}">
+ <button id="power__cold-boot" class="btn btn-secondary" ng-click="coldRebootConfirm()" role="button" ng-disabled="dataService.server_unreachable">
+ <icon file="icon-restart.svg"></icon>Cold reboot</button>
+ <p class="inline">Shuts down the server immediately, then restarts it</p>
+ <confirm title="cold reboot" confirm="confirmColdReboot" ng-show="confirmColdReboot" cancel="coldbootCancel" callback="coldReboot"></confirm>
+ </div>
+ <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmOrderlyShutdown) || dataService.loading, transitionAll: confirm && confirmOrderlyShutdown}">
+ <button id="power__soft-shutdown" class="btn btn-secondary" ng-click="orderlyShutdownConfirm()" role="button" ng-disabled="dataService.server_unreachable">
+ <icon file="icon-power.svg"></icon>Orderly shutdown</button>
+ <p class="inline">Attempts to stop all software on the server before removing power</p>
+ <confirm title="orderly shutdown" confirm="confirmOrderlyShutdown" ng-show="confirmOrderlyShutdown" cancel="orderlyShutdownCancel" callback="orderlyShutdown"></confirm>
+ </div>
+ <div class="column power-option" ng-hide="dataService.server_state == 'Off' || dataService.server_state == 'Unreachable'" ng-class="{disabled: dataService.server_unreachable || (confirm && !confirmImmediateShutdown) || dataService.loading, transitionAll: confirm && confirmImmediateShutdown}">
+ <button id="power__hard-shutdown" class="btn btn-secondary" ng-click="immediateShutdownConfirm()" role="button" ng-disabled="dataService.server_unreachable">
+ <icon file="icon-power.svg"></icon>Immediate shutdown</button>
+ <p class="inline">Removes power from the server without waiting for software to stop</p>
+ <confirm title="immediate shutdown" confirm="confirmImmediateShutdown" ng-show="confirmImmediateShutdown" cancel="immediatelyShutdownCancel" callback="immediateShutdown"></confirm>
+ </div>
</div>
</div>
</div>
diff --git a/app/server-control/controllers/power-operations-controller.js b/app/server-control/controllers/power-operations-controller.js
index 9713d21..986ac3b 100644
--- a/app/server-control/controllers/power-operations-controller.js
+++ b/app/server-control/controllers/power-operations-controller.js
@@ -10,11 +10,10 @@
'use strict';
angular.module('app.serverControl').controller('powerOperationsController', [
- '$scope', 'APIUtils', 'dataService', 'Constants', '$timeout', '$interval',
- '$q', 'toastService',
+ '$scope', 'APIUtils', 'dataService', 'Constants', '$interval', '$q',
+ 'toastService',
function(
- $scope, APIUtils, dataService, Constants, $timeout, $interval, $q,
- toastService) {
+ $scope, APIUtils, dataService, Constants, $interval, $q, toastService) {
$scope.dataService = dataService;
// Is a || of the other 4 "confirm" variables to ensure only
// one confirm is shown at a time.
@@ -25,35 +24,39 @@
$scope.confirmImmediateShutdown = false;
$scope.loading = true;
- var pollChassisStatusTimer = undefined;
- var pollStartTime = null;
+ // When a power operation is in progress, set to true,
+ // when a power operation completes (success/fail) set to false.
+ // This property is used to show/hide the 'in progress' message
+ // in markup.
+ $scope.operationPending = false;
/**
* Checks the host status provided by the dataService using an
* interval timer
* @param {string} statusType : host status type to check for
- * @param {number} timeout : timeout limit
- * @param {string} error : error message
+ * @param {number} timeout : timeout limit, defaults to 5 minutes
+ * @param {string} error : error message, defaults to 'Time out'
* @returns {Promise} : returns a deferred promise that will be fulfilled
* if the status is met or be rejected if the timeout is reached
*/
- var checkHostStatus = (statusType, timeout, error = 'Time out.') => {
- const deferred = $q.defer();
- const start = new Date();
- const checkHostStatusInverval = $interval(() => {
- let now = new Date();
- let timePassed = now.getTime() - start.getTime();
- if (timePassed > timeout) {
- deferred.reject(error);
- $interval.cancel(checkHostStatusInverval);
- }
- if (dataService.server_state === statusType) {
- deferred.resolve();
- $interval.cancel(checkHostStatusInverval);
- }
- }, Constants.POLL_INTERVALS.POWER_OP);
- return deferred.promise;
- };
+ var checkHostStatus =
+ (statusType, timeout = 300000, error = 'Time out.') => {
+ const deferred = $q.defer();
+ const start = new Date();
+ const checkHostStatusInverval = $interval(() => {
+ let now = new Date();
+ let timePassed = now.getTime() - start.getTime();
+ if (timePassed > timeout) {
+ deferred.reject(error);
+ $interval.cancel(checkHostStatusInverval);
+ }
+ if (dataService.server_state === statusType) {
+ deferred.resolve();
+ $interval.cancel(checkHostStatusInverval);
+ }
+ }, Constants.POLL_INTERVALS.POWER_OP);
+ return deferred.promise;
+ };
APIUtils.getLastPowerTime()
.then(
@@ -76,71 +79,56 @@
(dataService.server_state == 'Running') ? 'Off' : 'Running';
};
- $scope.powerOn = function() {
- $scope.loading = true;
+ /**
+ * Initiate Power on
+ */
+ $scope.powerOn = () => {
+ $scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.hostPowerOn()
- .then(function(response) {
- return response;
+ .then(() => {
+ // Check for on state
+ return checkHostStatus(
+ Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
+ Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
})
- .then(function(lastStatus) {
- return APIUtils.pollHostStatusTillOn();
- })
- .then(function(hostState) {
- $scope.loading = false;
- })
- .catch(function(error) {
+ .catch((error) => {
+ console.log(error);
toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
- $scope.loading = false;
- });
+ })
+ .finally(() => {
+ $scope.operationPending = false;
+ })
};
- function pollChassisStatusTillOff() {
- var deferred = $q.defer();
- pollChassisStatusTimer = $interval(function() {
- var now = new Date();
- if ((now.getTime() - pollStartTime.getTime()) >=
- Constants.TIMEOUT.CHASSIS_OFF) {
- $interval.cancel(pollChassisStatusTimer);
- pollChassisStatusTimer = undefined;
- deferred.reject(
- new Error(Constants.MESSAGES.POLL.CHASSIS_OFF_TIMEOUT));
- }
- APIUtils.getChassisState()
- .then(function(state) {
- if (state === Constants.CHASSIS_POWER_STATE.off_code) {
- $interval.cancel(pollChassisStatusTimer);
- pollChassisStatusTimer = undefined;
- deferred.resolve(state);
- }
- })
- .catch(function(error) {
- $interval.cancel(pollChassisStatusTimer);
- pollChassisStatusTimer = undefined;
- deferred.reject(error);
- });
- }, Constants.POLL_INTERVALS.POWER_OP);
-
- return deferred.promise;
- }
- $scope.warmReboot = function() {
- $scope.loading = true;
+ /**
+ * Initiate Orderly reboot
+ * Attempts to stop all software
+ */
+ $scope.warmReboot = () => {
+ $scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.hostReboot()
- .then(function(response) {
- return response;
+ .then(() => {
+ // Check for off state
+ return checkHostStatus(
+ Constants.HOST_STATE_TEXT.off, Constants.TIMEOUT.HOST_OFF,
+ Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
})
- .then(function(lastStatus) {
- return APIUtils.pollHostStatusTilReboot();
+ .then(() => {
+ // Check for on state
+ return checkHostStatus(
+ Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
+ Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
})
- .then(function(hostState) {
- $scope.loading = false;
- })
- .catch(function(error) {
+ .catch((error) => {
+ console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
- $scope.loading = false;
- });
+ })
+ .finally(() => {
+ $scope.operationPending = false;
+ })
};
$scope.warmRebootConfirm = function() {
@@ -152,33 +140,40 @@
$scope.confirmWarmReboot = true;
};
- $scope.coldReboot = function() {
- $scope.loading = true;
+ /**
+ * Initiate Immediate reboot
+ * Does not attempt to stop all software
+ */
+ $scope.coldReboot = () => {
+ $scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.chassisPowerOff()
- .then(function() {
+ .then(() => {
+ // Check for off state
return checkHostStatus(
Constants.HOST_STATE_TEXT.off,
Constants.TIMEOUT.HOST_OFF_IMMEDIATE,
Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
})
- .then(function() {
+ .then(() => {
return APIUtils.hostPowerOn();
})
- .then(function() {
+ .then(() => {
+ // Check for on state
return checkHostStatus(
Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
})
- .catch(function(error) {
+ .catch((error) => {
console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.COLD_REBOOT_FAILED);
})
- .finally(function() {
- $scope.loading = false;
+ .finally(() => {
+ $scope.operationPending = false;
})
};
+
$scope.coldRebootConfirm = function() {
if ($scope.confirm) {
return;
@@ -187,29 +182,30 @@
$scope.confirmColdReboot = true;
};
- $scope.orderlyShutdown = function() {
- $scope.loading = true;
+ /**
+ * Initiate Orderly shutdown
+ * Attempts to stop all software
+ */
+ $scope.orderlyShutdown = () => {
+ $scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.hostPowerOff()
- .then(function(response) {
- return response;
+ .then(() => {
+ // Check for off state
+ return checkHostStatus(
+ Constants.HOST_STATE_TEXT.off, Constants.TIMEOUT.HOST_OFF,
+ Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
})
- .then(function(lastStatus) {
- return APIUtils.pollHostStatusTillOff();
- })
- .then(function(hostState) {
- pollStartTime = new Date();
- return pollChassisStatusTillOff();
- })
- .then(function(chassisState) {
- $scope.loading = false;
- })
- .catch(function(error) {
+ .catch((error) => {
+ console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.ORDERLY_SHUTDOWN_FAILED);
- $scope.loading = false;
- });
+ })
+ .finally(() => {
+ $scope.operationPending = false;
+ })
};
+
$scope.orderlyShutdownConfirm = function() {
if ($scope.confirm) {
return;
@@ -218,27 +214,34 @@
$scope.confirmOrderlyShutdown = true;
};
- $scope.immediateShutdown = function() {
- $scope.loading = true;
+ /**
+ * Initiate Immediate shutdown
+ * Does not attempt to stop all software
+ */
+ $scope.immediateShutdown = () => {
+ $scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.chassisPowerOff()
- .then(function(response) {
- return response;
+ .then(() => {
+ // Check for off state
+ return checkHostStatus(
+ Constants.HOST_STATE_TEXT.off,
+ Constants.TIMEOUT.HOST_OFF_IMMEDIATE,
+ Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
})
- .then(function(lastStatus) {
- pollStartTime = new Date();
- return pollChassisStatusTillOff();
- })
- .then(function(chassisState) {
+ .then(() => {
dataService.setPowerOffState();
- $scope.loading = false;
})
- .catch(function(error) {
+ .catch((error) => {
+ console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.IMMEDIATE_SHUTDOWN_FAILED);
- $scope.loading = false;
- });
+ })
+ .finally(() => {
+ $scope.operationPending = false;
+ })
};
+
$scope.immediateShutdownConfirm = function() {
if ($scope.confirm) {
return;