diff options
author | Paul Spooren <paul@spooren.de> | 2017-10-21 17:01:07 +0200 |
---|---|---|
committer | Paul Spooren <spooren@informatik.uni-leipzig.de> | 2017-11-04 19:25:10 +0100 |
commit | 26c93debe9540412df464f200a7b4d5c7e8d2ac7 (patch) | |
tree | f5d20470e29cd5035f81b56e0f1ea65d855f88cb | |
parent | 9e325a5162296964661c60e9ccecb134936b4ec6 (diff) |
luci-app-attendedsysupgrade: upgrade to version 2
Add required ubus acls from package rpcd-mod-attendedsysupgrade.
luci-app-attendedsysupgrade: add postinst script
removes caches and restarts uhttpd as mentioned here:
https://github.com/aparcar/gsoc17-attended-sysupgrade/issues/58#issuecomment-339370803
luci-app-attendedsysupgrade: redesign
simplified the interface and made it more "luci" like
simplify ubus interaction, use uci_call und ubus_call functions
add optional settings:
auto_search:
search on opening the view without clicking
advanced_mode:
if set, allow package edits, show build log
rename updateserver to upgradeserver
rename url to server_url
luci-app-attendedsysupgrade: rename uci options
remove leading "upgrade" from uci options
also rename server_url to url
cleaning
use new api/ urls
wait for successfull reboot
Signed-off-by: Paul Spooren <paul@spooren.de>
3 files changed, 258 insertions, 214 deletions
diff --git a/applications/luci-app-attendedsysupgrade/luasrc/view/attendedsysupgrade.htm b/applications/luci-app-attendedsysupgrade/luasrc/view/attendedsysupgrade.htm index d04745abd1..e1f715daaa 100644 --- a/applications/luci-app-attendedsysupgrade/luasrc/view/attendedsysupgrade.htm +++ b/applications/luci-app-attendedsysupgrade/luasrc/view/attendedsysupgrade.htm @@ -75,39 +75,43 @@ %> <%+header%> <h2 name="content"><%:Attended Sysupgrade%></h2> -<div class="container"> - <div style="display: none" id="update_info" class="alert-message info"></div> - <div style="display: none" id="update_error" class="alert-message danger"></div> +<div class="cbi-map-descr"> + Easily search and install new releases and package upgrades. Sysupgrade images are created on demand based on locally installed packages. </div> +<div style="display: none" id="upgrade_info" class="alert-message info"></div> +<div style="display: none" id="upgrade_error" class="alert-message danger"></div> +<div style="display: none" id="packages" class="alert-message success"></div> <p> - <input class="cbi-button" style="display: none;" value="edit installed packages" onclick="edit_packages()" type="button" id="edit_button"> - <textarea style="display: none; width: 100%;" id="edit_packages" rows="15"></textarea> -</P> -<p> - <input class="cbi-button" value="search for updates" onclick="update_request()" type="button" id="update_button"> -</p> -<p> - <input style="display: none" class="cbi-button" value="show build log" onclick="window.open(data.log_url);" type="button" id="log_button"> +<textarea style="display: none; width: 100%;" id="edit_packages" rows="15"></textarea> </p> -<div style="display: none" id="packages" class="alert-message success"></div> -<div class="cbi-value" id="update_packages_container" style="display: block"> - <label class="cbi-value-title" for="update_packages"> - <input type="checkbox" name="update_packages" id="update_packages" /> - Search for package updates - </label> -</div> -<div class="cbi-value" id="keep_container" style="display: none"> - <label class="cbi-value-title" for="keep"> - <input type="checkbox" name="keep" id="keep" checked="checked" /> - Keep settings - </label> -</div> - +<fieldset class="cbi-section"> + <form method="post" action=""> + <div class="cbi-selection-node"> + <div class="cbi-value" id="keep_container" style="display: none"> + <div class="cbi-section-descr"> + Check "Keep settings" to retain the current configuration (requires a compatible firmware image). + </div> + <label class="cbi-value-title" for="keep">Keep settings:</label> + <div class="cbi-value-field"> + <input name="keep" id="keep" checked="checked" type="checkbox"> + </div> + </div> + <div class="cbi-value" id="edit_button" style="display: none"> + <div class="cbi-value-field"> + <input class="cbi-button" value="edit installed packages" onclick="edit_packages()" type="button"> + </div> + </div> + <div class="cbi-value cbi-value-last"> + <div class="cbi-value-field"> + <input class="cbi-button cbi-input-apply" value="Search for upgrades" style="display: none" onclick="upgrade_check()" type="button" id="upgrade_button"> + </div> + </div> + </div> + </form> +</fieldset> <script type="text/javascript"> - latest_version = ""; data = {}; -ubus_counter = 1 origin = document.location.href.replace(location.pathname, "") ubus_url = origin + "/ubus/" @@ -118,248 +122,180 @@ function edit_packages() { document.getElementById("edit_packages").style.display = "block"; } -// requests to the update server +// requests to the upgrade server function server_request(request_dict, path, callback) { - url = data.update_server + "/" + path request_dict.distro = data.release.distribution; request_dict.target = data.release.target.split("\/")[0]; request_dict.subtarget = data.release.target.split("\/")[1]; - var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("POST", url, true); - xmlhttp.setRequestHeader("Content-type", "application/json"); - xmlhttp.send(JSON.stringify(request_dict)); - xmlhttp.onerror = function(e) { - update_error("update server down") + var request = new XMLHttpRequest(); + request.open("POST", data.url + "/" + path, true); + request.setRequestHeader("Content-type", "application/json"); + request.send(JSON.stringify(request_dict)); + request.onerror = function(e) { + upgrade_error("upgrade server down") } - xmlhttp.addEventListener('load', function(event) { - callback(xmlhttp) + request.addEventListener('load', function(event) { + callback(request) }); } -// requests ubus via rpcd -function ubus_request(command, argument, params, callback) { - request_data = {}; - request_data.jsonrpc = "2.0"; - request_data.id = ubus_counter; - request_data.method = "call"; - request_data.params = [ data.ubus_rpc_session, command, argument, params ] - ubus_counter++ - var xmlhttp = new XMLHttpRequest(); - xmlhttp.open("POST", ubus_url, true); - xmlhttp.setRequestHeader("Content-type", "application/json"); - xmlhttp.onerror = function(e) { - setTimeout(back_online, 5000) - } - xmlhttp.addEventListener('load', function(event) { - if(command === "uci") { - ubus_request_callback_uci(xmlhttp, callback) - } else { - ubus_request_callback(xmlhttp, callback) - } - }); - xmlhttp.send(JSON.stringify(request_data)); +// initial setup, get system information +function setup() { + data["ubus_rpc_session"] = "<%=luci.dispatcher.context.authsession%>" + ubus_call("packagelist", "list", {}, "packagelist"); + ubus_call("system", "board", {}, "release"); + ubus_call("system", "board", {}, "board_name"); + ubus_call("system", "board", {}, "model"); + uci_call({ "config": "attendedsysupgrade", "section": "server", "option": "url" }) + uci_call({ "config": "attendedsysupgrade", "section": "client", "option": "upgrade_packages" }) + uci_call({ "config": "attendedsysupgrade", "section": "client", "option": "advanced_mode" }) + uci_call({ "config": "attendedsysupgrade", "section": "client", "option": "auto_search" }) + setup_ready(); } -// handle ubus_requests, set variables or perform functions -function ubus_request_callback(response_object, callback) { - if(response_object.status === 200) { - console.log(callback) - if(typeof callback === "string") { - response_json = JSON.parse(response_object.responseText).result[1] - if (callback == "release") { - latest_version = response_json.release.version - } - data[callback] = response_json[callback] +function setup_ready() { + if(ubus_counter != ubus_closed) { + setTimeout(setup_ready, 300) + } else { + if(data.auto_search == 1) { + upgrade_check(); } else { - callback(response_object) + document.getElementById("upgrade_button").style.display = "block"; } - } else { - console.log(respons_object.responseText) } } -function ubus_request_callback_uci(response_object, callback) { - if(response_object.status === 200) { - console.log(callback) - response_json = JSON.parse(response_object.responseText).result[1].value - data[callback] = response_json - - document.getElementById("update_packages").checked = data.update_packages; - } else { - console.log(respons_object.responseText) - } +function uci_call(option) { + ubus_call("uci", "get", option, option["option"]) } -// initial setup, get system information -function setup() { - data["ubus_rpc_session"] = "<%=luci.dispatcher.context.authsession%>" - ubus_request("packagelist", "list", {}, "packagelist"); - ubus_request("system", "board", {}, "release"); - ubus_request("system", "board", {}, "board_name"); - ubus_request("system", "board", {}, "model"); - ubus_request("uci", "get", { "config": "attendedsysupgrade", "section": "updateserver", "option": "url" }, "update_server") - ubus_request("uci", "get", { "config": "attendedsysupgrade", "section": "updateclient", "option": "update_packages" }, "update_packages") +ubus_counter = 0; +ubus_closed = 0; +function ubus_call(command, argument, params, variable) { + request_data = {}; + request_data.jsonrpc = "2.0"; + request_data.id = ubus_counter; + request_data.method = "call"; + request_data.params = [ data.ubus_rpc_session, command, argument, params ] + ubus_counter++; + var request = new XMLHttpRequest(); + request.open("POST", ubus_url, true); + request.setRequestHeader("Content-type", "application/json"); + request.addEventListener('load', function(event) { + if(request.status === 200) { + request_json = JSON.parse(request.responseText).result[1] + if(command === "uci") { + data[variable] = request_json.value + } else { + if (variable == "release") { + latest_version = request_json.release.version + } + data[variable] = request_json[variable] + } + ubus_closed++; + } + }); + request.send(JSON.stringify(request_data)); } -// shows notification if update is available -function update_info(info_output) { - document.getElementById("update_info").style.display = "block"; - document.getElementById("update_info").innerHTML = info_output; +// shows notification if upgrade is available +function upgrade_info(info_output, loading) { + document.getElementById("upgrade_info").style.display = "block"; + if(loading) { + loading_image = '<img src="/luci-static/resources/icons/loading.gif">' + } else { + loading_image = '' + } + document.getElementById("upgrade_info").innerHTML = loading_image + info_output; } -function update_error(error_output) { - document.getElementById("update_error").style.display = "block"; - document.getElementById("update_error").innerHTML = error_output; - document.getElementById("update_info").style.display = "none"; +function upgrade_error(error_output) { + document.getElementById("upgrade_error").style.display = "block"; + document.getElementById("upgrade_error").innerHTML = error_output; + document.getElementById("upgrade_info").style.display = "none"; } -// asks server for news updates, actually only based on relesae not packages -function update_request() { - console.log("update_request") +// asks server for news upgrades, actually only based on relesae not packages +function upgrade_check() { + upgrade_info("Searching for upgrades", true); request_dict = {} request_dict.version = data.release.version; request_dict.packages = data.packagelist; - if (document.getElementById("update_packages").checked == 1) { - request_dict.update_packages = 1 - } - server_request(request_dict, "update-request", update_request_callback) -} - -function update_request_callback(response_object) { - if (response_object.status === 500) { - // python crashed - update_error("internal server error, please try again later") - console.log("update server issue") - } else if (response_object.status === 502) { - // python part offline - update_error("internal server error, please try again later") - console.log("update server issue") - } else if (response_object.status === 503) { - // handle overload - update_error("server overloaded, retry in 5 minutes") - console.log("server overloaded") - setTimeout(update_request, 300000) - } else if (response_object.status === 201) { - update_info("imagebuilder not ready, please wait") - console.log("setting up imagebuilder") - setTimeout(update_request, 5000) - } else if (response_object.status === 204) { - // no updates - update_info("no updates available") - } else if (response_object.status === 400) { - // bad request - console.log(response_object.responseText) - response_object_content = JSON.parse(response_object.responseText) - update_error(response_object_content.error) - } else if (response_object.status === 200) { - // new release/updates - response_object_content = JSON.parse(response_object.responseText) - document.getElementById("edit_button").style.display = "block"; - document.getElementById("update_button").disabled = false; - update_request_200(response_object_content) - } -} - -function back_online() { - ubus_request("session", "login", {}, back_online_callback) -} - -function back_online_callback(response_object) { - if (response_object.status != 200) { - setTimeout(back_online, 5000) - } else { - update_info("upgrade successfull!") - document.getElementById("update_button").value = "reload page"; - document.getElementById("update_button").onclick = function() { location.reload(); } - } - -} - -function update_request_200(response_content) { - info_output = "" - if(response_content.version != undefined) { - info_output += "<h3>new update available</h3>" - info_output += data.release.version + " to " + response_content.version - latest_version = response_content.version; - } - if(response_content.updates != undefined) { - info_output += "<h3>package updates available</h3>" - for (update in response_content.updates) { - info_output += "<b>" + update + "</b>: " + response_content.updates[update][1] + " to " + response_content.updates[update][0] + "</br>" - } - } - data.packages = response_content.packages - update_info(info_output) - document.getElementById("update_button").value = "request image"; - document.getElementById("update_packages_container").style.display = "none"; - document.getElementById("update_button").onclick = image_request; + // not only search for new release, but for new package versions as well + request_dict.upgrade_packages = data.upgrade_packages + server_request(request_dict, "api/upgrade-check", upgrade_check_callback) } -// request the image, need merge with update_request -function image_request() { - console.log("image_request") - document.getElementById("update_button").disabled = true; - document.getElementById("update_packages_container").style.display = "none"; +// request the image, need merge with upgrade_request +function upgrade_request() { + console.log("upgrade_request") + document.getElementById("upgrade_button").disabled = true; document.getElementById("edit_packages").style.display = "none"; document.getElementById("edit_button").style.display = "none"; + document.getElementById("keep_container").style.display = "none"; request_dict = {} request_dict.version = latest_version; request_dict.board = data.board_name + if(data.edit_packages == true) { request_dict.packages = document.getElementById("edit_packages").value.split("\n") } else { request_dict.packages = data.packages; } request_dict.model = data.model - server_request(request_dict, "image-request", image_request_handler) + server_request(request_dict, "api/upgrade-request", upgrade_request_callback) } -function image_request_handler(response) { +function upgrade_request_callback(response) { if (response.status === 400) { response_content = JSON.parse(response.responseText) - update_error(response_content.error) + upgrade_error(response_content.error) } else if (response.status === 500) { response_content = JSON.parse(response.responseText) - update_error(response_content.error) + upgrade_error(response_content.error) if(response_content.log != undefined) { data.log_url = response_content.log - document.getElementById("log_button").style.display = "block"; } } else if (response.status === 503) { - update_error("please wait. server overloaded") + upgrade_error("please wait. server overloaded") // handle overload - setTimeout(image_request, 30000) + setTimeout(upgrade_request, 30000) } else if (response.status === 201) { response_content = JSON.parse(response.responseText) if(response_content.queue != undefined) { // in queue - update_info("please wait. you are in queue position " + response_content.queue) + upgrade_info("please wait. you are in queue position " + response_content.queue, true) console.log("queued") } else { - update_info("imagebuilder not ready, please wait") + upgrade_info("imagebuilder not ready, please wait", true) console.log("setting up imagebuilder") } - setTimeout(image_request, 5000) + setTimeout(upgrade_request, 5000) } else if (response.status === 206) { // building console.log("building") - update_info("building image") - setTimeout(image_request, 5000) + upgrade_info("building image", true) + setTimeout(upgrade_request, 5000) } else if (response.status === 200) { // ready to download response_content = JSON.parse(response.responseText); - data.image_url = response_content.url; - data.log_url = data.image_url + ".log"; - update_info("image created"); - document.getElementById("log_button").style.display = "block"; - document.getElementById("update_button").disabled = false; - document.getElementById("update_button").value = "sysupgrade"; - document.getElementById("update_button").onclick = download_image; + data.sysupgrade_url = response_content.sysupgrade; + + info_output = "Image created" + if(data.advanced_mode == 1) { + build_log = '</br><a target="_blank" href="' + data.sysupgrade_url + '.log">Build log</a>' + info_output += build_log + } + upgrade_info(info_output); + document.getElementById("keep_container").style.display = "block"; + document.getElementById("upgrade_button").disabled = false; + document.getElementById("upgrade_button").style.display = "block"; + document.getElementById("upgrade_button").value = "Sysupgrade"; + document.getElementById("upgrade_button").onclick = download_image; } } - // uploads received blob data to the server using cgi-io function upload_image(blob) { var upload_request = new XMLHttpRequest(); @@ -372,26 +308,44 @@ function upload_image(blob) { upload_request.addEventListener('load', function(event) { // this checksum should be parsed - document.getElementById("update_info").innerHTML = "flashing... please wait"; // show fancy indicator http://www.ajaxload.info/ + upgrade_info("Flashing firmware", true) + ubus_call("attendedsysupgrade", "sysupgrade", { "keep_settings": document.getElementById("keep").checked }, 'message'); + setTimeout(ping_ubus, 5000) + console.log(data.message); + }); - ubus_request("attendedsysupgrade", "sysupgrade", { "keep_settings": document.getElementById("keep").checked }, 'done'); - }); + upload_request.addEventListener('error', function(event) { + upgrade_info("uploading failed, please retry") + }); - upload_request.addEventListener('error', function(event) { - document.getElementById("update_info").innerHTML = "uploading failed, please retry" - }); + upload_request.open('POST', origin + '/cgi-bin/cgi-upload'); + upload_request.send(form_data); +} - upload_request.open('POST', origin + '/cgi-bin/cgi-upload'); - upload_request.send(form_data); +function ping_ubus() { + var request = new XMLHttpRequest(); + request.open("GET", ubus_url, true); + request.addEventListener('error', function(event) { + upgrade_info("Rebooting", true); + setTimeout(ping_ubus, 1000) + }); + request.addEventListener('load', function(event) { + upgrade_info("Success! Please reload web interface"); + document.getElementById("upgrade_button").value = "reload page"; + document.getElementById("upgrade_button").style.display = "block"; + document.getElementById("upgrade_button").disabled = false; + document.getElementById("upgrade_button").onclick = function() { location.reload(); } + }); + request.send(); } -// download image from server once the url was received by image_request +// download image from server once the url was received by upgrade_request function download_image() { console.log("download_image") - document.getElementById("update_button").value = "downloading image"; - document.getElementById("update_button").disabled = true; + document.getElementById("keep_container").style.display = "none"; + document.getElementById("upgrade_button").style.display = "none"; var download_request = new XMLHttpRequest(); - download_request.open("GET", data.image_url); + download_request.open("GET", data.sysupgrade_url); download_request.responseType = "arraybuffer"; download_request.onload = function () { @@ -400,10 +354,68 @@ function download_image() { upload_image(blob) } }; - document.getElementById("update_info").innerHTML = "downloading image"; + upgrade_info("downloading image", true); download_request.send(); } +function upgrade_check_callback(response_object) { + if (response_object.status === 500) { + // python crashed + upgrade_error("internal server error, please try again later") + console.log("upgrade server issue") + } else if (response_object.status === 502) { + // python part offline + upgrade_error("internal server error, please try again later") + console.log("upgrade server issue") + } else if (response_object.status === 503) { + // handle overload + upgrade_error("server overloaded, retry in 5 minutes") + console.log("server overloaded") + setTimeout(upgrade_request, 300000) + } else if (response_object.status === 201) { + upgrade_info("Setting up ImageBuilder", true) + console.log("setting up imagebuilder") + setTimeout(upgrade_request, 5000) + } else if (response_object.status === 204) { + // no upgrades + upgrade_info("No upgrades available") + } else if (response_object.status === 400) { + // bad request + console.log(response_object.responseText) + response_object_content = JSON.parse(response_object.responseText) + upgrade_error(response_object_content.error) + } else if (response_object.status === 200) { + // new release/upgrades + response_content = JSON.parse(response_object.responseText) + + // create simple output to tell user whats going to be upgrade (release/packages) + info_output = "" + if(response_content.version != undefined) { + info_output += "<h3>new upgrade available</h3>" + info_output += data.release.version + " to " + response_content.version + latest_version = response_content.version; + } + if(response_content.upgrades != undefined) { + info_output += "<h3>package upgrades available</h3>" + for (upgrade in response_content.upgrades) { + info_output += "<b>" + upgrade + "</b>: " + response_content.upgrades[upgrade][1] + " to " + response_content.upgrades[upgrade][0] + "</br>" + } + } + data.packages = response_content.packages + upgrade_info(info_output) + + // directly request image if not in advanced mode + if(data.advanced_mode == 1) { + document.getElementById("edit_button").style.display = "block"; + document.getElementById("upgrade_button").value = "request image"; + document.getElementById("upgrade_button").style.display = "block"; + document.getElementById("upgrade_button").disabled = false; + document.getElementById("upgrade_button").onclick = upgrade_request; + } else { + upgrade_request(); + } + } +} document.onload = setup() </script> diff --git a/applications/luci-app-attendedsysupgrade/root/etc/uci-defaults/40_luci-attendedsysupgrade b/applications/luci-app-attendedsysupgrade/root/etc/uci-defaults/40_luci-attendedsysupgrade new file mode 100755 index 0000000000..832744f7d8 --- /dev/null +++ b/applications/luci-app-attendedsysupgrade/root/etc/uci-defaults/40_luci-attendedsysupgrade @@ -0,0 +1,6 @@ +#!/bin/sh + +rm -rf /tmp/luci-indexcache /tmp/luci-modulecache/ +/etc/init.d/uhttpd restart + +return 0 diff --git a/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/attendedsysupgrade.json b/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/attendedsysupgrade.json new file mode 100644 index 0000000000..85d6e94a28 --- /dev/null +++ b/applications/luci-app-attendedsysupgrade/root/usr/share/rpcd/acl.d/attendedsysupgrade.json @@ -0,0 +1,26 @@ +{ + "attendedsysupgrade": { + "description": "attended sysupgrade via rpcd and luci", + "read": { + "ubus": { + "attendedsysupgrade": [ + "sysupgrade" + ], + "system": [ + "board" + ], + "uci": [ + "get" + ] + }, + "uci": [ + "*" + ] + }, + "write": { + "cgi-io": [ + "upload" + ] + } + } +} |