* luci/libs: more UVL hacking, needs to be rewritten later

This commit is contained in:
Jo-Philipp Wich 2008-08-16 03:41:57 +00:00
parent f94fb5ac18
commit 4ed96bb95e
2 changed files with 266 additions and 34 deletions

View file

@ -20,6 +20,8 @@ require("luci.fs")
require("luci.util") require("luci.util")
require("luci.model.uci") require("luci.model.uci")
require("luci.uvl.datatypes") require("luci.uvl.datatypes")
--require("luci.uvl.validation")
require("luci.uvl.dependencies")
TYPE_SECTION = 0x01 TYPE_SECTION = 0x01
TYPE_VARIABLE = 0x02 TYPE_VARIABLE = 0x02
@ -27,6 +29,7 @@ TYPE_ENUM = 0x03
local default_schemedir = "/etc/scheme" local default_schemedir = "/etc/scheme"
local function _assert( condition, fmt, ... ) local function _assert( condition, fmt, ... )
if not condition then if not condition then
return assert( nil, string.format( fmt, ... ) ) return assert( nil, string.format( fmt, ... ) )
@ -41,28 +44,72 @@ function UVL.__init__( self, schemedir )
self.schemedir = schemedir or default_schemedir self.schemedir = schemedir or default_schemedir
self.packages = { } self.packages = { }
self.beenthere = { }
self.uci = luci.model.uci self.uci = luci.model.uci
self.datatypes = luci.uvl.datatypes self.datatypes = luci.uvl.datatypes
end end
function UVL._scheme_section( self, uci, c, s )
if self.packages[c] and uci[s] then
return self.packages[c].sections[uci[s][".type"]]
end
end
function UVL._scheme_option( self, uci, c, s, o )
if self.packages[c] and uci[s] and uci[s][o] then
return self.packages[c].variables[uci[s][".type"]][o]
elseif self.packages[c] and self.packages[c].variables[s] then
return self.packages[c].variables[s][o]
end
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. --- Validate given configuration.
-- @param config Name of the configuration to validate -- @param config Name of the configuration to validate
-- @param scheme Scheme to validate against (optional) -- @param scheme Scheme to validate against (optional)
-- @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( self, config, scheme ) function UVL.validate( self, config )
if not scheme then self.uci.set_confdir( self.uci.confdir_default )
return false, "No scheme found" self.uci.load( config )
local co = self.uci.get_all( config )
local function _uci_foreach( type, func )
for k, v in pairs(co) do
if co[k]['.type'] == type then
func( k, v )
end
end
end end
for k, v in pairs( config ) do luci.util.dumptable(co)
local ok, err = self:validate_section( config, k, scheme )
for k, v in pairs( self.packages[config].sections ) do
_uci_foreach( k,
function(s)
local ok, err = self:validate_section( config, s, co )
if not ok then if not ok then
return ok, err return ok, err
end end
end end
)
end
return true, nil return true, nil
end end
@ -73,19 +120,51 @@ 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, scheme ) function UVL.validate_section( self, config, section, co, nodeps )
if not scheme then if not co then
return false, "No scheme found" self.uci.set_confdir( self.uci.confdir_default )
self.uci.load( config )
co = uci.get_all( config )
end end
for k, v in pairs( config[section] ) do local cs = co[section]
local ok, err = self:validate_option( config, section, k, scheme ) local scheme = self:_scheme_section( co, config, section )
if cs then
--luci.util.dumptable(cs)
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 if not ok then
print("ERR", err)
return ok, err return ok, err
end end
end end
end
--local dep_ok = nodeps or luci.uvl.dependencies.check_dependency( self, co, config, section )
--print( "DEP: ", dep_ok )
--print( "Validate section: ", config .. '.' .. section, nodeps and '(without depencies)' or '' )
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"
end
else
print( "Error, scheme section '" .. section .. "' not found in data" )
end
return true, nil return true, nil
end end
@ -97,36 +176,59 @@ 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, scheme ) function UVL.validate_option( self, config, section, option, co, nodeps, section2 )
if type(config) == "string" then if not co then
config = { ["variables"] = { [section] = { [option] = config } } } self.uci.set_confdir( self.uci.confdir_default )
self.uci.load( config )
co = uci.get_all( config )
end end
if not scheme then local cs = co[section]
return false, "No scheme found" 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"
end end
local sv = scheme.variables[section] if sv.required and not cs[option] then
if not sv then return false, "Requested section not found in scheme" end return false, "Mandatory variable '" ..
config .. '.' .. section .. '.' .. option ..
sv = sv[option] "' doesn't have a value"
if not sv then return false, "Requested option not found in scheme" end
if not ( config[section] and config[section][option] ) and sv.required then
return false, "Mandatory variable doesn't have a value"
end end
if sv.type then if sv.type == "enum" and cs[option] then
if self.datatypes[sv.type] then if not sv.values or not sv.values[cs[option]] then
if not self.datatypes[sv.type]( config[section][option] ) then return false, "Value '" .. ( cs[option] or '<nil>' ) .. "' of given option '" ..
return false, "Value of given option doesn't validate" config .. "." .. section .. "." .. option ..
"' is not defined in enum { " ..
table.concat(self:_keys(sv.values),", ") .. " }"
end
end
if sv.datatype and cs[option] then
if self.datatypes[sv.datatype] then
if not self.datatypes[sv.datatype]( cs[option] ) then
return false, "Value '" .. ( cs[option] or '<nil>' ) .. "' of given option '" ..
config .. "." .. ( section or section2 ) .. "." .. option ..
"' doesn't validate as datatype '" .. sv.datatype .. "'"
end end
else else
return false, "Unknown datatype '" .. sv.type .. "' encountered" return false, "Unknown datatype '" .. sv.datatype .. "' encountered"
end end
end end
if not nodeps then
return luci.uvl.dependencies.check_dependency(
self, co, config, section, option, nil, section2
)
end
return true, nil return true, nil
end end
@ -238,7 +340,7 @@ function UVL._read_scheme_parts( self, scheme, schemes )
for k, v in pairs( conf ) do for k, v in pairs( conf ) do
if v['.type'] == "variable" then if v['.type'] == "variable" then
_req( TYPE_VARIABLE, v, { "name", "type", "section" } ) _req( TYPE_VARIABLE, v, { "name", "section" } )
local r = _ref( TYPE_VARIABLE, v ) local r = _ref( TYPE_VARIABLE, v )

