diff options
Diffstat (limited to 'modules/graphite')
-rw-r--r-- | modules/graphite/.packaging/centos/7/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/centos/8/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/debian/10/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/debian/9/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/fedora/31/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/fedora/32/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/leap/15.2/NOTSUPPORTED | 6 | ||||
-rwxr-xr-x | modules/graphite/.packaging/leap/15.2/pre-test.sh | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/leap/15.2/rundeps | 6 | ||||
-rw-r--r-- | modules/graphite/.packaging/test.config | 4 | ||||
-rw-r--r-- | modules/graphite/.packaging/ubuntu/16.04/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/ubuntu/18.04/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/.packaging/ubuntu/20.04/rundeps | 1 | ||||
-rw-r--r-- | modules/graphite/README.rst | 49 | ||||
-rw-r--r-- | modules/graphite/graphite.lua | 145 |
15 files changed, 220 insertions, 0 deletions
diff --git a/modules/graphite/.packaging/centos/7/rundeps b/modules/graphite/.packaging/centos/7/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/centos/7/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/centos/8/rundeps b/modules/graphite/.packaging/centos/8/rundeps new file mode 100644 index 0000000..182251d --- /dev/null +++ b/modules/graphite/.packaging/centos/8/rundeps @@ -0,0 +1 @@ +lua5.1-cqueues diff --git a/modules/graphite/.packaging/debian/10/rundeps b/modules/graphite/.packaging/debian/10/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/debian/10/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/debian/9/rundeps b/modules/graphite/.packaging/debian/9/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/debian/9/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/fedora/31/rundeps b/modules/graphite/.packaging/fedora/31/rundeps new file mode 100644 index 0000000..182251d --- /dev/null +++ b/modules/graphite/.packaging/fedora/31/rundeps @@ -0,0 +1 @@ +lua5.1-cqueues diff --git a/modules/graphite/.packaging/fedora/32/rundeps b/modules/graphite/.packaging/fedora/32/rundeps new file mode 100644 index 0000000..182251d --- /dev/null +++ b/modules/graphite/.packaging/fedora/32/rundeps @@ -0,0 +1 @@ +lua5.1-cqueues diff --git a/modules/graphite/.packaging/leap/15.2/NOTSUPPORTED b/modules/graphite/.packaging/leap/15.2/NOTSUPPORTED new file mode 100644 index 0000000..b1ae77d --- /dev/null +++ b/modules/graphite/.packaging/leap/15.2/NOTSUPPORTED @@ -0,0 +1,6 @@ + +ERROR:test_packaging:Installing https://luarocks.org/cqueues-20190813.51-0.src.rock +164 Error: Failed extracting rel-20190813.tar.gz + +Doesn't works on GitLab CI/CD, but works on localhost. +gzip and tar packages are installed, all packages has same version as packages on localhost's docker container. diff --git a/modules/graphite/.packaging/leap/15.2/pre-test.sh b/modules/graphite/.packaging/leap/15.2/pre-test.sh new file mode 100755 index 0000000..9614066 --- /dev/null +++ b/modules/graphite/.packaging/leap/15.2/pre-test.sh @@ -0,0 +1 @@ +luarocks --lua-version 5.1 install cqueues --from=https://mah0x211.github.io/rocks/ diff --git a/modules/graphite/.packaging/leap/15.2/rundeps b/modules/graphite/.packaging/leap/15.2/rundeps new file mode 100644 index 0000000..8323887 --- /dev/null +++ b/modules/graphite/.packaging/leap/15.2/rundeps @@ -0,0 +1,6 @@ +libopenssl-devel +lua51-luarocks +git +tar +gzip +m4 diff --git a/modules/graphite/.packaging/test.config b/modules/graphite/.packaging/test.config new file mode 100644 index 0000000..c23033b --- /dev/null +++ b/modules/graphite/.packaging/test.config @@ -0,0 +1,4 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +modules.load('graphite') +assert(graphite) +quit() diff --git a/modules/graphite/.packaging/ubuntu/16.04/rundeps b/modules/graphite/.packaging/ubuntu/16.04/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/ubuntu/16.04/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/ubuntu/18.04/rundeps b/modules/graphite/.packaging/ubuntu/18.04/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/ubuntu/18.04/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/.packaging/ubuntu/20.04/rundeps b/modules/graphite/.packaging/ubuntu/20.04/rundeps new file mode 100644 index 0000000..3da806b --- /dev/null +++ b/modules/graphite/.packaging/ubuntu/20.04/rundeps @@ -0,0 +1 @@ +lua-cqueues diff --git a/modules/graphite/README.rst b/modules/graphite/README.rst new file mode 100644 index 0000000..2f86a6f --- /dev/null +++ b/modules/graphite/README.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-3.0-or-later + +.. _mod-graphite: + +Graphite/InfluxDB/Metronome +--------------------------- + +The ``graphite`` sends statistics over the Graphite_ protocol to either Graphite_, Metronome_, InfluxDB_ or any compatible storage. This allows powerful visualization over metrics collected by Knot Resolver. + +.. tip:: The Graphite server is challenging to get up and running, InfluxDB_ combined with Grafana_ are much easier, and provide richer set of options and available front-ends. Metronome_ by PowerDNS alternatively provides a mini-graphite server for much simpler setups. + +Example configuration: + +Only the ``host`` parameter is mandatory. + +By default the module uses UDP so it doesn't guarantee the delivery, set ``tcp = true`` to enable Graphite over TCP. If the TCP consumer goes down or the connection with Graphite is lost, resolver will periodically attempt to reconnect with it. + +.. code-block:: lua + + modules = { + graphite = { + prefix = hostname() .. worker.id, -- optional metric prefix + host = '127.0.0.1', -- graphite server address + port = 2003, -- graphite server port + interval = 5 * sec, -- publish interval + tcp = false -- set to true if you want TCP mode + } + } + +The module supports sending data to multiple servers at once. + +.. code-block:: lua + + modules = { + graphite = { + host = { '127.0.0.1', '1.2.3.4', '::1' }, + } + } + +Dependencies +^^^^^^^^^^^^ + +* `lua cqueues <https://25thandclement.com/~william/projects/cqueues.html>`_ package. + + +.. _Graphite: https://graphite.readthedocs.io/en/latest/feeding-carbon.html +.. _InfluxDB: https://influxdb.com/ +.. _Metronome: https://github.com/ahuPowerDNS/metronome +.. _Grafana: http://grafana.org/ diff --git a/modules/graphite/graphite.lua b/modules/graphite/graphite.lua new file mode 100644 index 0000000..8f6ba74 --- /dev/null +++ b/modules/graphite/graphite.lua @@ -0,0 +1,145 @@ +-- SPDX-License-Identifier: GPL-3.0-or-later +-- Load dependent modules +if not stats then modules.load('stats') end + +-- This is leader-only module +local M = {} +local socket = require("cqueues.socket") +local proto_txt = { + [socket.SOCK_DGRAM] = 'udp', + [socket.SOCK_STREAM] = 'tcp' +} + +local function make_socket(host, port, stype) + local s, err, status + -- timeout before next interval begins (roughly) + local timeout_sec = (M.interval - 10) / sec + + s = socket.connect({ host = host, port = port, type = stype }) + s:setmode('bn', 'bn') + s:settimeout(timeout_sec) + status, err = pcall(s.connect, s, timeout_sec) + if status == true and err == nil then + err = 'connect timeout' + s:close() + status = false + end + + if not status then + log('[graphite] connecting: %s@%d %s reason: %s', + host, port, proto_txt[stype], err) + return status, err + end + return s +end + +-- Create connected UDP socket +local function make_udp(host, port) + return make_socket(host, port, socket.SOCK_DGRAM) +end + +-- Create connected TCP socket +local function make_tcp(host, port) + return make_socket(host, port, socket.SOCK_STREAM) +end + +-- Send the metrics in a table to multiple Graphite consumers +local function publish_table(metrics, prefix, now) + local s + for i in ipairs(M.cli) do + local host = M.info[i] + + if M.cli[i] == -1 then + if host.tcp then + s = make_tcp(host.addr, host.port) + else + s = make_udp(host.addr, host.port) + end + if s then + M.cli[i] = s + end + end + + if M.cli[i] ~= -1 then + for key,val in pairs(metrics) do + local msg = key..' '..val..' '..now..'\n' + if prefix then + msg = prefix..'.'..msg + end + + local ok, err = pcall(M.cli[i].write, M.cli[i], msg) + if not ok then + local tcp = M.cli[i]['connect'] ~= nil + if tcp and host.seen + 2 * M.interval / 1000 <= now then + local sock_type = (host.tcp and socket.SOCK_STREAM) + or socket.SOCK_DGRAM + log('[graphite] reconnecting: %s@%d %s reason: %s', + host.addr, host.port, proto_txt[sock_type], err) + s = make_tcp(host.addr, host.port) + if s then + M.cli[i] = s + host.seen = now + else + M.cli[i] = -1 + break + end + end + end + end -- loop metrics + end + end -- loop M.cli +end + +function M.init() + M.ev = nil + M.cli = {} + M.info = {} + M.interval = 5 * sec + M.prefix = string.format('kresd.%s.%s', hostname(), worker.id) + return 0 +end + +function M.deinit() + if M.ev then event.cancel(M.ev) end + return 0 +end + +-- @function Publish results to the Graphite server(s) +function M.publish() + local now = os.time() + -- Publish built-in statistics + if not M.cli then error("no graphite server configured") end + publish_table(cache.stats(), M.prefix..'.cache', now) + publish_table(worker.stats(), M.prefix..'.worker', now) + -- Publish extended statistics if available + publish_table(stats.list(), M.prefix, now) + return 0 +end + +-- @function Make connection to Graphite server. +function M.add_server(_, host, port, tcp) + table.insert(M.cli, -1) + table.insert(M.info, {addr = host, port = port, tcp = tcp, seen = 0}) + return 0 +end + +function M.config(conf) + -- config defaults + if not conf then return 0 end + if not conf.port then conf.port = 2003 end + if conf.interval then M.interval = conf.interval end + if conf.prefix then M.prefix = conf.prefix end + if type(conf.host) == 'table' then + for _, val in pairs(conf.host) do + M:add_server(val, conf.port, conf.tcp) + end + else + M:add_server(conf.host, conf.port, conf.tcp) + end + -- start publishing stats + if M.ev then event.cancel(M.ev) end + M.ev = event.recurrent(M.interval, function() worker.coroutine(M.publish) end) + return 0 +end + +return M |