blob: 8d4926daa89d18b4f89bf0d6b665515516b63144 [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',
beccabroek90ae95e2019-01-15 16:55:57 -060015 'ngToast',
Andrew Geisslerd27bb132018-05-24 11:07:27 -070016 function(
Gunnar Mills806d39b2018-09-26 11:47:25 -050017 $scope, $window, APIUtils, dataService, $location, $anchorScroll,
beccabroek90ae95e2019-01-15 16:55:57 -060018 Constants, $interval, $q, $timeout, $interpolate, ngToast) {
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));
99 ngToast.danger('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));
109 ngToast.danger('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() {
125 APIUtils.bmcReboot(
126 function(response) {},
127 function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600128 console.log(JSON.stringify(error));
129 ngToast.danger('Unable to reboot BMC');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700130 });
131 }, 10000);
132 }
beccabroekc3abaa92018-08-14 13:47:18 -0500133 if ($scope.activate.reboot &&
134 ($scope.activate_image_type == 'Host')) {
135 // If image type being activated is a host image, the
136 // current power status of the server determines if the
137 // server should power on or reboot.
138 if ($scope.isServerOff()) {
139 powerOn();
140 } else {
141 warmReboot();
142 }
143 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700144 });
145 });
146 $scope.activate_confirm = false;
147 };
beccabroekc3abaa92018-08-14 13:47:18 -0500148 function powerOn() {
149 dataService.setUnreachableState();
150 APIUtils.hostPowerOn()
151 .then(function(response) {
152 return response;
153 })
154 .then(function(lastStatus) {
155 return APIUtils.pollHostStatusTillOn();
156 })
157 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600158 console.log(JSON.stringify(error));
159 ngToast.danger(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500160 });
161 };
162 function warmReboot() {
163 $scope.uploading = true;
164 dataService.setUnreachableState();
165 APIUtils.hostReboot()
166 .then(function(response) {
167 return response;
168 })
169 .then(function(lastStatus) {
170 return APIUtils.pollHostStatusTilReboot();
171 })
172 .catch(function(error) {
beccabroek90ae95e2019-01-15 16:55:57 -0600173 console.log(JSON.stringify(error));
174 ngToast.danger(Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
beccabroekc3abaa92018-08-14 13:47:18 -0500175 });
176 };
177 $scope.isServerOff = function() {
178 return dataService.server_state === Constants.HOST_STATE_TEXT.off;
179 };
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700180
181 $scope.upload = function() {
182 if ($scope.file) {
183 $scope.uploading = true;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700184 APIUtils.uploadImage($scope.file)
185 .then(
186 function(response) {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700187 $scope.uploading = false;
beccabroek90ae95e2019-01-15 16:55:57 -0600188 ngToast.success(
189 'Image file "' + $scope.file.name +
190 '" has been uploaded');
191 $scope.file = '';
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700192 $scope.loadFirmwares();
193 },
194 function(error) {
195 $scope.uploading = false;
196 console.log(error);
beccabroek90ae95e2019-01-15 16:55:57 -0600197 ngToast.danger('Unable to upload image file');
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700198 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700199 }
200 };
Iftekharul Islamc0161392017-06-14 15:46:15 -0500201
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700202 // TODO: openbmc/openbmc#1691 Add support to return
203 // the id of the newly created image, downloaded via
204 // tftp. Polling the number of software objects is a
205 // near term solution.
206 function waitForDownload() {
207 var deferred = $q.defer();
208 var startTime = new Date();
209 pollDownloadTimer = $interval(function() {
210 var now = new Date();
211 if ((now.getTime() - startTime.getTime()) >=
212 Constants.TIMEOUT.DOWNLOAD_IMAGE) {
213 $interval.cancel(pollDownloadTimer);
214 pollDownloadTimer = undefined;
215 deferred.reject(
216 new Error(Constants.MESSAGES.POLL.DOWNLOAD_IMAGE_TIMEOUT));
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700217 }
Iftekharul Islamc0161392017-06-14 15:46:15 -0500218
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700219 APIUtils.getFirmwares().then(
220 function(response) {
221 if (response.data.length === $scope.firmwares.length + 1) {
222 $interval.cancel(pollDownloadTimer);
223 pollDownloadTimer = undefined;
224 deferred.resolve(response.data);
225 }
226 },
227 function(error) {
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700228 $interval.cancel(pollDownloadTimer);
229 pollDownloadTimer = undefined;
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700230 deferred.reject(error);
231 });
232 }, Constants.POLL_INTERVALS.DOWNLOAD_IMAGE);
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500233
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700234 return deferred.promise;
235 }
236
237 $scope.download = function() {
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700238 if (!$scope.download_host || !$scope.download_filename) {
beccabroek90ae95e2019-01-15 16:55:57 -0600239 ngToast.danger('TFTP server IP address and file name are required!');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700240 return false;
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700241 }
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500242
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700243 $scope.downloading = true;
244 APIUtils.getFirmwares()
245 .then(function(response) {
246 $scope.firmwares = response.data;
247 })
248 .then(function() {
249 return APIUtils
250 .downloadImage($scope.download_host, $scope.download_filename)
251 .then(function(downloadStatus) {
252 return downloadStatus;
253 });
254 })
255 .then(function(downloadStatus) {
256 return waitForDownload();
257 })
258 .then(
259 function(newFirmwareList) {
260 $scope.download_host = '';
261 $scope.download_filename = '';
262 $scope.downloading = false;
beccabroek90ae95e2019-01-15 16:55:57 -0600263 ngToast.success('Download complete');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700264 $scope.loadFirmwares();
265 },
266 function(error) {
267 console.log(error);
beccabroek90ae95e2019-01-15 16:55:57 -0600268 ngToast.danger(
269 'Image file from TFTP server "' + $scope.download_host +
270 '" could not be downloaded');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700271 $scope.downloading = false;
272 });
273 };
CamVan Nguyen58301ec2018-04-20 21:33:01 -0500274
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700275 $scope.changePriority = function(imageId, imageVersion, from, to) {
276 $scope.priority_image_id = imageId;
277 $scope.priority_image_version = imageVersion;
278 $scope.priority_from = from;
279 $scope.priority_to = to;
280 $scope.confirm_priority = true;
281 };
282
283 $scope.confirmChangePriority = function() {
284 $scope.loading = true;
285 APIUtils.changePriority($scope.priority_image_id, $scope.priority_to)
286 .then(function(response) {
287 $scope.loading = false;
288 if (response.status == 'error') {
beccabroek90ae95e2019-01-15 16:55:57 -0600289 ngToast.danger('Unable to update boot priority');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700290 } else {
291 $scope.loadFirmwares();
292 }
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700293 });
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700294 $scope.confirm_priority = false;
295 };
296 $scope.deleteImage = function(imageId, imageVersion) {
297 $scope.delete_image_id = imageId;
298 $scope.delete_image_version = imageVersion;
299 $scope.confirm_delete = true;
300 };
301 $scope.confirmDeleteImage = function() {
302 $scope.loading = true;
303 APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
304 $scope.loading = false;
305 if (response.status == 'error') {
beccabroek90ae95e2019-01-15 16:55:57 -0600306 ngToast.danger('Unable to delete image');
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700307 } else {
308 $scope.loadFirmwares();
Andrew Geisslerba5e3f32018-05-24 10:58:00 -0700309 }
Andrew Geisslerd27bb132018-05-24 11:07:27 -0700310 });
311 $scope.confirm_delete = false;
312 };
313 $scope.fileNameChanged = function() {
314 $scope.file_empty = 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);