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")
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
-- @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 )
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
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" )
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
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
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
"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
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
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
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
--- /dev/null
+--[[
+
+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