blob: 89cf9c6dca7c1f9d2b780ace430a7147eddf381c [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) {
Dixsie Wolmersff7f1062019-10-04 12:49:32 -050062 let imageStateActive = (/\.Active$/).test(state.data);
63 let imageStateFailed = (/\.Failed$/).test(state.data);
64 if (imageStateActive || imageStateFailed) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -070065 $interval.cancel(pollActivationTimer);
66 pollActivationTimer = undefined;
Dixsie Wolmersff7f1062019-10-04 12:49:32 -050067 }
68 if (imageStateActive) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -070069 deferred.resolve(state);
Dixsie Wolmersff7f1062019-10-04 12:49:32 -050070 } else if (imageStateFailed) {
71 console.log('Image failed to activate: ', imageStateFailed);
72 toastService.error('Image failed to activate.');
73 deferred.reject(error);
Andrew Geisslerd27bb132018-05-24 11:07:27 -070074 }
75 },
76 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070077 $interval.cancel(pollActivationTimer);
78 pollActivationTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070079 console.log(error);
80 deferred.reject(error);
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070081 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -070082 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) {
beccabroek90ae95e2019-01-15 16:55:57 -0600103 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600104 toastService.error('Unable to activate image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700105 })
106 .then(function(activationState) {
107 waitForActive($scope.activate_image_id)
108 .then(
109 function(state) {
110 $scope.loadFirmwares();
111 },
112 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600113 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600114 toastService.error('Unable to activate image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700115 })
116 .then(function(state) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700117 if ($scope.activate.reboot &&
118 ($scope.activate_image_type == 'BMC')) {
119 // Despite the new image being active, issue,
120 // https://github.com/openbmc/openbmc/issues/2764, can
121 // cause a system to brick, if the system reboots before
122 // the service to set the U-Boot variables is complete.
123 // Wait 10 seconds before rebooting to ensure this service
124 // is complete. This issue is fixed in newer images, but
125 // the user may be updating from an older image that does
126 // not that have this fix.
127 // TODO: remove this timeout after sufficient time has
128 // passed.
129 $timeout(function() {
Dixsie Wolmersc57ec322019-04-26 12:58:51 -0500130 APIUtils.bmcReboot().then(
131 function(response) {
132 toastService.success('BMC is rebooting.')
133 },
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700134 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600135 console.log(JSON.stringify(error));
Dixsie Wolmersc57ec322019-04-26 12:58:51 -0500136 toastService.error('Unable to reboot BMC.');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700137 });
138 }, 10000);
139 }
beccabroekc3abaa92018-08-14 13:47:18 -0500140 if ($scope.activate.reboot &&
141 ($scope.activate_image_type == 'Host')) {
142 // If image type being activated is a host image, the
143 // current power status of the server determines if the
144 // server should power on or reboot.
145 if ($scope.isServerOff()) {
146 powerOn();
147 } else {
148 warmReboot();
149 }
150 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700151 });
152 });
153 $scope.activate_confirm = false;
154 };
beccabroekc3abaa92018-08-14 13:47:18 -0500155 function powerOn() {
156 dataService.setUnreachableState();
157 APIUtils.hostPowerOn()
158 .then(function(response) {
159 return response;
160 })
161 .then(function(lastStatus) {
162 return APIUtils.pollHostStatusTillOn();
163 })
164 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600165 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600166 toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500167 });
168 };
169 function warmReboot() {
170 $scope.uploading = true;
171 dataService.setUnreachableState();
172 APIUtils.hostReboot()
173 .then(function(response) {
174 return response;
175 })
176 .then(function(lastStatus) {
177 return APIUtils.pollHostStatusTilReboot();
178 })
179 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600180 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600181 toastService.error(
182 Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500183 });
184 };
185 $scope.isServerOff = function() {
186 return dataService.server_state === Constants.HOST_STATE_TEXT.off;
187 };
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700188
189 $scope.upload = function() {
190 if ($scope.file) {
191 $scope.uploading = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700192 APIUtils.uploadImage($scope.file)
193 .then(
194 function(response) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700195 $scope.uploading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600196 toastService.success(
beccabroek90ae95e2019-01-15 16:55:57 -0600197 'Image file "' + $scope.file.name +
198 '" has been uploaded');
199 $scope.file = '';
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700200 $scope.loadFirmwares();
201 },
202 function(error) {
203 $scope.uploading = false;
204 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600205 toastService.error('Unable to upload image file');
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700206 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700207 }
208 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500209
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700210 // TODO: openbmc/openbmc#1691 Add support to return
211 // the id of the newly created image, downloaded via
212 // tftp. Polling the number of software objects is a
213 // near term solution.
214 function waitForDownload() {
215 var deferred = $q.defer();
216 var startTime = new Date();
217 pollDownloadTimer = $interval(function() {
218 var now = new Date();
219 if ((now.getTime() - startTime.getTime()) >=
220 Constants.TIMEOUT.DOWNLOAD_IMAGE) {
221 $interval.cancel(pollDownloadTimer);
222 pollDownloadTimer = undefined;
223 deferred.reject(
224 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700225 }
Iftekharul Islamc0161392017-06-14 15:46:15 -0500226
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700227 APIUtils.getFirmwares().then(
228 function(response) {
229 if (response.data.length === $scope.firmwares.length + 1) {
230 $interval.cancel(pollDownloadTimer);
231 pollDownloadTimer = undefined;
232 deferred.resolve(response.data);
233 }
234 },
235 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700236 $interval.cancel(pollDownloadTimer);
237 pollDownloadTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700238 deferred.reject(error);
239 });
240 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500241
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700242 return deferred.promise;
243 }
244
245 $scope.download = function() {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700246 if (!$scope.download_host || !$scope.download_filename) {
beccabroek27ce84d2019-02-05 15:43:17 -0600247 toastService.error(
248 'TFTP server IP address and file name are required!');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700249 return false;
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700250 }
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500251
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700252 $scope.downloading = true;
253 APIUtils.getFirmwares()
254 .then(function(response) {
255 $scope.firmwares = response.data;
256 })
257 .then(function() {
258 return APIUtils
259 .downloadImage($scope.download_host, $scope.download_filename)
260 .then(function(downloadStatus) {
261 return downloadStatus;
262 });
263 })
264 .then(function(downloadStatus) {
265 return waitForDownload();
266 })
267 .then(
268 function(newFirmwareList) {
269 $scope.download_host = '';
270 $scope.download_filename = '';
271 $scope.downloading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600272 toastService.success('Download complete');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700273 $scope.loadFirmwares();
274 },
275 function(error) {
276 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600277 toastService.error(
beccabroek90ae95e2019-01-15 16:55:57 -0600278 'Image file from TFTP server "' + $scope.download_host +
279 '" could not be downloaded');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700280 $scope.downloading = false;
281 });
282 };
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500283
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700284 $scope.changePriority = function(imageId, imageVersion, from, to) {
285 $scope.priority_image_id = imageId;
286 $scope.priority_image_version = imageVersion;
287 $scope.priority_from = from;
288 $scope.priority_to = to;
289 $scope.confirm_priority = true;
290 };
291
292 $scope.confirmChangePriority = function() {
293 $scope.loading = true;
294 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to)
295 .then(function(response) {
296 $scope.loading = false;
297 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600298 toastService.error('Unable to update boot priority');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700299 } else {
300 $scope.loadFirmwares();
301 }
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700302 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700303 $scope.confirm_priority = false;
304 };
305 $scope.deleteImage = function(imageId, imageVersion) {
306 $scope.delete_image_id = imageId;
307 $scope.delete_image_version = imageVersion;
308 $scope.confirm_delete = true;
309 };
310 $scope.confirmDeleteImage = function() {
311 $scope.loading = true;
312 APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
313 $scope.loading = false;
314 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600315 toastService.error('Unable to delete image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700316 } else {
317 $scope.loadFirmwares();
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700318 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700319 });
320 $scope.confirm_delete = false;
321 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500322
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700323 $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}};
Iftekharul Islamc0161392017-06-14 15:46:15 -0500324
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700325 $scope.loadFirmwares = function() {
326 APIUtils.getFirmwares().then(function(result) {
327 $scope.firmwares = result.data;
328 $scope.bmcActiveVersion = result.bmcActiveVersion;
329 $scope.hostActiveVersion = result.hostActiveVersion;
330 });
331 };
332
333 $scope.loadFirmwares();
334 }
335 ]);
Michael Davis43366db2017-05-15 18:12:35 -0500336})(angular);