1/** 2 * Controller for power-operations 3 * 4 * @module app/serverControl 5 * @exports powerOperationsController 6 * @name powerOperationsController 7 */ 8 9window.angular && (function(angular) { 10 'use strict'; 11 12 angular.module('app.serverControl').controller('powerOperationsController', [ 13 '$scope', 'APIUtils', 'dataService', 'Constants', '$interval', '$q', 14 'toastService', '$uibModal', 15 function( 16 $scope, APIUtils, dataService, Constants, $interval, $q, toastService, 17 $uibModal) { 18 $scope.dataService = dataService; 19 $scope.loading = true; 20 $scope.oneTimeBootEnabled = false; 21 $scope.bootOverrideError = false; 22 $scope.bootSources = []; 23 $scope.boot = {}; 24 $scope.defaultRebootSetting = 'warm-reboot'; 25 $scope.defaultShutdownSetting = 'warm-shutdown'; 26 27 $scope.activeModal; 28 29 // When a power operation is in progress, set to true, 30 // when a power operation completes (success/fail) set to false. 31 // This property is used to show/hide the 'in progress' message 32 // in markup. 33 $scope.operationPending = false; 34 35 const modalTemplate = require('./power-operations-modal.html'); 36 37 const powerOperations = 38 {WARM_REBOOT: 0, COLD_REBOOT: 1, WARM_SHUTDOWN: 2, COLD_SHUTDOWN: 3}; 39 40 /** 41 * Checks the host status provided by the dataService using an 42 * interval timer 43 * @param {string} statusType : host status type to check for 44 * @param {number} timeout : timeout limit, defaults to 5 minutes 45 * @param {string} error : error message, defaults to 'Time out' 46 * @returns {Promise} : returns a deferred promise that will be fulfilled 47 * if the status is met or be rejected if the timeout is reached 48 */ 49 const checkHostStatus = 50 (statusType, timeout = 300000, error = 'Time out.') => { 51 const deferred = $q.defer(); 52 const start = new Date(); 53 const checkHostStatusInverval = $interval(() => { 54 let now = new Date(); 55 let timePassed = now.getTime() - start.getTime(); 56 if (timePassed > timeout) { 57 deferred.reject(error); 58 $interval.cancel(checkHostStatusInverval); 59 } 60 if (dataService.server_state === statusType) { 61 deferred.resolve(); 62 $interval.cancel(checkHostStatusInverval); 63 } 64 }, Constants.POLL_INTERVALS.POWER_OP); 65 return deferred.promise; 66 }; 67 68 /** 69 * Initiate Orderly reboot 70 * Attempts to stop all software 71 */ 72 const warmReboot = () => { 73 $scope.operationPending = true; 74 dataService.setUnreachableState(); 75 APIUtils.hostReboot() 76 .then(() => { 77 // Check for off state 78 return checkHostStatus( 79 Constants.HOST_STATE_TEXT.off, Constants.TIMEOUT.HOST_OFF, 80 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT); 81 }) 82 .then(() => { 83 // Check for on state 84 return checkHostStatus( 85 Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON, 86 Constants.MESSAGES.POLL.HOST_ON_TIMEOUT); 87 }) 88 .catch(error => { 89 console.log(error); 90 toastService.error( 91 Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED); 92 }) 93 .finally(() => { 94 $scope.operationPending = false; 95 }); 96 }; 97 98 /** 99 * Initiate Immediate reboot 100 * Does not attempt to stop all software 101 */ 102 const coldReboot = () => { 103 $scope.operationPending = true; 104 dataService.setUnreachableState(); 105 APIUtils.chassisPowerOff() 106 .then(() => { 107 // Check for off state 108 return checkHostStatus( 109 Constants.HOST_STATE_TEXT.off, 110 Constants.TIMEOUT.HOST_OFF_IMMEDIATE, 111 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT); 112 }) 113 .then(() => { 114 return APIUtils.hostPowerOn(); 115 }) 116 .then(() => { 117 // Check for on state 118 return checkHostStatus( 119 Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON, 120 Constants.MESSAGES.POLL.HOST_ON_TIMEOUT); 121 }) 122 .catch(error => { 123 console.log(error); 124 toastService.error( 125 Constants.MESSAGES.POWER_OP.COLD_REBOOT_FAILED); 126 }) 127 .finally(() => { 128 $scope.operationPending = false; 129 }); 130 }; 131 132 /** 133 * Initiate Orderly shutdown 134 * Attempts to stop all software 135 */ 136 const orderlyShutdown = () => { 137 $scope.operationPending = true; 138 dataService.setUnreachableState(); 139 APIUtils.hostPowerOff() 140 .then(() => { 141 // Check for off state 142 return checkHostStatus( 143 Constants.HOST_STATE_TEXT.off, Constants.TIMEOUT.HOST_OFF, 144 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT); 145 }) 146 .catch(error => { 147 console.log(error); 148 toastService.error( 149 Constants.MESSAGES.POWER_OP.ORDERLY_SHUTDOWN_FAILED); 150 }) 151 .finally(() => { 152 $scope.operationPending = false; 153 }); 154 }; 155 156 /** 157 * Initiate Immediate shutdown 158 * Does not attempt to stop all software 159 */ 160 const immediateShutdown = () => { 161 $scope.operationPending = true; 162 dataService.setUnreachableState(); 163 APIUtils.chassisPowerOff() 164 .then(() => { 165 // Check for off state 166 return checkHostStatus( 167 Constants.HOST_STATE_TEXT.off, 168 Constants.TIMEOUT.HOST_OFF_IMMEDIATE, 169 Constants.MESSAGES.POLL.HOST_OFF_TIMEOUT); 170 }) 171 .then(() => { 172 dataService.setPowerOffState(); 173 }) 174 .catch(error => { 175 console.log(error); 176 toastService.error( 177 Constants.MESSAGES.POWER_OP.IMMEDIATE_SHUTDOWN_FAILED); 178 }) 179 .finally(() => { 180 $scope.operationPending = false; 181 }); 182 }; 183 184 /** 185 * Initiate Power on 186 */ 187 $scope.powerOn = () => { 188 $scope.operationPending = true; 189 dataService.setUnreachableState(); 190 APIUtils.hostPowerOn() 191 .then(() => { 192 // Check for on state 193 return checkHostStatus( 194 Constants.HOST_STATE_TEXT.on, Constants.TIMEOUT.HOST_ON, 195 Constants.MESSAGES.POLL.HOST_ON_TIMEOUT); 196 }) 197 .catch(error => { 198 console.log(error); 199 toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED); 200 }) 201 .finally(() => { 202 $scope.operationPending = false; 203 }); 204 }; 205 206 /* 207 * Power operations modal 208 */ 209 const initPowerOperation = function(powerOperation) { 210 switch (powerOperation) { 211 case powerOperations.WARM_REBOOT: 212 warmReboot(); 213 break; 214 case powerOperations.COLD_REBOOT: 215 coldReboot(); 216 break; 217 case powerOperations.WARM_SHUTDOWN: 218 orderlyShutdown(); 219 break; 220 case powerOperations.COLD_SHUTDOWN: 221 immediateShutdown(); 222 break; 223 default: 224 // do nothing 225 } 226 }; 227 228 const powerOperationModal = function() { 229 $uibModal 230 .open({ 231 template: modalTemplate, 232 windowTopClass: 'uib-modal', 233 scope: $scope, 234 ariaLabelledBy: 'modal-operation' 235 }) 236 .result 237 .then(function(activeModal) { 238 initPowerOperation(activeModal); 239 }) 240 .finally(function() { 241 $scope.activeModal = undefined; 242 }); 243 }; 244 245 $scope.rebootConfirmModal = function() { 246 if ($scope.rebootForm.radioReboot.$modelValue == 'warm-reboot') { 247 $scope.activeModal = powerOperations.WARM_REBOOT; 248 } else if ($scope.rebootForm.radioReboot.$modelValue == 'cold-reboot') { 249 $scope.activeModal = powerOperations.COLD_REBOOT; 250 } 251 powerOperationModal(); 252 }; 253 254 $scope.shutdownConfirmModal = function() { 255 if ($scope.shutdownForm.radioShutdown.$modelValue == 'warm-shutdown') { 256 $scope.activeModal = powerOperations.WARM_SHUTDOWN; 257 } else if ( 258 $scope.shutdownForm.radioShutdown.$modelValue == 'cold-shutdown') { 259 $scope.activeModal = powerOperations.COLD_SHUTDOWN; 260 } 261 powerOperationModal(); 262 }; 263 264 $scope.resetForm = function() { 265 $scope.boot = angular.copy($scope.originalBoot); 266 $scope.TPMToggle = angular.copy($scope.originalTPMToggle); 267 }; 268 269 /* 270 * Get boot settings 271 */ 272 const loadBootSettings = function() { 273 APIUtils.getBootOptions() 274 .then(function(response) { 275 const boot = response.Boot; 276 const BootSourceOverrideEnabled = 277 boot['BootSourceOverrideEnabled']; 278 const BootSourceOverrideTarget = boot['BootSourceOverrideTarget']; 279 const bootSourceValues = 280 boot['BootSourceOverrideTarget@Redfish.AllowableValues']; 281 282 $scope.bootSources = bootSourceValues; 283 284 $scope.boot = { 285 BootSourceOverrideEnabled: BootSourceOverrideEnabled, 286 BootSourceOverrideTarget: BootSourceOverrideTarget 287 }; 288 289 if (BootSourceOverrideEnabled == 'Once') { 290 $scope.boot.oneTimeBootEnabled = true; 291 } 292 293 $scope.originalBoot = angular.copy($scope.boot); 294 }) 295 .catch(function(error) { 296 $scope.bootOverrideError = true; 297 toastService.error('Unable to get boot override values.'); 298 console.log( 299 'Error loading boot settings:', JSON.stringify(error)); 300 }); 301 $scope.loading = false; 302 }; 303 304 /* 305 * Get TPM status 306 */ 307 const loadTPMStatus = function() { 308 APIUtils.getTPMStatus() 309 .then(function(response) { 310 $scope.TPMToggle = response.data; 311 $scope.originalTPMToggle = angular.copy($scope.TPMToggle); 312 }) 313 .catch(function(error) { 314 toastService.error('Unable to get TPM policy status.'); 315 console.log('Error loading TPM status', JSON.stringify(error)); 316 }); 317 $scope.loading = false; 318 }; 319 320 /* 321 * Save boot settings 322 */ 323 $scope.saveBootSettings = function() { 324 if ($scope.hostBootSettings.bootSelected.$dirty || 325 $scope.hostBootSettings.oneTime.$dirty) { 326 const data = {}; 327 data.Boot = {}; 328 329 let isOneTimeBoot = $scope.boot.oneTimeBootEnabled; 330 let overrideTarget = $scope.boot.BootSourceOverrideTarget || 'None'; 331 let overrideEnabled = 'Disabled'; 332 333 if (isOneTimeBoot) { 334 overrideEnabled = 'Once'; 335 } else if (overrideTarget !== 'None') { 336 overrideEnabled = 'Continuous'; 337 } 338 339 data.Boot.BootSourceOverrideEnabled = overrideEnabled; 340 data.Boot.BootSourceOverrideTarget = overrideTarget; 341 342 APIUtils.saveBootSettings(data).then( 343 function(response) { 344 $scope.originalBoot = angular.copy($scope.boot); 345 toastService.success('Successfully updated boot settings.'); 346 }, 347 function(error) { 348 toastService.error('Unable to save boot settings.'); 349 console.log(JSON.stringify(error)); 350 }); 351 } 352 }; 353 354 /* 355 * Save TPM required policy 356 */ 357 $scope.saveTPMPolicy = function() { 358 if ($scope.hostBootSettings.toggle.$dirty) { 359 const tpmEnabled = $scope.TPMToggle.TPMEnable; 360 361 if (tpmEnabled === undefined) { 362 return; 363 } 364 365 APIUtils.saveTPMEnable(tpmEnabled) 366 .then( 367 function(response) { 368 $scope.originalTPMToggle = angular.copy($scope.TPMToggle); 369 toastService.success( 370 'Sucessfully updated TPM required policy.'); 371 }, 372 function(error) { 373 toastService.error('Unable to update TPM required policy.'); 374 console.log(JSON.stringify(error)); 375 }); 376 } 377 }; 378 379 /** 380 * Callback when boot setting option changed 381 */ 382 $scope.onChangeBootSetting = function() { 383 const bootSetting = $scope.hostBootSettings.bootSelected.$viewValue; 384 if (bootSetting === 'None') { 385 $scope.boot.oneTimeBootEnabled = false; 386 } 387 }; 388 389 /* 390 * Emitted every time the view is reloaded 391 */ 392 $scope.$on('$viewContentLoaded', function() { 393 APIUtils.getLastPowerTime() 394 .then( 395 function(data) { 396 if (data.data == 0) { 397 $scope.powerTime = 'not available'; 398 } else { 399 $scope.powerTime = data.data; 400 } 401 }, 402 function(error) { 403 toastService.error( 404 'Unable to get last power operation time.'); 405 console.log(JSON.stringify(error)); 406 }) 407 .finally(function() { 408 $scope.loading = false; 409 }); 410 411 loadBootSettings(); 412 loadTPMStatus(); 413 }); 414 } 415 ]); 416})(angular); 417