* luci/libs: uvl - cleanup round #1

This commit is contained in:
Jo-Philipp Wich 2008-08-16 20:06:51 +00:00
parent 74b1d8da3b
commit 5237b79adf
2 changed files with 175 additions and 130 deletions

View file

@ -27,6 +27,9 @@ TYPE_SECTION = 0x01
TYPE_VARIABLE = 0x02 TYPE_VARIABLE = 0x02
TYPE_ENUM = 0x03 TYPE_ENUM = 0x03
STRICT_UNKNOWN_SECTIONS = true
STRICT_UNKNOWN_OPTIONS = true
local default_schemedir = "/etc/scheme" local default_schemedir = "/etc/scheme"
@ -41,7 +44,6 @@ end
UVL = luci.util.class() UVL = luci.util.class()
function UVL.__init__( self, schemedir ) function UVL.__init__( self, schemedir )
self.schemedir = schemedir or default_schemedir self.schemedir = schemedir or default_schemedir
self.packages = { } self.packages = { }
self.beenthere = { } self.beenthere = { }
@ -88,27 +90,33 @@ function UVL.validate( self, config )
local co = self.uci.get_all( config ) local co = self.uci.get_all( config )
local function _uci_foreach( type, func ) local function _uci_foreach( type, func )
local ok, err
for k, v in pairs(co) do for k, v in pairs(co) do
if co[k]['.type'] == type then if co[k]['.type'] == type then
func( k, v ) ok, err = func( k, v )
if not ok then break end
end end
end end
return ok, err
end end
luci.util.dumptable(co)
for k, v in pairs( self.packages[config].sections ) do for k, v in pairs( self.packages[config].sections ) do
_uci_foreach( k, local ok, err = _uci_foreach( k,
function(s) function(s)
local ok, err = self:validate_section( config, s, co ) local sect = luci.uvl.section( self, co, k, config, s )
return self:_validate_section( sect )
if not ok then
return ok, err
end
end end
) )
if not ok then return false, err end
end
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"
end
end
end end
return true, nil return true, nil
@ -120,50 +128,36 @@ end
-- @param scheme Scheme to validate against -- @param scheme Scheme to validate against
-- @return Boolean indicating weather the given config validates -- @return Boolean indicating weather the given config validates
-- @return String containing the reason for errors (if any) -- @return String containing the reason for errors (if any)
function UVL.validate_section( self, config, section, co, nodeps ) function UVL._validate_section( self, section )
if not co then if section:values() then
self.uci.set_confdir( self.uci.confdir_default )
self.uci.load( config )
co = uci.get_all( config )
end
local cs = co[section] for _, v in ipairs(section:variables()) do
local scheme = self:_scheme_section( co, config, section ) local ok, err = self:_validate_option( v )
if cs then if not ok then
--luci.util.dumptable(cs) return ok, err
for k, v in pairs(self.packages[config].variables[cs[".type"]]) do
if k:sub(1,1) ~= "." then
local ok, err = self:validate_option( config, section, k, co, false, cs[".type"] )
if not ok then
print("ERR", err)
return ok, err
end
end end
end end
--local dep_ok = nodeps or luci.uvl.dependencies.check_dependency( self, co, config, section ) local ok, err = luci.uvl.dependencies.check( self, section )
--print( "DEP: ", dep_ok )
--print( "Validate section: ", config .. '.' .. section, nodeps and '(without depencies)' or '' ) if not ok then
local ok, err = luci.uvl.dependencies.check_dependency(
self, co, config, section, nil, true, cs[".type"]
)
if ok then
--print("Validated section!\n\n")
return true
else
print("ERR", "All possible dependencies failed. (Last error was: " .. err .. ")")
return false, "All possible dependencies failed" return false, "All possible dependencies failed"
end end
else else
print( "Error, scheme section '" .. section .. "' not found in data" ) print( "Error, scheme section '" .. section:sid() .. "' not found in data" )
end
if STRICT_UNKNOWN_OPTIONS and not section:section().dynamic then
for k, v in pairs(section:values()) do
if k:sub(1,1) ~= "." and not self.beenthere[
section:cid() .. '.' .. k
] then
return false, "Option '" .. section:sid() .. '.' .. k ..
"' not found in scheme"
end
end
end end
return true, nil return true, nil
@ -176,57 +170,46 @@ end
-- @param scheme Scheme to validate against -- @param scheme Scheme to validate against
-- @return Boolean indicating weather the given config validates -- @return Boolean indicating weather the given config validates
-- @return String containing the reason for errors (if any) -- @return String containing the reason for errors (if any)
function UVL.validate_option( self, config, section, option, co, nodeps, section2 ) function UVL._validate_option( self, option, nodeps )
if not co then if not option:option() then
self.uci.set_confdir( self.uci.confdir_default ) return false, "Requested option '" .. option:sid() ..
self.uci.load( config )
co = uci.get_all( config )
end
local cs = co[section]
local sv = self:_scheme_option( co, config, section, option ) or
self:_scheme_option( co, config, section2, option )
--print("VOPT", config, section, option )
if not sv then
return false, "Requested option '" ..
config .. '.' .. ( section or section2 ) .. '.' .. option ..
"' not found in scheme" "' not found in scheme"
end end
if sv.required and not cs[option] then if option:option().required and not option:value() then
return false, "Mandatory variable '" .. return false, "Mandatory variable '" .. option:cid() ..
config .. '.' .. section .. '.' .. option ..
"' doesn't have a value" "' doesn't have a value"
end end
if sv.type == "enum" and cs[option] then if option:option().type == "enum" and option:value() then
if not sv.values or not sv.values[cs[option]] then if not option:option().values or
return false, "Value '" .. ( cs[option] or '<nil>' ) .. "' of given option '" .. not option:option().values[option:value()]
config .. "." .. section .. "." .. option .. then
return false, "Value '" .. ( option:value() or '<nil>' ) ..
"' of given option '" .. option:cid() ..
"' is not defined in enum { " .. "' is not defined in enum { " ..
table.concat(self:_keys(sv.values),", ") .. " }" table.concat(self:_keys(option:option().values),", ") ..
" }"
end end
end end
if sv.datatype and cs[option] then if option:option().datatype and option:value() then
if self.datatypes[sv.datatype] then if self.datatypes[option:option().datatype] then
if not self.datatypes[sv.datatype]( cs[option] ) then if not self.datatypes[option:option().datatype](option:value()) then
return false, "Value '" .. ( cs[option] or '<nil>' ) .. "' of given option '" .. return false, "Value '" .. ( option:value() or '<nil>' ) ..
config .. "." .. ( section or section2 ) .. "." .. option .. "' of given option '" .. option:cid() ..
"' doesn't validate as datatype '" .. sv.datatype .. "'" "' doesn't validate as datatype '" ..
option:option().datatype .. "'"
end end
else else
return false, "Unknown datatype '" .. sv.datatype .. "' encountered" return false, "Unknown datatype '" ..
option:option().datatype .. "' encountered"
end end
end end
if not nodeps then if not nodeps then
return luci.uvl.dependencies.check_dependency( return luci.uvl.dependencies.check( self, option )
self, co, config, section, option, nil, section2
)
end end
return true, nil return true, nil
@ -321,9 +304,9 @@ function UVL._read_scheme_parts( self, scheme, schemes )
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:match("^depends") then
s["depends"] = _assert( s["depends"] = _assert(
self:_read_depency( v2, s["depends"] ), self:_read_dependency( v2, s["depends"] ),
"Section '%s' in scheme '%s' has malformed " .. "Section '%s' in scheme '%s' has malformed " ..
"depency specification in '%s'", "dependency specification in '%s'",
v.name or '<nil>', scheme or '<nil>', k v.name or '<nil>', scheme or '<nil>', k
) )
else else
@ -360,9 +343,9 @@ function UVL._read_scheme_parts( self, scheme, schemes )
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:match("^depends") then
t["depends"] = _assert( t["depends"] = _assert(
self:_read_depency( v, t["depends"] ), self:_read_dependency( v, t["depends"] ),
"Variable '%s' in scheme '%s' has malformed " .. "Variable '%s' in scheme '%s' has malformed " ..
"depency specification in '%s'", "dependency specification in '%s'",
v.name, scheme, k v.name, scheme, k
) )
elseif k:match("^validator") then elseif k:match("^validator") then
@ -429,8 +412,8 @@ function UVL._read_scheme_parts( self, scheme, schemes )
return self return self
end end
-- Read a depency specification -- Read a dependency specification
function UVL._read_depency( self, value, deps ) function UVL._read_dependency( self, value, deps )
local parts = luci.util.split( value, "%s*,%s*", nil, true ) local parts = luci.util.split( value, "%s*,%s*", nil, true )
local condition = { } local condition = { }
@ -487,7 +470,7 @@ function UVL._resolve_function( self, value )
if stat and mod then if stat and mod then
for j=i+1, #path-1 do for j=i+1, #path-1 do
if not type(mod) == "table" then if not type(mod) == "table" then
break; break
end end
mod = mod[path[j]] mod = mod[path[j]]
if not mod then if not mod then
@ -501,3 +484,80 @@ function UVL._resolve_function( self, value )
end end
end end
end end
section = luci.util.class()
function section.__init__(self, scheme, co, st, c, s)
self.csection = co[s]
self.ssection = scheme.packages[c].sections[st]
self.cref = { c, s }
self.sref = { c, st }
self.scheme = scheme
self.config = co
self.type = luci.uvl.TYPE_SECTION
end
function section.cid(self)
return ( self.cref[1] or '?' ) .. '.' .. ( self.cref[2] or '?' )
end
function section.sid(self)
return ( self.sref[1] or '?' ) .. '.' .. ( self.sref[2] or '?' )
end
function section.values(self)
return self.csection
end
function section.section(self)
return self.ssection
end
function section.variables(self)
local v = { }
if self.scheme.packages[self.sref[1]].variables[self.sref[2]] then
for o, _ in pairs(
self.scheme.packages[self.sref[1]].variables[self.sref[2]]
) do
table.insert( v, luci.uvl.option(
self.scheme, self.config, self.sref[2],
self.cref[1], self.cref[2], o
) )
end
end
return v
end
option = luci.util.class()
function option.__init__(self, scheme, co, st, c, s, o)
self.coption = co[s] and co[s][o] or nil
self.soption = scheme.packages[c].variables[st][o]
self.cref = { c, s, o }
self.sref = { c, st, o }
self.scheme = scheme
self.config = co
self.type = luci.uvl.TYPE_OPTION
end
function option.cid(self)
return ( self.cref[1] or '?' ) .. '.' ..
( self.cref[2] or '?' ) .. '.' ..
( self.cref[3] or '?' )
end
function option.sid(self)
return ( self.sref[1] or '?' ) .. '.' ..
( self.sref[2] or '?' ) .. '.' ..
( self.sref[3] or '?' )
end
function option.value(self)
return self.coption
end
function option.option(self)
return self.soption
end

