diff options
-rw-r--r-- | Makefile | 47 | ||||
-rw-r--r-- | config.mk | 10 | ||||
-rw-r--r-- | quagga-controller.lua | 109 | ||||
-rw-r--r-- | quagga-model.lua | 187 | ||||
-rw-r--r-- | quagga-status-html.lsp | 95 | ||||
-rw-r--r-- | quagga.menu | 3 |
6 files changed, 451 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b5e68a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +APP_NAME=quagga +PACKAGE=acf-$(APP_NAME) +VERSION=0.1 + +APP_DIST=\ + quagga-controller.lua \ + quagga-model.lua \ + quagga-status-html.lsp \ + quagga.menu \ + + +EXTRA_DIST=README Makefile config.mk + +DISTFILES=$(APP_DIST) $(EXTRA_DIST) + +TAR=tar + +P=$(PACKAGE)-$(VERSION) +tarball=$(P).tar.bz2 +install_dir=$(DESTDIR)/$(appdir)/$(APP_NAME) + +all: +clean: + rm -rf $(tarball) $(P) + +dist: $(tarball) + +install: + mkdir -p "$(install_dir)" + cp -a $(APP_DIST) "$(install_dir)" + +$(tarball): $(DISTFILES) + rm -rf $(P) + mkdir -p $(P) + cp $(DISTFILES) $(P) + $(TAR) -jcf $@ $(P) + rm -rf $(P) + +# target that creates a tar package, unpacks is and install from package +dist-install: $(tarball) + $(TAR) -jxf $(tarball) + $(MAKE) -C $(P) install DESTDIR=$(DESTDIR) + rm -rf $(P) + +include config.mk + +.PHONY: all clean dist install dist-install diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..45f4d21 --- /dev/null +++ b/config.mk @@ -0,0 +1,10 @@ +prefix=/usr +datadir=${prefix}/share +sysconfdir=${prefix}/etc +localstatedir=${prefix}/var +acfdir=${datadir}/acf +wwwdir=${acfdir}/www +cgibindir=${acfdir}/cgi-bin +appdir=${acfdir}/app +acflibdir=${acfdir}/lib +sessionsdir=${localstatedir}/lib/acf/sessions diff --git a/quagga-controller.lua b/quagga-controller.lua new file mode 100644 index 0000000..f59ac0a --- /dev/null +++ b/quagga-controller.lua @@ -0,0 +1,109 @@ +module(..., package.seeall) + +-- This is the object/text used when we want to add a new record + +require("format") + +local newrecordtxt = "[New]" + +local list_redir = function (self) + self.conf.action = "status" + self.conf.type = "redir" + error (self.conf) +end + +mvc = {} +mvc.on_load = function(self, parent) + if (self.worker[self.conf.action] == nil ) or ( self.conf.action == "init" ) then + self.worker[self.conf.action] = list_redir(self) + end +end + +local function displaycmdmanagement(disablestart,disablestop,disablerestart) + -- Add a management buttons + local management = {} + management.start = cfe({ name="cmdmanagement", + label="Program control-panel", + value="Start", + type="submit", + }) + management.stop = cfe({ name="cmdmanagement", + label="Program control-panel", + value="Stop", + type="submit", + }) + management.restart = cfe({ name="cmdmanagement", + label="Program control-panel", + value="Restart", + type="submit", + }) + + -- Disable management buttons based on if the process is running or not + if (disablestart) then management.start.disabled = "yes" end + if (disablestop) then management.stop.disabled = "yes" end + if (disablerestart) then management.restart.disabled = "yes" end + + return management +end + +-- ################################################################################ +-- PUBLIC FUNCTIONS +function status(self) + return { status=self.model.getstatus() } +end + +expert = function (self) + local modifications = self.clientdata.filecontent or "" + if ( self.clientdata.cmdsave ) then + modifications = self.model:update_filecontent(modifications) + end + local url = ENV["SCRIPT_NAME"] .. self.conf.prefix .. self.conf.controller + + -- Start/Stop/Restart process + local cmdmanagement + if ( self.clientdata.cmdmanagement) then + cmdmanagement = cfe({ + name="cmdmanagement", + label="Previous action result", + action=cfe({ + name="cmdmanagement", + value=string.lower(self.clientdata.cmdmanagement), -- This row contains start/stop/restart (one of these commands) + }), + }) + local actionresult, cmdmanagement = self.model:startstop_service( cmdmanagement.action ) + end + + local status=self.model.getstatus() + local file = self.model:get_filedetails() + + -- Add buttons + file.cmdsave = cfe ({ + name="cmdsave", + label="Apply settings", + value="Apply", + type="submit", + }) + if (self.clientdata.cmdsave) then + file.cmdsave.descr="* Changes has been saved!" + end + + + -- Management buttons + local disablestart,disablestop,disablerestart + -- Disable management buttons based on if the process is running or not + if (string.lower(status.status.value) == "enabled" ) then + disablestart = "yes" + else + disablestop = "yes" + end + -- Display management buttons + management = displaycmdmanagement(disablestart,disablestop,disablerestart) + + return ( { + status = status, + file = file, + modifications = modifications, + management = management, + cmdmanagement = cmdmanagement, + url = url, } ) +end diff --git a/quagga-model.lua b/quagga-model.lua new file mode 100644 index 0000000..c9ef9d1 --- /dev/null +++ b/quagga-model.lua @@ -0,0 +1,187 @@ +module(..., package.seeall) + +require("fs") +require("procps") +require("getopts") +require("format") +require("daemoncontrol") +require("validator") + +local configfile = "/etc/quagga/bgpd.conf" +local processname = "bgpd" +local baseurl = "/etc/quagga/" + +local descr = { + Type = { + ['incomplete']="The protocol address is being resolved", + ['negative']="This protocol address is not available", + ['cached']="Protocol address was resolved successfully", + ['route']="This is a dynamic shortcut", + ['dynamic']="This entry is from a node that connected to us", + ['local']="Local interface address", + ['static']="Static mapping from configuration file (e.g. address of core)", + }, + Flags= { +-- ['up']="Connection is fully usable", + ['lower-up']="ipsec connections is up, but registration is not yet done", + }, +} + +local function get_version() + local cmd_output_result, cmd_output_error + local cmd = "/sbin/apk_version -vs " .. processname .." 2>/dev/null" + local f = io.popen( cmd ) + local cmdresult = f:read("*l") + if (cmdresult) and (#cmdresult > 0) then + cmd_output_result = string.match(cmdresult,"^%S*") or "Unknown" + else + cmd_output_error = "Program not installed" + end + f:close() + return cmd_output_result,cmd_output_error +end + +local function autostarts() + local cmd_output_result, cmd_output_error + local cmd = "/sbin/rc_status | egrep '^S' | egrep '" .. processname .."' 2>/dev/null" + local f = io.popen( cmd ) + local cmdresult = f:read("*a") + if (cmdresult) and (#cmdresult > 0) then + cmd_output_result = "Process will autostart at next boot (at sequence '" .. string.match(cmdresult,"^%a+(%d%d)") .. "')" + else + cmd_output_error = "Not programmed to autostart" + end + f:close() + return cmd_output_result,cmd_output_error +end + +local function opennhrpctl_show() + local cmd_output_result={} + local cmd_output_result_table={} + local cmd_output_error, opennhrpstatus + local cmd = "/usr/sbin/opennhrpctl show 2>/dev/null" + local f = io.popen( cmd ) + for line in f:lines() do + if string.find(line, "^Status:") then + opennhrpstatus=line + else + table.insert(cmd_output_result, line) + end + end + f:close() + local cnt = 0 + for k,v in pairs(cmd_output_result) do + if string.find(v,"^Interface") then + cnt = cnt + 1 + cmd_output_result_table[cnt] = {} + end + if ( cnt > 0 ) and (v ~= "") then + local k = string.match(v,"^(.-):%s?.*") + cmd_output_result_table[cnt][k]=cfe({value=string.match(v,"^.-:%s?(.*)")}) + local statusdescription = string.lower(string.match(v,"^.-:%s?(.*)")) + if (type(descr[k]) == "table") then + cmd_output_result_table[cnt][k]['descr']=descr[k][statusdescription] + end + end + end + + local peers_list = {} + for k,v in pairs(cmd_output_result_table) do + if (v.Interface.value) and not (peers_list[v.Interface.value]) then + peers_list[v.Interface.value] = {} + end + table.insert(peers_list[v.Interface.value], v) + end + + return peers_list,opennhrpstatus,cmd_output_error +end + + +function process_status_text(procname) + local t = procps.pidof(procname) + if (t) and (#t > 0) then + return "Enabled" + else + return "Disabled" + end +end + +-- ################################################################################ +-- PUBLIC FUNCTIONS + +function startstop_service ( self, action ) + local cmd = action.value + local cmdresult,cmdmessage,cmderror,cmdaction = daemoncontrol.daemoncontrol(processname, cmd) + action.descr=cmdmessage + action.errtxt=cmderror + -- Reporting back (true|false, the original acition) + return cmdresult,action +end + +function getstatus() + local status = {} + local value, errtxt = get_version() + status.version = cfe({ name = "version", + label="Program version", + value=value, + errtxt=errtxt, + }) + + status.status = cfe({ name="status", + label="Program status", + value=process_status_text(processname), + }) + local autostart_sequense, autostart_errtxt = autostarts() + status.autostart = cfe({ name="autostart", + label="Autostart sequence", + value=autostart_sequense, + errtxt=autostart_errtxt, + }) + + local opennhrpctl_show, opennhrpctl_status = opennhrpctl_show() + status.stats = cfe({ name="stats", + label="Programstatus reports", + value=opennhrpctl_status, + }) + + status.show = cfe({ name="show", + label="Peers", + option=opennhrpctl_show, + }) + + return status +end + +function get_filedetails() + local path = configfile + local filedetails = fs.stat(path) + local file = {} + + file["filename"] = cfe({ + name="filename", + label="File name", + value=path, + }) + file["filesize"] = cfe({ + name="filesize", + label="File size", + value=filedetails.size or 0, + }) + file["mtime"] = cfe({ + name="mtime", + label="File date", + value=filedetails.mtime or "---", + }) + file["filecontent"] = cfe({ + type="longtext", + name="filecontent", + label="File content", + value=fs.read_file(path), + }) + return file +end +function update_filecontent (self, modifications) + local path = configfile + local file_result,err = fs.write_file(path, format.dostounix(modifications)) + return file_result +end diff --git a/quagga-status-html.lsp b/quagga-status-html.lsp new file mode 100644 index 0000000..765de12 --- /dev/null +++ b/quagga-status-html.lsp @@ -0,0 +1,95 @@ +<? local form = ... ?> +<? +--[[ DEBUG INFORMATION +io.write("<H1>DEBUGGING</H1><span style='color:red'><H2>DEBUG INFO: CFE</H2>") +io.write(html.cfe_unpack(form)) +io.write("</span>") +--]] +?> + +<? +function informationform(myform,tags) + for k,v in pairs(tags) do + if (myform[v]) then + local val = myform[v] + io.write("\t<DT") + if (#val.errtxt > 0) then io.write(" class='error'") end + io.write(">" .. val.label .. "</DT>\n") + + io.write("\t\t<DD>" .. val.value .. "\n") + if (val.descr) and (#val.descr > 0) then io.write("\t\t<P CLASS='descr'>" .. string.gsub(val.descr, "\n", "<BR>") .. "</P>\n") end + if (#val.errtxt > 0) then io.write("\t\t<P CLASS='error'>" .. string.gsub(val.errtxt, "\n", "<BR>") .. "</P>\n") end + io.write("\t\t</DD>\n") + end + end +end +?> + +<H1>SYSTEM INFO</H1> +<DL> +<? +local myform = form.status +local tags = { "status", "version", "autostart", } +informationform(myform,tags) +?> +</DL> + +<H2>PROGRAM SPECIFIC OPTIONS/INFORMATION</H2> +<DL> +<? + +local myform = form.status.show +io.write("\t<DT") +if (#myform.errtxt > 0) then io.write(" class='error'") end +io.write(">" .. myform.label .. "</DT>\n") +io.write("\t\t<DD>\n") +for k,v in pairs(myform.option or {}) do + io.write("\t\t\t<TABLE STYLE='margin-bottom:10px;'>") + io.write("\n\t\t\t<TR><TD STYLE='font-weight:bold;'><IMG SRC='/static/tango/16x16/places/network-server.png' width='16' height='16' alt> " .. k .. "</TD><TD></TD></TR>\n") + for k1,v1 in pairs(v) do + io.write("\n\t\t\t<TR STYLE='padding-bottom:10px;'><TD WIDTH='150px' STYLE='font-weight:bold;padding-left:20px;'><IMG SRC='/static/tango/16x16/status/") + + if (v1) and (string.lower(v1['Type']['value']) == "incomplete") then + io.write("network-error") + elseif (v1) and (string.lower(v1['Type']['value']) == "negative") then + io.write("network-offline") + elseif (v1) and (string.lower(v1['Flags']['value']) == "up") then + io.write("network-idle") + elseif (v1) and (string.lower(v1['Flags']['value']) == "used up") then + io.write("network-transmit-receive") + elseif (v1) and (string.lower(v1['Flags']['value']) == "used up") then + io.write("network-offline") + else + io.write("network-error") + end + io.write(".png' width='16' height='16' title='" .. (v1['Type']['descr'] or "") .. "'> " .. v1["Protocol-Address"]['value'] .. "</TD><TD STYLE='font-weight:bold;'></TD></TR>\n") + for k2,v2 in pairs(v1) do + +---[[ + if (k2) and not ((string.lower(k2) == "protocol-address") or + (string.lower(k2) == "interface")) then + io.write("<TR><TD STYLE='font-weight:bold;padding-left:40px;'>"..k2.."</TD><TD>"..v2['value']) + if (v2['descr']) and (#v2['descr'] > 0) then + io.write(" <I>(" .. (v2['descr'] or "") .. ")</I>") + end + io.write("</TD></TR>\n") + end +--]] + end + + end + + io.write("\t\t\t</TABLE>") +end +io.write("\t\t</DD>\n") +?> +</DL> + +<? +--[[ DEBUG INFORMATION +io.write("<H1>DEBUGGING</H1><span style='color:red'><H2>DEBUG INFO: CFE</H2>") +io.write(html.cfe_unpack(form)) +io.write("</span>") +--]] +?> + diff --git a/quagga.menu b/quagga.menu new file mode 100644 index 0000000..f999729 --- /dev/null +++ b/quagga.menu @@ -0,0 +1,3 @@ +#CAT GROUP/DESC TAB ACTION +Networking 46BGP Status status + |