blob: c8ce099cd43b0df8dc1d780edb61dc93428ef41c [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() {
Dixsie Wolmersc57ec322019-04-26 12:58:51 -0500125 APIUtils.bmcReboot().then(
126 function(response) {
127 toastService.success('BMC is rebooting.')
128 },
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700129 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600130 console.log(JSON.stringify(error));
Dixsie Wolmersc57ec322019-04-26 12:58:51 -0500131 toastService.error('Unable to reboot BMC.');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700132 });
133 }, 10000);
134 }
beccabroekc3abaa92018-08-14 13:47:18 -0500135 if ($scope.activate.reboot &&
136 ($scope.activate_image_type == 'Host')) {
137 // If image type being activated is a host image, the
138 // current power status of the server determines if the
139 // server should power on or reboot.
140 if ($scope.isServerOff()) {
141 powerOn();
142 } else {
143 warmReboot();
144 }
145 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700146 });
147 });
148 $scope.activate_confirm = false;
149 };
beccabroekc3abaa92018-08-14 13:47:18 -0500150 function powerOn() {
151 dataService.setUnreachableState();
152 APIUtils.hostPowerOn()
153 .then(function(response) {
154 return response;
155 })
156 .then(function(lastStatus) {
157 return APIUtils.pollHostStatusTillOn();
158 })
159 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600160 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600161 toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500162 });
163 };
164 function warmReboot() {
165 $scope.uploading = true;
166 dataService.setUnreachableState();
167 APIUtils.hostReboot()
168 .then(function(response) {
169 return response;
170 })
171 .then(function(lastStatus) {
172 return APIUtils.pollHostStatusTilReboot();
173 })
174 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600175 console.log(JSON.stringify(error));
beccabroek27ce84d2019-02-05 15:43:17 -0600176 toastService.error(
177 Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500178 });
179 };
180 $scope.isServerOff = function() {
181 return dataService.server_state === Constants.HOST_STATE_TEXT.off;
182 };
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700183
184 $scope.upload = function() {
185 if ($scope.file) {
186 $scope.uploading = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700187 APIUtils.uploadImage($scope.file)
188 .then(
189 function(response) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700190 $scope.uploading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600191 toastService.success(
beccabroek90ae95e2019-01-15 16:55:57 -0600192 'Image file "' + $scope.file.name +
193 '" has been uploaded');
194 $scope.file = '';
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700195 $scope.loadFirmwares();
196 },
197 function(error) {
198 $scope.uploading = false;
199 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600200 toastService.error('Unable to upload image file');
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700201 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700202 }
203 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500204
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700205 // TODO: openbmc/openbmc#1691 Add support to return
206 // the id of the newly created image, downloaded via
207 // tftp. Polling the number of software objects is a
208 // near term solution.
209 function waitForDownload() {
210 var deferred = $q.defer();
211 var startTime = new Date();
212 pollDownloadTimer = $interval(function() {
213 var now = new Date();
214 if ((now.getTime() - startTime.getTime()) >=
215 Constants.TIMEOUT.DOWNLOAD_IMAGE) {
216 $interval.cancel(pollDownloadTimer);
217 pollDownloadTimer = undefined;
218 deferred.reject(
219 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700220 }
Iftekharul Islamc0161392017-06-14 15:46:15 -0500221
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700222 APIUtils.getFirmwares().then(
223 function(response) {
224 if (response.data.length === $scope.firmwares.length + 1) {
225 $interval.cancel(pollDownloadTimer);
226 pollDownloadTimer = undefined;
227 deferred.resolve(response.data);
228 }
229 },
230 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700231 $interval.cancel(pollDownloadTimer);
232 pollDownloadTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700233 deferred.reject(error);
234 });
235 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500236
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700237 return deferred.promise;
238 }
239
240 $scope.download = function() {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700241 if (!$scope.download_host || !$scope.download_filename) {
beccabroek27ce84d2019-02-05 15:43:17 -0600242 toastService.error(
243 'TFTP server IP address and file name are required!');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700244 return false;
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700245 }
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500246
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700247 $scope.downloading = true;
248 APIUtils.getFirmwares()
249 .then(function(response) {
250 $scope.firmwares = response.data;
251 })
252 .then(function() {
253 return APIUtils
254 .downloadImage($scope.download_host, $scope.download_filename)
255 .then(function(downloadStatus) {
256 return downloadStatus;
257 });
258 })
259 .then(function(downloadStatus) {
260 return waitForDownload();
261 })
262 .then(
263 function(newFirmwareList) {
264 $scope.download_host = '';
265 $scope.download_filename = '';
266 $scope.downloading = false;
beccabroek27ce84d2019-02-05 15:43:17 -0600267 toastService.success('Download complete');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700268 $scope.loadFirmwares();
269 },
270 function(error) {
271 console.log(error);
beccabroek27ce84d2019-02-05 15:43:17 -0600272 toastService.error(
beccabroek90ae95e2019-01-15 16:55:57 -0600273 'Image file from TFTP server "' + $scope.download_host +
274 '" could not be downloaded');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700275 $scope.downloading = false;
276 });
277 };
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500278
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700279 $scope.changePriority = function(imageId, imageVersion, from, to) {
280 $scope.priority_image_id = imageId;
281 $scope.priority_image_version = imageVersion;
282 $scope.priority_from = from;
283 $scope.priority_to = to;
284 $scope.confirm_priority = true;
285 };
286
287 $scope.confirmChangePriority = function() {
288 $scope.loading = true;
289 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to)
290 .then(function(response) {
291 $scope.loading = false;
292 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600293 toastService.error('Unable to update boot priority');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700294 } else {
295 $scope.loadFirmwares();
296 }
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700297 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700298 $scope.confirm_priority = false;
299 };
300 $scope.deleteImage = function(imageId, imageVersion) {
301 $scope.delete_image_id = imageId;
302 $scope.delete_image_version = imageVersion;
303 $scope.confirm_delete = true;
304 };
305 $scope.confirmDeleteImage = function() {
306 $scope.loading = true;
307 APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
308 $scope.loading = false;
309 if (response.status == 'error') {
beccabroek27ce84d2019-02-05 15:43:17 -0600310 toastService.error('Unable to delete image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700311 } else {
312 $scope.loadFirmwares();
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700313 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700314 });
315 $scope.confirm_delete = false;
316 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500317
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700318 $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}};
Iftekharul Islamc0161392017-06-14 15:46:15 -0500319
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700320 $scope.loadFirmwares = function() {
321 APIUtils.getFirmwares().then(function(result) {
322 $scope.firmwares = result.data;
323 $scope.bmcActiveVersion = result.bmcActiveVersion;
324 $scope.hostActiveVersion = result.hostActiveVersion;
325 });
326 };
327
328 $scope.loadFirmwares();
329 }
330 ]);
Michael Davis43366db2017-05-15 18:12:35 -0500331})(angular);