blob: 9a811c3a68798f2ebee5f9ddfa1fb3744a2a9525 [file] [log] [blame]
Michael Davis43366db2017-05-15 18:12:35 -05001/**
2 * Controller for firmware
3 *
4 * @module app/configuration
5 * @exports firmwareController
6 * @name firmwareController
Michael Davis43366db2017-05-15 18:12:35 -05007 */
8
Andrew Geisslerba5e3f32018-05-24 10:58:00 -07009window.angular && (function(angular) {
10 'use strict';
Michael Davis43366db2017-05-15 18:12:35 -050011
Andrew Geisslerd27bb132018-05-24 11:07:27 -070012 angular.module('app.configuration').controller('firmwareController', [
Gunnar Mills806d39b2018-09-26 11:47:25 -050013 '$scope', '$window', 'APIUtils', 'dataService', '$location',
14 '$anchorScroll', 'Constants', '$interval', '$q', '$timeout', '$interpolate',
beccabroek27ce84d2019-02-05 15:43:17 -060015 'toastService',
Andrew Geisslerd27bb132018-05-24 11:07:27 -070016 function(
Gunnar Mills806d39b2018-09-26 11:47:25 -050017 $scope, $window, APIUtils, dataService, $location, $anchorScroll,
beccabroek27ce84d2019-02-05 15:43:17 -060018 Constants, $interval, $q, $timeout, $interpolate, toastService) {
Gunnar Mills806d39b2018-09-26 11:47:25 -050019 $scope.dataService = dataService;
20
Andrew Geisslerd27bb132018-05-24 11:07:27 -070021 // Scroll to target anchor
22 $scope.gotoAnchor = function() {
23 $location.hash('upload');
24 $anchorScroll();
25 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050026
Andrew Geisslerd27bb132018-05-24 11:07:27 -070027 $scope.firmwares = [];
28 $scope.bmcActiveVersion = '';
29 $scope.hostActiveVersion = '';
Andrew Geisslerd27bb132018-05-24 11:07:27 -070030 $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;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070043 $scope.activate = {reboot: true};
Iftekharul Islamc0161392017-06-14 15:46:15 -050044
Andrew Geisslerd27bb132018-05-24 11:07:27 -070045 var pollActivationTimer = undefined;
46 var pollDownloadTimer = undefined;
Gunnar Mills033025f2018-03-06 14:49:40 -060047
Andrew Geisslerd27bb132018-05-24 11:07:27 -070048 $scope.error = {modal_title: '', title: '', desc: '', type: 'warning'};
Iftekharul Islamc0161392017-06-14 15:46:15 -050049
Andrew Geisslerd27bb132018-05-24 11:07:27 -070050 $scope.activateImage = function(imageId, imageVersion, imageType) {
51 $scope.activate_image_id = imageId;
52 $scope.activate_image_version = imageVersion;
53 $scope.activate_image_type = imageType;
54 $scope.activate_confirm = true;
55 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050056
Andrew Geisslerd27bb132018-05-24 11:07:27 -070057 function waitForActive(imageId) {
58 var deferred = $q.defer();
59 var startTime = new Date();
60 pollActivationTimer = $interval(function() {
61 APIUtils.getActivation(imageId).then(
62 function(state) {
63 //@TODO: display an error message if image "Failed"
64 if (((/\.Active$/).test(state.data)) ||
65 ((/\.Failed$/).test(state.data))) {
66 $interval.cancel(pollActivationTimer);
67 pollActivationTimer = undefined;
68 deferred.resolve(state);
69 }
70 },
71 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070072 $interval.cancel(pollActivationTimer);
73 pollActivationTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070074 console.log(error);
75 deferred.reject(error);
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070076 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -070077 var now = new Date();
78 if ((now.getTime() - startTime.getTime()) >=
79 Constants.TIMEOUT.ACTIVATION) {
80 $interval.cancel(pollActivationTimer);
81 pollActivationTimer = undefined;
82 console.log('Time out activating image, ' + imageId);
83 deferred.reject(
84 'Time out. Image did not activate in allotted time.');
85 }
86 }, Constants.POLL_INTERVALS.ACTIVATION);
87 return deferred.promise;
88 }
89
90 $scope.activateConfirmed = function() {
91 APIUtils.activateImage($scope.activate_image_id)
92 .then(
93 function(state) {
94 $scope.loadFirmwares();
95 return state;
96 },
97 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -060098 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -060099 toastService.error('Unable to activate image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700100 })
101 .then(function(activationState) {
102 waitForActive($scope.activate_image_id)
103 .then(
104 function(state) {
105 $scope.loadFirmwares();
106 },
107 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600108 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600109 toastService.error('Unable to activate image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700110 })
111 .then(function(state) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700112 if ($scope.activate.reboot &&
113 ($scope.activate_image_type == 'BMC')) {
114 // Despite the new image being active, issue,
115 // https://github.com/openbmc/openbmc/issues/2764, can
116 // cause a system to brick, if the system reboots before
117 // the service to set the U-Boot variables is complete.
118 // Wait 10 seconds before rebooting to ensure this service
119 // is complete. This issue is fixed in newer images, but
120 // the user may be updating from an older image that does
121 // not that have this fix.
122 // TODO: remove this timeout after sufficient time has
123 // passed.
124 $timeout(function() {
125 APIUtils.bmcReboot(
126 function(response) {},
127 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600128 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600129 toastService.error('Unable to reboot BMC');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700130 });
131 }, 10000);
132 }
beccabroekc3abaa92018-08-14 13:47:18 -0500133 if ($scope.activate.reboot &&
134 ($scope.activate_image_type == 'Host')) {
135 // If image type being activated is a host image, the
136 // current power status of the server determines if the
137 // server should power on or reboot.
138 if ($scope.isServerOff()) {
139 powerOn();
140 } else {
141 warmReboot();
142 }
143 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700144 });
145 });
146 $scope.activate_confirm = false;
147 };
beccabroekc3abaa92018-08-14 13:47:18 -0500148 function powerOn() {
149 dataService.setUnreachableState();
150 APIUtils.hostPowerOn()
151 .then(function(response) {
152 return response;
153 })
154 .then(function(lastStatus) {
155 return APIUtils.pollHostStatusTillOn();
156 })
157 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600158 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600159 toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500160 });
161 };
162 function warmReboot() {
163 $scope.uploading = true;
164 dataService.setUnreachableState();
165 APIUtils.hostReboot()
166 .then(function(response) {
167 return response;
168 })
169 .then(function(lastStatus) {
170 return APIUtils.pollHostStatusTilReboot();
171 })
172 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600173 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600174 toastService.error(
175 Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500176 });
177 };
178 $scope.isServerOff = function() {
179 return dataService.server_state === Constants.HOST_STATE_TEXT.off;
180 };
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700181
182 $scope.upload = function() {
183 if ($scope.file) {
184 $scope.uploading = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700185 APIUtils.uploadImage($scope.file)
186 .then(
187 function(response) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700188 $scope.uploading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600189 toastService.success(
beccabroek90ae95e2019-01-15 16:55:57 -0600190 'Image file "' + $scope.file.name +
191 '" has been uploaded');
192 $scope.file = '';
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700193 $scope.loadFirmwares();
194 },
195 function(error) {
196 $scope.uploading = false;
197 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600198 toastService.error('Unable to upload image file');
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700199 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700200 }
201 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500202
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700203 // TODO: openbmc/openbmc#1691 Add support to return
204 // the id of the newly created image, downloaded via
205 // tftp. Polling the number of software objects is a
206 // near term solution.
207 function waitForDownload() {
208 var deferred = $q.defer();
209 var startTime = new Date();
210 pollDownloadTimer = $interval(function() {
211 var now = new Date();
212 if ((now.getTime() - startTime.getTime()) >=
213 Constants.TIMEOUT.DOWNLOAD_IMAGE) {
214 $interval.cancel(pollDownloadTimer);
215 pollDownloadTimer = undefined;
216 deferred.reject(
217 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700218 }
Iftekharul Islamc0161392017-06-14 15:46:15 -0500219
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700220 APIUtils.getFirmwares().then(
221 function(response) {
222 if (response.data.length === $scope.firmwares.length + 1) {
223 $interval.cancel(pollDownloadTimer);
224 pollDownloadTimer = undefined;
225 deferred.resolve(response.data);
226 }
227 },
228 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700229 $interval.cancel(pollDownloadTimer);
230 pollDownloadTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700231 deferred.reject(error);
232 });
233 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500234
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700235 return deferred.promise;
236 }
237
238 $scope.download = function() {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700239 if (!$scope.download_host || !$scope.download_filename) {
beccabroek27ce84d2019-02-05 15:43:17 -0600240 toastService.error(
241 'TFTP server IP address and file name are required!');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700242 return false;
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700243 }
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500244
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700245 $scope.downloading = true;
246 APIUtils.getFirmwares()
247 .then(function(response) {
248 $scope.firmwares = response.data;
249 })
250 .then(function() {
251 return APIUtils
252 .downloadImage($scope.download_host, $scope.download_filename)
253 .then(function(downloadStatus) {
254 return downloadStatus;
255 });
256 })
257 .then(function(downloadStatus) {
258 return waitForDownload();
259 })
260 .then(
261 function(newFirmwareList) {
262 $scope.download_host = '';
263 $scope.download_filename = '';
264 $scope.downloading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600265 toastService.success('Download complete');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700266 $scope.loadFirmwares();
267 },
268 function(error) {
269 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600270 toastService.error(
beccabroek90ae95e2019-01-15 16:55:57 -0600271 'Image file from TFTP server "' + $scope.download_host +
272 '" could not be downloaded');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700273 $scope.downloading = false;
274 });
275 };
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500276
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700277 $scope.changePriority = function(imageId, imageVersion, from, to) {
278 $scope.priority_image_id = imageId;
279 $scope.priority_image_version = imageVersion;
280 $scope.priority_from = from;
281 $scope.priority_to = to;
282 $scope.confirm_priority = true;
283 };
284
285 $scope.confirmChangePriority = function() {
286 $scope.loading = true;
287 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to)
288 .then(function(response) {
289 $scope.loading = false;
290 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600291 toastService.error('Unable to update boot priority');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700292 } else {
293 $scope.loadFirmwares();
294 }
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700295 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700296 $scope.confirm_priority = false;
297 };
298 $scope.deleteImage = function(imageId, imageVersion) {
299 $scope.delete_image_id = imageId;
300 $scope.delete_image_version = imageVersion;
301 $scope.confirm_delete = true;
302 };
303 $scope.confirmDeleteImage = function() {
304 $scope.loading = true;
305 APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
306 $scope.loading = false;
307 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600308 toastService.error('Unable to delete image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700309 } else {
310 $scope.loadFirmwares();
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700311 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700312 });
313 $scope.confirm_delete = false;
314 };
315 $scope.fileNameChanged = function() {
316 $scope.file_empty = false;
317 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500318
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700319 $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}};
Iftekharul Islamc0161392017-06-14 15:46:15 -0500320
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700321 $scope.loadFirmwares = function() {
322 APIUtils.getFirmwares().then(function(result) {
323 $scope.firmwares = result.data;
324 $scope.bmcActiveVersion = result.bmcActiveVersion;
325 $scope.hostActiveVersion = result.hostActiveVersion;
326 });
327 };
328
329 $scope.loadFirmwares();
330 }
331 ]);
Michael Davis43366db2017-05-15 18:12:35 -0500332})(angular);