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