diff options
author | Jo-Philipp Wich <jo@mein.io> | 2018-04-26 08:52:55 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2018-05-05 23:11:23 +0200 |
commit | 8deb9495515b97898514e8ffb8f002c8afe3bfa7 (patch) | |
tree | 65d4d7b831c273329c0a79a6b9e4a547e760481b /modules/luci-mod-admin-full | |
parent | 7cca3139591a08e62b7900115fe0bfd4a3df8f8a (diff) |
treewide: rework uci apply workflow
Switch to rpcd based uci apply/rollback workflow which helps to avoid soft-
bricking devices by requiring an explicit confirmation call after config
apply.
When a user now clicks "Save & Apply", LuCI first issues a call to uci apply
which commits and reloads configuration, then goes into a polling countdown
mode where it repeatedly attempts to call uci confirm.
If the committed configuration is sane, the confirm call will go through and
cancel rpcd's pending rollback timer.
If the configuration change leads to a loss of connectivity (e.g. due to bad
firewall rules or similar), the rollback mechanism will kick in after the
timeout and revert configuration files and pending changes to the pre-apply
state.
In order to cover such rare cases where a lost of connectivity is expected
and desired, the user is offered an "unchecked" apply option after timing
out, which allows committing and applying the changes anyway, without the
extra safety checks.
As a consequence of this change, the luci-reload mechanism is now completely
unsused since rpcd uses ubus config reload signals to reload affected
services, which means that only procd-enabled services will receive proper
reload treatment with the new workflow.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules/luci-mod-admin-full')
4 files changed, 100 insertions, 72 deletions
diff --git a/modules/luci-mod-admin-full/luasrc/controller/admin/uci.lua b/modules/luci-mod-admin-full/luasrc/controller/admin/uci.lua index ba317f9f4..9533ff5e6 100644 --- a/modules/luci-mod-admin-full/luasrc/controller/admin/uci.lua +++ b/modules/luci-mod-admin-full/luasrc/controller/admin/uci.lua @@ -11,54 +11,91 @@ function index() entry({"admin", "uci"}, nil, _("Configuration")) entry({"admin", "uci", "changes"}, call("action_changes"), _("Changes"), 40).query = {redir=redir} entry({"admin", "uci", "revert"}, post("action_revert"), _("Revert"), 30).query = {redir=redir} - entry({"admin", "uci", "apply"}, post("action_apply"), _("Apply"), 20).query = {redir=redir} - entry({"admin", "uci", "saveapply"}, post("action_apply"), _("Save & Apply"), 10).query = {redir=redir} + + local node + local authen = function(checkpass, allowed_users) + return "root", luci.http.formvalue("sid") + end + + node = entry({"admin", "uci", "apply_rollback"}, post("action_apply_rollback"), nil) + node.cors = true + node.sysauth_authenticator = authen + + node = entry({"admin", "uci", "apply_unchecked"}, post("action_apply_unchecked"), nil) + node.cors = true + node.sysauth_authenticator = authen + + node = entry({"admin", "uci", "confirm"}, post("action_confirm"), nil) + node.cors = true + node.sysauth_authenticator = authen end + function action_changes() - local uci = luci.model.uci.cursor() + local uci = require "luci.model.uci" local changes = uci:changes() luci.template.render("admin_uci/changes", { - changes = next(changes) and changes + changes = next(changes) and changes, + timeout = timeout }) end -function action_apply() - local path = luci.dispatcher.context.path - local uci = luci.model.uci.cursor() +function action_revert() + local uci = require "luci.model.uci" local changes = uci:changes() - local reload = {} - -- Collect files to be applied and commit changes + -- Collect files to be reverted + local r, tbl for r, tbl in pairs(changes) do - table.insert(reload, r) - if path[#path] ~= "apply" then - uci:load(r) - uci:commit(r) - uci:unload(r) - end + uci:revert(r) end - luci.template.render("admin_uci/apply", { - changes = next(changes) and changes, - configs = reload + luci.template.render("admin_uci/revert", { + changes = next(changes) and changes }) end -function action_revert() - local uci = luci.model.uci.cursor() - local changes = uci:changes() +local function ubus_state_to_http(errstr) + local map = { + ["Invalid command"] = 400, + ["Invalid argument"] = 400, + ["Method not found"] = 404, + ["Entry not found"] = 404, + ["No data"] = 204, + ["Permission denied"] = 403, + ["Timeout"] = 504, + ["Not supported"] = 500, + ["Unknown error"] = 500, + ["Connection failed"] = 503 + } - -- Collect files to be reverted - for r, tbl in pairs(changes) do - uci:load(r) - uci:revert(r) - uci:unload(r) + local code = map[errstr] or 200 + local msg = errstr or "OK" + + luci.http.status(code, msg) + + if code ~= 204 then + luci.http.prepare_content("text/plain") + luci.http.write(msg) end +end - luci.template.render("admin_uci/revert", { - changes = next(changes) and changes - }) +function action_apply_rollback() + local uci = require "luci.model.uci" + local _, errstr = uci:apply(true) + ubus_state_to_http(errstr) +end + +function action_apply_unchecked() + local uci = require "luci.model.uci" + local _, errstr = uci:apply(false) + ubus_state_to_http(errstr) +end + +function action_confirm() + local uci = require "luci.model.uci" + local _, errstr = uci:confirm() + ubus_state_to_http(errstr) end diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_uci/apply.htm b/modules/luci-mod-admin-full/luasrc/view/admin_uci/apply.htm deleted file mode 100644 index 370027e51..000000000 --- a/modules/luci-mod-admin-full/luasrc/view/admin_uci/apply.htm +++ /dev/null @@ -1,23 +0,0 @@ -<%# - Copyright 2008 Steven Barth <steven@midlink.org> - Copyright 2008 Jo-Philipp Wich <jow@openwrt.org> - Licensed to the public under the Apache License 2.0. --%> - -<%+header%> - -<h2 name="content"><%:Configuration%> / <%:Apply%></h2> - -<% if changes then %> - <%+cbi/apply_xhr%> - <%+admin_uci/changelog%> - - <%- cbi_apply_xhr('uci-apply', configs) -%> - - <p><strong><%:The following changes have been committed%>:</strong></p> - <%- uci_changelog(changes) -%> -<% else %> - <p><strong><%:There are no pending changes to apply!%></strong></p> -<% end %> - -<%+footer%> diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_uci/changes.htm b/modules/luci-mod-admin-full/luasrc/view/admin_uci/changes.htm index 6e725c888..9e9ce2be2 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_uci/changes.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_uci/changes.htm @@ -1,40 +1,41 @@ <%# Copyright 2008 Steven Barth <steven@midlink.org> - Copyright 2008-2015 Jo-Philipp Wich <jow@openwrt.org> + Copyright 2008-2018 Jo-Philipp Wich <jo@mein.io> Licensed to the public under the Apache License 2.0. -%> <%+header%> +<%- + local node, redir_url = luci.dispatcher.lookup(luci.http.formvalue("redir")) + + include("cbi/apply_widget") + include("admin_uci/changelog") + + cbi_apply_widget(redir_url or url("admin/uci/changes")) +-%> + <h2 name="content"><%:Configuration%> / <%:Changes%></h2> <% if changes then %> - <%+admin_uci/changelog%> <%- uci_changelog(changes) -%> <% else %> <p><strong><%:There are no pending changes!%></strong></p> <% end %> +<div class="alert-message" id="cbi_apply_status" style="display:none"></div> + <div class="cbi-page-actions"> - <% local node, url = luci.dispatcher.lookup(luci.http.formvalue("redir")); if url then %> + <% if redir_url then %> <div style="float:left"> - <form class="inline" method="get" action="<%=luci.util.pcdata(url)%>"> + <form class="inline" method="get" action="<%=luci.util.pcdata(redir_url)%>"> <input class="cbi-button cbi-button-link" style="float:left; margin:0" type="submit" value="<%:Back%>" /> </form> </div> <% end %> <div style="text-align:right"> - <form class="inline" method="post" action="<%=controller%>/admin/uci/apply"> - <input type="hidden" name="token" value="<%=token%>" /> - <input type="hidden" name="redir" value="<%=pcdata(luci.http.formvalue("redir"))%>" /> - <input class="cbi-button cbi-button-apply" type="submit" value="<%:Apply%>" /> - </form> - <form class="inline" method="post" action="<%=controller%>/admin/uci/saveapply"> - <input type="hidden" name="token" value="<%=token%>" /> - <input type="hidden" name="redir" value="<%=pcdata(luci.http.formvalue("redir"))%>" /> - <input class="cbi-button cbi-button-save" type="submit" value="<%:Save & Apply%>" /> - </form> + <input class="cbi-button cbi-button-save" type="button" id="apply_button" value="<%:Save & Apply%>" onclick="uci_apply(true); this.blur()" /> <form class="inline" method="post" action="<%=controller%>/admin/uci/revert"> <input type="hidden" name="token" value="<%=token%>" /> <input type="hidden" name="redir" value="<%=pcdata(luci.http.formvalue("redir"))%>" /> diff --git a/modules/luci-mod-admin-full/luasrc/view/admin_uci/revert.htm b/modules/luci-mod-admin-full/luasrc/view/admin_uci/revert.htm index 20327adff..dff53420a 100644 --- a/modules/luci-mod-admin-full/luasrc/view/admin_uci/revert.htm +++ b/modules/luci-mod-admin-full/luasrc/view/admin_uci/revert.htm @@ -1,26 +1,39 @@ <%# Copyright 2008 Steven Barth <steven@midlink.org> - Copyright 2008 Jo-Philipp Wich <jow@openwrt.org> + Copyright 2008-2018 Jo-Philipp Wich <jo@mein.io> Licensed to the public under the Apache License 2.0. -%> <%+header%> +<%- + local node, redir_url = luci.dispatcher.lookup(luci.http.formvalue("redir")) + + include("cbi/apply_widget") + include("admin_uci/changelog") + + cbi_apply_widget(redir_url or url("admin/uci/revert")) +-%> + <h2 name="content"><%:Configuration%> / <%:Revert%></h2> <% if changes then %> - <%+cbi/apply_xhr%> - <%+admin_uci/changelog%> - <p><strong><%:The following changes have been reverted%>:</strong></p> <%- uci_changelog(changes) -%> <% else %> <p><strong><%:There are no pending changes to revert!%></strong></p> <% end %> -<% local node, url = luci.dispatcher.lookup(luci.http.formvalue("redir")); if url then %> +<div class="alert-message" id="cbi_apply_status" style="display:none"></div> +<script type="text/javascript"> + document.addEventListener("DOMContentLoaded", function() { + uci_apply(true); + }); +</script> + +<% if redir_url then %> <div class="cbi-page-actions"> - <form class="inline" method="get" action="<%=luci.util.pcdata(url)%>"> + <form class="inline" method="get" action="<%=luci.util.pcdata(redir_url)%>"> <input class="cbi-button cbi-button-link" style="margin:0" type="submit" value="<%:Back%>" /> </form> </div> |