summaryrefslogtreecommitdiffstats
path: root/acf2
diff options
context:
space:
mode:
authorKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-10-31 22:51:19 +0200
committerKaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>2013-10-31 23:10:34 +0200
commitf7f72d6e0652a35308cd0ae8e5561aaa276ff59e (patch)
tree2568fbd641e2e1d90fb54122b23a5d8a69c48136 /acf2
parent95167c1a1a100bcadd19b3e2f1815d521e4cd4a0 (diff)
downloadaconf-f7f72d6e0652a35308cd0ae8e5561aaa276ff59e.tar.bz2
aconf-f7f72d6e0652a35308cd0ae8e5561aaa276ff59e.tar.xz
model: set netmask using CIDR notation
Diffstat (limited to 'acf2')
-rw-r--r--acf2/model/field.lua16
-rw-r--r--acf2/model/model.lua2
-rw-r--r--acf2/model/net.lua110
3 files changed, 113 insertions, 15 deletions
diff --git a/acf2/model/field.lua b/acf2/model/field.lua
index e09b639..809fdc3 100644
--- a/acf2/model/field.lua
+++ b/acf2/model/field.lua
@@ -82,11 +82,13 @@ end
function M.Field:load(context)
if not context.txn then return setmetatable({}, context) end
- local value = context.txn:get(context.addr)
+ local value = self:_load(context)
if value == nil then return self.default end
return value
end
+function M.Field:_load(context) return context.txn:get(context.addr) end
+
function M.Field:_validate(context, value)
if self.required and value == nil then
raise(context.path, 'Required value not set')
@@ -104,15 +106,15 @@ function M.Field:validate(context, value) end
function M.Field:save(context, value)
if not self.editable then raise(context.path, 'Is not editable') end
- context.txn:set(context.addr, self:_validate(context, value))
+ self:_save(context, self:_validate(context, value))
end
+function M.Field:_save(context, value) context.txn:set(context.addr, value) end
+
function M.Field:validate_saved(context)
self:save(context, self:load(context))
end
-function M.Field:clear(context) context.txn:set(context.addr) end
-
local Primitive = class(M.Field)
@@ -196,7 +198,7 @@ function M.TreeNode:load(context, options)
if context.txn and not (
(
options and options.create
- ) or self.create or context.txn:get(context.addr)
+ ) or self.create or self:_load(context)
) then return end
return self.itype(context, self.iparams)
end
@@ -210,14 +212,14 @@ function M.TreeNode:save(context, value)
return
end
- context.txn:set(context.addr)
+ self:_save(context)
if value then
if type(value) ~= 'table' then
raise(path, 'Cannot assign primitive value')
end
- context.txn:set(context.addr, {})
+ self:_save(context, {})
local new = self:load(context, {create=true})
local errors = err.ErrorDict()
diff --git a/acf2/model/model.lua b/acf2/model/model.lua
index 923ac05..cb1ebee 100644
--- a/acf2/model/model.lua
+++ b/acf2/model/model.lua
@@ -198,7 +198,7 @@ function M.Model:init(context)
end
end
if relevant then f:validate_saved()
- else f:clear() end
+ else f:_save() end
end
end
end
diff --git a/acf2/model/net.lua b/acf2/model/net.lua
index 27ab262..92d0738 100644
--- a/acf2/model/net.lua
+++ b/acf2/model/net.lua
@@ -15,38 +15,130 @@ local object = require('acf2.object')
local class = object.class
local super = object.super
+local pth = require('acf2.path')
local update = require('acf2.util').update
local stringy = require('stringy')
-M.IPv4Address = class(String)
+local BaseIPAddress = class(String)
+
+function BaseIPAddress:abs_mask_addr(context)
+ if self.mask_addr then
+ return pth.join(pth.parent(context.addr), self.mask_addr)
+ end
+end
+
+function BaseIPAddress:topology(context)
+ local res = super(self, BaseIPAddress):topology(context)
+ local maddr = self:abs_mask_addr(context)
+ if maddr then
+ table.insert(res, {path=context.path, addr=maddr, type=self.mask_type})
+ end
+ return res
+end
+
+function BaseIPAddress:invalid(context)
+ raise(context.path, 'Invalid IPv'..self.version..' address')
+end
+
+function BaseIPAddress:split(context, value)
+ local comps = stringy.split(value, '/')
+ if #comps == 1 then return value, self.length end
+
+ if #comps > 2 or not self.cidr then self:invalid(context) end
+
+ local mask = tonumber(comps[2])
+ if not mask or mask < 0 or mask > self.length then self:invalid(context) end
+ return comps[1], mask
+end
+
+function BaseIPAddress:_load(context)
+ local res = super(self, BaseIPAddress):_load(context)
+ local maddr = self:abs_mask_addr(context)
+ if res and maddr then
+ return res..'/'..(self:mask2cidr(context.txn:get(maddr)) or self.length)
+ end
+ return res
+end
+
+function BaseIPAddress:_save(context, value)
+ local maddr = self:abs_mask_addr(context)
+ if maddr then
+ local cidr
+ if value then value, cidr = self:split(context, value) end
+ context.txn:set(maddr, cidr and self:cidr2mask(cidr))
+ end
+ super(self, BaseIPAddress):_save(context, value)
+end
+
+
+M.IPv4Address = class(BaseIPAddress)
+
+function M.IPv4Address:init(params)
+ super(self, M.IPv4Address):init(
+ update(params, {version=4, length=32, mask_type='string'})
+ )
+end
function M.IPv4Address:validate(context, value)
super(self, M.IPv4Address):validate(context, value)
+ local address = self:split(context, value)
+
local function test(...)
if #{...} ~= 4 then return true end
for _, octet in ipairs{...} do
if tonumber(octet) > 255 then return true end
end
end
- if test(value:match('^(%d+)%.(%d+)%.(%d+)%.(%d+)$')) then
- raise(context.path, 'Invalid IPv4 address')
+ if test(address:match('^(%d+)%.(%d+)%.(%d+)%.(%d+)$')) then
+ self:invalid(context)
+ end
+end
+
+function M.IPv4Address:mask2cidr(mask)
+ local acc = 0
+ for i, comp in ipairs(stringy.split(mask, '.')) do
+ acc = acc + math.pow(256, 4 - i) * tonumber(comp)
+ end
+ local res = 32
+ while acc % 2 == 0 do
+ res = res - 1
+ assert(res > -1)
+ acc = acc / 2
end
+ return res
end
+function M.IPv4Address:cidr2mask(cidr)
+ local acc = (math.pow(2, cidr) - 1) * math.pow(2, 32 - cidr)
+ local comps = {}
+ for i = 4,1,-1 do
+ comps[i] = acc % 256
+ acc = math.floor(acc / 256)
+ end
+ return table.concat(comps, '.')
+end
-M.IPv6Address = class(String)
+
+M.IPv6Address = class(BaseIPAddress)
+
+function M.IPv6Address:init(params)
+ super(self, M.IPv6Address):init(
+ update(params, {version=6, length=128, mask_type='number'})
+ )
+end
function M.IPv6Address:validate(context, value)
super(self, M.IPv6Address):validate(context, value)
+ local address = self:split(context, value)
- local function invalid() raise(context.path, 'Invalid IPv6 address') end
+ local function invalid() self:invalid(context) end
- if value == '' then invalid() end
+ if address == '' then invalid() end
- local comps = stringy.split(value, ':')
+ local comps = stringy.split(address, ':')
if #comps < 3 then invalid() end
local function collapse(i, ofs)
@@ -72,6 +164,10 @@ function M.IPv6Address:validate(context, value)
end
end
+function M.IPv6Address:mask2cidr(mask) return mask end
+
+function M.IPv6Address:cidr2mask(cidr) return cidr end
+
M.IPAddress = class(Union)