blob: 04a7c782f9e2d0d6f61fa93af99addc72fa174c3 [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', [
13 '$scope', '$window', 'APIUtils', 'dataService', '$location',
beccabroekc3abaa92018-08-14 13:47:18 -050014 '$anchorScroll', 'Constants', '$interval', '$q', '$timeout', '$interpolate',
Andrew Geisslerd27bb132018-05-24 11:07:27 -070015 function(
16 $scope, $window, APIUtils, dataService, $location, $anchorScroll,
beccabroekc3abaa92018-08-14 13:47:18 -050017 Constants, $interval, $q, $timeout, $interpolate) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -070018 $scope.dataService = dataService;
Michael Davis43366db2017-05-15 18:12:35 -050019
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 = '';
29 $scope.display_error = false;
30 $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;
43 $scope.upload_success = false;
44 $scope.activate = {reboot: true};
45 $scope.download_error_msg = '';
46 $scope.download_success = false;
Iftekharul Islamc0161392017-06-14 15:46:15 -050047
Andrew Geisslerd27bb132018-05-24 11:07:27 -070048 var pollActivationTimer = undefined;
49 var pollDownloadTimer = undefined;
Gunnar Mills033025f2018-03-06 14:49:40 -060050
Andrew Geisslerd27bb132018-05-24 11:07:27 -070051 $scope.error = {modal_title: '', title: '', desc: '', type: 'warning'};
Iftekharul Islamc0161392017-06-14 15:46:15 -050052
Andrew Geisslerd27bb132018-05-24 11:07:27 -070053 $scope.activateImage = function(imageId, imageVersion, imageType) {
54 $scope.activate_image_id = imageId;
55 $scope.activate_image_version = imageVersion;
56 $scope.activate_image_type = imageType;
57 $scope.activate_confirm = true;
58 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050059
Andrew Geisslerd27bb132018-05-24 11:07:27 -070060 $scope.displayError = function(data) {
61 $scope.error = data;
62 $scope.display_error = true;
63 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050064
Andrew Geisslerd27bb132018-05-24 11:07:27 -070065 function waitForActive(imageId) {
66 var deferred = $q.defer();
67 var startTime = new Date();
68 pollActivationTimer = $interval(function() {
69 APIUtils.getActivation(imageId).then(
70 function(state) {
71 //@TODO: display an error message if image "Failed"
72 if (((/\.Active$/).test(state.data)) ||
73 ((/\.Failed$/).test(state.data))) {
74 $interval.cancel(pollActivationTimer);
75 pollActivationTimer = undefined;
76 deferred.resolve(state);
77 }
78 },
79 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070080 $interval.cancel(pollActivationTimer);
81 pollActivationTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070082 console.log(error);
83 deferred.reject(error);
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070084 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -070085 var now = new Date();
86 if ((now.getTime() - startTime.getTime()) >=
87 Constants.TIMEOUT.ACTIVATION) {
88 $interval.cancel(pollActivationTimer);
89 pollActivationTimer = undefined;
90 console.log('Time out activating image, ' + imageId);
91 deferred.reject(
92 'Time out. Image did not activate in allotted time.');
93 }
94 }, Constants.POLL_INTERVALS.ACTIVATION);
95 return deferred.promise;
96 }
97
98 $scope.activateConfirmed = function() {
99 APIUtils.activateImage($scope.activate_image_id)
100 .then(
101 function(state) {
102 $scope.loadFirmwares();
103 return state;
104 },
105 function(error) {
106 $scope.displayError({
107 modal_title: 'Error during activation call',
108 title: 'Error during activation call',
109 desc: JSON.stringify(error.data),
110 type: 'Error'
111 });
112 })
113 .then(function(activationState) {
114 waitForActive($scope.activate_image_id)
115 .then(
116 function(state) {
117 $scope.loadFirmwares();
118 },
119 function(error) {
120 $scope.displayError({
121 modal_title: 'Error during image activation',
122 title: 'Error during image activation',
123 desc: JSON.stringify(error.data),
124 type: 'Error'
125 });
126 })
127 .then(function(state) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700128 if ($scope.activate.reboot &&
129 ($scope.activate_image_type == 'BMC')) {
130 // Despite the new image being active, issue,
131 // https://github.com/openbmc/openbmc/issues/2764, can
132 // cause a system to brick, if the system reboots before
133 // the service to set the U-Boot variables is complete.
134 // Wait 10 seconds before rebooting to ensure this service
135 // is complete. This issue is fixed in newer images, but
136 // the user may be updating from an older image that does
137 // not that have this fix.
138 // TODO: remove this timeout after sufficient time has
139 // passed.
140 $timeout(function() {
141 APIUtils.bmcReboot(
142 function(response) {},
143 function(error) {
144 $scope.displayError({
145 modal_title: 'Error during BMC reboot',
146 title: 'Error during BMC reboot',
147 desc: JSON.stringify(error.data),
148 type: 'Error'
149 });
150 });
151 }, 10000);
152 }
beccabroekc3abaa92018-08-14 13:47:18 -0500153 if ($scope.activate.reboot &&
154 ($scope.activate_image_type == 'Host')) {
155 // If image type being activated is a host image, the
156 // current power status of the server determines if the
157 // server should power on or reboot.
158 if ($scope.isServerOff()) {
159 powerOn();
160 } else {
161 warmReboot();
162 }
163 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700164 });
165 });
166 $scope.activate_confirm = false;
167 };
beccabroekc3abaa92018-08-14 13:47:18 -0500168 function powerOn() {
169 dataService.setUnreachableState();
170 APIUtils.hostPowerOn()
171 .then(function(response) {
172 return response;
173 })
174 .then(function(lastStatus) {
175 return APIUtils.pollHostStatusTillOn();
176 })
177 .catch(function(error) {
178 dataService.activateErrorModal({
179 title: Constants.MESSAGES.POWER_OP.POWER_ON_FAILED,
180 description: error.statusText ?
181 $interpolate(
182 Constants.MESSAGES.ERROR_MESSAGE_DESC_TEMPLATE)(
183 {status: error.status, description: error.statusText}) :
184 error
185 });
186 });
187 };
188 function warmReboot() {
189 $scope.uploading = true;
190 dataService.setUnreachableState();
191 APIUtils.hostReboot()
192 .then(function(response) {
193 return response;
194 })
195 .then(function(lastStatus) {
196 return APIUtils.pollHostStatusTilReboot();
197 })
198 .catch(function(error) {
199 dataService.activateErrorModal({
200 title: Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED,
201 description: error.statusText ?
202 $interpolate(
203 Constants.MESSAGES.ERROR_MESSAGE_DESC_TEMPLATE)(
204 {status: error.status, description: error.statusText}) :
205 error
206 });
207 });
208 };
209 $scope.isServerOff = function() {
210 return dataService.server_state === Constants.HOST_STATE_TEXT.off;
211 };
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700212
213 $scope.upload = function() {
214 if ($scope.file) {
215 $scope.uploading = true;
216 $scope.upload_success = false;
217 APIUtils.uploadImage($scope.file)
218 .then(
219 function(response) {
220 $scope.file = '';
221 $scope.uploading = false;
222 $scope.upload_success = true;
223 $scope.loadFirmwares();
224 },
225 function(error) {
226 $scope.uploading = false;
227 console.log(error);
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700228 $scope.displayError({
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700229 modal_title: 'Error during image upload',
230 title: 'Error during image upload',
231 desc: error,
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700232 type: 'Error'
233 });
234 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700235 }
236 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500237
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700238 // TODO: openbmc/openbmc#1691 Add support to return
239 // the id of the newly created image, downloaded via
240 // tftp. Polling the number of software objects is a
241 // near term solution.
242 function waitForDownload() {
243 var deferred = $q.defer();
244 var startTime = new Date();
245 pollDownloadTimer = $interval(function() {
246 var now = new Date();
247 if ((now.getTime() - startTime.getTime()) >=
248 Constants.TIMEOUT.DOWNLOAD_IMAGE) {
249 $interval.cancel(pollDownloadTimer);
250 pollDownloadTimer = undefined;
251 deferred.reject(
252 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700253 }
Iftekharul Islamc0161392017-06-14 15:46:15 -0500254
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700255 APIUtils.getFirmwares().then(
256 function(response) {
257 if (response.data.length === $scope.firmwares.length + 1) {
258 $interval.cancel(pollDownloadTimer);
259 pollDownloadTimer = undefined;
260 deferred.resolve(response.data);
261 }
262 },
263 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700264 $interval.cancel(pollDownloadTimer);
265 pollDownloadTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700266 deferred.reject(error);
267 });
268 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500269
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700270 return deferred.promise;
271 }
272
273 $scope.download = function() {
274 $scope.download_success = false;
275 $scope.download_error_msg = '';
276 if (!$scope.download_host || !$scope.download_filename) {
277 $scope.download_error_msg = 'Field is required!';
278 return false;
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700279 }
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500280
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700281 $scope.downloading = true;
282 APIUtils.getFirmwares()
283 .then(function(response) {
284 $scope.firmwares = response.data;
285 })
286 .then(function() {
287 return APIUtils
288 .downloadImage($scope.download_host, $scope.download_filename)
289 .then(function(downloadStatus) {
290 return downloadStatus;
291 });
292 })
293 .then(function(downloadStatus) {
294 return waitForDownload();
295 })
296 .then(
297 function(newFirmwareList) {
298 $scope.download_host = '';
299 $scope.download_filename = '';
300 $scope.downloading = false;
301 $scope.download_success = true;
302 $scope.loadFirmwares();
303 },
304 function(error) {
305 console.log(error);
306 $scope.displayError({
307 modal_title: 'Error during downloading Image',
308 title: 'Error during downloading Image',
309 desc: error,
310 type: 'Error'
311 });
312 $scope.downloading = false;
313 });
314 };
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500315
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700316 $scope.changePriority = function(imageId, imageVersion, from, to) {
317 $scope.priority_image_id = imageId;
318 $scope.priority_image_version = imageVersion;
319 $scope.priority_from = from;
320 $scope.priority_to = to;
321 $scope.confirm_priority = true;
322 };
323
324 $scope.confirmChangePriority = function() {
325 $scope.loading = true;
326 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to)
327 .then(function(response) {
328 $scope.loading = false;
329 if (response.status == 'error') {
330 $scope.displayError({
331 modal_title: response.data.description,
332 title: response.data.description,
333 desc: response.data.exception,
334 type: 'Error'
335 });
336 } else {
337 $scope.loadFirmwares();
338 }
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700339 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700340 $scope.confirm_priority = false;
341 };
342 $scope.deleteImage = function(imageId, imageVersion) {
343 $scope.delete_image_id = imageId;
344 $scope.delete_image_version = imageVersion;
345 $scope.confirm_delete = true;
346 };
347 $scope.confirmDeleteImage = function() {
348 $scope.loading = true;
349 APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
350 $scope.loading = false;
351 if (response.status == 'error') {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700352 $scope.displayError({
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700353 modal_title: response.data.description,
354 title: response.data.description,
355 desc: response.data.exception,
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700356 type: 'Error'
357 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700358 } else {
359 $scope.loadFirmwares();
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700360 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700361 });
362 $scope.confirm_delete = false;
363 };
364 $scope.fileNameChanged = function() {
365 $scope.file_empty = false;
366 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500367
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700368 $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}};
Iftekharul Islamc0161392017-06-14 15:46:15 -0500369
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700370 $scope.loadFirmwares = function() {
371 APIUtils.getFirmwares().then(function(result) {
372 $scope.firmwares = result.data;
373 $scope.bmcActiveVersion = result.bmcActiveVersion;
374 $scope.hostActiveVersion = result.hostActiveVersion;
375 });
376 };
377
378 $scope.loadFirmwares();
379 }
380 ]);
Michael Davis43366db2017-05-15 18:12:35 -0500381})(angular);