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