* luci/libs: uvl: fix handling of boolean options, better error descriptions, implement "required" and "unique" scheme properties for sections
This commit is contained in:
parent
d33e42b536
commit
ed5c3eacf0
3 changed files with 114 additions and 33 deletions
|
@ -19,6 +19,7 @@ module( "luci.uvl", package.seeall )
|
|||
require("luci.fs")
|
||||
require("luci.util")
|
||||
require("luci.model.uci")
|
||||
require("luci.uvl.loghelper")
|
||||
require("luci.uvl.datatypes")
|
||||
--require("luci.uvl.validation")
|
||||
require("luci.uvl.dependencies")
|
||||
|
@ -48,19 +49,10 @@ function UVL.__init__( self, schemedir )
|
|||
self.packages = { }
|
||||
self.beenthere = { }
|
||||
self.uci = luci.model.uci
|
||||
self.log = luci.uvl.loghelper
|
||||
self.datatypes = luci.uvl.datatypes
|
||||
end
|
||||
|
||||
function UVL._keys( self, tbl )
|
||||
local keys = { }
|
||||
if tbl then
|
||||
for k, _ in luci.util.kspairs(tbl) do
|
||||
table.insert( keys, k )
|
||||
end
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
|
||||
--- Validate given configuration.
|
||||
-- @param config Name of the configuration to validate
|
||||
|
@ -68,24 +60,29 @@ end
|
|||
-- @return Boolean indicating weather the given config validates
|
||||
-- @return String containing the reason for errors (if any)
|
||||
function UVL.validate( self, config )
|
||||
|
||||
self.uci.load_config( config )
|
||||
self.beenthere = { }
|
||||
|
||||
local co = self.uci.get_all( config )
|
||||
local sc = { }
|
||||
|
||||
local function _uci_foreach( type, func )
|
||||
local ok, err
|
||||
for k, v in pairs(co) do
|
||||
if co[k]['.type'] == type then
|
||||
sc[type] = sc[type] + 1
|
||||
ok, err = func( k, v )
|
||||
if not ok then break end
|
||||
if not ok then
|
||||
err = self.log.config_error( config, err )
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
|
||||
for k, v in pairs( self.packages[config].sections ) do
|
||||
sc[k] = 0
|
||||
local ok, err = _uci_foreach( k,
|
||||
function(s)
|
||||
local sect = luci.uvl.section( self, co, k, config, s )
|
||||
|
@ -98,12 +95,25 @@ function UVL.validate( self, config )
|
|||
if STRICT_UNKNOWN_SECTIONS then
|
||||
for k, v in pairs(co) do
|
||||
if not self.beenthere[config..'.'..k] then
|
||||
return false, "Section '" .. config .. '.' .. co[k]['.type'] ..
|
||||
"' not found in scheme"
|
||||
return false, self.log.config_error( config,
|
||||
"Section '" .. config .. '.' .. co[k]['.type'] ..
|
||||
"' not found in scheme" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, k in ipairs(luci.util.keys(sc)) do
|
||||
local s = self.packages[config].sections[k]
|
||||
|
||||
if s.required and sc[k] == 0 then
|
||||
return false, self.log.config_error( config,
|
||||
'Required section "' .. k .. '" not found in config' )
|
||||
elseif s.unique and sc[k] > 1 then
|
||||
return false, self.log.config_error( config,
|
||||
'Unique section "' .. k .. '" occurs multiple times in config' )
|
||||
end
|
||||
end
|
||||
|
||||
return true, nil
|
||||
end
|
||||
|
||||
|
@ -152,14 +162,14 @@ function UVL._validate_section( self, section )
|
|||
local ok, err = self:_validate_option( v )
|
||||
|
||||
if not ok then
|
||||
return ok, err
|
||||
return ok, self.log.section_error( section, err )
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = luci.uvl.dependencies.check( self, section )
|
||||
|
||||
if not ok then
|
||||
return false, "All possible dependencies failed"
|
||||
return false, err
|
||||
end
|
||||
else
|
||||
print( "Error, scheme section '" .. section:sid() .. "' not found in data" )
|
||||
|
@ -191,7 +201,7 @@ function UVL._validate_option( self, option, nodeps )
|
|||
if not option:option() and
|
||||
not ( option:section() and option:section().dynamic )
|
||||
then
|
||||
return false, "Requested option '" .. option:sid() ..
|
||||
return false, "Option '" .. option:cid() ..
|
||||
"' not found in scheme"
|
||||
end
|
||||
|
||||
|
@ -208,7 +218,7 @@ function UVL._validate_option( self, option, nodeps )
|
|||
return false, "Value '" .. ( option:value() or '<nil>' ) ..
|
||||
"' of given option '" .. option:cid() ..
|
||||
"' is not defined in enum { " ..
|
||||
table.concat(self:_keys(option:option().values),", ") ..
|
||||
table.concat(luci.util.keys(option:option().values),", ") ..
|
||||
" }"
|
||||
end
|
||||
end
|
||||
|
@ -301,6 +311,11 @@ function UVL._read_scheme_parts( self, scheme, schemes )
|
|||
return r
|
||||
end
|
||||
|
||||
-- helper function to read bools
|
||||
local function _bool( v )
|
||||
return ( v == "true" or v == "yes" or v == "on" or v == "1" )
|
||||
end
|
||||
|
||||
-- Step 1: get all sections
|
||||
for i, conf in ipairs( schemes ) do
|
||||
for k, v in pairs( conf ) do
|
||||
|
@ -331,6 +346,8 @@ function UVL._read_scheme_parts( self, scheme, schemes )
|
|||
"dependency specification in '%s'",
|
||||
v.name or '<nil>', scheme or '<nil>', k
|
||||
)
|
||||
elseif k == "dynamic" or k == "unique" or k == "required" then
|
||||
s[k] = _bool(v2)
|
||||
else
|
||||
s[k] = v2
|
||||
end
|
||||
|
@ -361,27 +378,31 @@ function UVL._read_scheme_parts( self, scheme, schemes )
|
|||
|
||||
local t = s[v.name]
|
||||
|
||||
for k, v in pairs(v) do
|
||||
for k, v2 in pairs(v) do
|
||||
if k ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
|
||||
if k:match("^depends") then
|
||||
t["depends"] = _assert(
|
||||
self:_read_dependency( v, t["depends"] ),
|
||||
"Variable '%s' in scheme '%s' has malformed " ..
|
||||
"dependency specification in '%s'",
|
||||
v.name, scheme, k
|
||||
self:_read_dependency( v2, t["depends"] ),
|
||||
'Invalid reference "%s" in "%s.%s.%s"',
|
||||
v2, v.name, scheme, k
|
||||
)
|
||||
elseif k:match("^validator") then
|
||||
t["validators"] = _assert(
|
||||
self:_read_validator( v, t["validators"] ),
|
||||
self:_read_validator( v2, t["validators"] ),
|
||||
"Variable '%s' in scheme '%s' has malformed " ..
|
||||
"validator specification in '%s'",
|
||||
v.name, scheme, k
|
||||
)
|
||||
elseif k == "required" then
|
||||
t[k] = _bool(v2)
|
||||
else
|
||||
t[k] = v
|
||||
t[k] = v2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
t.type = t.type or "variable"
|
||||
t.required = t.required or false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -61,7 +61,10 @@ function check( self, object, nodeps )
|
|||
|
||||
if item.depends then
|
||||
local ok = false
|
||||
local valid, err
|
||||
local valid, err = false,
|
||||
string.format( 'In dependency check for %s "%s":',
|
||||
( object.type == luci.uvl.TYPE_SECTION and "section" or "option" ),
|
||||
object:cid() )
|
||||
|
||||
for _, dep in ipairs(item.depends) do
|
||||
local subcondition = true
|
||||
|
@ -82,23 +85,21 @@ function check( self, object, nodeps )
|
|||
ref[1], ref[2], ref[3]
|
||||
)
|
||||
|
||||
valid, err = self:_validate_option( option, true )
|
||||
valid, err2 = self:_validate_option( option, true )
|
||||
if valid then
|
||||
if not (
|
||||
( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
|
||||
( ref[3] and object.config[ref[2]][ref[3]] ) == v
|
||||
) then
|
||||
subcondition = false
|
||||
err = type(v) ~= "boolean"
|
||||
and "Option '" .. table.concat( ref, "." ) ..
|
||||
"' doesn't match requested value '" .. v .. '"'
|
||||
or "Option '" .. table.concat( ref, "." ) ..
|
||||
"' has no value"
|
||||
|
||||
err = err .. "\n" ..
|
||||
self.log.dump_dependency( dep, ref, v )
|
||||
break
|
||||
end
|
||||
else
|
||||
subcondition = false
|
||||
err = err .. "\n" ..
|
||||
self.log.dump_dependency( dep, ref, nil, err2 )
|
||||
break
|
||||
end
|
||||
end
|
||||
|
|
59
libs/uvl/luasrc/uvl/loghelper.lua
Normal file
59
libs/uvl/luasrc/uvl/loghelper.lua
Normal file
|
@ -0,0 +1,59 @@
|
|||
--[[
|
||||
|
||||
UCI Validation Layer - Logging utilities
|
||||
(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||
(c) 2008 Steven Barth <steven@midlink.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
$Id$
|
||||
|
||||
]]--
|
||||
|
||||
module( "luci.uvl.loghelper", package.seeall )
|
||||
|
||||
function config_error( config, message )
|
||||
return string.format(
|
||||
'Error in config "%s":\n%s',
|
||||
config, message or "Unknown error"
|
||||
)
|
||||
end
|
||||
|
||||
function section_error( section, message )
|
||||
return string.format(
|
||||
'Error in section "%s":\n%s',
|
||||
section:cid(), message or "Unknown error"
|
||||
)
|
||||
end
|
||||
|
||||
function dump_dependency( dep, ref, v, e )
|
||||
local str = nil
|
||||
|
||||
for k, v in luci.util.spairs( dep,
|
||||
function(a,b)
|
||||
a = ( type(dep[a]) ~= "boolean" and "_" or "" ) .. a
|
||||
b = ( type(dep[b]) ~= "boolean" and "_" or "" ) .. b
|
||||
return a < b
|
||||
end
|
||||
) do
|
||||
str = ( str and str .. " and " or "Dependency (" ) .. k ..
|
||||
( type(v) ~= "boolean" and "=" .. v or "" )
|
||||
end
|
||||
|
||||
str = string.format(
|
||||
'%s) failed:\n\t%s',
|
||||
str, e or string.format(
|
||||
'Option "%s" %s',
|
||||
table.concat( ref, "." ), (
|
||||
type(v) == "boolean"
|
||||
and "has no value" or 'is not equal "' .. v .. '"'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return str
|
||||
end
|
Loading…
Reference in a new issue