Add fixes for cold reboot
This fixes the cold reboot issue with the following steps
- It applies the command to shut off the chassis.
- Then verify the chassis is off. It checks for every 5 seconds.
During this time the spinner displays. A 5min timeout has been
added.
- Once the chassis is off, it turns on the host.
fixes openbmc/openbmc#2795
Change-Id: I119a1c95e57c10ccee27be1512a1fc38cde307fa
Signed-off-by: Iftekharul Islam <iffy.ryan@ibm.com>
Signed-off-by: CamVan Nguyen <ctnguyen@us.ibm.com>
diff --git a/app/common/directives/app-header.js b/app/common/directives/app-header.js
index 1ba55ba..2a34381 100644
--- a/app/common/directives/app-header.js
+++ b/app/common/directives/app-header.js
@@ -24,7 +24,7 @@
if(!userModel.isLoggedIn()){
return;
}
- APIUtils.getHostState(function(status){
+ APIUtils.getHostState().then(function(status){
if(status == 'xyz.openbmc_project.State.Host.HostState.Off'){
dataService.setPowerOffState();
}else if(status == 'xyz.openbmc_project.State.Host.HostState.Running'){
@@ -32,6 +32,8 @@
}else{
dataService.setBootingState();
}
+ }, function(error){
+ dataService.activateErrorModal();
});
}
diff --git a/app/common/directives/errors.html b/app/common/directives/errors.html
index 0fd2234..f3e98b6 100644
--- a/app/common/directives/errors.html
+++ b/app/common/directives/errors.html
@@ -1,17 +1,17 @@
<!-- Unexpected error -->
-<section class="modal" aria-hidden="true" aria-labelledby="modalTitle" aria-describedby="modalDescription" role="dialog" ng-class="{'active': display_error}">
+<section class="modal" aria-hidden="true" aria-labelledby="modalTitle" aria-describedby="modalDescription" role="dialog" ng-class="{'active': dataService.displayErrorModal}">
<div class="modal__upload-fail" role="document">
- <div class="screen-reader-offscreen modal-description">Unexpected error</div><!-- accessibility only; used for screen readers -->
+ <div class="screen-reader-offscreen modal-description">{{dataService.errorModalDetails.title}}</div><!-- accessibility only; used for screen readers -->
<div class="page-header ">
- <span class="icon icon__warning inline"><span class="accessible-text" role="alert">Unexpected error</span></span>
- <h1 class="modal-title h4 inline">Unexpected error</h1>
+ <span class="icon icon__warning inline"><span class="accessible-text" role="alert">{{dataService.errorModalDetails.title}}</span></span>
+ <h1 class="modal-title h4 inline">{{dataService.errorModalDetails.title}}</h1>
</div>
<div class="modal__content">
- <p>Oops! An unexpected error occurred. Record specific details of the issue, then contact your company support services.</p>
+ <p>{{dataService.errorModalDetails.description}}</p>
</div>
<div class="modal__button-wrapper">
- <button class="inline btn-primary" ng-click="display_error = false;">Close</button>
+ <button class="inline btn-primary" ng-click="dataService.deactivateErrorModal()">Close</button>
</div>
</div>
</section>
-<div class="modal-overlay" tabindex="-1" ng-class="{'active': (multi_server_recent)}"></div>
\ No newline at end of file
+<div class="modal-overlay" tabindex="-1" ng-class="{'active': dataService.displayErrorModal}"></div>
diff --git a/app/common/directives/errors.js b/app/common/directives/errors.js
new file mode 100644
index 0000000..2c9b109
--- /dev/null
+++ b/app/common/directives/errors.js
@@ -0,0 +1,18 @@
+window.angular && (function (angular) {
+ 'use strict';
+
+ angular
+ .module('app.common.directives')
+ .directive('errors', ['APIUtils', function (APIUtils) {
+ return {
+ 'restrict': 'E',
+ 'template': require('./errors.html'),
+ 'scope': {
+ 'path': '='
+ },
+ 'controller': ['$scope','dataService', function($scope, dataService){
+ $scope.dataService = dataService;
+ }]
+ };
+ }]);
+})(window.angular);
diff --git a/app/common/services/api-utils.js b/app/common/services/api-utils.js
index 1e28127..25cbec9 100644
--- a/app/common/services/api-utils.js
+++ b/app/common/services/api-utils.js
@@ -33,10 +33,11 @@
LED_STATE: Constants.LED_STATE,
LED_STATE_TEXT: Constants.LED_STATE_TEXT,
HOST_SESSION_STORAGE_KEY: Constants.API_CREDENTIALS.host_storage_key,
- getChassisState: function(callback){
+ getChassisState: function(){
+ var deferred = $q.defer();
$http({
method: 'GET',
- url: DataService.getHost() + "/xyz/openbmc_project/state/chassis0",
+ url: DataService.getHost() + "/xyz/openbmc_project/state/chassis0/attr/CurrentPowerState",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
@@ -45,15 +46,18 @@
}).then(function(response){
var json = JSON.stringify(response.data);
var content = JSON.parse(json);
- callback(content.data.CurrentPowerState);
+ deferred.resolve(content.data);
}, function(error){
console.log(error);
+ deferred.reject(error);
});
+ return deferred.promise;
},
- getHostState: function(callback){
+ getHostState: function(){
+ var deferred = $q.defer();
$http({
method: 'GET',
- url: DataService.getHost() + "/xyz/openbmc_project/state/host0",
+ url: DataService.getHost() + "/xyz/openbmc_project/state/host0/attr/CurrentHostState",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
@@ -62,10 +66,12 @@
}).then(function(response){
var json = JSON.stringify(response.data);
var content = JSON.parse(json);
- callback(content.data.CurrentHostState);
+ deferred.resolve(content.data);
}, function(error){
console.log(error);
+ deferred.reject(error);
});
+ return deferred.promise;
},
getNetworkInfo: function(){
var deferred = $q.defer();
@@ -287,7 +293,8 @@
}
});
},
- chassisPowerOff: function(callback){
+ chassisPowerOff: function(){
+ var deferred = $q.defer();
$http({
method: 'PUT',
url: DataService.getHost() + "/xyz/openbmc_project/state/chassis0/attr/RequestedPowerTransition",
@@ -300,16 +307,12 @@
}).then(function(response){
var json = JSON.stringify(response.data);
var content = JSON.parse(json);
- if(callback){
- return callback(content.status);
- }
+ deferred.resolve(content.status);
}, function(error){
- if(callback){
- callback(error);
- }else{
- console.log(error);
- }
+ console.log(error);
+ deferred.reject(error);
});
+ return deferred.promise;
},
setLEDState: function(state, callback){
$http({
@@ -359,7 +362,8 @@
}
});
},
- hostPowerOn: function(callback){
+ hostPowerOn: function(){
+ var deferred = $q.defer();
$http({
method: 'PUT',
url: DataService.getHost() + "/xyz/openbmc_project/state/host0/attr/RequestedHostTransition",
@@ -372,18 +376,15 @@
}).then(function(response){
var json = JSON.stringify(response.data);
var content = JSON.parse(json);
- if(callback){
- return callback(content.status);
- }
+ deferred.resolve(content.status);
}, function(error){
- if(callback){
- callback(error);
- }else{
- console.log(error);
- }
+ console.log(error);
+ deferred.reject(error);
});
+ return deferred.promise;
},
- hostPowerOff: function(callback){
+ hostPowerOff: function(){
+ var deferred = $q.defer();
$http({
method: 'PUT',
url: DataService.getHost() + "/xyz/openbmc_project/state/host0/attr/RequestedHostTransition",
@@ -396,18 +397,15 @@
}).then(function(response){
var json = JSON.stringify(response.data);
var content = JSON.parse(json);
- if(callback){
- return callback(content.status);
- }
+ deferred.resolve(content.status);
}, function(error){
- if(callback){
- callback(error);
- }else{
- console.log(error);
- }
+ console.log(error);
+ deferred.reject(error);
});
+ return deferred.promise;
},
- hostReboot: function(callback){
+ hostReboot: function(){
+ var deferred = $q.defer();
$http({
method: 'PUT',
url: DataService.getHost() + "/xyz/openbmc_project/state/host0/attr/RequestedHostTransition",
@@ -420,16 +418,13 @@
}).then(function(response){
var json = JSON.stringify(response.data);
var content = JSON.parse(json);
- if(callback){
- return callback(content.status);
- }
+ deferred.resolve(content.status);
}, function(error){
- if(callback){
- callback(error);
- }else{
- console.log(error);
- }
+ console.log(error);
+ deferred.reject(error);
});
+
+ return deferred.promise;
},
hostShutdown: function(callback){
$http({
diff --git a/app/common/services/constants.js b/app/common/services/constants.js
index 0bb2123..21241da 100644
--- a/app/common/services/constants.js
+++ b/app/common/services/constants.js
@@ -27,11 +27,15 @@
},
CHASSIS_POWER_STATE: {
on: 'On',
- off: 'Off'
+ 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',
off: 'Off',
+ off_code: 'xyz.openbmc_project.State.Host.HostState.Off',
booting: 'Quiesced',
unreachable: 'Unreachable'
},
@@ -107,21 +111,31 @@
],
SENSOR_SORT_ORDER_DEFAULT: 8,
FIRMWARE: {
- ACTIVATE_FIRMWARE: 'xyz.openbmc_project.Software.Activation.RequestedActivations.Active',
- FUNCTIONAL_OBJPATH: '/xyz/openbmc_project/software/functional'
+ ACTIVATE_FIRMWARE: 'xyz.openbmc_project.Software.Activation.RequestedActivations.Active',
+ FUNCTIONAL_OBJPATH: '/xyz/openbmc_project/software/functional'
},
- POLL_INTERVALS: {
- ACTIVATION: 5000
+ POLL_INTERVALS: {
+ ACTIVATION: 5000,
+ POWER_OP: 5000,
},
- TIMEOUT: {
- ACTIVATION: 1000 * 60 * 10, // 10 mins
+ TIMEOUT: {
+ ACTIVATION: 1000 * 60 * 10, // 10 mins
+ CHASSIS_OFF: 1000 * 60 * 5, // 5 mins
+ HOST_ON: 1000 * 60 * 5, // 5 mins
},
MESSAGES: {
- SENSOR: {
- NO_SENSOR_DATA: 'There are no sensors found.',
- CRITICAL_NO_SENSOR_DATA: 'There are no sensors in Critical state.',
- WARNING_NO_SENSOR_DATA: 'There are no sensors in Warning state.'
- }
+ POLL: {
+ TIMEOUT: 'Time out. Did not reach power state in allotted time.',
+ },
+ SENSOR: {
+ NO_SENSOR_DATA: 'There are no sensors found.',
+ CRITICAL_NO_SENSOR_DATA: 'There are no sensors in Critical state.',
+ WARNING_NO_SENSOR_DATA: 'There are no sensors in Warning state.'
+ },
+ ERROR_MODAL: {
+ TITLE: 'Unexpected error',
+ DESCRIPTION: 'Oops! An unexpected error occurred. Record specific details of the issue, then contact your company support services.'
+ }
},
POWER_CAP_TEXT: {
unit: 'W',
diff --git a/app/common/services/dataService.js b/app/common/services/dataService.js
index 201e79c..d84821a 100644
--- a/app/common/services/dataService.js
+++ b/app/common/services/dataService.js
@@ -33,6 +33,10 @@
this.hostname = "";
this.mac_address = "";
this.remote_window_active = false;
+
+ this.displayErrorModal = false;
+ this.errorModalDetails = {};
+
this.ignoreHttpError = false;
this.getServerId = function(){
return this.host.replace(/^https?\:\/\//ig,"");
@@ -121,6 +125,25 @@
this.server_health = Constants.SERVER_HEALTH.good;
}
+
+ this.activateErrorModal = function(data){
+ if(data && data.hasOwnProperty('title')){
+ this.errorModalDetails.title = data.title;
+ }else{
+ this.errorModalDetails.title = Constants.MESSAGES.ERROR_MODAL.TITLE;
+ }
+
+ if(data && data.hasOwnProperty('description')){
+ this.errorModalDetails.description = data.description;
+ }else{
+ this.errorModalDetails.description = Constants.MESSAGES.ERROR_MODAL.DESCRIPTION;
+ }
+ this.displayErrorModal = true;
+ }
+
+ this.deactivateErrorModal = function(){
+ this.displayErrorModal = false;
+ }
}]);
})(window.angular);
diff --git a/app/index.html b/app/index.html
index c55dfaf..0c2e401 100644
--- a/app/index.html
+++ b/app/index.html
@@ -16,5 +16,6 @@
<main ng-view ng-class="{'content__container': dataService.showNavigation, 'login__wrapper': !dataService.showNavigation}">
</main>
+ <errors></errors>
</body>
</html>
diff --git a/app/index.js b/app/index.js
index 95d7fa3..11ddc29 100644
--- a/app/index.js
+++ b/app/index.js
@@ -39,6 +39,7 @@
import filters_index from './common/filters/index.js'
import directives_index from './common/directives/index.js'
+import errors from './common/directives/errors.js'
import app_header from './common/directives/app-header.js'
import app_navigation from './common/directives/app-navigation.js'
import confirm from './common/directives/confirm.js'
diff --git a/app/server-control/controllers/power-operations-controller.html b/app/server-control/controllers/power-operations-controller.html
index f0de309..876ce24 100644
--- a/app/server-control/controllers/power-operations-controller.html
+++ b/app/server-control/controllers/power-operations-controller.html
@@ -1,4 +1,4 @@
-<loader loading="dataService.loading"></loader>
+<loader loading="dataService.loading || loading"></loader>
<div id="power-operations">
<div class="row column">
<h1>Server power operations</h1>
diff --git a/app/server-control/controllers/power-operations-controller.js b/app/server-control/controllers/power-operations-controller.js
index 4ae753e..461c1e7 100644
--- a/app/server-control/controllers/power-operations-controller.js
+++ b/app/server-control/controllers/power-operations-controller.js
@@ -16,8 +16,11 @@
'$scope',
'APIUtils',
'dataService',
+ 'Constants',
'$timeout',
- function($scope, APIUtils, dataService, $timeout){
+ '$interval',
+ '$q',
+ function($scope, APIUtils, dataService, Constants, $timeout, $interval, $q){
$scope.dataService = dataService;
$scope.confirm = false;
$scope.power_confirm = false;
@@ -25,6 +28,11 @@
$scope.coldboot_confirm = false;
$scope.orderly_confirm = false;
$scope.immediately_confirm = false;
+ $scope.loading = false;
+
+ var pollChassisStatusTimer = undefined;
+ var pollHostStatusTimer = undefined;
+ var pollStartTime = null;
//@TODO: call api and get proper state
$scope.toggleState = function(){
@@ -33,8 +41,8 @@
$scope.togglePower = function(){
var method = (dataService.server_state == 'Running') ? 'hostPowerOff' : 'hostPowerOn';
- //@TODO: show progress or set class orange
- APIUtils[method](function(response){
+ //@TODO: show progress or set class orange
+ APIUtils[method]().then(function(response){
//update state based on response
//error case?
if(response == null){
@@ -56,10 +64,59 @@
$scope.confirm = true;
$scope.power_confirm = true;
};
+
+ 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.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;
+ }
+ function pollHostStatusTillOn(){
+ var deferred = $q.defer();
+ pollHostStatusTimer = $interval(function(){
+ var now = new Date();
+ if((now.getTime() - pollStartTime.getTime()) >= Constants.TIMEOUT.HOST_ON){
+ $interval.cancel(pollHostStatusTimer);
+ pollHostStatusTimer = undefined;
+ deferred.reject(new Error(Constants.MESSAGES.POLL.TIMEOUT));
+ }
+ APIUtils.getHostState().then(function(state){
+ if(state === Constants.HOST_STATE_TEXT.on_code){
+ $interval.cancel(pollHostStatusTimer);
+ pollHostStatusTimer = undefined;
+ deferred.resolve(state);
+ }
+ }).catch(function(error){
+ $interval.cancel(pollHostStatusTimer);
+ pollHostStatusTimer = undefined;
+ deferred.reject(error);
+ });
+ }, Constants.POLL_INTERVALS.POWER_OP);
+
+ return deferred.promise;
+ }
$scope.warmReboot = function(){
//@TODO:show progress
dataService.setBootingState();
- APIUtils.hostReboot(function(response){
+ APIUtils.hostReboot().then(function(response){
if(response){
dataService.setPowerOnState();
}else{
@@ -84,7 +141,26 @@
};
$scope.coldReboot = function(){
- $scope.warmReboot();
+ $scope.loading = true;
+ dataService.setBootingState();
+ APIUtils.chassisPowerOff().then(function(state){
+ return state;
+ }).then(function(lastState) {
+ pollStartTime = new Date();
+ return pollChassisStatusTillOff();
+ }).then(function(chassisState) {
+ APIUtils.hostPowerOn().then(function(hostState){
+ return hostState;
+ })
+ }).then(function(hostState) {
+ pollStartTime = new Date();
+ return pollHostStatusTillOn();
+ }).then(function(state) {
+ dataService.setPowerOnState();
+ $scope.loading = false;
+ }).catch(function(error){
+ $scope.loading = false;
+ });
};
$scope.coldRebootConfirm = function(){
if($scope.confirm) {
@@ -96,7 +172,7 @@
$scope.orderlyShutdown = function(){
//@TODO:show progress
- APIUtils.hostPowerOff(function(response){
+ APIUtils.hostPowerOff().then(function(response){
if(response){
dataService.setPowerOffState();
}else{