blob: 6f8ec07053b467253735c1228deb4369edbe8859 [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',
Gunnar Mills6f4e12e2019-05-23 11:11:42 -050014 '$anchorScroll', 'Constants', '$interval', '$q', '$timeout', 'toastService',
Andrew Geisslerd27bb132018-05-24 11:07:27 -070015 function(
Gunnar Mills806d39b2018-09-26 11:47:25 -050016 $scope, $window, APIUtils, dataService, $location, $anchorScroll,
Gunnar Mills6f4e12e2019-05-23 11:11:42 -050017 Constants, $interval, $q, $timeout, toastService) {
Gunnar Mills806d39b2018-09-26 11:47:25 -050018 $scope.dataService = dataService;
19
Andrew Geisslerd27bb132018-05-24 11:07:27 -070020 // Scroll to target anchor
21 $scope.gotoAnchor = function() {
22 $location.hash('upload');
23 $anchorScroll();
24 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050025
Andrew Geisslerd27bb132018-05-24 11:07:27 -070026 $scope.firmwares = [];
27 $scope.bmcActiveVersion = '';
28 $scope.hostActiveVersion = '';
Andrew Geisslerd27bb132018-05-24 11:07:27 -070029 $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;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070042 $scope.activate = {reboot: true};
Iftekharul Islamc0161392017-06-14 15:46:15 -050043
Andrew Geisslerd27bb132018-05-24 11:07:27 -070044 var pollActivationTimer = undefined;
45 var pollDownloadTimer = undefined;
Gunnar Mills033025f2018-03-06 14:49:40 -060046
Andrew Geisslerd27bb132018-05-24 11:07:27 -070047 $scope.error = {modal_title: '', title: '', desc: '', type: 'warning'};
Iftekharul Islamc0161392017-06-14 15:46:15 -050048
Andrew Geisslerd27bb132018-05-24 11:07:27 -070049 $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 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050055
Andrew Geisslerd27bb132018-05-24 11:07:27 -070056 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 //@TODO: display an error message if image "Failed"
63 if (((/\.Active$/).test(state.data)) ||
64 ((/\.Failed$/).test(state.data))) {
65 $interval.cancel(pollActivationTimer);
66 pollActivationTimer = undefined;
67 deferred.resolve(state);
68 }
69 },
70 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070071 $interval.cancel(pollActivationTimer);
72 pollActivationTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070073 console.log(error);
74 deferred.reject(error);
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070075 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -070076 var now = new Date();
77 if ((now.getTime() - startTime.getTime()) >=
78 Constants.TIMEOUT.ACTIVATION) {
79 $interval.cancel(pollActivationTimer);
80 pollActivationTimer = undefined;
81 console.log('Time out activating image, ' + imageId);
82 deferred.reject(
83 'Time out. Image did not activate in allotted time.');
84 }
85 }, Constants.POLL_INTERVALS.ACTIVATION);
86 return deferred.promise;
87 }
88
89 $scope.activateConfirmed = function() {
90 APIUtils.activateImage($scope.activate_image_id)
91 .then(
92 function(state) {
93 $scope.loadFirmwares();
94 return state;
95 },
96 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -060097 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -060098 toastService.error('Unable to activate image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -070099 })
100 .then(function(activationState) {
101 waitForActive($scope.activate_image_id)
102 .then(
103 function(state) {
104 $scope.loadFirmwares();
105 },
106 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600107 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600108 toastService.error('Unable to activate image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700109 })
110 .then(function(state) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700111 if ($scope.activate.reboot &&
112 ($scope.activate_image_type == 'BMC')) {
113 // Despite the new image being active, issue,
114 // https://github.com/openbmc/openbmc/issues/2764, can
115 // cause a system to brick, if the system reboots before
116 // the service to set the U-Boot variables is complete.
117 // Wait 10 seconds before rebooting to ensure this service
118 // is complete. This issue is fixed in newer images, but
119 // the user may be updating from an older image that does
120 // not that have this fix.
121 // TODO: remove this timeout after sufficient time has
122 // passed.
123 $timeout(function() {
Dixsie Wolmersc57ec322019-04-26 12:58:51 -0500124 APIUtils.bmcReboot().then(
125 function(response) {
126 toastService.success('BMC is rebooting.')
127 },
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700128 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600129 console.log(JSON.stringify(error));
Dixsie Wolmersc57ec322019-04-26 12:58:51 -0500130 toastService.error('Unable to reboot BMC.');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700131 });
132 }, 10000);
133 }
beccabroekc3abaa92018-08-14 13:47:18 -0500134 if ($scope.activate.reboot &&
135 ($scope.activate_image_type == 'Host')) {
136 // If image type being activated is a host image, the
137 // current power status of the server determines if the
138 // server should power on or reboot.
139 if ($scope.isServerOff()) {
140 powerOn();
141 } else {
142 warmReboot();
143 }
144 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700145 });
146 });
147 $scope.activate_confirm = false;
148 };
beccabroekc3abaa92018-08-14 13:47:18 -0500149 function powerOn() {
150 dataService.setUnreachableState();
151 APIUtils.hostPowerOn()
152 .then(function(response) {
153 return response;
154 })
155 .then(function(lastStatus) {
156 return APIUtils.pollHostStatusTillOn();
157 })
158 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600159 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600160 toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500161 });
162 };
163 function warmReboot() {
164 $scope.uploading = true;
165 dataService.setUnreachableState();
166 APIUtils.hostReboot()
167 .then(function(response) {
168 return response;
169 })
170 .then(function(lastStatus) {
171 return APIUtils.pollHostStatusTilReboot();
172 })
173 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600174 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600175 toastService.error(
176 Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500177 });
178 };
179 $scope.isServerOff = function() {
180 return dataService.server_state === Constants.HOST_STATE_TEXT.off;
181 };
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700182
183 $scope.upload = function() {
184 if ($scope.file) {
185 $scope.uploading = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700186 APIUtils.uploadImage($scope.file)
187 .then(
188 function(response) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700189 $scope.uploading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600190 toastService.success(
beccabroek90ae95e2019-01-15 16:55:57 -0600191 'Image file "' + $scope.file.name +
192 '" has been uploaded');
193 $scope.file = '';
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700194 $scope.loadFirmwares();
195 },
196 function(error) {
197 $scope.uploading = false;
198 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600199 toastService.error('Unable to upload image file');
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700200 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700201 }
202 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500203
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700204 // TODO: openbmc/openbmc#1691 Add support to return
205 // the id of the newly created image, downloaded via
206 // tftp. Polling the number of software objects is a
207 // near term solution.
208 function waitForDownload() {
209 var deferred = $q.defer();
210 var startTime = new Date();
211 pollDownloadTimer = $interval(function() {
212 var now = new Date();
213 if ((now.getTime() - startTime.getTime()) >=
214 Constants.TIMEOUT.DOWNLOAD_IMAGE) {
215 $interval.cancel(pollDownloadTimer);
216 pollDownloadTimer = undefined;
217 deferred.reject(
218 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700219 }
Iftekharul Islamc0161392017-06-14 15:46:15 -0500220
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700221 APIUtils.getFirmwares().then(
222 function(response) {
223 if (response.data.length === $scope.firmwares.length + 1) {
224 $interval.cancel(pollDownloadTimer);
225 pollDownloadTimer = undefined;
226 deferred.resolve(response.data);
227 }
228 },
229 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700230 $interval.cancel(pollDownloadTimer);
231 pollDownloadTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700232 deferred.reject(error);
233 });
234 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500235
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700236 return deferred.promise;
237 }
238
239 $scope.download = function() {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700240 if (!$scope.download_host || !$scope.download_filename) {
beccabroek27ce84d2019-02-05 15:43:17 -0600241 toastService.error(
242 'TFTP server IP address and file name are required!');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700243 return false;
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700244 }
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500245
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700246 $scope.downloading = true;
247 APIUtils.getFirmwares()
248 .then(function(response) {
249 $scope.firmwares = response.data;
250 })
251 .then(function() {
252 return APIUtils
253 .downloadImage($scope.download_host, $scope.download_filename)
254 .then(function(downloadStatus) {
255 return downloadStatus;
256 });
257 })
258 .then(function(downloadStatus) {
259 return waitForDownload();
260 })
261 .then(
262 function(newFirmwareList) {
263 $scope.download_host = '';
264 $scope.download_filename = '';
265 $scope.downloading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600266 toastService.success('Download complete');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700267 $scope.loadFirmwares();
268 },
269 function(error) {
270 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600271 toastService.error(
beccabroek90ae95e2019-01-15 16:55:57 -0600272 'Image file from TFTP server "' + $scope.download_host +
273 '" could not be downloaded');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700274 $scope.downloading = false;
275 });
276 };
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500277
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700278 $scope.changePriority = function(imageId, imageVersion, from, to) {
279 $scope.priority_image_id = imageId;
280 $scope.priority_image_version = imageVersion;
281 $scope.priority_from = from;
282 $scope.priority_to = to;
283 $scope.confirm_priority = true;
284 };
285
286 $scope.confirmChangePriority = function() {
287 $scope.loading = true;
288 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to)
289 .then(function(response) {
290 $scope.loading = false;
291 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600292 toastService.error('Unable to update boot priority');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700293 } else {
294 $scope.loadFirmwares();
295 }
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700296 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700297 $scope.confirm_priority = false;
298 };
299 $scope.deleteImage = function(imageId, imageVersion) {
300 $scope.delete_image_id = imageId;
301 $scope.delete_image_version = imageVersion;
302 $scope.confirm_delete = true;
303 };
304 $scope.confirmDeleteImage = function() {
305 $scope.loading = true;
306 APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
307 $scope.loading = false;
308 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600309 toastService.error('Unable to delete image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700310 } else {
311 $scope.loadFirmwares();
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700312 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700313 });
314 $scope.confirm_delete = false;
315 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500316
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700317 $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}};
Iftekharul Islamc0161392017-06-14 15:46:15 -0500318
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700319 $scope.loadFirmwares = function() {
320 APIUtils.getFirmwares().then(function(result) {
321 $scope.firmwares = result.data;
322 $scope.bmcActiveVersion = result.bmcActiveVersion;
323 $scope.hostActiveVersion = result.hostActiveVersion;
324 });
325 };
326
327 $scope.loadFirmwares();
328 }
329 ]);
Michael Davis43366db2017-05-15 18:12:35 -0500330})(angular);