1a481a69 |
(defpackage :zfs-cleaner.utils
(:use :cl)
(:export #:regex-match #:include #:exclude #:pick
#:snapshot-to-vector #:vector-to-lt #:key-transform
#:combine #:derive #:cumsum #:over #:on #:shortcut
#:defun-ct))
(in-package :zfs-cleaner.utils)
(defmacro shortcut (name function &body bound-args)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(setf (fdefinition ',name)
(,function ,@bound-args))))
(defmacro defun-ct (name (&rest args) &body body)
`(eval-when (:load-toplevel :compile-toplevel :execute)
(defun ,name ,args
,@body)))
(defun-ct regex-match (regex)
(lambda (data)
(cl-ppcre:scan-to-strings regex data)))
(defun-ct include (pred)
(lambda (seq)
(remove-if-not pred seq)))
(defun-ct exclude (pred)
(lambda (seq)
(remove-if pred seq)))
(defun-ct pick (selector)
(lambda (seq)
(map 'list selector seq)))
(defun-ct key-transform (fun key-get key-set)
(lambda (it)
(let ((key-val (funcall key-get it)))
(funcall key-set
(funcall fun key-val)))))
(defun-ct combine (fun1 fun2)
(lambda (item)
(list (funcall fun1 item)
(funcall fun2 item))))
(defun-ct derive (diff-fun &key (key #'identity))
(lambda (list)
(mapcar (lambda (next cur)
(cons (funcall diff-fun (funcall key next) (funcall key cur))
next))
(cdr list)
list)))
(defun-ct cumsum (&key (add-fun #'+) (key #'identity) (combine (lambda (x y) y x)) (zero 0))
(lambda (seq)
(nreverse
(reduce (lambda (accum next)
(let ((key-val (funcall key next))
(old-val (if accum
(funcall key (car accum))
zero)))
(cons (funcall combine
(funcall add-fun old-val key-val)
next)
accum)))
seq
:initial-value ()))))
(defun-ct over (fun &key (result-type 'list))
(lambda (seq)
(map result-type fun seq)))
(defun-ct on (fun key)
(lambda (it)
(funcall fun (funcall key it))))
(defpackage :zfs-cleaner
(:use :cl :zfs-cleaner.utils))
(in-package :zfs-cleaner)
(defgeneric %get-snapshots (env)
(:method ((env (eql :dev)))
(alexandria:read-file-into-string #p "/tmp/feeds"))
(:method ((env (eql :prod)))
(with-output-to-string (s)
(uiop:run-program "zfs list -r -t snapshot -H -p tank/feed_archive/feeds"
:output s))))
(defun get-snapshots (&optional (env :dev))
(%get-snapshots env))
(shortcut find-date regex-match
'(:group
(:named-register "year" (:greedy-repetition 4 4 :digit-class)) #\-
(:named-register "month" (:greedy-repetition 2 2 :digit-class)) #\-
(:named-register "day" (:greedy-repetition 2 2 :digit-class)) #\-
(:named-register "hour" (:greedy-repetition 2 2 :digit-class)) #\-
(:named-register "minute" (:greedy-repetition 2 2 :digit-class))))
(defun-ct snapshot-to-vector (name)
(map 'vector #'parse-integer
(nth-value 1 (find-date name))))
(defun-ct vector-to-lt (vec)
(apply 'local-time:encode-timestamp
0 0
(coerce (reverse vec)
'list)))
(defstruct (zfs-date (:type vector))
year month day hour minute)
(defparameter +date-format+
'((:year 4) #\-
(:month 2) #\-
(:day 2) #\-
(:hour 2) #\-
(:min 2)))
(defun get-snapshots-to-prune (snapshots)
(labels ((first-column (it)
(elt (fwoar.string-utils:split #\tab it)
0))
(is-saved-snapshot (ts)
(or (is-hourly-snapshot ts)
(not (is-stale ts))))
(is-hourly-snapshot (it)
(= (local-time:timestamp-minute it)
0))
(is-stale (item)
(local-time:timestamp< item
(local-time:timestamp- (local-time:now)
2 :day))))
(funcall (alexandria:compose (pick #'second)
(exclude (alexandria:compose (on #'is-saved-snapshot #'first)))
(over (combine (alexandria:compose #'vector-to-lt
#'snapshot-to-vector)
#'first-column))
(include 'find-date))
(fwoar.string-utils:split #\newline snapshots))))
(defun dev-main ()
(let ((snapshots (get-snapshots :dev)))
(format t "~&~{~a~%~}" (get-snapshots-to-prune snapshots))))
(defun prod-main ()
(let ((snapshots (get-snapshots :prod)))
(format t "~&~{~a~%~}" (get-snapshots-to-prune snapshots))))
|