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', 'toastService', 15 function( 16 $scope, $window, APIUtils, dataService, $location, $anchorScroll, 17 Constants, $interval, $q, $timeout, toastService) { 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.activate_confirm = false; 30 $scope.delete_image_id = ''; 31 $scope.delete_image_version = ''; 32 $scope.activate_image_id = ''; 33 $scope.activate_image_version = ''; 34 $scope.activate_image_type = ''; 35 $scope.priority_image_id = ''; 36 $scope.priority_image_version = ''; 37 $scope.priority_from = -1; 38 $scope.priority_to = -1; 39 $scope.confirm_priority = false; 40 $scope.file_empty = true; 41 $scope.uploading = false; 42 $scope.activate = {reboot: true}; 43 44 var pollActivationTimer = undefined; 45 var pollDownloadTimer = undefined; 46 47 $scope.error = {modal_title: '', title: '', desc: '', type: 'warning'}; 48 49 $scope.activateImage = function(imageId, imageVersion, imageType) { 50 $scope.activate_image_id = imageId; 51 $scope.activate_image_version = imageVersion; 52 $scope.activate_image_type = imageType; 53 $scope.activate_confirm = true; 54 }; 55 56 function waitForActive(imageId) { 57 var deferred = $q.defer(); 58 var startTime = new Date(); 59 pollActivationTimer = $interval(function() { 60 APIUtils.getActivation(imageId).then( 61 function(state) { 62 let imageStateActive = (/\.Active$/).test(state.data); 63 let imageStateFailed = (/\.Failed$/).test(state.data); 64 if (imageStateActive || imageStateFailed) { 65 $interval.cancel(pollActivationTimer); 66 pollActivationTimer = undefined; 67 } 68 if (imageStateActive) { 69 deferred.resolve(state); 70 } else if (imageStateFailed) { 71 console.log('Image failed to activate: ', imageStateFailed); 72 toastService.error('Image failed to activate.'); 73 deferred.reject(error); 74 } 75 }, 76 function(error) { 77 $interval.cancel(pollActivationTimer); 78 pollActivationTimer = undefined; 79 console.log(error); 80 deferred.reject(error); 81 }); 82 var now = new Date(); 83 if ((now.getTime() - startTime.getTime()) >= 84 Constants.TIMEOUT.ACTIVATION) { 85 $interval.cancel(pollActivationTimer); 86 pollActivationTimer = undefined; 87 console.log('Time out activating image, ' + imageId); 88 deferred.reject( 89 'Time out. Image did not activate in allotted time.'); 90 } 91 }, Constants.POLL_INTERVALS.ACTIVATION); 92 return deferred.promise; 93 } 94 95 $scope.activateConfirmed = function() { 96 APIUtils.activateImage($scope.activate_image_id) 97 .then( 98 function(state) { 99 $scope.loadFirmwares(); 100 return state; 101 }, 102 function(error) { 103 console.log(JSON.stringify(error)); 104 toastService.error('Unable to activate image'); 105 }) 106 .then(function(activationState) { 107 waitForActive($scope.activate_image_id) 108 .then( 109 function(state) { 110 $scope.loadFirmwares(); 111 }, 112 function(error) { 113 console.log(JSON.stringify(error)); 114 toastService.error('Unable to activate image'); 115 }) 116 .then(function(state) { 117 if ($scope.activate.reboot && 118 ($scope.activate_image_type == 'BMC')) { 119 APIUtils.bmcReboot().then( 120 function(response) { 121 toastService.success('BMC is rebooting.') 122 }, 123 function(error) { 124 console.log(JSON.stringify(error)); 125 toastService.error('Unable to reboot BMC.'); 126 }); 127 } 128 if ($scope.activate.reboot && 129 ($scope.activate_image_type == 'Host')) { 130 // If image type being activated is a host image, the 131 // current power status of the server determines if the 132 // server should power on or reboot. 133 if ($scope.isServerOff()) { 134 powerOn(); 135 } else { 136 warmReboot(); 137 } 138 } 139 }); 140 }); 141 $scope.activate_confirm = false; 142 }; 143 function powerOn() { 144 dataService.setUnreachableState(); 145 APIUtils.hostPowerOn() 146 .then(function(response) { 147 return response; 148 }) 149 .then(function(lastStatus) { 150 return APIUtils.pollHostStatusTillOn(); 151 }) 152 .catch(function(error) { 153 console.log(JSON.stringify(error)); 154 toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED); 155 }); 156 }; 157 function warmReboot() { 158 $scope.uploading = true; 159 dataService.setUnreachableState(); 160 APIUtils.hostReboot() 161 .then(function(response) { 162 return response; 163 }) 164 .then(function(lastStatus) { 165 return APIUtils.pollHostStatusTilReboot(); 166 }) 167 .catch(function(error) { 168 console.log(JSON.stringify(error)); 169 toastService.error( 170 Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED); 171 }); 172 }; 173 $scope.isServerOff = function() { 174 return dataService.server_state === Constants.HOST_STATE_TEXT.off; 175 }; 176 177 $scope.upload = function() { 178 if ($scope.file) { 179 $scope.uploading = true; 180 APIUtils.uploadImage($scope.file) 181 .then( 182 function(response) { 183 $scope.uploading = false; 184 toastService.success( 185 'Image file "' + $scope.file.name + 186 '" has been uploaded'); 187 $scope.file = ''; 188 $scope.loadFirmwares(); 189 }, 190 function(error) { 191 $scope.uploading = false; 192 console.log(error); 193 toastService.error('Unable to upload image file'); 194 }); 195 } 196 }; 197 198 // TODO: openbmc/openbmc#1691 Add support to return 199 // the id of the newly created image, downloaded via 200 // tftp. Polling the number of software objects is a 201 // near term solution. 202 function waitForDownload() { 203 var deferred = $q.defer(); 204 var startTime = new Date(); 205 pollDownloadTimer = $interval(function() { 206 var now = new Date(); 207 if ((now.getTime() - startTime.getTime()) >= 208 Constants.TIMEOUT.DOWNLOAD_IMAGE) { 209 $interval.cancel(pollDownloadTimer); 210 pollDownloadTimer = undefined; 211 deferred.reject( 212 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT)); 213 } 214 215 APIUtils.getFirmwares().then( 216 function(response) { 217 if (response.data.length === $scope.firmwares.length + 1) { 218 $interval.cancel(pollDownloadTimer); 219 pollDownloadTimer = undefined; 220 deferred.resolve(response.data); 221 } 222 }, 223 function(error) { 224 $interval.cancel(pollDownloadTimer); 225 pollDownloadTimer = undefined; 226 deferred.reject(error); 227 }); 228 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE); 229 230 return deferred.promise; 231 } 232 233 $scope.download = function() { 234 if (!$scope.download_host || !$scope.download_filename) { 235 toastService.error( 236 'TFTP server IP address and file name are required!'); 237 return false; 238 } 239 240 $scope.downloading = true; 241 APIUtils.getFirmwares() 242 .then(function(response) { 243 $scope.firmwares = response.data; 244 }) 245 .then(function() { 246 return APIUtils 247 .downloadImage($scope.download_host, $scope.download_filename) 248 .then(function(downloadStatus) { 249 return downloadStatus; 250 }); 251 }) 252 .then(function(downloadStatus) { 253 return waitForDownload(); 254 }) 255 .then( 256 function(newFirmwareList) { 257 $scope.download_host = ''; 258 $scope.download_filename = ''; 259 $scope.downloading = false; 260 toastService.success('Download complete'); 261 $scope.loadFirmwares(); 262 }, 263 function(error) { 264 console.log(error); 265 toastService.error( 266 'Image file from TFTP server "' + $scope.download_host + 267 '" could not be downloaded'); 268 $scope.downloading = false; 269 }); 270 }; 271 272 $scope.changePriority = function(imageId, imageVersion, from, to) { 273 $scope.priority_image_id = imageId; 274 $scope.priority_image_version = imageVersion; 275 $scope.priority_from = from; 276 $scope.priority_to = to; 277 $scope.confirm_priority = true; 278 }; 279 280 $scope.confirmChangePriority = function() { 281 $scope.loading = true; 282 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to) 283 .then(function(response) { 284 $scope.loading = false; 285 if (response.status == 'error') { 286 toastService.error('Unable to update boot priority'); 287 } else { 288 $scope.loadFirmwares(); 289 } 290 }); 291 $scope.confirm_priority = false; 292 }; 293 $scope.deleteImage = function(imageId, imageVersion) { 294 $scope.delete_image_id = imageId; 295 $scope.delete_image_version = imageVersion; 296 $scope.confirm_delete = true; 297 }; 298 $scope.confirmDeleteImage = function() { 299 $scope.loading = true; 300 APIUtils.deleteImage($scope.delete_image_id).then(function(response) { 301 $scope.loading = false; 302 if (response.status == 'error') { 303 toastService.error('Unable to delete image'); 304 } else { 305 $scope.loadFirmwares(); 306 } 307 }); 308 $scope.confirm_delete = false; 309 }; 310 311 $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}}; 312 313 $scope.loadFirmwares = function() { 314 APIUtils.getFirmwares().then(function(result) { 315 $scope.firmwares = result.data; 316 $scope.bmcActiveVersion = result.bmcActiveVersion; 317 $scope.hostActiveVersion = result.hostActiveVersion; 318 }); 319 }; 320 321 $scope.loadFirmwares(); 322 } 323 ]); 324})(angular); 325