diff options
author | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2013-10-31 22:51:19 +0200 |
---|---|---|
committer | Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi> | 2013-10-31 23:10:34 +0200 |
commit | f7f72d6e0652a35308cd0ae8e5561aaa276ff59e (patch) | |
tree | 2568fbd641e2e1d90fb54122b23a5d8a69c48136 /acf2 | |
parent | 95167c1a1a100bcadd19b3e2f1815d521e4cd4a0 (diff) | |
download | aconf-f7f72d6e0652a35308cd0ae8e5561aaa276ff59e.tar.bz2 aconf-f7f72d6e0652a35308cd0ae8e5561aaa276ff59e.tar.xz |
model: set netmask using CIDR notation
Diffstat (limited to 'acf2')
-rw-r--r-- | acf2/model/field.lua | 16 | ||||
-rw-r--r-- | acf2/model/model.lua | 2 | ||||
-rw-r--r-- | acf2/model/net.lua | 110 |
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) |