blob: 50deff313e546b638077916a71ee3bd1b5d07466 [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 Millse13275b2018-09-20 16:29:17 -050013 '$scope', '$window', 'APIUtils', '$location', '$anchorScroll', 'Constants',
14 '$interval', '$q', '$timeout', '$interpolate',
Andrew Geisslerd27bb132018-05-24 11:07:27 -070015 function(
Gunnar Millse13275b2018-09-20 16:29:17 -050016 $scope, $window, APIUtils, $location, $anchorScroll, Constants,
17 $interval, $q, $timeout, $interpolate) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -070018 // Scroll to target anchor
19 $scope.gotoAnchor = function() {
20 $location.hash('upload');
21 $anchorScroll();
22 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050023
Andrew Geisslerd27bb132018-05-24 11:07:27 -070024 $scope.firmwares = [];
25 $scope.bmcActiveVersion = '';
26 $scope.hostActiveVersion = '';
27 $scope.display_error = false;
28 $scope.activate_confirm = false;
29 $scope.delete_image_id = '';
30 $scope.delete_image_version = '';
31 $scope.activate_image_id = '';
32 $scope.activate_image_version = '';
33 $scope.activate_image_type = '';
34 $scope.priority_image_id = '';
35 $scope.priority_image_version = '';
36 $scope.priority_from = -1;
37 $scope.priority_to = -1;
38 $scope.confirm_priority = false;
39 $scope.file_empty = true;
40 $scope.uploading = false;
41 $scope.upload_success = false;
42 $scope.activate = {reboot: true};
43 $scope.download_error_msg = '';
44 $scope.download_success = false;
Iftekharul Islamc0161392017-06-14 15:46:15 -050045
Andrew Geisslerd27bb132018-05-24 11:07:27 -070046 var pollActivationTimer = undefined;
47 var pollDownloadTimer = undefined;
Gunnar Mills033025f2018-03-06 14:49:40 -060048
Andrew Geisslerd27bb132018-05-24 11:07:27 -070049 $scope.error = {modal_title: '', title: '', desc: '', type: 'warning'};
Iftekharul Islamc0161392017-06-14 15:46:15 -050050
Andrew Geisslerd27bb132018-05-24 11:07:27 -070051 $scope.activateImage = function(imageId, imageVersion, imageType) {
52 $scope.activate_image_id = imageId;
53 $scope.activate_image_version = imageVersion;
54 $scope.activate_image_type = imageType;
55 $scope.activate_confirm = true;
56 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050057
Andrew Geisslerd27bb132018-05-24 11:07:27 -070058 $scope.displayError = function(data) {
59 $scope.error = data;
60 $scope.display_error = true;
61 };
Iftekharul Islamc0161392017-06-14 15:46:15 -050062
Andrew Geisslerd27bb132018-05-24 11:07:27 -070063 function waitForActive(imageId) {
64 var deferred = $q.defer();
65 var startTime = new Date();
66 pollActivationTimer = $interval(function() {
67 APIUtils.getActivation(imageId).then(
68 function(state) {
69 //@TODO: display an error message if image "Failed"
70 if (((/\.Active$/).test(state.data)) ||
71 ((/\.Failed$/).test(state.data))) {
72 $interval.cancel(pollActivationTimer);
73 pollActivationTimer = undefined;
74 deferred.resolve(state);
75 }
76 },
77 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070078 $interval.cancel(pollActivationTimer);
79 pollActivationTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -070080 console.log(error);
81 deferred.reject(error);
Andrew Geisslerba5e3f32018-05-24 10:58:00 -070082 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -070083 var now = new Date();
84 if ((now.getTime() - startTime.getTime()) >=
85 Constants.TIMEOUT.ACTIVATION) {
86 $interval.cancel(pollActivationTimer);
87 pollActivationTimer = undefined;
88 console.log('Time out activating image, ' + imageId);
89 deferred.reject(
90 'Time out. Image did not activate in allotted time.');
91 }
92 }, Constants.POLL_INTERVALS.ACTIVATION);
93 return deferred.promise;
94 }
95
96 $scope.activateConfirmed = function() {
97 APIUtils.activateImage($scope.activate_image_id)
98 .then(
99 function(state) {
100 $scope.loadFirmwares();
101 return state;
102 },
103 function(error) {
104 $scope.displayError({
105 modal_title: 'Error during activation call',
106 title: 'Error during activation call',
107 desc: JSON.stringify(error.data),
108 type: 'Error'
109 });
110 })
111 .then(function(activationState) {
112 waitForActive($scope.activate_image_id)
113 .then(
114 function(state) {
115 $scope.loadFirmwares();
116 },
117 function(error) {
118 $scope.displayError({
119 modal_title: 'Error during image activation',
120 title: 'Error during image activation',
121 desc: JSON.stringify(error.data),
122 type: 'Error'
123 });
124 })
125 .then(function(state) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700126 if ($scope.activate.reboot &&
127 ($scope.activate_image_type == 'BMC')) {
128 // Despite the new image being active, issue,
129 // https://github.com/openbmc/openbmc/issues/2764, can
130 // cause a system to brick, if the system reboots before
131 // the service to set the U-Boot variables is complete.
132 // Wait 10 seconds before rebooting to ensure this service
133 // is complete. This issue is fixed in newer images, but
134 // the user may be updating from an older image that does
135 // not that have this fix.
136 // TODO: remove this timeout after sufficient time has
137 // passed.
138 $timeout(function() {
139 APIUtils.bmcReboot(
140 function(response) {},
141 function(error) {
142 $scope.displayError({
143 modal_title: 'Error during BMC reboot',
144 title: 'Error during BMC reboot',
145 desc: JSON.stringify(error.data),
146 type: 'Error'
147 });
148 });
149 }, 10000);
150 }
beccabroekc3abaa92018-08-14 13:47:18 -0500151 if ($scope.activate.reboot &&
152 ($scope.activate_image_type == 'Host')) {
153 // If image type being activated is a host image, the
154 // current power status of the server determines if the
155 // server should power on or reboot.
156 if ($scope.isServerOff()) {
157 powerOn();
158 } else {
159 warmReboot();
160 }
161 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700162 });
163 });
164 $scope.activate_confirm = false;
165 };
beccabroekc3abaa92018-08-14 13:47:18 -0500166 function powerOn() {
167 dataService.setUnreachableState();
168 APIUtils.hostPowerOn()
169 .then(function(response) {
170 return response;
171 })
172 .then(function(lastStatus) {
173 return APIUtils.pollHostStatusTillOn();
174 })
175 .catch(function(error) {
176 dataService.activateErrorModal({
177 title: Constants.MESSAGES.POWER_OP.POWER_ON_FAILED,
178 description: error.statusText ?
179 $interpolate(
180 Constants.MESSAGES.ERROR_MESSAGE_DESC_TEMPLATE)(
181 {status: error.status, description: error.statusText}) :
182 error
183 });
184 });
185 };
186 function warmReboot() {
187 $scope.uploading = true;
188 dataService.setUnreachableState();
189 APIUtils.hostReboot()
190 .then(function(response) {
191 return response;
192 })
193 .then(function(lastStatus) {
194 return APIUtils.pollHostStatusTilReboot();
195 })
196 .catch(function(error) {
197 dataService.activateErrorModal({
198 title: Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED,
199 description: error.statusText ?
200 $interpolate(
201 Constants.MESSAGES.ERROR_MESSAGE_DESC_TEMPLATE)(
202 {status: error.status, description: error.statusText}) :
203 error
204 });
205 });
206 };
207 $scope.isServerOff = function() {
208 return dataService.server_state === Constants.HOST_STATE_TEXT.off;
209 };
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700210
211 $scope.upload = function() {
212 if ($scope.file) {
213 $scope.uploading = true;
214 $scope.upload_success = false;
215 APIUtils.uploadImage($scope.file)
216 .then(
217 function(response) {
218 $scope.file = '';
219 $scope.uploading = false;
220 $scope.upload_success = true;
221 $scope.loadFirmwares();
222 },
223 function(error) {
224 $scope.uploading = false;
225 console.log(error);
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700226 $scope.displayError({
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700227 modal_title: 'Error during image upload',
228 title: 'Error during image upload',
229 desc: error,
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700230 type: 'Error'
231 });
232 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700233 }
234 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500235
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700236 // TODO: openbmc/openbmc#1691 Add support to return
237 // the id of the newly created image, downloaded via
238 // tftp. Polling the number of software objects is a
239 // near term solution.
240 function waitForDownload() {
241 var deferred = $q.defer();
242 var startTime = new Date();
243 pollDownloadTimer = $interval(function() {
244 var now = new Date();
245 if ((now.getTime() - startTime.getTime()) >=
246 Constants.TIMEOUT.DOWNLOAD_IMAGE) {
247 $interval.cancel(pollDownloadTimer);
248 pollDownloadTimer = undefined;
249 deferred.reject(
250 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700251 }
Iftekharul Islamc0161392017-06-14 15:46:15 -0500252
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700253 APIUtils.getFirmwares().then(
254 function(response) {
255 if (response.data.length === $scope.firmwares.length + 1) {
256 $interval.cancel(pollDownloadTimer);
257 pollDownloadTimer = undefined;
258 deferred.resolve(response.data);
259 }
260 },
261 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700262 $interval.cancel(pollDownloadTimer);
263 pollDownloadTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700264 deferred.reject(error);
265 });
266 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500267
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700268 return deferred.promise;
269 }
270
271 $scope.download = function() {
272 $scope.download_success = false;
273 $scope.download_error_msg = '';
274 if (!$scope.download_host || !$scope.download_filename) {
275 $scope.download_error_msg = 'Field is required!';
276 return false;
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700277 }
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500278
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700279 $scope.downloading = true;
280 APIUtils.getFirmwares()
281 .then(function(response) {
282 $scope.firmwares = response.data;
283 })
284 .then(function() {
285 return APIUtils
286 .downloadImage($scope.download_host, $scope.download_filename)
287 .then(function(downloadStatus) {
288 return downloadStatus;
289 });
290 })
291 .then(function(downloadStatus) {
292 return waitForDownload();
293 })
294 .then(
295 function(newFirmwareList) {
296 $scope.download_host = '';
297 $scope.download_filename = '';
298 $scope.downloading = false;
299 $scope.download_success = true;
300 $scope.loadFirmwares();
301 },
302 function(error) {
303 console.log(error);
304 $scope.displayError({
305 modal_title: 'Error during downloading Image',
306 title: 'Error during downloading Image',
307 desc: error,
308 type: 'Error'
309 });
310 $scope.downloading = false;
311 });
312 };
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500313
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700314 $scope.changePriority = function(imageId, imageVersion, from, to) {
315 $scope.priority_image_id = imageId;
316 $scope.priority_image_version = imageVersion;
317 $scope.priority_from = from;
318 $scope.priority_to = to;
319 $scope.confirm_priority = true;
320 };
321
322 $scope.confirmChangePriority = function() {
323 $scope.loading = true;
324 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to)
325 .then(function(response) {
326 $scope.loading = false;
327 if (response.status == 'error') {
328 $scope.displayError({
329 modal_title: response.data.description,
330 title: response.data.description,
331 desc: response.data.exception,
332 type: 'Error'
333 });
334 } else {
335 $scope.loadFirmwares();
336 }
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700337 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700338 $scope.confirm_priority = false;
339 };
340 $scope.deleteImage = function(imageId, imageVersion) {
341 $scope.delete_image_id = imageId;
342 $scope.delete_image_version = imageVersion;
343 $scope.confirm_delete = true;
344 };
345 $scope.confirmDeleteImage = function() {
346 $scope.loading = true;
347 APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
348 $scope.loading = false;
349 if (response.status == 'error') {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700350 $scope.displayError({
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700351 modal_title: response.data.description,
352 title: response.data.description,
353 desc: response.data.exception,
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700354 type: 'Error'
355 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700356 } else {
357 $scope.loadFirmwares();
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700358 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700359 });
360 $scope.confirm_delete = false;
361 };
362 $scope.fileNameChanged = function() {
363 $scope.file_empty = false;
364 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500365
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700366 $scope.filters = {bmc: {imageType: 'BMC'}, host: {imageType: 'Host'}};
Iftekharul Islamc0161392017-06-14 15:46:15 -0500367
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700368 $scope.loadFirmwares = function() {
369 APIUtils.getFirmwares().then(function(result) {
370 $scope.firmwares = result.data;
371 $scope.bmcActiveVersion = result.bmcActiveVersion;
372 $scope.hostActiveVersion = result.hostActiveVersion;
373 });
374 };
375
376 $scope.loadFirmwares();
377 }
378 ]);
Michael Davis43366db2017-05-15 18:12:35 -0500379})(angular);