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