summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-10-27 23:44:31 +0200
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-10-31 23:04:16 +0200
commit31e261fd9aaf1e882d8df368b5842e4d5b612e62 (patch)
tree6764ca6f44c135d0b1f1711392a0a9ef8b57e50e
parentf9d87d59a158fcb1326e647b2ec40c4436125765 (diff)
downloadacf2-31e261fd9aaf1e882d8df368b5842e4d5b612e62.tar.bz2
acf2-31e261fd9aaf1e882d8df368b5842e4d5b612e62.tar.xz
augeas backend: explicit addresses for predicate-based parameter access
-rw-r--r--acf2/model/root.lua6
-rw-r--r--acf2/modules/net.lua8
-rw-r--r--acf2/persistence/backends/augeas.lua265
-rw-r--r--acf2/transaction/init.lua2
4 files changed, 191 insertions, 90 deletions
diff --git a/acf2/model/root.lua b/acf2/model/root.lua
index 61debec..7269e79 100644
--- a/acf2/model/root.lua
+++ b/acf2/model/root.lua
@@ -45,7 +45,8 @@ function M.topology(addr, create)
while #addr > 0 do
if create then
- top = setdefault(defaults(top).members, addr[1], {})
+ top = setdefault(defaults(top).members, addr[1], {order=order})
+ order = order + 1
else
top = top.members[addr[1]] or top.members[pth.wildcard]
if not top then return end
@@ -66,9 +67,6 @@ function M.register(name, field, params)
for _, record in ipairs(node.topology(root:fetch(name))) do
local top = M.topology(record.addr, true)
- setdefault(top, 'order', order)
- order = order + 1
-
local function set(k, v)
setdefault(top, k, v)
assert(top[k] == v)
diff --git a/acf2/modules/net.lua b/acf2/modules/net.lua
index b03ae9d..de31be9 100644
--- a/acf2/modules/net.lua
+++ b/acf2/modules/net.lua
@@ -8,11 +8,13 @@ local M = require('acf2.model')
local Host = M.new()
Host.address = M.net.IPAddress{addr='ipaddr'}
Host.canonical = M.String{ui_name='Canonical name'}
-Host.alias = M.Set{type=M.String, ui_name='Aliases', ui_member='Alias'}
+Host.alias = M.Set{
+ type=M.String, addr='alias/#', ui_name='Aliases', ui_member='Alias'
+}
local Resolv = M.new()
-Resolv.servers = M.List{type=M.net.IPAddress, addr='nameserver'}
-Resolv['search-domains'] = M.List{type=M.String, addr='search/domain'}
+Resolv.servers = M.List{type=M.net.IPAddress, addr='nameserver/#'}
+Resolv['search-domains'] = M.List{type=M.String, addr='search/domain/#'}
local Net = M.new()
Net['host-name'] = M.String{addr='/augeas/etc/hostname/hostname'}
diff --git a/acf2/persistence/backends/augeas.lua b/acf2/persistence/backends/augeas.lua
index b37aa25..f883409 100644
--- a/acf2/persistence/backends/augeas.lua
+++ b/acf2/persistence/backends/augeas.lua
@@ -4,6 +4,7 @@ See LICENSE file for license details
--]]
local topology = require('acf2.model.root').topology
+local class = require('acf2.object').class
local pth = require('acf2.path')
local tostr = require('acf2.persistence.util').tostring
@@ -11,138 +12,240 @@ local util = require('acf2.util')
local copy = util.copy
-local function aug_path(path) return pth.join('/files', unpack(path)) end
+local stringy = require('stringy')
-local function strip_name(name)
- return type(name) == 'string' and name:match('^[^][/=)%s]+') or name
+
+local function array_join(tbl, value)
+ local res = copy(tbl)
+ table.insert(res, value)
+ return res
+end
+
+local function array_without_last(tbl)
+ local res = copy(tbl)
+ res[#res] = nil
+ return res
+end
+
+
+local function basename(path)
+ local res, pred = path:match('^.*/([#%w._-]+)([^/]*)$')
+ assert(res)
+ assert(res ~= '#')
+ assert(pred == '' or pred:match('^%[.+%]$'))
+ return res
end
-local function ipath(path, index) return path..'['..index..']' end
+local function append_pred(path, pred) return path..'['..pred..']' end
-local backend = require('acf2.object').class()
+local function key_mode(mode) return mode and stringy.startswith(mode, '@') end
-function backend:init() self.aug = require('augeas').init() end
+local function key(mode)
+ assert(key_mode(mode))
+ return mode == '@' and '.' or mode:sub(2, -1)
+end
-function backend:find(path, leaf)
- util.map(
- function(comp)
- assert(
- comp == strip_name(comp) and (
- type(comp) == 'number' or not comp:match('^%.+$')
- )
- )
- end,
- path
- )
- local res = aug_path(path)
-
- if #self.aug:match(res) == 0 and #path > 1 and leaf then
- local index = path[#path]
- if type(index) == 'number' then
- local ppath = copy(path)
- table.remove(ppath)
- ppath = aug_path(ppath)
-
- if #self.aug:match(ppath) > 0 and #self.aug:match(
- ppath..'/*'
- ) == 0 then
- return ipath(ppath, index), ppath, index
+local function conv_path(path)
+ local res = '/files'
+ if #path == 0 then return res, nil, {} end
+
+ path = copy(path)
+ local mode
+ local keys = {}
+
+ repeat
+ local comp = path[1]
+
+ if comp == '#' or key_mode(comp) then
+ assert(not mode)
+ mode = comp
+ elseif not mode then
+ res = res..'/'..comp
+ keys = {}
+ else
+ if mode == '#' then
+ assert(type(comp) == 'number')
+ res = append_pred(res, comp)
+ else
+ assert(type(comp) == 'string' and comp:match('^[%w _-]+$'))
+ res = append_pred(res, key(mode).." = '"..comp.."'")
+ table.insert(keys, key(mode))
end
+ mode = nil
end
- end
- return res
+ table.remove(path, 1)
+ until #path == 0
+
+ return res, mode, keys
+end
+
+
+local function aug_top(path)
+ path = copy(path)
+ table.insert(path, 1, 'augeas')
+ return topology(path)
end
+
+local backend = class()
+
+function backend:init() self.aug = require('augeas').init() end
+
function backend:get(path, top)
+ local apath, mode, keys = conv_path(path)
+ local existence = top == true
+ if existence or not top then top = aug_top(path) end
+
local tpe = top and top.type
- local leaf = tpe and tpe ~= 'table'
- local apath, mvpath = self:find(path, leaf or not tpe)
+ local leaf = tpe ~= 'table' and not mode
- local matches = self.aug:match(apath)
- if mvpath then
- assert(#matches < 2)
- leaf = true
- end
+ local matches = self.aug:match(apath..(not leaf and not mode and '/*' or ''))
- if #matches == 0 then return end
+ if #matches == 0 and not mode then return end
- if #matches > 1 then
- assert(not leaf)
- local res = {}
- path = copy(path)
- for i, _ in ipairs(matches) do
- table.insert(path, i)
- if self:get(path) then table.insert(res, i) end
- table.remove(path)
+ if mode and #path > 1 and not self:get(
+ array_without_last(array_without_last(path)), true
+ ) then
+ return
+ end
+
+ if not tpe and not mode then
+ if #matches > 1 then
+ leaf = false
+ mode = '#'
+ else
+ local children = self.aug:match(apath..'/*')
+ if #children > 0 then
+ leaf = false
+ matches = children
+ end
end
- return res
+ end
+
+ if leaf then
+ assert(#matches == 1)
+ return self.aug:get(apath)
end
- local value = self.aug:get(matches[1])
- if value then return tpe == 'table' and {1} or value end
- if leaf then return end
+ if existence then return true end
local names = {}
- for _, child in ipairs(self.aug:match(apath..'/*')) do
- names[strip_name(pth.name(child))] = true
+
+ for i, child in ipairs(matches) do
+ local name
+ if not mode then
+ name = basename(child)
+ name = tonumber(name) or name
+ if util.contains(keys, name) then name = nil end
+ elseif mode == '#' then name = i
+ else
+ name = self.aug:get(child..(mode == '@' and '' or '/'..key(mode)))
+ end
+
+ if name and self:get(array_join(path, name), true) then
+ names[name] = true
+ end
end
+
return util.keys(names)
end
function backend:set(mods)
- local gcpaths = {}
+ local gc = {}
for _, mod in ipairs(mods) do
local path, value = unpack(mod)
- local delete = value == nil
- self.aug:rm(aug_path(path)..(delete and '' or '/*'))
+ local function insert(path, new)
+ local apath, mode, keys = conv_path(path)
+ if mode then path[#path] = nil end
+ if #path == 0 then return apath, keys end
+ local name = path[#path]
+
+ local parent = array_without_last(path)
+ local ppath, pmode = conv_path(parent)
- local apath, mvpath, index = self:find(path, type(value) ~= 'table')
- local mpath = mvpath or apath
+ if pmode then
+ gc[pth.join(unpack(array_without_last(parent)))] = false
+ end
- if not delete then
- if #self.aug:match(mpath) == 0 then
+ if pmode == '#' then
+ local count = #self.aug:match(ppath)
+ while count < name do
+ insert(parent, true)
+ count = count + 1
+ end
+ return apath, keys
+ end
+
+ local matches = self.aug:match(apath)
+ local count = #matches
+
+ if count > 0 and not new then return apath, keys end
+
+ if key_mode(pmode) then
+ ppath = insert(parent, true)
+ local key = key(pmode)
+ self.aug:set(ppath..'/'..key, name)
+ return append_pred(ppath, key), keys
+ end
+
+ if #matches == 0 then
+ matches = self.aug:match(ppath..'/*')
local function order(path)
- local top = topology('/augeas'..path)
+ local top = aug_top(path)
return top and top.order
end
- local ord = order(pth.join('/', unpack(path)))
+ local ord = order(path)
- for _, sibling in ipairs(self.aug:match(pth.parent(mpath)..'/*')) do
- local sord = order(strip_name(sibling):sub(7, -1))
+ for _, sibling in ipairs(matches) do
+ local sord = order(array_join(parent, basename(sibling)))
if sord and sord > ord then
- self.aug:insert(sibling, pth.name(mpath), true)
- break
+ self.aug:insert(sibling, name, true)
+ return apath, keys
end
end
end
- if mvpath then
- local size = #self.aug:match(mvpath)
- while size < index do
- self.aug:insert(ipath(mvpath, size), pth.name(mvpath))
- size = size + 1
- end
+ if #matches == 0 then
+ if new then self.aug:set(apath, nil) end
+ return apath, keys
end
+
+ self.aug:insert(matches[#matches], name)
+ return append_pred(apath, count + 1), keys
end
- if type(value) == 'table' then value = nil end
- if not delete or mvpath then
- self.aug:set(apath, not delete and tostr(value) or nil)
+ local apath, keys = insert(path)
+ local is_table = type(value) == 'table'
+
+ if not (is_table or util.contains(keys, '.')) then
+ self.aug:set(apath, value ~= nil and tostr(value) or nil)
end
- if delete or value == '' then gcpaths[mpath] = true end
+ util.setdefault(gc, pth.join(unpack(path)), true)
end
- local function gc(path)
- if path == '/' or #self.aug:match(path..'/*') > 0 then return end
- if self.aug:rm(path.."[. = '']") > 0 then gc(pth.parent(path)) end
+ for path, _ in pairs(gc) do
+ local p = pth.split(path)
+ while #p > 0 do
+ local value = self:get(p)
+
+ if (
+ type(value) == 'string' and value ~= ''
+ ) or (
+ type(value) == 'table' and #value > 0
+ ) then
+ break
+ end
+
+ if gc[pth.join(unpack(p))] ~= false then self.aug:rm(conv_path(p)) end
+ p[#p] = nil
+ end
end
- for p, _ in pairs(gcpaths) do gc(p) end
if self.aug:save() ~= 0 then
print('Augeas save failed')
diff --git a/acf2/transaction/init.lua b/acf2/transaction/init.lua
index eb316b5..8f11495 100644
--- a/acf2/transaction/init.lua
+++ b/acf2/transaction/init.lua
@@ -22,8 +22,6 @@ local function remove_list_value(list, value)
return
end
end
-
- assert(false)
end