View file

@ -0,0 +1,130 @@
--[[
UCI Validation Layer - Main Library
(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.dependencies", package.seeall )
local function _assert( condition, fmt, ... )
if not condition then
return assert( nil, string.format( fmt, ... ) )
else
return condition
end
end
function _parse_reference( r, c, s, o )
local ref = { }
local vars = {
config = c,
section = s,
option = o
}
for i, v in ipairs(luci.util.split(r,".")) do
table.insert( ref, (v:gsub( "%$(.+)", function(n) return vars[n] end )) )
end
if #ref == 1 and c and s then
ref = { c, s, ref[1] }
elseif #ref == 2 and c then
ref = { c, unpack(ref) }
elseif #ref ~= 3 then
print("INVALID REFERENCE: "..#ref, c, s, o)
ref = nil
end
return ref
end
function check_dependency( self, uci, conf, sect, optn, nodeps, section2 )
-- print( "Depency check: ", conf .. '.' .. sect .. ( optn and '.' .. optn or '' ) )
local key = conf .. '.' .. sect .. ( optn and '.' .. optn or '' )
if not self.beenthere[key] then
self.beenthere[key] = true
else
print("CIRCULAR DEPENDENCY!")
return false, "Recursive depency detected"
end
-- check for config
if not self.packages[conf] then self:read_scheme(conf) end
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
local ok = false
local valid, err
for _, dep in ipairs(item.depends) do
--print("DEP:",luci.util.serialize_data(dep))
local subcondition = true
for k, v in pairs(dep) do
-- XXX: better error
local ref = _assert( _parse_reference(k,conf,sect,optn),
"Ambiguous dependency reference '" .. k .. "' given" )
-- XXX: true -> nodeps
valid, err = self:validate_option(ref[1], ref[2], ref[3], uci, true, section2)
if valid then
--print("CHK:",uci[ref[2]][ref[3]],v,unpack(ref))
if not (
( type(v) == "boolean" and uci[ref[2]][ref[3]] ) or
( ref[3] and uci[ref[2]][ref[3]] ) == v
) then
subcondition = false
err = type(v) ~= "boolean"
and "Option '" .. table.concat( ref, "." ) .. "' doesn't match requested type '" .. v .. '"'
or "Option '" .. table.concat( ref, "." ) .. "' has no value"
break
end
else
subcondition = false
break
end
end
if subcondition then
-- print( " -> Success (condition matched)\n" )
return true
end
end
-- print( " -> Failed\n" )
return false, err
end
-- print( " -> Success (no depends)\n" )
return true
end