summaryrefslogtreecommitdiffstats
path: root/interfaces-model.lua
diff options
context:
space:
mode:
Diffstat (limited to 'interfaces-model.lua')
-rw-r--r--interfaces-model.lua478
1 files changed, 318 insertions, 160 deletions
diff --git a/interfaces-model.lua b/interfaces-model.lua
index 57949a3..776cd6f 100644
--- a/interfaces-model.lua
+++ b/interfaces-model.lua
@@ -11,23 +11,63 @@ module (..., package.seeall)
-- directory for all we know) The public module functions are defined
-- further below
-local iface = { tags = { "name", "comment", "method", "address",
- "netmask", "gateway", "hostname", "provider",
- "pre-up", "up", "down", "post-down" },
- methods = { "dhcp", "static", "ppp", "loopback", "manual" },
- label = { ['name']="Interface names" ,
- ['comment']="Comments",
- ['method']="Method",
- ['address']="Address",
- ['netmask']="Netmask",
- ['gateway']="Gateway",
- ['hostname']="Hostname",
- ['provider']="Provider",
- ['pre-up']="'pre-up' actions",
- ['up']="'up' actions",
- ['down']="'down' actions",
- ['post-down']="'post-down' actions",
- },
+local iface = { tags = { comment = {type="longtext", label="Comments"},
+ auto = {type="boolean", value=false, label="Auto bring-up"},
+ name = {label="Interface Name"},
+ family = {type="select", label="Address Family", option={"inet", "ipx", "inet6"}},
+ ['pre-up'] = {type="longtext", label="'pre-up' actions"},
+ up = {type="longtext", label="'up' actions"},
+ down = {type="longtext", label="'down' actions"},
+ ['post-down'] = {type="longtext", label="'post-down' actions"} },
+ method_tag = {type="select", label="Method"},
+ family_methods = { inet = {"loopback", "static", "manual",
+ "dhcp", "bootp", "ppp", "wvdial"},
+ ipx = {"static", "dynamic"},
+ inet6 = {"loopback", "static", "manual", "v4tunnel"} },
+ method_options = {inet = {loopback = {},
+ static = {address = {label="Address"},
+ netmask = {label="Netmask"},
+ broadcast = {label="Broadcast address"},
+ network = {label="Network address"},
+ metric = {label="Routing metric"},
+ gateway = {label="Default gateway"},
+ pointopoint = {label="Point-to-point address"},
+ media = {label="Medium type"},
+ hwaddress = {label="Hardware address"},
+ mtu = {label="MTU size"} },
+ manual = {},
+ dhcp = {hostname = {label="Hostname"},
+ leasehours = {label="Preferred lease time (hours)"},
+ leasetime = {label="Preferred lease time (seconds)"},
+ vendor = {label="Vendor class identifier"},
+ client = {label="Client identifier"},
+ hwaddress = {label="Hardware address"} },
+ bootp = {bootfile = {label="Boot file"},
+ server = {label="Server address"},
+ hwaddr = {label="Hardware address"} },
+ ppp = {provider = {label="Provider name"} },
+ wvdial = {provider = {label="Provider name"} },
+ },
+ ipx = { static = {frame = {label="Ethernet frame type"},
+ netnum = {label="Network number"} },
+ dynamic = {frame = {label="Ethernet frame type"} },
+ },
+ inet6 ={loopback = {},
+ static = {address = {label="Address"},
+ netmask = {label="Netmask"},
+ gateway = {label="Default gateway"},
+ media = {label="Medium type"},
+ hwaddress = {label="Hardware address"},
+ mtu = {label="MTU size"} },
+ manual = {},
+ v4tunnel = {address = {label="Address"},
+ netmask = {label="Netmask"},
+ endpoint = {label="Endpoint address"},
+ ['local'] = {label="Local address"},
+ gateway = {label="Default gateway"},
+ ttl = {label="TTL setting"} },
+ },
+ },
-- lowlevel functions will insert file and array here
}
@@ -35,7 +75,7 @@ local iface = { tags = { "name", "comment", "method", "address",
-- Lowlevel functions - they do things directly to iface.
local filename = "/etc/network/interfaces"
iface.read_file = function ()
- iface.file = cfe { name=filename, filename=filename }
+ iface.file = cfe({ type="longtext", label=filename })
local file = io.open(filename)
local rc = false
if file then
@@ -50,44 +90,52 @@ end
iface.write_file = function ()
local rc = false
- local file = io.open ( iface.file.name, "w")
+ local file = io.open ( iface.file.label, "w")
if file then
file:write ( iface.file.value )
file:close()
rc = true
end
return rc
- end
+end
-function iface.iface_type ( )
+iface.iface_type = function (family, method)
local f = {}
- for k,v in pairs(iface.tags) do
- f[v] = cfe { name = v, label = (iface.label[v] or "") }
+ for name,table in pairs(iface.tags) do
+ f[name] = cfe(table)
end
+ f.family.value = family or ""
+ if family and iface.family_methods[family] then
+ f.method = cfe(iface.method_tag)
+ f.method.option = iface.family_methods[family]
+ f.method.value = method or ""
- for k,v in pairs ({"comment", "pre-up", "up", "down", "post-down"}) do
- f[v].type ="longtext"
+ if method and iface.method_options[family][method] then
+ for name,table in pairs(iface.method_options[family][method]) do
+ f[name] = cfe(table)
+ end
+ elseif method then
+ f.method.errtxt = "Invalid method"
+ end
+ elseif family then
+ f.family.errtxt = "Invalid family"
end
-
- f.method.type = "select"
- f.method.option = iface.methods
- return (f)
+
+ return cfe({ type="group", value=f, label="Interface description" })
end
-- return true or false + the index of iface.array matching "name"
-function iface.index (name)
- if name== nil or #name == 0 then
- return true, 0
- end
-
- if iface.array == nil then
- iface.unpack_interfaces ()
- end
+iface.index = function (name)
+ if name and #name > 0 then
+ if iface.array == nil then
+ iface.unpack_interfaces ()
+ end
- for k,v in ipairs(iface.array) do
- if name == v.name.value then
- return true, k
+ for k,v in ipairs(iface.array) do
+ if name == v.value.name.value then
+ return true, k
+ end
end
end
@@ -95,7 +143,7 @@ function iface.index (name)
end
-function iface.append ( self, value, prefix )
+iface.append = function (self, value, prefix)
self = self or ""
-- if we already have some values, then append a newline
if #self > 0 then self = self .. "\n" end
@@ -105,176 +153,254 @@ function iface.append ( self, value, prefix )
return self .. str
end
-function iface.expand ( self, prefix )
+iface.expand = function (self, prefix)
if #self == 0 then
return ""
end
-- force the string to end in a single linefeed
self = string.gsub (self, "\r", "")
self = string.gsub (self, "[\n]*$", "\n")
- local str = ""
- for line in string.gmatch ( self, ".-\n") do
+ local strings = {}
+ for line in string.gmatch(self, "(.-)\n") do
if #line > 0 then
- str = str .. prefix .. " " .. line
+ strings[#strings+1] = prefix .. " " .. line
end
end
- return str
+ return table.concat(strings, "\n")
end
-function iface.unpack_interfaces ()
+iface.unpack_interfaces = function ()
if iface.array == nil then
iface.read_file()
iface.array = {}
- end
-
- -- call it array so we don't have to call it iface.array everywhere
- local array = iface.array
- local count = 0
- array[count] = iface.iface_type()
-
- for line in string.gmatch ( iface.file.value, ".-\n") do
- -- strip leading spaces, tabs
- line = string.gsub (line, "^[%s]*", "")
- line = string.gsub (line, "\n*$", "")
- -- it can be #, auto, iface, or a parameter
- if string.match(line, "^#") then
- array[count].comment.value =
- iface.append(array[count].comment.value, line , "#%s*" )
- elseif string.match(line, "^auto") then
- -- do nothing
- elseif string.match(line, "^iface") then
- count = count + 1
- array[count] = iface.iface_type()
- -- iface <name> [inet | ipx] <method> -- we assume inet
- array[count].name.value,
- array[count].method.value =
- string.match(line, "%w+%s+(%w+)%s+%w+%s+(%w+)")
- else -- it must be some kind of parameter
- local param, val =
- string.match(line, "(%S+)%s*(.*)$")
- if (param) then
- if not (array[count][param]) then array[count][param] = {} end
- array[count][param].value =
- iface.append (array[count][param].value, val)
+ -- call it array so we don't have to call it iface.array everywhere
+ local array = iface.array
+ local comment = ""
+ local auto = {}
+
+ for line in string.gmatch ( iface.file.value, ".-\n") do
+ -- strip leading spaces, tabs
+ line = string.gsub (line, "^[%s]*", "")
+ line = string.gsub (line, "\n*$", "")
+ -- it can be #, auto, iface, or a parameter
+ if string.match(line, "^#") then
+ comment = iface.append(comment, line , "#%s*" )
+ elseif string.match(line, "^auto") then
+ local name = string.match(line, "auto%s+(%w+)")
+ auto[name] = true
+ elseif string.match(line, "^iface") then
+ local name, family, method = string.match(line, "%w+%s+(%w+)%s+(%w+)%s+(%w+)")
+ array[#array + 1] = iface.iface_type(family, method)
+ local interface = array[#array].value
+ interface.comment.value = comment
+ comment = ""
+ -- iface <name> [inet | ipx] <method> -- we assume inet
+ interface.name.value = name
+ elseif #array then
+ -- it must be some kind of parameter
+ local param, val =
+ string.match(line, "(%S+)%s*(.*)$")
+ if (param) then
+ local interface = array[#array].value
+ if not (interface[param]) then
+ interface[param] = cfe({label=param, errtxt = "Unknown parameter"})
+ end
+ interface[param].value =
+ iface.append (interface[param].value, val)
+ end
+ end
+ end
+ for i,int in ipairs(array) do
+ if auto[int.value.name.value] then
+ int.value.auto.value = true
end
end
end
- -- now move the comments to go with the interface
- for n = count,1,-1 do
- array[n].comment.value = array[n-1].comment.value
- end
-
- return array
+ return cfe({ type="list", value=iface.array, label="Interfaces" })
end
-function iface.pack_interfaces()
+iface.pack_interfaces = function ()
local str = ""
- for n = 1,#iface.array,1 do
- local me = iface.array[n]
- for k,v in pairs (iface.tags) do
- if v == "comment" then
- str = str .. "\n" .. iface.expand ( me[v].value, "#")
- elseif v == "method" then
- str = str .. string.format ("\nauto %s\niface %s inet %s\n",
- me.name.value, me.name.value,
- me.method.value)
- elseif v == "name" then
- -- nothing
- else
- str = str .. iface.expand( me[v].value, "\t" .. v)
+ local strings = {}
+ for n,int in ipairs(iface.array) do
+ local me = int.value
+ if me.comment.value ~= "" then
+ strings[#strings+1] = iface.expand(me.comment.value, "#")
+ end
+ if me.auto.value then
+ strings[#strings+1] = "auto " .. me.name.value
+ end
+ strings[#strings+1] = string.format("iface %s %s %s", me.name.value,
+ me.family.value, me.method.value)
+ for name,entry in pairs(me) do
+ if name~="comment" and name~="name" and name~="family" and name~="method" and name~="auto"
+ and entry.value ~= "" then
+ strings[#strings+1] = iface.expand(entry.value, "\t"..name)
end
end
+ strings[#strings+1] = ""
end
- return str
+
+ return table.concat(strings, "\n")
end
-function iface.commit()
+iface.commit = function ()
iface.file.value = iface.pack_interfaces()
return iface.write_file()
end
-function iface.add_after ( name, def )
- -- if the new if.name is already in the table, then fail
- local rc, idx = iface.index(def.name.value)
- if idx > 0 then
- def.name.errtxt = "This interface is already defined"
- return false, def
+iface.add_after = function (def, name)
+ -- if the new def.name is already in the table, then fail
+ local rc, idx = iface.index(def.value.name.value)
+ if rc == true then
+ def.value.name.errtxt = "This interface is already defined"
+ rc = false
+ else
+ rc, def = iface.validate( def )
end
- -- if the name to insert after doesn't exist, just fail
- rc, idx = iface.index(name)
- if rc == false then
- return false, def
+ -- if the name to insert after doesn't exist, just add it to the end
+ if rc then
+ rc, idx = iface.index(name)
+ if rc == false then
+ idx = #iface.array
+ end
+ table.insert( iface.array, idx+1, def )
+ rc = iface.commit()
end
- rc, def = iface.validate (def)
if rc == false then
- return rc, def
+ def.errtxt = "Failed to create interface"
end
-
- table.insert( iface.array, idx+1, def )
- return iface.commit() , def
+
+ return def
end
-function iface.read ( name )
- -- if the name is blank, then return an empty def
- local rc, idx = iface.index(name or "")
- if name == nil or #name == 0 or rc == false then
- return rc, iface.iface_type()
+iface.read = function (name)
+ -- if the name is blank, then return nil
+ iface.unpack_interfaces()
+ local rc, idx = iface.index(name)
+ if rc == false then
+ return nil
end
- return iface.commit(), iface.array[idx]
+ return iface.array[idx]
end
-function iface.update ( def)
+iface.update = function (def)
-- if the def by that name doesn't exist, fail
- local rc, idx = iface.index(def.name.value or "" )
- if idx == 0 then
- def.name.errtxt = "This is an invalid interface name"
- return false, def
+ local rc, idx = iface.index(def.value.name.value or "" )
+ if rc == false then
+ def.value.name.errtxt = "This is an invalid interface name"
+ else
+ rc, def = iface.validate ( def )
end
-
- rc, def = iface.validate ( def )
+
+ if rc then
+ iface.array[idx] = def
+ rc = iface.commit()
+ end
+
if rc == false then
- return rc, def
+ def.errtxt = "Failed to update interface"
end
- iface.array[idx] = def
- return iface.commit(), def
+ return def
end
-function iface.delete (name )
- local rc, idx = iface.index(name or "" )
- if idx == 0 then
- rc = false
- else
+iface.delete = function (name)
+ local rc, idx = iface.index(name)
+ if rc then
table.remove (iface.array, idx )
+ rc = iface.commit()
+ end
+ local value
+ if rc then
+ value = "Interface deleted"
+ else
+ value = "Interface not found"
end
- return iface.commit()
+ return cfe({ value=value, label="Delete result" })
end
-iface.validate = function ( def )
- if #def.name.value == 0 then
- iface.name.errtxt = "The interface must have a name"
- end
- def.method.errtxt = "Method specified is invalid"
- for k,v in pairs (iface.methods) do
- if def.method.value == v then
- def.method.errtxt = ""
+iface.validate = function (def)
+ local success = true
+ -- since the structure is different depending on the family and method ...
+ local method = ""
+ if def.value.method then method = def.value.method.value end
+ local newdef = iface.iface_type(def.value.family.value, method)
+ for name,option in pairs(newdef.value) do
+ if option.errtxt then
+ success = false
+ end
+ if def.value[name] then
+ option.value = def.value[name].value
end
end
+
+ -- Now, start to validate (family and method already validated)
+ if #newdef.value.name.value == 0 then
+ newdef.value.name.errtxt = "The interface must have a name"
+ success = false
+ end
-- More validation tests go here ---
--
- local rc = true
- for k,v in pairs(def) do
- if def[k].errtxt then result = false end
+
+ if not success then
+ newdef.errtxt = "Failed validation"
+ end
+
+ return success, newdef
+end
+
+iface.ifup = function (name)
+ name = name or ""
+ local cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin ifup "..name
+ local f = io.popen(cmd)
+ local cmdresult = f:read("*a")
+ f:close()
+
+ if cmdresult == "" then
+ cmdresult = "Interface up"
end
- return result, def
+ return cfe({ type="longtext", value=cmdresult, label="ifup "..name })
+end
+
+iface.ifdown = function (name)
+ name = name or ""
+ local cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin ifdown "..name
+ local f = io.popen(cmd)
+ local cmdresult = f:read("*a")
+ f:close()
+
+ if cmdresult == "" then
+ cmdresult = "Interface down"
+ end
+
+ return cfe({ type="longtext", value=cmdresult, label="ifdown "..name })
+end
+
+iface.ipaddr = function ()
+ local cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin ip addr"
+ local f = io.popen(cmd)
+ local cmdresult = f:read("*a")
+ f:close()
+
+ return cfe({ type="longtext", value=cmdresult, label="ip addr" })
+end
+
+iface.iproute = function ()
+ local cmd = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin ip route"
+ local f = io.popen(cmd)
+ local cmdresult = f:read("*a")
+ f:close()
+
+ return cfe({ type="longtext", value=cmdresult, label="ip route" })
end
-------------------------------------------------------------------------------
@@ -285,22 +411,54 @@ get_all_interfaces = iface.unpack_interfaces
get_iface_by_name = iface.read
-create_iface_by_name = iface.add_after
+get_iface = iface.iface_type
-update_iface_by_name = function (def, name )
- -- make sure name we think we are updating is name we are updating
- def.name.value = name
- return iface.update (def)
-end
+create_iface = iface.add_after
+
+update_iface = iface.update
delete_iface_by_name = iface.delete
-get_status = function ()
- local status = {}
- status.interfacesfile = cfe({
- name="interfacesfile",
+ifup_by_name = iface.ifup
+
+ifdown_by_name = iface.ifdown
+
+get_status = function ()
+ local interfacesfile = cfe({
label="Interfaces file",
value=filename,
})
- return status
+ if not fs.is_file(filename) then
+ interfacesfile.errtxt = "File not found"
+ end
+ return cfe({ type="group", value={filename=interfacesfile, ipaddr=iface.ipaddr(), iproute=iface.iproute()}, label="Status" })
+end
+
+get_file = function ()
+ if not iface.file then
+ iface.read_file()
+ end
+
+ local file = cfe({ value=filename, label="Interfaces file" })
+ local filesize = cfe({ value="0", label="File size" })
+ local mtime = cfe({ value="---", label="File date" })
+ local filedetails = fs.stat(filename)
+ if filedetails then
+ filesize.value = filedetails.size
+ mtime.value = filedetails.mtime
+ else
+ file.errtxt = "File not found"
+ end
+
+ return cfe({ type="group", value={filename=file, filecontent=iface.file, filesize=filesize, mtime=mtime}, label="Interfaces file details" })
+end
+
+write_file = function (newfile)
+ if not iface.file then
+ iface.read_file()
+ end
+ iface.file.value = newfile.value.filecontent.value
+ iface.write_file()
+
+ return get_file()
end