* luci/libs: uvl: add support for list values in schemes and configurations
This commit is contained in:
parent
63c48c8dde
commit
70aa9bb855
2 changed files with 95 additions and 76 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue