diff options
| author | Dixsie Wolmers <dixsiew@gmail.com> | 2019-06-21 13:48:06 -0500 |
|---|---|---|
| committer | Gunnar Mills <gmills@us.ibm.com> | 2019-08-21 21:14:16 +0000 |
| commit | e368108fc4fb3777ff02089f81b551d9735b393f (patch) | |
| tree | ac27cc621cca640442b09d5489157eb8304e6a05 /app/server-control | |
| parent | fb79e54c55af0b516d35c94c702213b7549acafe (diff) | |
| download | phosphor-webui-e368108fc4fb3777ff02089f81b551d9735b393f.tar.gz phosphor-webui-e368108fc4fb3777ff02089f81b551d9735b393f.zip | |
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>
Diffstat (limited to 'app/server-control')
4 files changed, 548 insertions, 171 deletions
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 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 - </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="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 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> + <!-- 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 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> + <!-- 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> + </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> - </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 @@ window.angular && (function(angular) { 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 @@ window.angular && (function(angular) { // 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 @@ window.angular && (function(angular) { * @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 @@ window.angular && (function(angular) { 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 @@ window.angular && (function(angular) { 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 @@ window.angular && (function(angular) { 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 @@ window.angular && (function(angular) { 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 @@ window.angular && (function(angular) { .then(() => { dataService.setPowerOffState(); }) - .catch((error) => { + .catch(error => { console.log(error); toastService.error( Constants.MESSAGES.POWER_OP.IMMEDIATE_SHUTDOWN_FAILED); }) .finally(() => { $scope.operationPending = false; + }); + }; + + /** + * 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.immediateShutdownConfirm = function() { - if ($scope.confirm) { - return; + $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; } - $scope.confirm = true; - $scope.confirmImmediateShutdown = true; + 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; + } + .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; + } } - @include mediaQuery(x-small){ - padding: 1.8em 0 1em 0; + + .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 |

