diff options
Diffstat (limited to 'elisp')
-rw-r--r-- | elisp/cmk.el | 212 |
1 files changed, 123 insertions, 89 deletions
diff --git a/elisp/cmk.el b/elisp/cmk.el index 7071a38..4ef87e2 100644 --- a/elisp/cmk.el +++ b/elisp/cmk.el @@ -18,61 +18,12 @@ ;; CMK livestatus ;; ;;; Code: +(require 'subr-x) +(require 'cl-lib) (require 'dash) -(defun cmk-parse-rows-problems (buffer) - (with-current-buffer buffer - (goto-char (point-min)) - (let (data) - (while (not (eobp)) - (let* ((row - (split-string (buffer-substring-no-properties (point) (line-end-position)) ";")) - (state (pcase (string-to-number (car row)) - (0 (propertize "OK" 'face 'font-lock-string-face)) - (1 (propertize "WARN" 'face 'font-lock-warning-face)) - (2 (propertize "CRIT" 'face 'font-lock-keyword-face)) - (3 (propertize "UNKN" 'face 'font-lock-builtin-face)) - (a (number-to-string a)))) - (date (->> (nth 3 row) - (string-to-number) - (seconds-to-time) - (format-time-string "%Y-%m-%d %H:%M"))) - (msg (->> (nth 4 row) - (replace-regexp-in-string (rx "(" (+ "!") ")") "")))) - (push (list (count-lines 1 (point)) - (vector state (cadr row) - (caddr row) - date msg)) data)) - (forward-line)) - data))) - -(defun cmk-oneshot (&optional keep-buffer) - "One shot socket connection. non-nil KEEP-BUFFER after socket closes." - (make-network-process - :name "Checkmk" - :remote "/tmp/ingridcmk.socket" - :buffer "CMK" - :sentinel (lambda (process event) - (message "Process: %s had the event '%s'" process event) - (unless keep-buffer - (kill-buffer (process-buffer process)))))) - -(let* ((cmks (cmk-oneshot))) - ;; (process-send-string cmks "GET hosts\nColumns: address name\n\n") - ;; (process-send-string cmks - ;; "GET log - ;; Columns: host_name service_description log_type log_plugin_output log_state log_state_type log_time - ;; Filter: log_time >= 1657270676 - ;; Filter: class = 1 - ;; Filter: class = 3 - ;; Filter: class = 8 - ;; Or: 3\n\n") - - (when (buffer-live-p (get-buffer "CMK Problems")) (kill-buffer "CMK Problems")) - (with-current-buffer (process-buffer cmks) (erase-buffer)) - (process-send-string cmks "GET services -Columns: service_state host_name service_description service_last_state_change service_plugin_output -Filter: service_state = 0 +(defconst cmk-lq-problems-filter + "Filter: service_state = 0 Filter: service_has_been_checked = 1 And: 2 Negate: @@ -89,46 +40,129 @@ Filter: host_state = 2 Filter: host_has_been_checked = 1 And: 2 Negate:\n\n") - (with-current-buffer (get-buffer-create "CMK Problems") - (erase-buffer) - (tabulated-list-mode) - (setq tabulated-list-format `[("STATE" 5 t) - ("HOST" 9 t) - ("Service" 20 t) - ("Since" 16) - ("Message" 18 t) - ]) - (setq tabulated-list-sort-key '("STATE" . nil)) - (accept-process-output cmks 0.1) - (setq tabulated-list-entries (cmk-parse-rows-problems (process-buffer cmks))) - (tabulated-list-init-header) - (tabulated-list-print) - (display-buffer (current-buffer)))) -(let* ((cmks (cmk-oneshot t))) - (with-current-buffer (process-buffer cmks) (erase-buffer)) - (process-send-string cmks "GET services -Columns: service_description host_name service_perf_data -Filter: service_description ~ CPU utilization -OutputFormat: csv -Separators: 10 9 44 124\n\n") - (accept-process-output cmks 0.1) - (display-buffer (process-buffer cmks))) +(defun cmk-state-coloring (state) + "Font color for numeric STATE input as string." + (cl-check-type state string) + (pcase (string-to-number state) + (0 (propertize "OK" 'face 'font-lock-string-face)) + (1 (propertize "WARN" 'face 'font-lock-warning-face)) + (2 (propertize "CRIT" 'face 'font-lock-keyword-face)) + (3 (propertize "UNKN" 'face 'font-lock-builtin-face)) + (_ state))) + +(defun cmk-timestamp-to-date (timestamp &optional fmt-str) + "TIMESTAMP to human readable date follownig FMT-STR. +Default is \"%Y-%m-%d %H:%M\"." + (thread-last + (if (stringp timestamp) + (string-to-number timestamp) + timestamp) + (seconds-to-time) + (format-time-string (or fmt-str "%Y-%m-%d %H:%M")))) + +(defun cmk-purge-bangs (str) + "Remove cmk status output bangs from STR." + (replace-regexp-in-string (rx "(" (+ "!") ")") "" str)) -(let* ((now (time-convert nil 'integer)) - (start (- now 7200)) - (cmks (cmk-oneshot t))) +(defun cmk-oneshot (&optional keep-buffer) + "One shot socket connection. non-nil KEEP-BUFFER after socket closes." + (make-network-process + :name "Checkmk" + :remote "/tmp/ingridcmk.socket" + :buffer "CMK" + :sentinel (lambda (process event) + (message "Process: %s had the event '%s'" process (string-trim event)) + (unless keep-buffer + (kill-buffer (process-buffer process)))))) + +(defun cmk-colums-from-spec (spec) + "Extract the column names from SPEC and return a livestatus column command." + (concat "Columns: " + (mapconcat + (lambda (column) + (or (plist-get (cdddr column) :column) (car column))) + spec + " "))) - (with-current-buffer (process-buffer cmks) (erase-buffer)) - (process-send-string cmks (format "GET services -Columns: host_name rrddata:1:user:%d:%d:1 -Filter: service_description ~ CPU utilization -OutputFormat: json\n\n" start now)) - (accept-process-output cmks 0.1) - (with-current-buffer (process-buffer cmks) +(defconst cmk-problem-col-spec + '(("STATE" 5 t :column "service_state" :parser cmk-state-coloring ) + ("Host" 9 t :column "host_name") + ("Service" 20 t :column "service_description") + ("Since" 16 nil :column "service_last_state_change" :parser cmk-timestamp-to-date) + ("Check output" 18 t :column "service_plugin_output" :parser cmk-purge-bangs) + )) + +(defun cmk-parse-rows (buffer column-spec) + "Return tabulated entries from BUFFER according to COLUMN-SPEC." + (with-current-buffer buffer (goto-char (point-min)) - (display-buffer (current-buffer)) - (json-parse-buffer))) + (cl-loop until (eobp) + collect + (list (count-lines 1 (point)) + (cl-map 'vector + (lambda (entry spec) + (let ((parser (or (plist-get (cdddr spec) :parser) #'identity))) + (funcall parser entry))) + (split-string (buffer-substring-no-properties (point) (line-end-position)) ";") + column-spec)) + do (forward-line)))) + +;; (process-send-string cmks "GET hosts\nColumns: address name\n\n") +;; (process-send-string cmks +;; "GET log +;; Columns: host_name service_description log_type log_plugin_output log_state log_state_type log_time +;; Filter: log_time >= 1657270676 +;; Filter: class = 1 +;; Filter: class = 3 +;; Filter: class = 8 +;; Or: 3\n\n") + +(defun cmk-problems () + (interactive) + (let ((cmks (cmk-oneshot 'keep)) + (buffer (get-buffer-create "*CMK Problems*"))) + (with-current-buffer (process-buffer cmks) (erase-buffer)) + (process-send-string cmks + (concat (string-join `("GET services" + ,(cmk-colums-from-spec cmk-problem-col-spec) + "Filter: service_description ~ load") "\n") + "\n\n")) + (with-current-buffer buffer + (tabulated-list-mode) + (setq tabulated-list-format (vconcat cmk-problem-col-spec)) + (setq tabulated-list-sort-key '("STATE" . nil)) + (accept-process-output cmks 0.1) + (setq tabulated-list-entries (cmk-parse-rows (process-buffer cmks) cmk-problem-col-spec)) + (tabulated-list-init-header) + (tabulated-list-print) + (display-buffer (current-buffer))))) + +;;; Get a different csv +;; (let* ((cmks (cmk-oneshot t))) +;; (with-current-buffer (process-buffer cmks) (erase-buffer)) +;; (process-send-string cmks "GET services +;; Columns: service_description host_name service_perf_data +;; Filter: service_description ~ CPU utilization +;; OutputFormat: csv +;; Separators: 10 9 44 124\n\n") +;; (accept-process-output cmks 0.1) +;; (display-buffer (process-buffer cmks))) + +;;; Get some metric data +;; (let* ((now (time-convert nil 'integer)) +;; (start (- now 7200)) +;; (cmks (cmk-oneshot t))) +;; (with-current-buffer (process-buffer cmks) (erase-buffer)) +;; (process-send-string cmks (format "GET services +;; Columns: host_name rrddata:1:user:%d:%d:1 +;; Filter: service_description ~ CPU utilization +;; OutputFormat: json\n\n" start now)) +;; (accept-process-output cmks 0.1) +;; (with-current-buffer (process-buffer cmks) +;; (goto-char (point-min)) +;; (display-buffer (current-buffer)) +;; (json-parse-buffer))) (provide 'cmk) |