luci-app-nlbwmon: new package
[project/luci.git] / applications / luci-app-nlbwmon / luasrc / controller / nlbw.lua
1 -- Copyright 2017 Jo-Philipp Wich <jo@mein.io>
2 -- Licensed to the public under the Apache License 2.0.
3
4 module("luci.controller.nlbw", package.seeall)
5
6 function index()
7         entry({"admin", "nlbw"}, firstchild(), _("Bandwidth Monitor"), 80)
8         entry({"admin", "nlbw", "display"}, template("nlbw/display"), _("Display"), 1)
9         entry({"admin", "nlbw", "config"}, cbi("nlbw/config"), _("Configuration"), 2)
10         entry({"admin", "nlbw", "backup"}, template("nlbw/backup"), _("Backup"), 3)
11         entry({"admin", "nlbw", "data"}, call("action_data"), nil, 4)
12         entry({"admin", "nlbw", "list"}, call("action_list"), nil, 5)
13         entry({"admin", "nlbw", "ptr"}, call("action_ptr"), nil, 6).leaf = true
14         entry({"admin", "nlbw", "download"}, call("action_download"), nil, 7)
15         entry({"admin", "nlbw", "restore"}, post("action_restore"), nil, 8)
16 end
17
18 local function exec(cmd, args, writer)
19         local os = require "os"
20         local nixio = require "nixio"
21
22         local fdi, fdo = nixio.pipe()
23         local pid = nixio.fork()
24
25         if pid > 0 then
26                 fdo:close()
27
28                 while true do
29                         local buffer = fdi:read(2048)
30                         local wpid, stat, code = nixio.waitpid(pid, "nohang")
31
32                         if writer and buffer and #buffer > 0 then
33                                 writer(buffer)
34                         end
35
36                         if wpid and stat == "exited" then
37                                 break
38                         end
39                 end
40         elseif pid == 0 then
41                 nixio.dup(fdo, nixio.stdout)
42                 fdi:close()
43                 fdo:close()
44                 nixio.exece(cmd, args, nil)
45                 nixio.stdout:close()
46                 os.exit(1)
47         end
48 end
49
50 function action_data()
51         local http = require "luci.http"
52
53         local types = {
54                 csv = "text/csv",
55                 json = "application/json"
56         }
57
58         local args = { }
59         local mtype = http.formvalue("type") or "json"
60         local delim = http.formvalue("delim") or ";"
61         local period = http.formvalue("period")
62         local group_by = http.formvalue("group_by")
63         local order_by = http.formvalue("order_by")
64
65         if types[mtype] then
66                 args[#args+1] = "-c"
67                 args[#args+1] = mtype
68         else
69                 http.status(400, "Unsupported type")
70                 return
71         end
72
73         if delim and #delim > 0 then
74                 args[#args+1] = "-s%s" % delim
75         end
76
77         if period and #period > 0 then
78                 args[#args+1] = "-t"
79                 args[#args+1] = period
80         end
81
82         if group_by and #group_by > 0 then
83                 args[#args+1] = "-g"
84                 args[#args+1] = group_by
85         end
86
87         if order_by and #order_by > 0 then
88                 args[#args+1] = "-o"
89                 args[#args+1] = order_by
90         end
91
92         http.prepare_content(types[mtype])
93         exec("/usr/sbin/nlbw", args, http.write)
94 end
95
96 function action_list()
97         local http = require "luci.http"
98
99         local fd = io.popen("/usr/sbin/nlbw -c list")
100         local periods = { }
101
102         if fd then
103                 while true do
104                         local period = fd:read("*l")
105
106                         if not period then
107                                 break
108                         end
109
110                         periods[#periods+1] = period
111                 end
112
113                 fd:close()
114         end
115
116         http.prepare_content("application/json")
117         http.write_json(periods)
118 end
119
120 function action_ptr(...)
121         local http = require "luci.http"
122         local util = require "luci.util"
123
124         http.prepare_content("application/json")
125         http.write_json(util.ubus("network.rrdns", "lookup", {
126                 addrs = {...}, timeout = 3000
127         }))
128 end
129
130 function action_download()
131         local nixio = require "nixio"
132         local http = require "luci.http"
133         local sys = require "luci.sys"
134         local uci = require "luci.model.uci".cursor()
135
136         local dir = uci:get_first("nlbwmon", "nlbwmon", "database_directory")
137                 or "/var/lib/nlbwmon"
138
139         if dir and nixio.fs.stat(dir, "type") == "dir" then
140                 local n = "nlbwmon-backup-%s-%s.tar.gz"
141                         %{ sys.hostname(), os.date("%Y-%m-%d") }
142
143                 http.prepare_content("application/octet-stream")
144                 http.header("Content-Disposition", "attachment; filename=\"%s\"" % n)
145                 exec("/bin/tar", { "-C", dir, "-c", "-z", ".", "-f", "-" }, http.write)
146         else
147                 http.status(500, "Unable to find database directory")
148         end
149 end
150
151 function action_restore()
152         local nixio = require "nixio"
153         local http = require "luci.http"
154         local i18n = require "luci.i18n"
155         local tpl = require "luci.template"
156         local uci = require "luci.model.uci".cursor()
157
158         local tmp = "/tmp/nlbw-restore.tar.gz"
159         local dir = uci:get_first("nlbwmon", "nlbwmon", "database_directory")
160                 or "/var/lib/nlbwmon"
161
162         local fp
163         http.setfilehandler(
164                 function(meta, chunk, eof)
165                         if not fp and meta and meta.name == "archive" then
166                                 fp = io.open(tmp, "w")
167                         end
168                         if fp and chunk then
169                                 fp:write(chunk)
170                         end
171                         if fp and eof then
172                                 fp:close()
173                         end
174                 end)
175
176         local files = { }
177         local tar = io.popen("/bin/tar -tzf %s" % tmp, "r")
178         if tar then
179                 while true do
180                         local file = tar:read("*l")
181                         if not file then
182                                 break
183                         elseif file:match("^%d%d%d%d%d%d%d%d%.db%.gz$") or
184                                file:match("^%./%d%d%d%d%d%d%d%d%.db%.gz$") then
185                                 files[#files+1] = file
186                         end
187                 end
188                 tar:close()
189         end
190
191         if #files == 0 then
192                 http.status(500, "Internal Server Error")
193                 tpl.render("nlbw/backup", {
194                         message = i18n.translate("Invalid or empty backup archive")
195                 })
196                 return
197         end
198
199
200         local output = { }
201
202         exec("/etc/init.d/nlbwmon", { "stop" })
203         exec("/bin/mkdir", { "-p", dir })
204
205         exec("/bin/tar", { "-C", dir, "-vxzf", tmp, unpack(files) },
206                 function(chunk) output[#output+1] = chunk:match("%S+") end)
207
208         exec("/bin/rm", { "-f", tmp })
209         exec("/etc/init.d/nlbwmon", { "start" })
210
211         tpl.render("nlbw/backup", {
212                 message = i18n.translatef(
213                         "The following database files have been restored: %s",
214                         table.concat(output, ", "))
215         })
216 end