* luci/libs: uvl: add support for list values in schemes and configurations

This commit is contained in:
Jo-Philipp Wich 2008-08-18 20:37:13 +00:00
parent 63c48c8dde
commit 70aa9bb855
2 changed files with 95 additions and 76 deletions

View file

@ -46,6 +46,10 @@ STRICT_UNKNOWN_OPTIONS = true
-- treat failed external validators as error -- treat failed external validators as error
STRICT_EXTERNAL_VALIDATORS = true STRICT_EXTERNAL_VALIDATORS = true
--- Boolean; default true;
-- treat list values stored as options like errors
STRICT_LIST_TYPE = true
local default_schemedir = "/etc/scheme" local default_schemedir = "/etc/scheme"
@ -193,8 +197,8 @@ function UVL.validate_section( self, config, section )
self, co, co[section]['.type'], config, section self, co, co[section]['.type'], config, section
) ) ) )
else else
return false, "Section '" .. config .. '.' .. section .. return false, 'Section "' .. config .. '.' .. section ..
"' not found in config. Nothing to do." '" not found in config. Nothing to do.'
end end
end end
@ -227,9 +231,9 @@ function UVL.validate_option( self, config, section, option )
self, co, co[section]['.type'], config, section, option self, co, co[section]['.type'], config, section, option
) ) ) )
else else
return false, "Option '" .. return false, 'Option "' ..
config .. '.' .. section .. '.' .. option .. config .. '.' .. section .. '.' .. option ..
"' not found in config. Nothing to do." '" not found in config. Nothing to do.'
end end
end end
@ -252,7 +256,7 @@ function UVL._validate_section( self, section )
return false, err return false, err
end end
else else
print( "Error, scheme section '" .. section:sid() .. "' not found in data" ) return false, 'Option "' .. section:sid() .. '" not found in config'
end end
if STRICT_UNKNOWN_OPTIONS and not section:section().dynamic then if STRICT_UNKNOWN_OPTIONS and not section:section().dynamic then
@ -271,44 +275,45 @@ end
function UVL._validate_option( self, option, nodeps ) function UVL._validate_option( self, option, nodeps )
if not option:option() and local item = option:option()
not ( option:section() and option:section().dynamic ) local val = option:value()
then
return false, "Option '" .. option:cid() ..
"' not found in scheme"
end
if option:option() then if not item and not ( option:section() and option:section().dynamic ) then
if option:option().required and not option:value() then return false, 'Option "' .. option:cid() ..
return false, "Mandatory variable '" .. option:cid() .. '" not found in scheme'
"' doesn't have a value"
elseif item then
if item.required and not val then
return false, 'Mandatory variable "' .. option:cid() ..
'" does not have a value'
end end
if option:option().type == "enum" and option:value() then if item.type == "enum" and val then
if not option:option().values or if not item.values or not item.values[val] then
not option:option().values[option:value()] return false, 'Value "' .. ( val or '<nil>' ) ..
then '" of given option "' .. option:cid() ..
return false, "Value '" .. ( option:value() or '<nil>' ) .. '" is not defined in enum { ' ..
"' of given option '" .. option:cid() .. table.concat( luci.util.keys(item.values), ", " ) ..
"' is not defined in enum { " .. ' }'
table.concat(luci.util.keys(option:option().values),", ") .. end
" }" elseif item.type == "list" and val then
if type(val) ~= "table" and STRICT_LIST_TYPE then
return false, 'Option "' .. option:cid() ..
'" is defined as list but stored as plain value'
end end
end end
if option:option().datatype and option:value() then if item.datatype and val then
if self.datatypes[option:option().datatype] then if self.datatypes[item.datatype] then
if not self.datatypes[option:option().datatype]( if not self.datatypes[item.datatype]( val ) then
option:value() return false, 'Value "' .. ( val or '<nil>' ) ..
) then '" of given option "' .. option:cid() ..
return false, "Value '" .. ( option:value() or '<nil>' ) .. '" does not validate as datatype "' ..
"' of given option '" .. option:cid() .. item.datatype .. '"'
"' doesn't validate as datatype '" ..
option:option().datatype .. "'"
end end
else else
return false, "Unknown datatype '" .. return false, 'Unknown datatype "' ..
option:option().datatype .. "' encountered" item.datatype .. '" encountered'
end end
end end
@ -346,7 +351,7 @@ function UVL.read_scheme( self, scheme )
return self:_read_scheme_parts( scheme, schemes ) return self:_read_scheme_parts( scheme, schemes )
else else
error( error(
'Can\'t find scheme "' .. scheme .. 'Can not find scheme "' .. scheme ..
'" in "' .. self.schemedir .. '"' '" in "' .. self.schemedir .. '"'
) )
end end
@ -426,7 +431,7 @@ function UVL._read_scheme_parts( self, scheme, schemes )
for k, v2 in pairs(v) do for k, v2 in pairs(v) do
if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then if k ~= "name" and k ~= "package" and k:sub(1,1) ~= "." then
if k:match("^depends") then if k == "depends" then
s["depends"] = _assert( s["depends"] = _assert(
self:_read_dependency( v2, s["depends"] ), self:_read_dependency( v2, s["depends"] ),
'Section "%s" in scheme "%s" has malformed ' .. 'Section "%s" in scheme "%s" has malformed ' ..
@ -471,13 +476,13 @@ function UVL._read_scheme_parts( self, scheme, schemes )
for k, v2 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 ~= "name" and k ~= "section" and k:sub(1,1) ~= "." then
if k:match("^depends") then if k == "depends" then
t["depends"] = _assert( t["depends"] = _assert(
self:_read_dependency( v2, t["depends"] ), self:_read_dependency( v2, t["depends"] ),
'Invalid reference "%s" in "%s.%s.%s"', 'Invalid reference "%s" in "%s.%s.%s"',
v2, v.name, scheme, k v2, v.name, scheme, k
) )
elseif k:match("^validator") then elseif k == "validator" then
t["validators"] = _assert( t["validators"] = _assert(
self:_read_validator( v2, t["validators"] ), self:_read_validator( v2, t["validators"] ),
'Variable "%s" in scheme "%s" has malformed ' .. 'Variable "%s" in scheme "%s" has malformed ' ..
@ -507,7 +512,6 @@ function UVL._read_scheme_parts( self, scheme, schemes )
_req( TYPE_ENUM, v, { "value", "variable" } ) _req( TYPE_ENUM, v, { "value", "variable" } )
local r = _ref( TYPE_ENUM, v ) local r = _ref( TYPE_ENUM, v )
local p = _assert( self.packages[r[1]], local p = _assert( self.packages[r[1]],
'Enum "%s" in scheme "%s" references unknown package "%s"', 'Enum "%s" in scheme "%s" references unknown package "%s"',
v.value, scheme, r[1] ) v.value, scheme, r[1] )
@ -548,53 +552,63 @@ function UVL._read_scheme_parts( self, scheme, schemes )
end end
-- Read a dependency specification -- Read a dependency specification
function UVL._read_dependency( self, value, deps ) function UVL._read_dependency( self, values, deps )
local parts = luci.util.split( value, "%s*,%s*", nil, true ) local expr = "%$?[a-zA-Z0-9_]+"
local condition = { } if values then
values = ( type(values) == "table" and values or { values } )
for _, value in ipairs(values) do
local parts = luci.util.split( value, "%s*,%s*", nil, true )
local condition = { }
for i, val in ipairs(parts) do
local k, v = unpack(luci.util.split(val, "%s*=%s*", nil, true))
for i, val in ipairs(parts) do if k and (
local k, v = unpack(luci.util.split( val, "%s*=%s*", nil, true )) k:match("^"..expr.."%."..expr.."%."..expr.."$") or
k:match("^"..expr.."%."..expr.."$") or
k:match("^"..expr.."$")
) then
condition[k] = v or true
else
return nil
end
end
if k and ( if not deps then
k:match("^%$?[a-zA-Z0-9_]+%.%$?[a-zA-Z0-9_]+%.%$?[a-zA-Z0-9_]+$") or deps = { condition }
k:match("^%$?[a-zA-Z0-9_]+%.%$?[a-zA-Z0-9_]+$") or else
k:match("^%$?[a-zA-Z0-9_]+$") table.insert( deps, condition )
) then end
condition[k] = v or true
else
return nil
end end
end end
if not deps then
deps = { condition }
else
table.insert( deps, condition )
end
return deps return deps
end end
-- Read a validator specification -- Read a validator specification
function UVL._read_validator( self, value, validators ) function UVL._read_validator( self, values, validators )
if value then if values then
local validator values = ( type(values) == "table" and values or { values } )
for _, value in ipairs(values) do
local validator
if value:match("^exec:") then if value:match("^exec:") then
validator = value:gsub("^exec:","") validator = value:gsub("^exec:","")
elseif value:match("^lua:") then elseif value:match("^lua:") then
validator = self:_resolve_function( (value:gsub("^lua:","") ) ) validator = self:_resolve_function( (value:gsub("^lua:","") ) )
end
if validator then
if not validators then
validators = { validator }
else
table.insert( validators, validator )
end end
return validators if validator then
if not validators then
validators = { validator }
else
table.insert( validators, validator )
end
else
return nil
end
end end
return validators
end end
end end

View file

@ -69,8 +69,8 @@ $Id$
Usage: Usage:
uvl --help uvl --help
uvl [--silent] [--schemedir=DIR] uvl [--silent] [--schemedir=DIR]
[--no-strict-sections] [--no-strict-options] [--no-strict-sections] [--no-strict-options] [--no-strict-validators]
[--no-strict-validators] config[.section[.option]] [--no-strict-lists] config[.section[.option]]
Options: Options:
--help --help
@ -90,6 +90,9 @@ Options:
--no-strict-validators --no-strict-validators
Don't invalidate config if an external validator fails. Don't invalidate config if an external validator fails.
--no-strict-lists
Don't invalidate lists that are stored options.
]=]) ]=])
os.exit(255) os.exit(255)
else else
@ -99,6 +102,8 @@ else
( options['no-strict-options'] and false or true ) ( options['no-strict-options'] and false or true )
luci.uvl.STRICT_EXTERNAL_VALIDATORS = luci.uvl.STRICT_EXTERNAL_VALIDATORS =
( options['no-strict-validators'] and false or true ) ( options['no-strict-validators'] and false or true )
luci.uvl.STRICT_LIST_TYPE =
( options['no-strict-lists'] and false or true )
local uvl = luci.uvl.UVL( local uvl = luci.uvl.UVL(
type(options.schemedir) == "string" and options.schemedir or nil type(options.schemedir) == "string" and options.schemedir or nil