Update the GUI after an image activated

When activating an image the firmware page does not refresh
after the image is activated and show the image as "Active".
To get the image to show as "Active", a user must refresh the
page.

The activating image code now waits until the image is activated
and the REST call returns "Active" or "Failed" and then refreshes
the page.

Resolves openbmc/openbmc#2966

Tested: Activated an image and verfied it refreshed after
"Active".
Change-Id: Ic68e3c9e6cb5c2ea4f5d66f48fd09252c4807f26
Signed-off-by: Gunnar Mills <gmills@us.ibm.com>
diff --git a/app/common/services/api-utils.js b/app/common/services/api-utils.js
index 980f1a2..c20b2ca 100644
--- a/app/common/services/api-utils.js
+++ b/app/common/services/api-utils.js
@@ -634,6 +634,19 @@
                   console.log(error);
                 });
               },
+              getActivation: function(imageId){
+                return $http({
+                  method: 'GET',
+                  url: DataService.getHost() + "/xyz/openbmc_project/software/" + imageId + "/attr/Activation",
+                  headers: {
+                    'Accept': 'application/json',
+                    'Content-Type': 'application/json'
+                  },
+                  withCredentials: true
+                }).then(function(response){
+                  return response.data;
+                });
+              },
               getFirmwares: function(){
                 var deferred = $q.defer();
                 $http({
diff --git a/app/common/services/constants.js b/app/common/services/constants.js
index 57e2e19..af0965d 100644
--- a/app/common/services/constants.js
+++ b/app/common/services/constants.js
@@ -110,6 +110,12 @@
                   ACTIVATE_FIRMWARE: 'xyz.openbmc_project.Software.Activation.RequestedActivations.Active',
                   FUNCTIONAL_OBJPATH: '/xyz/openbmc_project/software/functional'
                 },
+               POLL_INTERVALS: {
+                  ACTIVATION: 5000
+                },
+               TIMEOUT: {
+                  ACTIVATION: 1000 * 60 * 10, // 10 mins
+                },
                 MESSAGES: {
                   SENSOR: {
                     NO_SENSOR_DATA: 'There are no sensors found.',
diff --git a/app/configuration/controllers/firmware-controller.js b/app/configuration/controllers/firmware-controller.js
index b9a87b7..8fe0e83 100644
--- a/app/configuration/controllers/firmware-controller.js
+++ b/app/configuration/controllers/firmware-controller.js
@@ -20,7 +20,9 @@
                 '$location',
                 '$anchorScroll',
                 'Constants',
-                function ($scope, $window, APIUtils, dataService, $location, $anchorScroll, Constants) {
+                '$interval',
+                '$q',
+                function ($scope, $window, APIUtils, dataService, $location, $anchorScroll, Constants, $interval, $q) {
                     $scope.dataService = dataService;
 
                     //Scroll to target anchor
@@ -47,6 +49,8 @@
                     $scope.file_empty = true;
                     $scope.uploading = false;
 
+                    var pollActivationTimer = undefined;
+
                     $scope.error = {
                         modal_title: "",
                         title: "",
@@ -66,22 +70,58 @@
                         $scope.display_error = true;
                     }
 
-                    $scope.activateConfirmed = function(){
-                        $scope.uploading = true;
-                        APIUtils.activateImage($scope.activate_image_id).then(function(response){
-                            $scope.uploading = false;
-                            if(response.status == 'error'){
-                                $scope.displayError({
-                                    modal_title: response.data.description,
-                                    title: response.data.description,
-                                    desc: response.data.exception,
-                                    type: 'Error'
-                                });
-                            }else{
-                                $scope.loadFirmwares();
-                            }
+                    function waitForActive(imageId){
+                      var deferred = $q.defer();
+                      var startTime = new Date();
+                      pollActivationTimer = $interval(function(){
+                        APIUtils.getActivation(imageId).then(function(state){
+                          //@TODO: display an error message if image "Failed"
+                          if(((/\.Active$/).test(state.data)) || ((/\.Failed$/).test(state.data))){
+                            $interval.cancel(pollActivationTimer);
+                            pollActivationTimer = undefined;
+                            deferred.resolve(state);
+                          }
+                        }, function(error){
+                          $interval.cancel(pollActivationTimer);
+                          pollActivationTimer = undefined;
+                          console.log(error);
+                          deferred.reject(error);
                         });
-                        $scope.activate_confirm = false;
+                        var now = new Date();
+                        if((now.getTime() - startTime.getTime()) >= Constants.TIMEOUT.ACTIVATION){
+                          $interval.cancel(pollActivationTimer);
+                          pollActivationTimer = undefined;
+                          console.log("Time out activating image, " + imageId);
+                          deferred.reject("Time out. Image did not activate in allotted time.");
+                        }
+                      }, Constants.POLL_INTERVALS.ACTIVATION);
+                      return deferred.promise;
+                    }
+
+                    $scope.activateConfirmed = function(){
+                        APIUtils.activateImage($scope.activate_image_id).then(function(state){
+                          $scope.loadFirmwares();
+                          return state;
+                        }, function(error){
+                          $scope.displayError({
+                            modal_title: 'Error during activation call',
+                            title: 'Error during activation call',
+                            desc: JSON.stringify(error.data),
+                            type: 'Error'
+                          });
+                        }).then(function(activationState){
+                          waitForActive($scope.activate_image_id).then(function(state){
+                            $scope.loadFirmwares();
+                        }, function(error){
+                          $scope.displayError({
+                            modal_title: 'Error during image activation',
+                            title: 'Error during image activation',
+                            desc: JSON.stringify(error.data),
+                            type: 'Error'
+                          });
+                        });
+                      });
+                      $scope.activate_confirm = false;
                     }
 
                     $scope.upload = function(){
@@ -188,7 +228,6 @@
                         $scope.file_empty = false;
                     }
 
-                    $scope.uploading = false;
                     $scope.filters = {
                         bmc: {
                             imageType: 'BMC'