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-base/luasrc/model/uci.lua | |
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-base/luasrc/model/uci.lua')
-rw-r--r-- | modules/luci-base/luasrc/model/uci.lua | 139 |
1 files changed, 73 insertions, 66 deletions
diff --git a/modules/luci-base/luasrc/model/uci.lua b/modules/luci-base/luasrc/model/uci.lua index fc2a605b34..34323f08bd 100644 --- a/modules/luci-base/luasrc/model/uci.lua +++ b/modules/luci-base/luasrc/model/uci.lua @@ -3,6 +3,7 @@ local os = require "os" local util = require "luci.util" +local conf = require "luci.config" local table = require "table" @@ -143,22 +144,84 @@ function commit(self, config) return (err == nil), ERRSTR[err] end ---[[ -function apply(self, configs, command) - local _, config +function apply(self, rollback) + local _, err + + if rollback then + local timeout = tonumber(conf.apply and conf.apply.rollback or "") or 0 - assert(not command, "Apply command not supported anymore") + _, err = call("apply", { + timeout = (timeout > 30) and timeout or 30, + rollback = true + }) - if type(configs) == "table" then - for _, config in ipairs(configs) do - call("service", "event", { - type = "config.change", - data = { package = config } + if not err then + util.ubus("session", "set", { + ubus_rpc_session = session_id, + values = { rollback = os.time() + timeout } }) end + else + _, err = call("changes", {}) + + if not err then + if type(_) == "table" and type(_.changes) == "table" then + local k, v + for k, v in pairs(_.changes) do + _, err = call("commit", { config = k }) + if err then + break + end + end + end + end + + if not err then + _, err = call("apply", { rollback = false }) + end + end + + return (err == nil), ERRSTR[err] +end + +function confirm(self) + local _, err = call("confirm", {}) + if not err then + util.ubus("session", "set", { + ubus_rpc_session = session_id, + values = { rollback = 0 } + }) end + return (err == nil), ERRSTR[err] +end + +function rollback(self) + local _, err = call("rollback", {}) + if not err then + util.ubus("session", "set", { + ubus_rpc_session = session_id, + values = { rollback = 0 } + }) + end + return (err == nil), ERRSTR[err] +end + +function rollback_pending(self) + local deadline, err = util.ubus("session", "get", { + ubus_rpc_session = session_id, + keys = { "rollback" } + }) + + if type(deadline) == "table" and + type(deadline.values) == "table" and + type(deadline.values.rollback) == "number" and + deadline.values.rollback > os.time() + then + return true, deadline.values.rollback - os.time() + end + + return false, ERRSTR[err] end -]] function foreach(self, config, stype, callback) @@ -425,59 +488,3 @@ function delete_all(self, config, stype, comparator) return (err == nil), ERRSTR[err] end - - -function apply(self, configlist, command) - configlist = self:_affected(configlist) - if command then - return { "/sbin/luci-reload", unpack(configlist) } - else - return os.execute("/sbin/luci-reload %s >/dev/null 2>&1" - % util.shellquote(table.concat(configlist, " "))) - end -end - --- Return a list of initscripts affected by configuration changes. -function _affected(self, configlist) - configlist = type(configlist) == "table" and configlist or { configlist } - - -- Resolve dependencies - local reloadlist = { } - - local function _resolve_deps(name) - local reload = { name } - local deps = { } - - self:foreach("ucitrack", name, - function(section) - if section.affects then - for i, aff in ipairs(section.affects) do - deps[#deps+1] = aff - end - end - end) - - local i, dep - for i, dep in ipairs(deps) do - local j, add - for j, add in ipairs(_resolve_deps(dep)) do - reload[#reload+1] = add - end - end - - return reload - end - - -- Collect initscripts - local j, config - for j, config in ipairs(configlist) do - local i, e - for i, e in ipairs(_resolve_deps(config)) do - if not util.contains(reloadlist, e) then - reloadlist[#reloadlist+1] = e - end - end - end - - return reloadlist -end |