1/**
2 * Controller for firmware
3 *
4 * @module app/configuration
5 * @exports firmwareController
6 * @name firmwareController
7 */
8
9window.angular && (function (angular) {
10    'use strict';
11
12    angular
13        .module('app.configuration')
14        .controller('firmwareController', [
15                '$scope',
16                '$window',
17                'APIUtils',
18                'dataService',
19                '$location',
20                '$anchorScroll',
21                'Constants',
22                '$interval',
23                '$q',
24                '$timeout',
25                function ($scope, $window, APIUtils, dataService, $location, $anchorScroll, Constants, $interval, $q, $timeout) {
26                    $scope.dataService = dataService;
27
28                    //Scroll to target anchor
29                    $scope.gotoAnchor = function () {
30                        $location.hash('upload');
31                        $anchorScroll();
32                    };
33
34                    $scope.firmwares = [];
35                    $scope.bmcActiveVersion = "";
36                    $scope.hostActiveVersion = "";
37                    $scope.display_error = false;
38                    $scope.activate_confirm = false;
39                    $scope.delete_image_id = "";
40                    $scope.delete_image_version = "";
41                    $scope.activate_image_id = "";
42                    $scope.activate_image_version = "";
43                    $scope.activate_image_type = "";
44                    $scope.priority_image_id = "";
45                    $scope.priority_image_version = "";
46                    $scope.priority_from = -1;
47                    $scope.priority_to = -1;
48                    $scope.confirm_priority = false;
49                    $scope.file_empty = true;
50                    $scope.uploading = false;
51                    $scope.upload_success = false;
52                    $scope.activate = { reboot: true };
53                    $scope.download_error_msg = "";
54                    $scope.download_success = false;
55
56                    var pollActivationTimer = undefined;
57                    var pollDownloadTimer = undefined;
58
59                    $scope.error = {
60                        modal_title: "",
61                        title: "",
62                        desc: "",
63                        type: "warning"
64                    };
65
66                    $scope.activateImage = function(imageId, imageVersion, imageType){
67                        $scope.activate_image_id = imageId;
68                        $scope.activate_image_version = imageVersion;
69                        $scope.activate_image_type = imageType;
70                        $scope.activate_confirm = true;
71                    }
72
73                    $scope.displayError = function(data){
74                        $scope.error = data;
75                        $scope.display_error = true;
76                    }
77
78                    function waitForActive(imageId){
79                      var deferred = $q.defer();
80                      var startTime = new Date();
81                      pollActivationTimer = $interval(function(){
82                        APIUtils.getActivation(imageId).then(function(state){
83                          //@TODO: display an error message if image "Failed"
84                          if(((/\.Active$/).test(state.data)) || ((/\.Failed$/).test(state.data))){
85                            $interval.cancel(pollActivationTimer);
86                            pollActivationTimer = undefined;
87                            deferred.resolve(state);
88                          }
89                        }, function(error){
90                          $interval.cancel(pollActivationTimer);
91                          pollActivationTimer = undefined;
92                          console.log(error);
93                          deferred.reject(error);
94                        });
95                        var now = new Date();
96                        if((now.getTime() - startTime.getTime()) >= Constants.TIMEOUT.ACTIVATION){
97                          $interval.cancel(pollActivationTimer);
98                          pollActivationTimer = undefined;
99                          console.log("Time out activating image, " + imageId);
100                          deferred.reject("Time out. Image did not activate in allotted time.");
101                        }
102                      }, Constants.POLL_INTERVALS.ACTIVATION);
103                      return deferred.promise;
104                    }
105
106                    $scope.activateConfirmed = function(){
107                        APIUtils.activateImage($scope.activate_image_id).then(function(state){
108                          $scope.loadFirmwares();
109                          return state;
110                        }, function(error){
111                          $scope.displayError({
112                            modal_title: 'Error during activation call',
113                            title: 'Error during activation call',
114                            desc: JSON.stringify(error.data),
115                            type: 'Error'
116                          });
117                        }).then(function(activationState){
118                          waitForActive($scope.activate_image_id).then(function(state){
119                            $scope.loadFirmwares();
120                        }, function(error){
121                          $scope.displayError({
122                            modal_title: 'Error during image activation',
123                            title: 'Error during image activation',
124                            desc: JSON.stringify(error.data),
125                            type: 'Error'
126                          });
127                        }).then(function(state){
128                          if($scope.activate.reboot){
129                            // Despite the new image being active, issue,
130                            // https://github.com/openbmc/openbmc/issues/2764, can cause a
131                            // system to brick, if the system reboots before the service to set
132                            // the U-Boot variables is complete. Wait 10 seconds before rebooting
133                            // to ensure this service is complete. This issue is fixed in newer images, but
134                            // the user may be updating from an older image that does not that have this fix.
135                            // TODO: remove this timeout after sufficient time has passed.
136                            $timeout(function() {
137                              APIUtils.bmcReboot(function(response){}, function(error){
138                                $scope.displayError({
139                                  modal_title: 'Error during BMC reboot',
140                                  title: 'Error during BMC reboot',
141                                  desc: JSON.stringify(error.data),
142                                  type: 'Error'
143                                });
144                              });
145                            }, 10000);
146                          }
147                        });
148                      });
149                      $scope.activate_confirm = false;
150                    }
151
152                    $scope.upload = function(){
153                      if($scope.file) {
154                        $scope.uploading = true;
155                        $scope.upload_success = false;
156                        APIUtils.uploadImage($scope.file).then(function(response){
157                          $scope.file = "";
158                          $scope.uploading = false;
159                          $scope.upload_success = true;
160                          $scope.loadFirmwares();
161                        }, function(error){
162                          $scope.uploading = false;
163                          console.log(error);
164                          $scope.displayError({
165                            modal_title: 'Error during image upload',
166                            title: 'Error during image upload',
167                            desc: error,
168                            type: 'Error'
169                          });
170                        });
171                      }
172                    }
173
174                    //TODO: openbmc/openbmc#1691 Add support to return
175                    //the id of the newly created image, downloaded via
176                    //tftp. Polling the number of software objects is a
177                    //near term solution.
178                    function waitForDownload(){
179                        var deferred = $q.defer();
180                        var startTime = new Date();
181                        pollDownloadTimer = $interval(function(){
182                            var now = new Date();
183                            if((now.getTime() - startTime.getTime()) >= Constants.TIMEOUT.DOWNLOAD_IMAGE){
184                                $interval.cancel(pollDownloadTimer);
185                                pollDownloadTimer = undefined;
186                                deferred.reject(new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
187                            }
188
189                            APIUtils.getFirmwares().then(function(response){
190                                if(response.data.length === $scope.firmwares.length + 1){
191                                    $interval.cancel(pollDownloadTimer);
192                                    pollDownloadTimer = undefined;
193                                    deferred.resolve(response.data);
194                                }
195                            }, function(error){
196                                $interval.cancel(pollDownloadTimer);
197                                pollDownloadTimer = undefined;
198                                deferred.reject(error);
199                            });
200                        }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
201
202                        return deferred.promise;
203                    }
204
205                    $scope.download = function(){
206                        $scope.download_success = false;
207                        $scope.download_error_msg = "";
208                        if(!$scope.download_host || !$scope.download_filename){
209                          $scope.download_error_msg = "Field is required!";
210                          return false;
211                        }
212
213                        $scope.downloading = true;
214                        APIUtils.getFirmwares().then(function(response){
215                            $scope.firmwares =  response.data;
216                        }).then(function(){
217                            return APIUtils.downloadImage($scope.download_host,
218                                    $scope.download_filename).then(function(downloadStatus){
219                                return downloadStatus;
220                            });
221                        }).then(function(downloadStatus){
222                            return waitForDownload();
223                        }).then(function(newFirmwareList){
224                            $scope.download_host = "";
225                            $scope.download_filename = "";
226                            $scope.downloading = false;
227                            $scope.download_success = true;
228                            $scope.loadFirmwares();
229                        }, function(error){
230                            console.log(error);
231                            $scope.displayError({
232                                modal_title: 'Error during downloading Image',
233                                title: 'Error during downloading Image',
234                                desc: error,
235                                type: 'Error'
236                            });
237                            $scope.downloading = false;
238                        });
239                    }
240
241                    $scope.changePriority = function(imageId, imageVersion, from, to){
242                        $scope.priority_image_id = imageId;
243                        $scope.priority_image_version = imageVersion;
244                        $scope.priority_from = from;
245                        $scope.priority_to = to;
246                        $scope.confirm_priority = true;
247                    }
248
249                    $scope.confirmChangePriority = function(){
250                        $scope.loading = true;
251                        APIUtils.changePriority($scope.priority_image_id, $scope.priority_to).then(function(response){
252                            $scope.loading = false;
253                            if(response.status == 'error'){
254                                $scope.displayError({
255                                    modal_title: response.data.description,
256                                    title: response.data.description,
257                                    desc: response.data.exception,
258                                    type: 'Error'
259                                });
260                            }else{
261                                $scope.loadFirmwares();
262                            }
263                        });
264                        $scope.confirm_priority = false;
265                    }
266                    $scope.deleteImage = function(imageId, imageVersion){
267                        $scope.delete_image_id = imageId;
268                        $scope.delete_image_version = imageVersion;
269                        $scope.confirm_delete = true;
270                    }
271                    $scope.confirmDeleteImage = function(){
272                        $scope.loading = true;
273                        APIUtils.deleteImage($scope.delete_image_id).then(function(response){
274                            $scope.loading = false;
275                            if(response.status == 'error'){
276                                $scope.displayError({
277                                    modal_title: response.data.description,
278                                    title: response.data.description,
279                                    desc: response.data.exception,
280                                    type: 'Error'
281                                });
282                            }else{
283                                $scope.loadFirmwares();
284                            }
285                        });
286                        $scope.confirm_delete = false;
287                    }
288                    $scope.fileNameChanged = function(){
289                        $scope.file_empty = false;
290                    }
291
292                    $scope.filters = {
293                        bmc: {
294                            imageType: 'BMC'
295                        },
296                        host: {
297                            imageType: 'Host'
298                        }
299                    };
300
301                    $scope.loadFirmwares = function(){
302                        APIUtils.getFirmwares().then(function(result){
303                           $scope.firmwares = result.data;
304                           $scope.bmcActiveVersion = result.bmcActiveVersion;
305                           $scope.hostActiveVersion = result.hostActiveVersion;
306                        });
307                    }
308
309                    $scope.loadFirmwares();
310                }
311            ]
312        );
313
314})(angular);
315