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 +  | 
