summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-attendedsysupgrade/htdocs
diff options
context:
space:
mode:
Diffstat (limited to 'applications/luci-app-attendedsysupgrade/htdocs')
-rw-r--r--applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js727
1 files changed, 359 insertions, 368 deletions
diff --git a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js
index b7cbdc2231..8c1a5a84b1 100644
--- a/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js
+++ b/applications/luci-app-attendedsysupgrade/htdocs/luci-static/resources/view/attendedsysupgrade/overview.js
@@ -7,6 +7,7 @@
'require poll';
'require request';
'require dom';
+'require fs';
var callPackagelist = rpc.declare({
object: 'rpc-sys',
@@ -21,432 +22,422 @@ var callSystemBoard = rpc.declare({
var callUpgradeStart = rpc.declare({
object: 'rpc-sys',
method: 'upgrade_start',
- params: [ 'keep' ],
+ params: ['keep'],
});
+/**
+ * Returns the branch of a given version. This helps to offer upgrades
+ * for point releases (aka within the branch).
+ *
+ * Logic:
+ * SNAPSHOT -> SNAPSHOT
+ * 21.02-SNAPSHOT -> 21.02
+ * 21.02.0-rc1 -> 21.02
+ * 19.07.8 -> 19.07
+ *
+ * @param {string} version
+ * Input version from which to determine the branch
+ * @returns {string}
+ * The determined branch
+ */
function get_branch(version) {
- // determine branch of a version
- // SNAPSHOT -> SNAPSHOT
- // 21.02-SNAPSHOT -> 21.02
- // 21.02.0-rc1 -> 21.02
- // 19.07.8 -> 19.07
return version.replace('-SNAPSHOT', '').split('.').slice(0, 2).join('.');
}
+/**
+ * The OpenWrt revision string contains both a hash as well as the number
+ * commits since the OpenWrt/LEDE reboot. It helps to determine if a
+ * snapshot is newer than another.
+ *
+ * @param {string} revision
+ * Revision string of a OpenWrt device
+ * @returns {integer}
+ * The number of commits since OpenWrt/LEDE reboot
+ */
function get_revision_count(revision) {
return parseInt(revision.substring(1).split('-')[0]);
}
-function error_api_connect(response) {
- ui.showModal(_('Error connecting to upgrade server'), [
- E('p', {},
- _('Could not reach API at "%s". Please try again later.')
- .format(response.url)),
- E('pre', {}, response.responseText),
- E('div', {
- class: 'right',
- },
- [
- E('div', {
- class: 'btn',
- click: ui.hideModal,
+return view.extend({
+ steps: {
+ init: _('10% Received build request'),
+ download_imagebuilder: _('20% Downloading ImageBuilder archive'),
+ unpack_imagebuilder: _('40% Setup ImageBuilder'),
+ calculate_packages_hash: _('60% Validate package selection'),
+ building_image: _('80% Generating firmware image')
},
- _('Close')),
- ]),
- ]);
-}
-function install_sysupgrade(url, keep, sha256) {
- displayStatus('notice spinning',
- E('p', _('Downloading firmware from server to browser')));
- request
- .get(url, {
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
+ data: {
+ url: '',
+ revision: '',
+ advanced_mode: 0,
},
- responseType: 'blob',
- })
- .then((response) => {
- var form_data = new FormData();
- form_data.append('sessionid', rpc.getSessionID());
- form_data.append('filename', '/tmp/firmware.bin');
- form_data.append('filemode', 600);
- form_data.append('filedata', response.blob());
-
- displayStatus('notice spinning',
- E('p', _('Uploading firmware from browser to device')));
- request
- .get(`${L.env.cgi_base}/cgi-upload`, {
- method: 'PUT',
- content: form_data,
- })
- .then((response) => response.json())
- .then((response) => {
- if (response.sha256sum != sha256) {
- displayStatus('warning', [
- E('b', _('Wrong checksum')),
- E('p',
- _('Error during download of firmware. Please try again')),
- E('div', {
- class: 'btn',
- click: ui.hideModal,
- },
- _('Close')),
- ]);
+
+ firmware: {
+ profile: '',
+ target: '',
+ version: '',
+ packages: [],
+ diff_packages: true,
+ },
+
+ handle200: function (response) {
+ res = response.json();
+ var image;
+ for (image of res.images) {
+ if (this.data.rootfs_type == image.filesystem) {
+ if (this.data.efi) {
+ if (image.type == 'combined-efi') {
+ break;
+ }
} else {
- displayStatus(
- 'warning spinning',
- E('p',
- _('Installing the sysupgrade. Do not unpower device!')));
- L.resolveDefault(callUpgradeStart(keep), {})
- .then((response) => {
- if (keep) {
- ui.awaitReconnect(window.location.host);
- } else {
- ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
+ if (image.type == 'sysupgrade' || image.type == 'combined') {
+ break;
}
- });
}
- });
- });
-}
-
-function request_sysupgrade(server_url, data) {
- var res, req;
-
- if (data.request_hash) {
- req = request.get(`${server_url}/api/v1/build/${data.request_hash}`);
- } else {
- req = request.post(`${server_url}/api/v1/build`, {
- profile: data.board_name,
- target: data.target,
- version: data.version,
- packages: data.packages,
- diff_packages: true,
- });
- }
-
- req.then((response) => {
- switch (response.status) {
- case 200:
- res = response.json();
- var image;
- for (image of res.images) {
- if (image.type == 'sysupgrade') {
- break;
- }
}
- if (image.name != undefined) {
- var sysupgrade_url = `${server_url}/store/${res.bin_dir}/${image.name}`;
-
- var keep = E('input', {
- type: 'checkbox',
- });
- keep.checked = true;
-
- var fields = [
- _('Version'),
- `${res.version_number} ${res.version_code}`,
- _('File'),
- E('a', {
- href: sysupgrade_url,
- },
- image.name),
- _('SHA256'),
- image.sha256,
- _('Build Date'),
- res.build_at,
- _('Target'),
- res.target,
- ];
-
- var table = E('div', {
- class: 'table',
- });
-
- for (var i = 0; i < fields.length; i += 2) {
- table.appendChild(E('tr', {
- class: 'tr',
- },
- [
- E('td', {
- class: 'td left',
- width: '33%',
- },
- [ fields[i] ]),
- E('td', {
- class: 'td left',
- },
- [ fields[i + 1] ]),
- ]));
- }
-
- var modal_body = [
- table,
- E('p', {class: 'mt-2'},
- E('label', {
- class: 'btn',
- },
- [
- keep, ' ',
- _('Keep settings and retain the current configuration')
- ])),
- E('div', {
- class: 'right',
- },
- [
- E('div', {
- class: 'btn',
- click: ui.hideModal,
- },
- _('Cancel')),
- ' ',
- E('div', {
- class: 'btn cbi-button-action',
- click: function() {
- install_sysupgrade(sysupgrade_url, keep.checked,
- image.sha256);
- },
- },
- _('Install Sysupgrade')),
- ]),
- ];
+ }
- ui.showModal(_('Successfully created sysupgrade image'), modal_body);
- }
+ if (image.name != undefined) {
+ var sysupgrade_url = `${this.data.url}/store/${res.bin_dir}/${image.name}`;
+
+ var keep = E('input', { type: 'checkbox' });
+ keep.checked = true;
- break;
- case 202:
- res = response.json();
- data.request_hash = res.request_hash;
-
- if ('queue_position' in res)
- displayStatus('notice spinning',
- E('p', _('Request in build queue position %s')
- .format(res.queue_position)));
- else
- displayStatus('notice spinning',
- E('p', _('Building firmware sysupgrade image')));
-
- setTimeout(function() { request_sysupgrade(server_url, data); }, 5000);
- break;
- case 400: // bad request
- case 422: // bad package
- case 500: // build failed
- res = response.json();
- var body = [
- E('p', {}, res.detail),
- E('p', {}, _('Please report the error message and request')),
- E('b', {}, _('Request to server:')),
- E('pre', {}, JSON.stringify(data, null, 4)),
+ var fields = [
+ _('Version'), `${res.version_number} ${res.version_code}`,
+ _('SHA256'), image.sha256,
];
- if (res.stdout) {
- body.push(E('b', {}, 'STDOUT:'));
- body.push(E('pre', {}, res.stdout));
+ if (this.data.advanced_mode == 1) {
+ fields.push(
+ _('Profile'), res.id,
+ _('Target'), res.target,
+ _('Build Date'), res.build_at,
+ _('Filename'), image.name,
+ _('Filesystem'), image.filesystem,
+ )
}
- if (res.stderr) {
- body.push(E('b', {}, 'STDERR:'));
- body.push(E('pre', {}, res.stderr));
+ fields.push('', E('a', { href: sysupgrade_url }, _('Download firmware image')))
+
+ var table = E('div', { class: 'table' });
+
+ for (var i = 0; i < fields.length; i += 2) {
+ table.appendChild(E('tr', { class: 'tr' }, [
+ E('td', { class: 'td left', width: '33%' }, [fields[i]]),
+ E('td', { class: 'td left' }, [fields[i + 1]]),
+ ]));
}
- body = body.concat([
- E('div', {
- class: 'right',
- },
- [
- E('div', {
- class: 'btn',
- click: ui.hideModal,
- },
- _('Close')),
- ]),
- ]);
- ui.showModal(_('Error building the sysupgrade'), body);
- break;
+ var modal_body = [
+ table,
+ E('p', { class: 'mt-2' },
+ E('label', { class: 'btn' }, [
+ keep, ' ',
+ _('Keep settings and retain the current configuration')
+ ])),
+ E('div', { class: 'right' }, [
+ E('div', { class: 'btn', click: ui.hideModal }, _('Cancel')), ' ',
+ E('button', {
+ 'class': 'btn cbi-button cbi-button-positive important',
+ 'click': ui.createHandlerFn(this, function () {
+ this.handleInstall(sysupgrade_url, keep.checked, image.sha256)
+ })
+ }, _('Install firmware image')),
+ ]),
+ ];
+
+ ui.showModal(_('Successfully created firmware image'), modal_body);
}
- });
-}
+ },
+
+ handle202: function (response) {
+ response = response.json();
+ this.data.request_hash = res.request_hash;
-async function check_sysupgrade(server_url, system_board, packages) {
- var {board_name} = system_board;
- var {target, version, revision} = system_board.release;
- var current_branch = get_branch(version);
- var advanced_mode =
- uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0;
- var candidates = [];
- var response;
-
- displayStatus('notice spinning',
- E('p', _('Searching for an available sysupgrade of %s - %s')
- .format(version, revision)));
-
- if (version.endsWith('SNAPSHOT')) {
- response =
- await request.get(`${server_url}/api/v1/revision/${version}/${target}`);
- if (!response.ok) {
- error_api_connect(response);
- return;
+ if ('queue_position' in response) {
+ ui.showModal(_('Queued...'), [
+ E('p', { 'class': 'spinning' }, _('Request in build queue position %s').format(response.queue_position))
+ ]);
+ } else {
+ ui.showModal(_('Building Firmware...'), [
+ E('p', { 'class': 'spinning' }, _('Progress: %s').format(this.steps[response.imagebuilder_status]))
+ ]);
}
+ },
- const remote_revision = response.json().revision;
+ handleError: function (response) {
+ response = response.json();
+ var body = [
+ E('p', {}, _('Server response: %s').format(response.detail)),
+ E('a', { href: 'https://github.com/openwrt/asu/issues' }, _('Please report the error message and request')),
+ E('p', {}, _('Request Data:')),
+ E('pre', {}, JSON.stringify({ ...this.data, ...this.firmware }, null, 4)),
+ ];
- if (get_revision_count(revision) < get_revision_count(remote_revision)) {
- candidates.push(version);
+ if (response.stdout) {
+ body.push(E('b', {}, 'STDOUT:'));
+ body.push(E('pre', {}, response.stdout));
}
- } else {
- response = await request.get(`${server_url}/api/overview`, {
- timeout: 8000,
- });
-
- if (!response.ok) {
- error_api_connect(response);
- return;
+
+ if (response.stderr) {
+ body.push(E('b', {}, 'STDERR:'));
+ body.push(E('pre', {}, response.stderr));
}
- const latest = response.json().latest;
+ body = body.concat([
+ E('div', { class: 'right' }, [
+ E('div', { class: 'btn', click: ui.hideModal }, _('Close')),
+ ]),
+ ]);
- for (let remote_version of latest) {
- var remote_branch = get_branch(remote_version);
+ ui.showModal(_('Error building the firmware image'), body);
+ },
- // already latest version installed
- if (version == remote_version) {
- break;
- }
+ handleRequest: function () {
+ var request_url = `${this.data.url}/api/v1/build`;
+ var method = "POST"
+ var content = this.firmware;
+
+ /**
+ * If `request_hash` is available use a GET request instead of
+ * sending the entire object.
+ */
+ if (this.data.request_hash) {
+ request_url += `/${this.data.request_hash}`;
+ content = {};
+ method = "GET"
+ }
- // skip branch upgrades outside the advanced mode
- if (current_branch != remote_branch && advanced_mode == 0) {
- continue;
- }
+ request.request(request_url, { method: method, content: content })
+ .then((response) => {
+ switch (response.status) {
+ case 202:
+ this.handle202(response);
+ break;
+ case 200:
+ poll.stop();
+ this.handle200(response);
+ break;
+ case 400: // bad request
+ case 422: // bad package
+ case 500: // build failed
+ poll.stop();
+ this.handleError(response);
+ break;
+ }
+ });
+ },
- candidates.unshift(remote_version);
+ handleInstall: function (url, keep, sha256) {
+ ui.showModal(_('Downloading...'), [
+ E('p', { 'class': 'spinning' }, _('Downloading firmware from server to browser'))
+ ]);
- // don't offer branches older than the current
- if (current_branch == remote_branch) {
- break;
- }
+ request.get(url, {
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ responseType: 'blob',
+ })
+ .then((response) => {
+ var form_data = new FormData();
+ form_data.append('sessionid', rpc.getSessionID());
+ form_data.append('filename', '/tmp/firmware.bin');
+ form_data.append('filemode', 600);
+ form_data.append('filedata', response.blob());
+
+ ui.showModal(_('Uploading...'), [
+ E('p', { 'class': 'spinning' }, _('Uploading firmware from browser to device'))
+ ]);
+
+ request
+ .get(`${L.env.cgi_base}/cgi-upload`, {
+ method: 'PUT',
+ content: form_data,
+ })
+ .then((response) => response.json())
+ .then((response) => {
+ if (response.sha256sum != sha256) {
+
+ ui.showModal(_('Wrong checksum'), [
+ E('p', _('Error during download of firmware. Please try again')),
+ E('div', { class: 'btn', click: ui.hideModal }, _('Close'))
+ ]);
+ } else {
+ ui.showModal(_('Installing...'), [
+ E('p', { class: 'spinning' }, _('Installing the sysupgrade. Do not unpower device!'))
+ ]);
+
+ L.resolveDefault(callUpgradeStart(keep), {})
+ .then((response) => {
+ if (keep) {
+ ui.awaitReconnect(window.location.host);
+ } else {
+ ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
+ }
+ });
+ }
+ });
+ });
+ },
+
+ handleCheck: function () {
+ var { url, revision } = this.data
+ var { version, target } = this.firmware
+ var candidates = [];
+ var response;
+ var request_url = `${url}/api/overview`;
+ if (version.endsWith('SNAPSHOT')) {
+ request_url = `${url}/api/v1/revision/${version}/${target}`;
}
- }
- if (candidates.length) {
- var m, s, o;
+ ui.showModal(_('Searching...'), [
+ E('p', { 'class': 'spinning' },
+ _('Searching for an available sysupgrade of %s - %s').format(version, revision))
+ ]);
- var mapdata = {
- request: {
- board_name: board_name,
- target: target,
- version: candidates[0],
- packages: Object.keys(packages).sort(),
- },
- };
+ L.resolveDefault(request.get(request_url))
+ .then(response => {
+ if (!response.ok) {
+ ui.showModal(_('Error connecting to upgrade server'), [
+ E('p', {}, _('Could not reach API at "%s". Please try again later.').format(response.url)),
+ E('pre', {}, response.responseText),
+ E('div', { class: 'right' }, [
+ E('div', { class: 'btn', click: ui.hideModal }, _('Close'))
+ ]),
+ ]);
+ return;
+ }
+ if (version.endsWith('SNAPSHOT')) {
+ const remote_revision = response.json().revision;
+ if (get_revision_count(revision) < get_revision_count(remote_revision)) {
+ candidates.push([version, remote_revision]);
+ }
+ } else {
+ const latest = response.json().latest;
- m = new form.JSONMap(mapdata, '');
+ for (let remote_version of latest) {
+ var remote_branch = get_branch(remote_version);
- s = m.section(form.NamedSection, 'request', 'example', '',
- 'Use defaults for the safest update');
- o = s.option(form.ListValue, 'version', 'Select firmware version');
- for (let candidate of candidates) {
- o.value(candidate, candidate);
- }
+ // already latest version installed
+ if (version == remote_version) {
+ break;
+ }
- if (advanced_mode == 1) {
- o = s.option(form.Value, 'board_name', 'Board Name / Profile');
- o = s.option(form.DynamicList, 'packages', 'Packages');
- }
+ // skip branch upgrades outside the advanced mode
+ if (this.data.branch != remote_branch && this.data.advanced_mode == 0) {
+ continue;
+ }
- m.render().then(function(form_rendered) {
- ui.showModal(_('New upgrade available'), [
- form_rendered,
- E('div', {
- class: 'right',
- },
- [
- E('div', {
- class: 'btn',
- click: ui.hideModal,
- },
- _('Cancel')),
- ' ',
- E('div', {
- class: 'btn cbi-button-action',
- click: function() {
- m.save().then((foo) => {
- request_sysupgrade(server_url, mapdata.request);
- });
- },
- },
- _('Request Sysupgrade')),
- ]),
- ]);
- });
- } else {
- ui.showModal(_('No upgrade available'), [
- E('p', {},
- _('The device runs the latest firmware version %s - %s')
- .format(version, revision)),
- E('div', {
- class: 'right',
- },
- [
- E('div', {
- class: 'btn',
- click: ui.hideModal,
- },
- _('Close')),
- ]),
- ]);
- }
-}
+ candidates.unshift([remote_version, null]);
-function displayStatus(type, content) {
- if (type) {
- var message = ui.showModal('', '');
+ // don't offer branches older than the current
+ if (this.data.branch == remote_branch) {
+ break;
+ }
+ }
+ }
- message.classList.add('alert-message');
- DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
+ if (candidates.length) {
+ var m, s, o;
- if (content)
- dom.content(message, content);
- } else {
- ui.hideModal();
- }
-}
+ var mapdata = {
+ request: {
+ profile: this.firmware.profile,
+ version: candidates[0][0],
+ packages: Object.keys(this.firmware.packages).sort(),
+ },
+ };
-return view.extend({
- load: function() {
+ var map = new form.JSONMap(mapdata, '');
+
+ s = map.section(form.NamedSection, 'request', '', '', 'Use defaults for the safest update');
+ o = s.option(form.ListValue, 'version', 'Select firmware version');
+ for (let candidate of candidates) {
+ o.value(candidate[0], candidate[1] ? `${candidate[0]} - ${candidate[1]}` : candidate[0]);
+ }
+
+ if (this.data.advanced_mode == 1) {
+ o = s.option(form.Value, 'profile', _('Board Name / Profile'));
+ o = s.option(form.DynamicList, 'packages', _('Packages'));
+ }
+
+ L.resolveDefault(map.render()).
+ then(form_rendered => {
+ ui.showModal(_('New firmware upgrade available'), [
+ E('p', _('Currently running: %s - %s').format(this.firmware.version, this.data.revision)),
+ form_rendered,
+ E('div', { class: 'right' }, [
+ E('div', { class: 'btn', click: ui.hideModal }, _('Cancel')), ' ',
+ E('button', {
+ 'class': 'btn cbi-button cbi-button-positive important',
+ 'click': ui.createHandlerFn(this, function () {
+ map.save().then(() => {
+ this.firmware.packages = mapdata.request.packages;
+ this.firmware.version = mapdata.request.version;
+ this.firmware.profile = mapdata.request.profile;
+ poll.add(L.bind(this.handleRequest, this), 5);
+ });
+ })
+ }, _('Request firmware image')),
+ ]),
+ ]);
+ });
+ } else {
+ ui.showModal(_('No upgrade available'), [
+ E('p', _('The device runs the latest firmware version %s - %s').format(version, revision)),
+ E('div', { class: 'right' }, [
+ E('div', { class: 'btn', click: ui.hideModal }, _('Close')),
+ ]),
+ ]);
+ }
+
+ });
+ },
+
+ load: function () {
return Promise.all([
L.resolveDefault(callPackagelist(), {}),
L.resolveDefault(callSystemBoard(), {}),
+ fs.stat("/sys/firmware/efi"),
+ fs.read("/proc/mounts"),
uci.load('attendedsysupgrade'),
]);
},
- render: function(res) {
- var packages = res[0].packages;
- var system_board = res[1];
- var auto_search =
- uci.get_first('attendedsysupgrade', 'client', 'auto_search') || 1;
- var server_url = uci.get_first('attendedsysupgrade', 'server', 'url');
-
- var view = [
- E('h2', _('Attended Sysupgrade')),
- E('p',
- _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
- E('p',
- _('This is done by building a new firmware on demand via an online service.')),
- ];
- if (auto_search == 1) {
- check_sysupgrade(server_url, system_board, packages);
+ render: function (res) {
+ this.data.app_version = res[0].packages['luci-app-attendedsysupgrade'];
+ this.firmware.packages = res[0].packages;
+
+ this.firmware.profile = res[1].board_name;
+ this.firmware.target = res[1].release.target;
+ this.firmware.version = res[1].release.version;
+ this.data.branch = get_branch(res[1].release.version);
+ this.data.revision = res[1].release.revision;
+ this.data.efi = res[2];
+ if (res[1].rootfs_type) {
+ this.data.rootfs_type = res[1].rootfs_type;
+ } else {
+ this.data.rootfs_type = res[3].split(/\r?\n/)[0].split(' ')[2]
}
- view.push(E('p', {
- class: 'btn cbi-button-positive',
- click:
- function() { check_sysupgrade(server_url, system_board, packages); },
- },
- _('Search for sysupgrade')));
+ this.data.url = uci.get_first('attendedsysupgrade', 'server', 'url');
+ this.data.advanced_mode = uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0
- return view;
+ return E('p', [
+ E('h2', _('Attended Sysupgrade')),
+ E('p', _('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
+ E('p', _('This is done by building a new firmware on demand via an online service.')),
+ E('p', _('Currently running: %s - %s').format(this.firmware.version, this.data.revision)),
+ E('button', {
+ 'class': 'btn cbi-button cbi-button-positive important',
+ 'click': ui.createHandlerFn(this, this.handleCheck)
+ }, _('Search for firmware upgrade'))
+ ]);
},
+ handleSaveApply: null,
+ handleSave: null,
+ handleReset: null
});