Add boot option override and TPM enable toggle
- Adds ability to set a bootsource override to allowable target value
- Adds ability to enable or disable TPM required policy
- Replaces power operations confirm directive with bootstrap modal
Tested: Confirmed override settings saved to redfish but unable to verify
if settings are automatically set to disabled by petitboot after a
one time boot. Passes DAP.
Resolves openbmc/phosphor-webui#82
Resolves openbmc/phosphor-webui#90
Signed-off-by: Dixsie Wolmers <dixsiew@gmail.com>
Change-Id: If0ffd6f9328939d70c7958ee11fb90bd20a1e685
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/app/assets/icons/icon-pending.svg b/app/assets/icons/icon-pending.svg
new file mode 100644
index 0000000..5d48c00
--- /dev/null
+++ b/app/assets/icons/icon-pending.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="pending-a" d="M13.12,14.13l.15-.13-.7-.91a6.89,6.89,0,0,0,1.56-2,6.79,6.79,0,0,0,.71-3,6.88,6.88,0,0,0-6.9-6.86V0A8,8,0,0,1,16,8a7.92,7.92,0,0,1-.84,3.56A8,8,0,0,1,13.12,14.13Zm-2.28,1.34a8.08,8.08,0,0,1-2.9.53V14.86a6.93,6.93,0,0,0,2.53-.48ZM3.48,13.22a6.92,6.92,0,0,0,2.08,1.19l-.39,1.07a8.07,8.07,0,0,1-2.4-1.39Zm-2.33-4A6.77,6.77,0,0,0,2,11.43L1,12A7.9,7.9,0,0,1,0,9.38ZM5.54,1.58a6.91,6.91,0,0,0-2.06,1.2L2.77,1.9A8.07,8.07,0,0,1,5.15.5ZM2,4.57a6.77,6.77,0,0,0-.79,2.22L0,6.59A7.9,7.9,0,0,1,1,4Zm8.24,7L7,8.38V3H8.14V7.9L11,10.76Z"/></svg>
\ No newline at end of file
diff --git a/app/common/services/api-utils.js b/app/common/services/api-utils.js
index 27b122d..ba48a49 100644
--- a/app/common/services/api-utils.js
+++ b/app/common/services/api-utils.js
@@ -679,6 +679,45 @@
data: JSON.stringify({'data': state})
})
},
+ getBootOptions: function() {
+ return $http({
+ method: 'GET',
+ url: DataService.getHost() + '/redfish/v1/Systems/system',
+ withCredentials: true
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
+ saveBootSettings: function(data) {
+ return $http({
+ method: 'PATCH',
+ url: DataService.getHost() + '/redfish/v1/Systems/system',
+ withCredentials: true,
+ data: data
+ });
+ },
+ getTPMStatus: function() {
+ return $http({
+ method: 'GET',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/control/host0/TPMEnable',
+ withCredentials: true
+ })
+ .then(function(response) {
+ return response.data;
+ });
+ },
+ saveTPMEnable: function(data) {
+ return $http({
+ method: 'PUT',
+ url: DataService.getHost() +
+ '/xyz/openbmc_project/control/host0/TPMEnable/attr/TPMEnable',
+ withCredentials: true,
+ data: JSON.stringify({'data': data})
+ })
+ },
+
bmcReboot: function() {
return $http({
method: 'PUT',
diff --git a/app/common/services/constants.js b/app/common/services/constants.js
index ae82e76..37dd29f 100644
--- a/app/common/services/constants.js
+++ b/app/common/services/constants.js
@@ -113,11 +113,11 @@
'Time out. Did not download image in allotted time.',
},
POWER_OP: {
- POWER_ON_FAILED: 'Power On Failed',
- WARM_REBOOT_FAILED: 'Warm Reboot Failed',
- COLD_REBOOT_FAILED: 'Cold Reboot Failed',
- ORDERLY_SHUTDOWN_FAILED: 'Orderly Shutdown Failed',
- IMMEDIATE_SHUTDOWN_FAILED: 'Immediate Shutdown Failed',
+ POWER_ON_FAILED: 'Power on failed.',
+ WARM_REBOOT_FAILED: 'Warm reboot failed.',
+ COLD_REBOOT_FAILED: 'Cold reboot failed.',
+ ORDERLY_SHUTDOWN_FAILED: 'Orderly shutdown failed.',
+ IMMEDIATE_SHUTDOWN_FAILED: 'Immediate shutdown failed.',
},
SENSOR: {
NO_SENSOR_DATA: 'There are no sensors found.',
diff --git a/app/common/styles/elements/modals.scss b/app/common/styles/elements/modals.scss
index 0e21a39..450a26b 100644
--- a/app/common/styles/elements/modals.scss
+++ b/app/common/styles/elements/modals.scss
@@ -107,6 +107,15 @@
}
.uib-modal__content {
padding: 1em;
+ .modal-header {
+ display: block;
+ }
+ .btn--close {
+ padding: 0;
+ svg {
+ height: 1.6em;
+ }
+ }
}
.uib-modal {
@@ -118,4 +127,4 @@
height: 2em;
}
}
-}
\ No newline at end of file
+}
diff --git a/app/server-control/controllers/power-operations-controller.html b/app/server-control/controllers/power-operations-controller.html
index ddf8bda..2ef5323 100644
--- a/app/server-control/controllers/power-operations-controller.html
+++ b/app/server-control/controllers/power-operations-controller.html
@@ -1,58 +1,172 @@
<loader loading="dataService.loading || loading"></loader>
-<div id="power-operations">
- <div class="row column">
- <h1>Server power operations</h1>
- <div class="power__current-status page-header">
- <h2 class="inline">Current status</h2>
- <div class="power__status-log inline float-right">Last power operation at <span class="courier-bold">{{powerTime | localeDate}}</span></div>
- </div>
+<div id="power-operations" class="power-operations">
+ <div class="row column">
+ <h1>Server power operations</h1>
+ <div class="power__current-status page-header">
+ <h2 class="inline">Current status</h2>
+ <div class="power__status-log inline float-right">
+ Last power operation at
+ <span class="courier-bold">{{ powerTime | localeDate }}</span>
+ </div>
</div>
- <div class="row column">
- <div id="power-indicator-bar" class="power__indicator-bar" ng-class="{'power__state-on': dataService.server_state == 'Running', 'power__state-off': dataService.server_state == 'Off', 'power__state-indet': dataService.server_state == 'Standby', 'power__state-error': dataService.server_state == 'Quiesced'}">
- <p class="inline">{{dataService.hostname}} - {{dataService.server_id}}</p>
- <h3 class="power__state inline no-margin h3"><span>{{dataService.server_state | quiescedToError}}</span></h3>
- </div>
+ </div>
+ <!-- Power Indicator Bar -->
+ <div class="row column">
+ <div id="power-indicator-bar" class="power__indicator-bar"
+ ng-class="{'power__state-on': dataService.server_state == 'Running', 'power__state-off': dataService.server_state == 'Off', 'power__state-indet': dataService.server_state == 'Standby', 'power__state-error': dataService.server_state == 'Quiesced'}">
+ <p class="inline">
+ {{ dataService.hostname }} - {{ dataService.server_id }}
+ </p>
+ <h3 class="power__state inline no-margin h3">
+ <span>{{ dataService.server_state | quiescedToError }}</span>
+ </h3>
</div>
- <div class="row column">
- <div class="row column">
- <h3 class="subhead">Select a power operation</h3>
+ </div>
+ <div class="row column">
+ <!-- Boot Settings Column -->
+ <div class="large-4 columns boot-options-wrapper">
+ <form id="host-boot-settings" name="hostBootSettings" class="host-boot-settings" novalidate>
+ <h2 class="subhead boot-options">Host OS boot settings</h2>
+ <div class="boot-settings-form">
+ <div class="boot-options">
+ <label for="boot-selected">
+ Boot setting override</label>
+ <select id="boot-selected" name="bootSelected" id="boot-selected" ng-disabled="dataService.server_unreachable || bootOverrideError"
+ ng-model="boot.BootSourceOverrideTarget">
+ <option class="courier-bold" value="{{bootSource}}" ng-repeat="bootSource in bootSources">
+ {{ bootSource }}
+ </option>
+ </select>
+ <div class="boot-options one-time-boot-setting">
+ <div class="align-self-center">
+ <label class="control-check" id="one-time-label"> <span class="inline boot-checkbox">Enable one time boot</span>
+ <input type="checkbox" name="oneTime"
+ ng-disabled="dataService.server_unreachable || bootOverrideError || boot.BootSourceOverrideTarget =='None' " ng-model="boot.oneTimeBootEnabled"
+ ng-change="oneTimeBootEnabled" />
+ <span class="control__indicator"> </span>
+ </label>
+ </div>
+ </div>
</div>
- <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>
- <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
+ <!-- TPM Required -->
+ <div class="boot-options one-time-boot-setting">
+ <div class="boot-options">
+ <h3 class="content-label">
+ TPM required policy</h3>
+ <p> Enable to ensure the system only boots when the TPM is functional. </p>
+ <!-- Toggle component -->
+ <div class="toggle-container">
+ <div class="toggle">
+ <input
+ id="toggle__switch-round"
+ class="toggle-switch toggle-switch__round-flat"
+ name="toggle"
+ type="checkbox"
+ tabindex="0"
+ ng-model="TPMToggle.TPMEnable"
+ ng-disabled="dataService.server_unreachable"
+ />
+ <label for="toggle__switch-round" tabindex="0"
+ >TPM required policy is {{ TPMToggle.TPMEnable ? "On" : "Off" }}</label
+ >
+ </div>
+ <span>
+ {{ TPMToggle.TPMEnable ? "On" : "Off" }}
+ </span>
+ </div>
+ </div>
+ </div>
+ <!-- form actions -->
+ <div class="boot-form-actions">
+ <button type="submit" class="btn btn-primary" ng-click="saveBootSettings();saveTPMPolicy();hostBootSettings.$setPristine()" ng-disabled="dataService.server_unreachable || hostBootSettings.$pristine;">
+ Save
+ </button>
+ <button type="reset" class="btn btn-secondary" ng-disabled="dataService.server_unreachable || hostBootSettings.$pristine" ng-click="resetForm();hostBootSettings.$setPristine()">
+ Cancel
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+ <!-- Power Operations Column -->
+ <div class="large-8 columns operations-wrapper">
+ <h2 class="subhead boot-operations">Operations</h2>
+ <!-- Pending one time boot alert -->
+ <div class="alert-warning"
+ ng-if="boot.oneTimeBootEnabled" ng-hide="dataService.server_state == 'Unreachable'">
+ <div class="pending-icon">
+ <icon file="icon-pending.svg"></icon>
+ </div>
+ <p class="alert-pending">
+ Pending one time boot. Next boot will be performed with the
+ specified one time boot settings. Subsequent boots will be performed
+ with the default settings.
+ </p>
+ </div>
+ <!-- Pending reboot warning -->
+ <p ng-show="operationPending">
+ There are no power operations to display while power operation is in
+ progress. When complete, any new power operations will be displayed
+ here.
+ </p>
+ <div ng-show="!operationPending">
+ <!-- Power on displays only when server is off -->
+ <div class="row column" ng-show="dataService.server_state == 'Off'"
+ ng-class="{disabled: dataService.server_unreachable}">
+ <button id="power__power-on" class="btn btn-primary inline" ng-click="powerOn()" role="button"
+ ng-disabled="dataService.server_unreachable">
+ Power on
+ </button>
+ </div>
+ <!-- Reboot/shutdown column -->
+ <div
+ ng-show="dataService.server_state !== 'Off'">
+ <div class="reboot__operations">
+ <form id="reboot-form" name="rebootForm" class="reboot-form">
+ <fieldset>
+ <legend class="boot-operations">Reboot server</legend>
+ <label class="control-radio">Orderly - OS shuts down, then server reboots
+ <input type="radio" name="radioReboot"
+ ng-model="defaultRebootSetting" value="warm-reboot" />
+ <span class="control__indicator control__indicator-on"></span>
+ </label>
+ <label class="control-radio">Immediate - Server reboots without OS shutting down; may cause data corruption
+ <input type="radio" name="radioReboot"
+ ng-model="defaultRebootSetting" value="cold-reboot" />
+ <span class="control__indicator control__indicator-on"></span>
+ </label>
+ </fieldset>
+ <div>
+ <button class="btn-primary" ng-click="rebootConfirmModal()" type="submit" ng-disabled="dataService.server_unreachable">
+ Reboot
</button>
- <p class="inline">Attempts to power on the server</p>
+ </div>
+ </form>
+ </div>
+ <!-- Shutdown Field Row -->
+ <div class="shutdown__operations">
+ <form id="shutdown-form" name="shutdownForm" class="shutdown-form">
+ <fieldset>
+ <legend class="boot-operations">Shutdown server</legend>
+ <label class="control-radio">Orderly - OS shuts down, then server shuts down
+ <input type="radio" name="radioShutdown" ng-model="defaultShutdownSetting"
+ value='warm-shutdown' />
+ <span class="control__indicator control__indicator-on"></span>
+ </label>
+ <label class="control-radio">Immediate - Server shuts down without OS shutting down; may cause data corruption
+ <input type="radio" name="radioShutdown" ng-model="defaultShutdownSetting"
+ value='cold-shutdown' />
+ <span class="control__indicator control__indicator-on"></span>
+ </label>
+ </fieldset>
+ <div>
+ <button class="btn-primary" ng-click="shutdownConfirmModal()" type="submit" ng-disabled="dataService.server_unreachable">
+ Shut down
+ </button>
</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>
+ </form>
+ </div>
</div>
-</div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/app/server-control/controllers/power-operations-controller.js b/app/server-control/controllers/power-operations-controller.js
index 986ac3b..89376c6 100644
--- a/app/server-control/controllers/power-operations-controller.js
+++ b/app/server-control/controllers/power-operations-controller.js
@@ -11,18 +11,20 @@
angular.module('app.serverControl').controller('powerOperationsController', [
'$scope', 'APIUtils', 'dataService', 'Constants', '$interval', '$q',
- 'toastService',
+ 'toastService', '$uibModal',
function(
- $scope, APIUtils, dataService, Constants, $interval, $q, toastService) {
+ $scope, APIUtils, dataService, Constants, $interval, $q, toastService,
+ $uibModal) {
$scope.dataService = dataService;
- // Is a || of the other 4 "confirm" variables to ensure only
- // one confirm is shown at a time.
- $scope.confirm = false;
- $scope.confirmWarmReboot = false;
- $scope.confirmColdReboot = false;
- $scope.confirmOrderlyShutdown = false;
- $scope.confirmImmediateShutdown = false;
$scope.loading = true;
+ $scope.oneTimeBootEnabled = false;
+ $scope.bootOverrideError = false;
+ $scope.bootSources = [];
+ $scope.boot = {};
+ $scope.defaultRebootSetting = 'warm-reboot';
+ $scope.defaultShutdownSetting = 'warm-shutdown';
+
+ $scope.activeModal;
// When a power operation is in progress, set to true,
// when a power operation completes (success/fail) set to false.
@@ -30,6 +32,11 @@
// in markup.
$scope.operationPending = false;
+ const modalTemplate = require('./power-operations-modal.html');
+
+ const powerOperations =
+ {WARM_REBOOT: 0, COLD_REBOOT: 1, WARM_SHUTDOWN: 2, COLD_SHUTDOWN: 3};
+
/**
* Checks the host status provided by the dataService using an
* interval timer
@@ -39,7 +46,7 @@
* @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 =
+ const checkHostStatus =
(statusType, timeout = 300000, error = 'Time out.') => {
const deferred = $q.defer();
const start = new Date();
@@ -58,54 +65,11 @@
return deferred.promise;
};
- APIUtils.getLastPowerTime()
- .then(
- function(data) {
- if (data.data == 0) {
- $scope.powerTime = 'not available';
- } else {
- $scope.powerTime = data.data;
- }
- },
- function(error) {
- console.log(JSON.stringify(error));
- })
- .finally(function() {
- $scope.loading = false;
- });
-
- $scope.toggleState = function() {
- dataService.server_state =
- (dataService.server_state == 'Running') ? 'Off' : 'Running';
- };
-
- /**
- * Initiate Power on
- */
- $scope.powerOn = () => {
- $scope.operationPending = true;
- dataService.setUnreachableState();
- APIUtils.hostPowerOn()
- .then(() => {
- // Check for on state
- return checkHostStatus(
- Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
- Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
- })
- .catch((error) => {
- console.log(error);
- toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
- })
- .finally(() => {
- $scope.operationPending = false;
- })
- };
-
/**
* Initiate Orderly reboot
* Attempts to stop all software
*/
- $scope.warmReboot = () => {
+ const warmReboot = () => {
$scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.hostReboot()
@@ -121,30 +85,21 @@
Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
})
- .catch((error) => {
+ .catch(error => {
console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
})
.finally(() => {
$scope.operationPending = false;
- })
- };
-
- $scope.warmRebootConfirm = function() {
- if ($scope.confirm) {
- // If another "confirm" is already shown return
- return;
- }
- $scope.confirm = true;
- $scope.confirmWarmReboot = true;
+ });
};
/**
* Initiate Immediate reboot
* Does not attempt to stop all software
*/
- $scope.coldReboot = () => {
+ const coldReboot = () => {
$scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.chassisPowerOff()
@@ -164,29 +119,21 @@
Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
})
- .catch((error) => {
+ .catch(error => {
console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.COLD_REBOOT_FAILED);
})
.finally(() => {
$scope.operationPending = false;
- })
- };
-
- $scope.coldRebootConfirm = function() {
- if ($scope.confirm) {
- return;
- }
- $scope.confirm = true;
- $scope.confirmColdReboot = true;
+ });
};
/**
* Initiate Orderly shutdown
* Attempts to stop all software
*/
- $scope.orderlyShutdown = () => {
+ const orderlyShutdown = () => {
$scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.hostPowerOff()
@@ -196,29 +143,21 @@
Constants.HOST_STATE_TEXT.off, Constants.TIMEOUT.HOST_OFF,
Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT);
})
- .catch((error) => {
+ .catch(error => {
console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.ORDERLY_SHUTDOWN_FAILED);
})
.finally(() => {
$scope.operationPending = false;
- })
- };
-
- $scope.orderlyShutdownConfirm = function() {
- if ($scope.confirm) {
- return;
- }
- $scope.confirm = true;
- $scope.confirmOrderlyShutdown = true;
+ });
};
/**
* Initiate Immediate shutdown
* Does not attempt to stop all software
*/
- $scope.immediateShutdown = () => {
+ const immediateShutdown = () => {
$scope.operationPending = true;
dataService.setUnreachableState();
APIUtils.chassisPowerOff()
@@ -232,23 +171,236 @@
.then(() => {
dataService.setPowerOffState();
})
- .catch((error) => {
+ .catch(error => {
console.log(error);
toastService.error(
Constants.MESSAGES.POWER_OP.IMMEDIATE_SHUTDOWN_FAILED);
})
.finally(() => {
$scope.operationPending = false;
- })
+ });
};
- $scope.immediateShutdownConfirm = function() {
- if ($scope.confirm) {
- return;
- }
- $scope.confirm = true;
- $scope.confirmImmediateShutdown = true;
+ /**
+ * Initiate Power on
+ */
+ $scope.powerOn = () => {
+ $scope.operationPending = true;
+ dataService.setUnreachableState();
+ APIUtils.hostPowerOn()
+ .then(() => {
+ // Check for on state
+ return checkHostStatus(
+ Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON,
+ Constants.MESSAGES.POLL.HOST_ON_TIMEOUT);
+ })
+ .catch(error => {
+ console.log(error);
+ toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
+ })
+ .finally(() => {
+ $scope.operationPending = false;
+ });
};
+
+ /*
+ * Power operations modal
+ */
+ const initPowerOperation = function(powerOperation) {
+ switch (powerOperation) {
+ case powerOperations.WARM_REBOOT:
+ warmReboot();
+ break;
+ case powerOperations.COLD_REBOOT:
+ coldReboot();
+ break;
+ case powerOperations.WARM_SHUTDOWN:
+ orderlyShutdown();
+ break;
+ case powerOperations.COLD_SHUTDOWN:
+ immediateShutdown();
+ break;
+ default:
+ // do nothing
+ }
+ };
+
+ const powerOperationModal = function() {
+ $uibModal
+ .open({
+ template: modalTemplate,
+ windowTopClass: 'uib-modal',
+ scope: $scope,
+ ariaLabelledBy: 'modal-operation'
+ })
+ .result
+ .then(function(activeModal) {
+ initPowerOperation(activeModal);
+ })
+ .finally(function() {
+ $scope.activeModal = undefined;
+ });
+ };
+
+ $scope.rebootConfirmModal = function() {
+ if ($scope.rebootForm.radioReboot.$modelValue == 'warm-reboot') {
+ $scope.activeModal = powerOperations.WARM_REBOOT;
+ } else if ($scope.rebootForm.radioReboot.$modelValue == 'cold-reboot') {
+ $scope.activeModal = powerOperations.COLD_REBOOT;
+ }
+ powerOperationModal();
+ };
+
+ $scope.shutdownConfirmModal = function() {
+ if ($scope.shutdownForm.radioShutdown.$modelValue == 'warm-shutdown') {
+ $scope.activeModal = powerOperations.WARM_SHUTDOWN;
+ } else if (
+ $scope.shutdownForm.radioShutdown.$modelValue == 'cold-shutdown') {
+ $scope.activeModal = powerOperations.COLD_SHUTDOWN;
+ }
+ powerOperationModal();
+ };
+
+ $scope.resetForm = function() {
+ $scope.boot = angular.copy($scope.originalBoot);
+ $scope.TPMToggle = angular.copy($scope.originalTPMToggle);
+ };
+
+ /*
+ * Get boot settings
+ */
+ const loadBootSettings = function() {
+ APIUtils.getBootOptions()
+ .then(function(response) {
+ const boot = response.Boot;
+ const BootSourceOverrideEnabled =
+ boot['BootSourceOverrideEnabled'];
+ const BootSourceOverrideTarget = boot['BootSourceOverrideTarget'];
+ const bootSourceValues =
+ boot['BootSourceOverrideTarget@Redfish.AllowableValues'];
+
+ $scope.bootSources = bootSourceValues;
+
+ $scope.boot = {
+ BootSourceOverrideEnabled: BootSourceOverrideEnabled,
+ BootSourceOverrideTarget: BootSourceOverrideTarget
+ };
+
+ if (BootSourceOverrideEnabled == 'Once') {
+ $scope.boot.oneTimeBootEnabled = true;
+ }
+
+ $scope.originalBoot = angular.copy($scope.boot);
+ })
+ .catch(function(error) {
+ $scope.bootOverrideError = true;
+ toastService.error('Unable to get boot override values.');
+ console.log(
+ 'Error loading boot settings:', JSON.stringify(error));
+ });
+ $scope.loading = false;
+ };
+
+ /*
+ * Get TPM status
+ */
+ const loadTPMStatus = function() {
+ APIUtils.getTPMStatus()
+ .then(function(response) {
+ $scope.TPMToggle = response.data;
+ $scope.originalTPMToggle = angular.copy($scope.TPMToggle);
+ })
+ .catch(function(error) {
+ toastService.error('Unable to get TPM policy status.');
+ console.log('Error loading TPM status', JSON.stringify(error));
+ });
+ $scope.loading = false;
+ };
+
+ /*
+ * Save boot settings
+ */
+ $scope.saveBootSettings = function() {
+ if ($scope.hostBootSettings.bootSelected.$dirty ||
+ $scope.hostBootSettings.oneTime.$dirty) {
+ const data = {};
+ data.Boot = {};
+
+ let isOneTimeBoot = $scope.boot.oneTimeBootEnabled;
+ let overrideTarget = $scope.boot.BootSourceOverrideTarget || 'None';
+ let overrideEnabled = 'Disabled';
+
+ if (isOneTimeBoot) {
+ overrideEnabled = 'Once';
+ } else if (overrideTarget !== 'None') {
+ overrideEnabled = 'Continuous';
+ }
+
+ data.Boot.BootSourceOverrideEnabled = overrideEnabled;
+ data.Boot.BootSourceOverrideTarget = overrideTarget;
+
+ APIUtils.saveBootSettings(data).then(
+ function(response) {
+ $scope.originalBoot = angular.copy($scope.boot);
+ toastService.success('Successfully updated boot settings.');
+ },
+ function(error) {
+ toastService.error('Unable to save boot settings.');
+ console.log(JSON.stringify(error));
+ });
+ }
+ };
+
+ /*
+ * Save TPM required policy
+ */
+ $scope.saveTPMPolicy = function() {
+ if ($scope.hostBootSettings.toggle.$dirty) {
+ const tpmEnabled = $scope.TPMToggle.TPMEnable;
+
+ if (tpmEnabled === undefined) {
+ return;
+ }
+
+ APIUtils.saveTPMEnable(tpmEnabled)
+ .then(
+ function(response) {
+ $scope.originalTPMToggle = angular.copy($scope.TPMToggle);
+ toastService.success(
+ 'Sucessfully updated TPM required policy.');
+ },
+ function(error) {
+ toastService.error('Unable to update TPM required policy.');
+ console.log(JSON.stringify(error));
+ });
+ }
+ };
+
+ /*
+ * Emitted every time the view is reloaded
+ */
+ $scope.$on('$viewContentLoaded', function() {
+ APIUtils.getLastPowerTime()
+ .then(
+ function(data) {
+ if (data.data == 0) {
+ $scope.powerTime = 'not available';
+ } else {
+ $scope.powerTime = data.data;
+ }
+ },
+ function(error) {
+ toastService.error(
+ 'Unable to get last power operation time.');
+ console.log(JSON.stringify(error));
+ })
+ .finally(function() {
+ $scope.loading = false;
+ });
+
+ loadBootSettings();
+ loadTPMStatus();
+ });
}
]);
})(angular);
diff --git a/app/server-control/controllers/power-operations-modal.html b/app/server-control/controllers/power-operations-modal.html
new file mode 100644
index 0000000..3524b4c
--- /dev/null
+++ b/app/server-control/controllers/power-operations-modal.html
@@ -0,0 +1,48 @@
+<!-- Shutdown and Reboot modal -->
+<div class="uib-modal__content">
+ <button
+ aria-label="Close"
+ type="button"
+ class="btn btn--close float-right"
+ ng-click="$dismiss()"
+ >
+ <icon file="icon-close.svg"></icon>
+ </button>
+ <div class="modal-header" id="modal-operation">
+ <h3>
+ <div class="icon__warning inline" aria-label="Warning"></div>
+ {{
+ activeModal === 2 || activeModal === 3
+ ? "Server shutdown will cause outage"
+ : "Server reboot will cause outage"
+ }}
+ </h3>
+ </div>
+ <div class="modal-body">
+ <p ng-if="activeModal === 2 || activeModal === 3">
+ Are you sure you want to
+ {{ activeModal === 2 ? "orderly" : "immediate" }}
+ shutdown?
+ </p>
+ <p ng-if="activeModal === 0 || activeModal === 1">
+ Are you sure you want to
+ {{ activeModal === 0 ? "orderly" : "immediate" }}
+ reboot?
+ </p>
+ </div>
+ <div class="modal-footer">
+ <!-- Power operation confirm buttons -->
+ <button
+ type="submit"
+ class="btn btn-primary"
+ ng-click="$close(activeModal)"
+ >
+ <span ng-if="activeModal === 0 || activeModal === 1">Reboot</span>
+ <span ng-if="activeModal === 2 || activeModal === 3">Shutdown</span>
+ </button>
+ <!-- Cancel modal power confirmation -->
+ <button type="button" class="btn btn-secondary" ng-click="$dismiss()">
+ Cancel
+ </button>
+ </div>
+</div>
diff --git a/app/server-control/styles/power-operations.scss b/app/server-control/styles/power-operations.scss
index fbc5a7f..6a29dfd 100644
--- a/app/server-control/styles/power-operations.scss
+++ b/app/server-control/styles/power-operations.scss
@@ -1,7 +1,6 @@
// Power Operations SCSS
-#power-operations {
-
+.power-operations {
// Power Current status wrapper
.power__current-status {
border-bottom: 1px solid $border-color-01;
@@ -11,9 +10,9 @@
// Power state indicator on/off
.power__state {
font-weight: 700;
- margin-top: -.3em;
+ margin-top: -0.3em;
span:before {
- content: '';
+ content: "";
position: absolute;
@extend .icon__off;
margin-left: -25px;
@@ -24,16 +23,16 @@
.power__indicator-bar {
font-weight: bold;
width: 100%;
- padding: 1em 2em .7em;
+ padding: 1em 2em 0.7em;
margin-bottom: 3em;
background-size: 200% 100%;
- background-image: linear-gradient(to right, darken($background-02,3%) 50%, $accent-02--02 50%);
+ background-image: linear-gradient(to right, darken($background-02, 3%) 50%, $accent-02--02 50%);
background-position: 0;
transition: background-position 2s ease;
overflow: hidden;
- display:flex;
+ display: flex;
justify-content: space-between;
- align-items:center;
+ align-items: center;
p {
padding: 0;
margin: 0;
@@ -43,8 +42,8 @@
background-position: -100%;
.power__state {
span:before {
- content: '';
- @extend .icon__good;
+ content: "";
+ @extend .icon__good;
}
}
}
@@ -53,7 +52,7 @@
color: $primary-dark;
.power__state {
span:before {
- content: '';
+ content: "";
@extend .icon__off;
}
}
@@ -64,7 +63,7 @@
.power__state {
span:before {
color: $status-warn;
- content: '';
+ content: "";
@extend .icon__warning;
}
}
@@ -74,28 +73,92 @@
color: $primary-dark;
.power__state {
span:before {
- content: '';
+ content: "";
@extend .icon__critical;
}
}
}
}
- // Power button options
- .power-option {
- padding: 1.8em 0 1em 0;
- position: relative;
- overflow: hidden;
- min-height: 1px;
- min-width: 100%;
- .btn {
- margin-bottom: .5em;
- margin-right: 1em;
- min-width: 240px;
+ .boot-options,
+ .boot-operations {
+ margin-bottom: 1em;
+ }
+
+ .boot-options {
+ .control-check {
+ padding-top: 4px;
}
- @include mediaQuery(x-small){
- padding: 1.8em 0 1em 0;
+ .boot-checkbox {
+ padding-left: 2em;
+ text-transform: none;
+ font-weight: 400;
+ font-size: 16px;
+ color: $primary-dark;
}
}
+ .boot-options-wrapper {
+ padding: 0 2em 2em 0;
+ }
+
+ .operations-wrapper {
+ .reboot__operations,
+ .shutdown__operations {
+ margin-bottom: 1.5em;
+ .control-radio {
+ padding: 0.3em 1em 0 2em;
+ text-transform: none;
+ font-weight: 400;
+ font-size: 16px;
+ color: $primary-dark;
+ }
+ }
+
+ .alert-warning {
+ border: 1px solid $accent-03--01;
+ padding: 1em;
+ margin-bottom: 1em;
+ display: flex;
+ align-items: center;
+ .pending-icon {
+ padding: 0 1em 0 0;
+ }
+ .alert-pending {
+ margin-top: 0.3em;
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ .boot-settings-form {
+ background-color: $base-02--06;
+ padding: 2em;
+ .boot-form-actions {
+ margin-bottom: 4em;
+ }
+ .btn {
+ display: block;
+ float: right;
+ margin: 0.5em 0 0 1em;
+ }
+ }
+
+ .control-radio .control__indicator-on {
+ width: 20px;
+ height: 20px;
+ top: 3px;
+ left: 3px;
+ }
+
+ .control-radio .control__indicator-on:after {
+ top: 3px;
+ left: 3px;
+ width: 10px;
+ height: 10px;
+ }
+
+ .icon__warning {
+ width: 24px;
+ }
} //end power-operations
diff --git a/app/server-health/styles/log.scss b/app/server-health/styles/log.scss
index 60bbba9..301dc2c 100644
--- a/app/server-health/styles/log.scss
+++ b/app/server-health/styles/log.scss
@@ -312,7 +312,6 @@
}
}
-/*p*/
.event__description {
margin-bottom: 0;
line-height: 1.7;