From a280c3b1faa2847b6d4f850a55d0cd4ea8453283 Mon Sep 17 00:00:00 2001 From: Ted Trask Date: Fri, 30 May 2008 18:37:19 +0000 Subject: Updated interfaces to use new cfe model, add more iface families, add more flexibility to editing, show ip status, bring up/down interfaces, and general cleanup. git-svn-id: svn://svn.alpinelinux.org/acf/alpine-baselayout/trunk@1191 ab2d0c66-481e-0410-8bed-d214d4d58bed --- interfaces-model.lua | 478 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 318 insertions(+), 160 deletions(-) (limited to 'interfaces-model.lua') 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 [inet | ipx] -- 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 [inet | ipx] -- 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 -- cgit v1.2.3