From 31e261fd9aaf1e882d8df368b5842e4d5b612e62 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 27 Oct 2013 23:44:31 +0200 Subject: augeas backend: explicit addresses for predicate-based parameter access --- acf2/model/root.lua | 6 +- acf2/modules/net.lua | 8 +- acf2/persistence/backends/augeas.lua | 265 ++++++++++++++++++++++++----------- acf2/transaction/init.lua | 2 - 4 files changed, 191 insertions(+), 90 deletions(-) (limited to 'acf2') 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 -- cgit v1.2.3