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/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);