Checkmk
is an IT monitoring software. I used to work for them some time ago
and I still use it in my personal live. It provides a Web User Interface(UI)
with dashboards to see the status of your monitored system. The way that UI
queries they monitoring software is via the livestatus
.
You can query it directly over the terminal using unixcat
and in the
documentation they explain how to implement a minimal Python
client. The
terminal is cumbersome, because the command line is one dimensional. It is hard
to edit your queries. That is when scripts are useful, you have a file you can
edit and then execute. Whether a simple Bash
script or even the Python
script the hosting scripting language gets on the way of your query.
It is then again much nicer to use the command line pipe, that way you have a
file dedicated for your query and another program being the client to live
status. That is the moment I think, it would certainly be better in Emacs
.
The goal is straightforward. Have a dedicated mode to edit livestatus
queries
and make it executable in some way to get the result.
Emacs
can create a network progress, that is the way to connect to a socket.
You get then the IO buffer. Setting up the connection to a local socket
looks like this.
1(defun cmk-oneshot (&optional keep-buffer)
2 "One shot socket connection. non-nil KEEP-BUFFER after socket closes."
3 (make-network-process
4 :name "Checkmk"
5 :host 'local
6 :service 6557
7 :buffer "CMK"
8 :sentinel (lambda (process event)
9 (message "Process: %s had the event '%s'" process (string-trim event))
10 (unless keep-buffer
11 (kill-buffer (process-buffer process))))))
In this case I hard-coded where my livestatus
socket is listening, which is a
bad practice. I should better have a struct with the connection configuration.
That is an exercise left to the reader.
The function that sends the query is simple.
1(defun cmk-livestatus-query (query)
2 "One shot livestatus QUERY, return network process."
3 (let ((cmks (cmk-oneshot 'keep)))
4 (with-current-buffer (process-buffer cmks) (erase-buffer))
5 (process-send-string cmks query)
6 cmks))
That one needs another wrapper to display the results in a readable way.
1(defvar-local cmk-livestatus-query ""
2 "Livestatus query in buffer.")
3
4(defun cmk-livestatus-get-csv-result (query)
5 "Send QUERY to a new livestatus process.
6Display result buffer in csv mode with aligned entries."
7 (let ((cmk (cmk-livestatus-query query)))
8 (switch-to-buffer (process-buffer cmk))
9 (csv-set-separator ?\;)
10 (csv-align-mode)
11 (setq-local cmk-livestatus-query query)))
Abusing that Emacs-Lisp
is a Lisp 2, I dare to call the buffer local
variable that stores the query that generated the result the same as the
function that performs the query against the livestatus
socket.
Lastly I need to setup the buffer where I’ll edit the query and then pipe it to
the query function.
1(defun cmk-custom-livestatus-query ()
2 "Open buffer to write a livestatus query."
3 (interactive)
4 (let ((query cmk-livestatus-query)
5 (edit-buffer (get-buffer-create "*LQ*")))
6 (with-current-buffer edit-buffer
7 (cmk-livestatus-mode)
8 (if (string-blank-p query) (insert "GET ") (insert query))
9 (local-set-key "\C-c\C-c"
10 (lambda ()
11 (interactive)
12 (let ((new-query (-> (buffer-string) (string-trim) (concat "\n\n"))))
13 (kill-buffer)
14 (cmk-livestatus-get-csv-result new-query)))))
15 (switch-to-buffer edit-buffer)))
For the convenience of editing the query in a dedicated buffer I still need to
define cmk-livestatus-mode
. At this point, I’ll only focus on syntax
highlighting. Using regular expressions to highlight keywords.
1(defconst cmk-livestatus-headers
2 '("Filter" "Columns" "Stats" "Limit" "ColumnHeaders" "OutputFormat"))
3
4(define-derived-mode cmk-livestatus-mode prog-mode "livestatus"
5 "Major mode to edit livestatus queries."
6 (setq font-lock-defaults `(((,(rx bol "GET" eow) . font-lock-keyword-face)
7 (,(rx word-start (or "hosts" "services") word-end) . font-lock-constant-face)
8 (,(concat "^" (regexp-opt cmk-livestatus-headers 'words) ":[[:space:]]") . font-lock-function-name-face)
9 (,(rx bol (or "And" "Or" "Negate") ":" whitespace) . font-lock-keyword-face)
10 (,(rx whitespace (or "~" "=" "=~" "~~" "<" ">" "<=" ">=") whitespace) . font-lock-keyword-face)))))
That is all you need. Not it is super convenient to perform some quick queries
to the monitoring server anytime you want. It is a little improvement, yet the
true advantage comes with saved queries and displaying views. In essence, an
alternative UI in Emacs. That comes in the next post.