View file

@ -1,6 +1,6 @@
--[[ --[[
UCI Validation Layer - Main Library UCI Validation Layer - Dependency helper
(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> (c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
(c) 2008 Steven Barth <steven@midlink.org> (c) 2008 Steven Barth <steven@midlink.org>
@ -42,70 +42,58 @@ function _parse_reference( r, c, s, o )
elseif #ref == 2 and c then elseif #ref == 2 and c then
ref = { c, unpack(ref) } ref = { c, unpack(ref) }
elseif #ref ~= 3 then elseif #ref ~= 3 then
print("INVALID REFERENCE: "..#ref, c, s, o)
ref = nil ref = nil
end end
return ref return ref
end end
function check_dependency( self, uci, conf, sect, optn, nodeps, section2 ) function check( self, object, nodeps )
-- print( "Depency check: ", conf .. '.' .. sect .. ( optn and '.' .. optn or '' ) ) if not self.beenthere[object:cid()] then
self.beenthere[object:cid()] = true
local key = conf .. '.' .. sect .. ( optn and '.' .. optn or '' )
if not self.beenthere[key] then
self.beenthere[key] = true
else else
print("CIRCULAR DEPENDENCY!") return false, "Recursive dependency for '" .. object:sid() .. "' found"
return false, "Recursive depency detected"
end end
-- check for config local item = object.type == luci.uvl.TYPE_SECTION
if not self.packages[conf] then self:read_scheme(conf) end and object:section() or object:option()
local item = self.packages[conf]
-- check for section
if sect then
item = _assert( self:_scheme_section( uci, conf, sect ) or self:_scheme_section( uci, conf, section2 ),
"Unknown section '%s' in scheme '%s' requested",
sect or '<nil>', conf or '<nil>' )
-- check for option
if optn then
item = _assert( self:_scheme_option( uci, conf, sect, optn ) or
self:_scheme_option( uci, conf, section2, optn ),
"Unknown variable '%s' in scheme '%s', section '%s' requested",
optn or '<nil>', conf or '<nil>', sect or '<nil>' )
end
end
if item.depends then if item.depends then
local ok = false local ok = false
local valid, err local valid, err
for _, dep in ipairs(item.depends) do for _, dep in ipairs(item.depends) do
--print("DEP:",luci.util.serialize_data(dep))
local subcondition = true local subcondition = true
for k, v in pairs(dep) do for k, v in pairs(dep) do
-- XXX: better error -- XXX: better error
local ref = _assert( _parse_reference(k,conf,sect,optn), local ref = _parse_reference( k, unpack(object.cref) )
"Ambiguous dependency reference '" .. k .. "' given" )
-- XXX: true -> nodeps if not ref then
valid, err = self:validate_option(ref[1], ref[2], ref[3], uci, true, section2) return false, "Ambiguous dependency reference '" .. k ..
"' for object '" .. object:sid() .. "' given"
end
local option = luci.uvl.option(
self, object.config,
object.config[ref[2]]
and object.config[ref[2]]['.type']
or object.sref[2],
ref[1], ref[2], ref[3]
)
valid, err = self:_validate_option( option, true )
if valid then if valid then
--print("CHK:",uci[ref[2]][ref[3]],v,unpack(ref))
if not ( if not (
( type(v) == "boolean" and uci[ref[2]][ref[3]] ) or ( type(v) == "boolean" and object.config[ref[2]][ref[3]] ) or
( ref[3] and uci[ref[2]][ref[3]] ) == v ( ref[3] and object.config[ref[2]][ref[3]] ) == v
) then ) then
subcondition = false subcondition = false
err = type(v) ~= "boolean" err = type(v) ~= "boolean"
and "Option '" .. table.concat( ref, "." ) .. "' doesn't match requested type '" .. v .. '"' and "Option '" .. table.concat( ref, "." ) ..
or "Option '" .. table.concat( ref, "." ) .. "' has no value" "' doesn't match requested value '" .. v .. '"'
or "Option '" .. table.concat( ref, "." ) ..
"' has no value"
break break
end end
@ -116,15 +104,12 @@ function check_dependency( self, uci, conf, sect, optn, nodeps, section2 )
end end
if subcondition then if subcondition then
-- print( " -> Success (condition matched)\n" )
return true return true
end end
end end
-- print( " -> Failed\n" )
return false, err return false, err
end end
-- print( " -> Success (no depends)\n" )
return true return true
end end