143366db7SMichael Davis/** 243366db7SMichael Davis * Controller for firmware 343366db7SMichael Davis * 443366db7SMichael Davis * @module app/configuration 543366db7SMichael Davis * @exports firmwareController 643366db7SMichael Davis * @name firmwareController 743366db7SMichael Davis */ 843366db7SMichael Davis 943366db7SMichael Daviswindow.angular && (function(angular) { 1043366db7SMichael Davis 'use strict'; 1143366db7SMichael Davis 12d27bb135SAndrew Geissler angular.module('app.configuration').controller('firmwareController', [ 13806d39beSGunnar Mills '$scope', '$window', 'APIUtils', 'dataService', '$location', 146f4e12e3SGunnar Mills '$anchorScroll', 'Constants', '$interval', '$q', '$timeout', 'toastService', 15d27bb135SAndrew Geissler function( 16806d39beSGunnar Mills $scope, $window, APIUtils, dataService, $location, $anchorScroll, 176f4e12e3SGunnar Mills Constants, $interval, $q, $timeout, toastService) { 18806d39beSGunnar Mills $scope.dataService = dataService; 19806d39beSGunnar Mills 2043366db7SMichael Davis // Scroll to target anchor 2143366db7SMichael Davis $scope.gotoAnchor = function() { 2243366db7SMichael Davis $location.hash('upload'); 2343366db7SMichael Davis $anchorScroll(); 2443366db7SMichael Davis }; 25c016139fSIftekharul Islam 26c016139fSIftekharul Islam $scope.firmwares = []; 27ba5e3f34SAndrew Geissler $scope.bmcActiveVersion = ''; 28ba5e3f34SAndrew Geissler $scope.hostActiveVersion = ''; 29e7f83970SGunnar Mills $scope.activate_confirm = false; 30ba5e3f34SAndrew Geissler $scope.delete_image_id = ''; 31ba5e3f34SAndrew Geissler $scope.delete_image_version = ''; 32ba5e3f34SAndrew Geissler $scope.activate_image_id = ''; 33ba5e3f34SAndrew Geissler $scope.activate_image_version = ''; 34ba5e3f34SAndrew Geissler $scope.activate_image_type = ''; 35ba5e3f34SAndrew Geissler $scope.priority_image_id = ''; 36ba5e3f34SAndrew Geissler $scope.priority_image_version = ''; 371acb412dSIftekharul Islam $scope.priority_from = -1; 381acb412dSIftekharul Islam $scope.priority_to = -1; 391acb412dSIftekharul Islam $scope.confirm_priority = false; 40c016139fSIftekharul Islam $scope.file_empty = true; 41c016139fSIftekharul Islam $scope.uploading = false; 42d27bb135SAndrew Geissler $scope.activate = {reboot: true}; 43c016139fSIftekharul Islam 44033025f3SGunnar Mills var pollActivationTimer = undefined; 4558301ec8SCamVan Nguyen var pollDownloadTimer = undefined; 46033025f3SGunnar Mills 47d27bb135SAndrew Geissler $scope.error = {modal_title: '', title: '', desc: '', type: 'warning'}; 48c016139fSIftekharul Islam 49ee6efd85SGunnar Mills $scope.activateImage = function(imageId, imageVersion, imageType) { 50c016139fSIftekharul Islam $scope.activate_image_id = imageId; 51e7f83970SGunnar Mills $scope.activate_image_version = imageVersion; 52ee6efd85SGunnar Mills $scope.activate_image_type = imageType; 53e7f83970SGunnar Mills $scope.activate_confirm = true; 54ba5e3f34SAndrew Geissler }; 55c016139fSIftekharul Islam 56033025f3SGunnar Mills function waitForActive(imageId) { 57033025f3SGunnar Mills var deferred = $q.defer(); 58033025f3SGunnar Mills var startTime = new Date(); 59033025f3SGunnar Mills pollActivationTimer = $interval(function() { 60d27bb135SAndrew Geissler APIUtils.getActivation(imageId).then( 61d27bb135SAndrew Geissler function(state) { 62*ff7f1068SDixsie Wolmers let imageStateActive = (/\.Active$/).test(state.data); 63*ff7f1068SDixsie Wolmers let imageStateFailed = (/\.Failed$/).test(state.data); 64*ff7f1068SDixsie Wolmers if (imageStateActive || imageStateFailed) { 65033025f3SGunnar Mills $interval.cancel(pollActivationTimer); 66033025f3SGunnar Mills pollActivationTimer = undefined; 67*ff7f1068SDixsie Wolmers } 68*ff7f1068SDixsie Wolmers if (imageStateActive) { 69033025f3SGunnar Mills deferred.resolve(state); 70*ff7f1068SDixsie Wolmers } else if (imageStateFailed) { 71*ff7f1068SDixsie Wolmers console.log('Image failed to activate: ', imageStateFailed); 72*ff7f1068SDixsie Wolmers toastService.error('Image failed to activate.'); 73*ff7f1068SDixsie Wolmers deferred.reject(error); 74033025f3SGunnar Mills } 75d27bb135SAndrew Geissler }, 76d27bb135SAndrew Geissler function(error) { 77033025f3SGunnar Mills $interval.cancel(pollActivationTimer); 78033025f3SGunnar Mills pollActivationTimer = undefined; 79033025f3SGunnar Mills console.log(error); 80033025f3SGunnar Mills deferred.reject(error); 81033025f3SGunnar Mills }); 82033025f3SGunnar Mills var now = new Date(); 83d27bb135SAndrew Geissler if ((now.getTime() - startTime.getTime()) >= 84d27bb135SAndrew Geissler Constants.TIMEOUT.ACTIVATION) { 85033025f3SGunnar Mills $interval.cancel(pollActivationTimer); 86033025f3SGunnar Mills pollActivationTimer = undefined; 87ba5e3f34SAndrew Geissler console.log('Time out activating image, ' + imageId); 88d27bb135SAndrew Geissler deferred.reject( 89d27bb135SAndrew Geissler 'Time out. Image did not activate in allotted time.'); 90033025f3SGunnar Mills } 91033025f3SGunnar Mills }, Constants.POLL_INTERVALS.ACTIVATION); 92033025f3SGunnar Mills return deferred.promise; 93033025f3SGunnar Mills } 94033025f3SGunnar Mills 95e7f83970SGunnar Mills $scope.activateConfirmed = function() { 96d27bb135SAndrew Geissler APIUtils.activateImage($scope.activate_image_id) 97d27bb135SAndrew Geissler .then( 98d27bb135SAndrew Geissler function(state) { 99033025f3SGunnar Mills $scope.loadFirmwares(); 100033025f3SGunnar Mills return state; 101d27bb135SAndrew Geissler }, 102d27bb135SAndrew Geissler function(error) { 10390ae95ebSbeccabroek console.log(JSON.stringify(error)); 10427ce84d2Sbeccabroek toastService.error('Unable to activate image'); 105d27bb135SAndrew Geissler }) 106d27bb135SAndrew Geissler .then(function(activationState) { 107d27bb135SAndrew Geissler waitForActive($scope.activate_image_id) 108d27bb135SAndrew Geissler .then( 109d27bb135SAndrew Geissler function(state) { 1102a489554SIftekharul Islam $scope.loadFirmwares(); 111d27bb135SAndrew Geissler }, 112d27bb135SAndrew Geissler function(error) { 11390ae95ebSbeccabroek console.log(JSON.stringify(error)); 11427ce84d2Sbeccabroek toastService.error('Unable to activate image'); 115d27bb135SAndrew Geissler }) 116d27bb135SAndrew Geissler .then(function(state) { 117d27bb135SAndrew Geissler if ($scope.activate.reboot && 118d27bb135SAndrew Geissler ($scope.activate_image_type == 'BMC')) { 119c57ec32fSDixsie Wolmers APIUtils.bmcReboot().then( 120c57ec32fSDixsie Wolmers function(response) { 121c57ec32fSDixsie Wolmers toastService.success('BMC is rebooting.') 122c57ec32fSDixsie Wolmers }, 123d27bb135SAndrew Geissler function(error) { 12490ae95ebSbeccabroek console.log(JSON.stringify(error)); 125c57ec32fSDixsie Wolmers toastService.error('Unable to reboot BMC.'); 1266d9ef5acSGunnar Mills }); 1276d9ef5acSGunnar Mills } 128c3abaa9bSbeccabroek if ($scope.activate.reboot && 129c3abaa9bSbeccabroek ($scope.activate_image_type == 'Host')) { 130c3abaa9bSbeccabroek // If image type being activated is a host image, the 131c3abaa9bSbeccabroek // current power status of the server determines if the 132c3abaa9bSbeccabroek // server should power on or reboot. 133c3abaa9bSbeccabroek if ($scope.isServerOff()) { 134c3abaa9bSbeccabroek powerOn(); 135c3abaa9bSbeccabroek } else { 136c3abaa9bSbeccabroek warmReboot(); 137c3abaa9bSbeccabroek } 138c3abaa9bSbeccabroek } 139033025f3SGunnar Mills }); 1402a489554SIftekharul Islam }); 141e7f83970SGunnar Mills $scope.activate_confirm = false; 142ba5e3f34SAndrew Geissler }; 143c3abaa9bSbeccabroek function powerOn() { 144c3abaa9bSbeccabroek dataService.setUnreachableState(); 145c3abaa9bSbeccabroek APIUtils.hostPowerOn() 146c3abaa9bSbeccabroek .then(function(response) { 147c3abaa9bSbeccabroek return response; 148c3abaa9bSbeccabroek }) 149c3abaa9bSbeccabroek .then(function(lastStatus) { 150c3abaa9bSbeccabroek return APIUtils.pollHostStatusTillOn(); 151c3abaa9bSbeccabroek }) 152c3abaa9bSbeccabroek .catch(function(error) { 15390ae95ebSbeccabroek console.log(JSON.stringify(error)); 15427ce84d2Sbeccabroek toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED); 155c3abaa9bSbeccabroek }); 156c3abaa9bSbeccabroek }; 157c3abaa9bSbeccabroek function warmReboot() { 158c3abaa9bSbeccabroek $scope.uploading = true; 159c3abaa9bSbeccabroek dataService.setUnreachableState(); 160c3abaa9bSbeccabroek APIUtils.hostReboot() 161c3abaa9bSbeccabroek .then(function(response) { 162c3abaa9bSbeccabroek return response; 163c3abaa9bSbeccabroek }) 164c3abaa9bSbeccabroek .then(function(lastStatus) { 165c3abaa9bSbeccabroek return APIUtils.pollHostStatusTilReboot(); 166c3abaa9bSbeccabroek }) 167c3abaa9bSbeccabroek .catch(function(error) { 16890ae95ebSbeccabroek console.log(JSON.stringify(error)); 16927ce84d2Sbeccabroek toastService.error( 17027ce84d2Sbeccabroek Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED); 171c3abaa9bSbeccabroek }); 172c3abaa9bSbeccabroek }; 173c3abaa9bSbeccabroek $scope.isServerOff = function() { 174c3abaa9bSbeccabroek return dataService.server_state === Constants.HOST_STATE_TEXT.off; 175c3abaa9bSbeccabroek }; 176c016139fSIftekharul Islam 177c016139fSIftekharul Islam $scope.upload = function() { 178dddb1497SGunnar Mills if ($scope.file) { 179c016139fSIftekharul Islam $scope.uploading = true; 180d27bb135SAndrew Geissler APIUtils.uploadImage($scope.file) 181d27bb135SAndrew Geissler .then( 182d27bb135SAndrew Geissler function(response) { 183c016139fSIftekharul Islam $scope.uploading = false; 18427ce84d2Sbeccabroek toastService.success( 18590ae95ebSbeccabroek 'Image file "' + $scope.file.name + 18690ae95ebSbeccabroek '" has been uploaded'); 18790ae95ebSbeccabroek $scope.file = ''; 188c016139fSIftekharul Islam $scope.loadFirmwares(); 189d27bb135SAndrew Geissler }, 190d27bb135SAndrew Geissler function(error) { 19187c3db65SGunnar Mills $scope.uploading = false; 192f97adb9fSCamVan Nguyen console.log(error); 19327ce84d2Sbeccabroek toastService.error('Unable to upload image file'); 194c016139fSIftekharul Islam }); 195dddb1497SGunnar Mills } 196ba5e3f34SAndrew Geissler }; 197c016139fSIftekharul Islam 19858301ec8SCamVan Nguyen // TODO: openbmc/openbmc#1691 Add support to return 19958301ec8SCamVan Nguyen // the id of the newly created image, downloaded via 20058301ec8SCamVan Nguyen // tftp. Polling the number of software objects is a 20158301ec8SCamVan Nguyen // near term solution. 20258301ec8SCamVan Nguyen function waitForDownload() { 20358301ec8SCamVan Nguyen var deferred = $q.defer(); 20458301ec8SCamVan Nguyen var startTime = new Date(); 20558301ec8SCamVan Nguyen pollDownloadTimer = $interval(function() { 20658301ec8SCamVan Nguyen var now = new Date(); 207d27bb135SAndrew Geissler if ((now.getTime() - startTime.getTime()) >= 208d27bb135SAndrew Geissler Constants.TIMEOUT.DOWNLOAD_IMAGE) { 20958301ec8SCamVan Nguyen $interval.cancel(pollDownloadTimer); 21058301ec8SCamVan Nguyen pollDownloadTimer = undefined; 211d27bb135SAndrew Geissler deferred.reject( 212d27bb135SAndrew Geissler new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT)); 21358301ec8SCamVan Nguyen } 21458301ec8SCamVan Nguyen 215d27bb135SAndrew Geissler APIUtils.getFirmwares().then( 216d27bb135SAndrew Geissler function(response) { 21758301ec8SCamVan Nguyen if (response.data.length === $scope.firmwares.length + 1) { 21858301ec8SCamVan Nguyen $interval.cancel(pollDownloadTimer); 21958301ec8SCamVan Nguyen pollDownloadTimer = undefined; 22058301ec8SCamVan Nguyen deferred.resolve(response.data); 22158301ec8SCamVan Nguyen } 222d27bb135SAndrew Geissler }, 223d27bb135SAndrew Geissler function(error) { 22458301ec8SCamVan Nguyen $interval.cancel(pollDownloadTimer); 22558301ec8SCamVan Nguyen pollDownloadTimer = undefined; 22658301ec8SCamVan Nguyen deferred.reject(error); 22758301ec8SCamVan Nguyen }); 22858301ec8SCamVan Nguyen }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE); 22958301ec8SCamVan Nguyen 23058301ec8SCamVan Nguyen return deferred.promise; 23158301ec8SCamVan Nguyen } 23258301ec8SCamVan Nguyen 2331acb412dSIftekharul Islam $scope.download = function() { 2346d7b4a8dSGunnar Mills if (!$scope.download_host || !$scope.download_filename) { 23527ce84d2Sbeccabroek toastService.error( 23627ce84d2Sbeccabroek 'TFTP server IP address and file name are required!'); 2376d7b4a8dSGunnar Mills return false; 2386d7b4a8dSGunnar Mills } 23958301ec8SCamVan Nguyen 2401acb412dSIftekharul Islam $scope.downloading = true; 241d27bb135SAndrew Geissler APIUtils.getFirmwares() 242d27bb135SAndrew Geissler .then(function(response) { 24358301ec8SCamVan Nguyen $scope.firmwares = response.data; 244d27bb135SAndrew Geissler }) 245d27bb135SAndrew Geissler .then(function() { 246d27bb135SAndrew Geissler return APIUtils 247d27bb135SAndrew Geissler .downloadImage($scope.download_host, $scope.download_filename) 248d27bb135SAndrew Geissler .then(function(downloadStatus) { 24958301ec8SCamVan Nguyen return downloadStatus; 25058301ec8SCamVan Nguyen }); 251d27bb135SAndrew Geissler }) 252d27bb135SAndrew Geissler .then(function(downloadStatus) { 25358301ec8SCamVan Nguyen return waitForDownload(); 254d27bb135SAndrew Geissler }) 255d27bb135SAndrew Geissler .then( 256d27bb135SAndrew Geissler function(newFirmwareList) { 257ba5e3f34SAndrew Geissler $scope.download_host = ''; 258ba5e3f34SAndrew Geissler $scope.download_filename = ''; 2591acb412dSIftekharul Islam $scope.downloading = false; 26027ce84d2Sbeccabroek toastService.success('Download complete'); 26158301ec8SCamVan Nguyen $scope.loadFirmwares(); 262d27bb135SAndrew Geissler }, 263d27bb135SAndrew Geissler function(error) { 26458301ec8SCamVan Nguyen console.log(error); 26527ce84d2Sbeccabroek toastService.error( 26690ae95ebSbeccabroek 'Image file from TFTP server "' + $scope.download_host + 26790ae95ebSbeccabroek '" could not be downloaded'); 26858301ec8SCamVan Nguyen $scope.downloading = false; 2691acb412dSIftekharul Islam }); 270ba5e3f34SAndrew Geissler }; 2711acb412dSIftekharul Islam 2726473a41dSGunnar Mills $scope.changePriority = function(imageId, imageVersion, from, to) { 2731acb412dSIftekharul Islam $scope.priority_image_id = imageId; 2746473a41dSGunnar Mills $scope.priority_image_version = imageVersion; 2751acb412dSIftekharul Islam $scope.priority_from = from; 2761acb412dSIftekharul Islam $scope.priority_to = to; 2771acb412dSIftekharul Islam $scope.confirm_priority = true; 278ba5e3f34SAndrew Geissler }; 2791acb412dSIftekharul Islam 2801acb412dSIftekharul Islam $scope.confirmChangePriority = function() { 2811acb412dSIftekharul Islam $scope.loading = true; 282d27bb135SAndrew Geissler APIUtils.changePriority($scope.priority_image_id, $scope.priority_to) 283d27bb135SAndrew Geissler .then(function(response) { 2841acb412dSIftekharul Islam $scope.loading = false; 2851acb412dSIftekharul Islam if (response.status == 'error') { 28627ce84d2Sbeccabroek toastService.error('Unable to update boot priority'); 287d27bb135SAndrew Geissler } else { 2881acb412dSIftekharul Islam $scope.loadFirmwares(); 2891acb412dSIftekharul Islam } 2901acb412dSIftekharul Islam }); 2911acb412dSIftekharul Islam $scope.confirm_priority = false; 292ba5e3f34SAndrew Geissler }; 293607a120aSGunnar Mills $scope.deleteImage = function(imageId, imageVersion) { 294c016139fSIftekharul Islam $scope.delete_image_id = imageId; 295607a120aSGunnar Mills $scope.delete_image_version = imageVersion; 296c016139fSIftekharul Islam $scope.confirm_delete = true; 297ba5e3f34SAndrew Geissler }; 2982a489554SIftekharul Islam $scope.confirmDeleteImage = function() { 2992a489554SIftekharul Islam $scope.loading = true; 3002a489554SIftekharul Islam APIUtils.deleteImage($scope.delete_image_id).then(function(response) { 3012a489554SIftekharul Islam $scope.loading = false; 3022a489554SIftekharul Islam if (response.status == 'error') { 30327ce84d2Sbeccabroek toastService.error('Unable to delete image'); 304d27bb135SAndrew Geissler } else { 3052a489554SIftekharul Islam $scope.loadFirmwares(); 3062a489554SIftekharul Islam } 3072a489554SIftekharul Islam }); 308c016139fSIftekharul Islam $scope.confirm_delete = false; 309ba5e3f34SAndrew Geissler }; 310c016139fSIftekharul Islam 311d27bb135SAndrew Geissler $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}}; 312c016139fSIftekharul Islam 313c016139fSIftekharul Islam $scope.loadFirmwares = function() { 314df3bd124SMichael Davis APIUtils.getFirmwares().then(function(result) { 315df3bd124SMichael Davis $scope.firmwares = result.data; 316df3bd124SMichael Davis $scope.bmcActiveVersion = result.bmcActiveVersion; 317df3bd124SMichael Davis $scope.hostActiveVersion = result.hostActiveVersion; 318c016139fSIftekharul Islam }); 319ba5e3f34SAndrew Geissler }; 320c016139fSIftekharul Islam 321c016139fSIftekharul Islam $scope.loadFirmwares(); 32243366db7SMichael Davis } 323ba5e3f34SAndrew Geissler ]); 32443366db7SMichael Davis})(angular); 325