automate choosing the next tapes

"Stefan G. Weichinger via Bacula-users" <[email protected]> Thu, 29 Jan 2026 09:45:56 +0100
Newsgroups gmane.comp.bacula.user
Message-ID <[email protected]>
It's been a while that I already started to write a small script to help 
me and a customer to improve our weekly change of tapes:

they use a small tape library with 8 slots, with their schedule etc we 
roughly change tapes each week or so.

To choose the tapes to remove and which ones to supply I look at bacularis.

The default "algorithm" is something like:

- look at the Volumes for the default Pool "Daily"

- sort them by LastWritten

- only the Enabled ones.

- Retentiontime over

- Status Full, Recycle, Append ...

- not in changer

I do that "manually" by looking and filtering in my head  more or less ;-)


Now I'd like to give it another try and wrap that into a python script.

There should be a cronjob that tells us which tapes to remove from and 
which to put into the changer (for the standard schedule).

Do you guys see something missing in my approach above?

With a bit of LLM I came up with this so far:

--- code ---

#!/usr/bin/env python3

import psycopg2
import sys


from datetime import datetime, timedelta

DB_CONFIG = {
     "host": "localhost",
     "dbname": "bacula",
     "user": "bacula",
     "password": "somepass"
}

POOL_NAME = "Daily"
USABLE_STATUS = ("Append", "Recycle", "Full")

RETENTION_DAYS = 30

QUERY = """
SELECT
     m.mediaid,
     m.volumename,
     m.volstatus,
     m.lastwritten,
     m.inchanger
FROM media m
JOIN pool p ON m.poolid = p.poolid
WHERE
     p.name = %s
     AND m.volstatus IN %s
     AND m.lastwritten IS NOT NULL
ORDER BY
     m.lastwritten,
     m.mediaid
LIMIT 8;
"""

def main():
     # Verbindung zur DB herstellen
     conn = psycopg2.connect(**DB_CONFIG)
     cur = conn.cursor()

     # Abfrage ausführen
     cur.execute(QUERY, (POOL_NAME, USABLE_STATUS))
     rows = cur.fetchall()

     if not rows:
         print(f"CRITICAL: Keine verwendbaren Tapes im Pool '{POOL_NAME}'")
         sys.exit(2)

     print(f"Nächste 8 Tapes mit abgelaufener Retention im Pool 
'{POOL_NAME}':\n")

     now = datetime.now()

     for volid, volume, status, lastwritten, inchanger in rows:

     lastwritten_str = lastwritten if lastwritten else "Noch nie verwendet"

         if lastwritten:
             lastwritten_date = lastwritten
         else:
             lastwritten_date = datetime.min  # Wenn nie benutzt, setze 
auf minimalen Wert

         expiration_date = lastwritten_date + timedelta(days=RETENTION_DAYS)

         if expiration_date <= now:
             retention_status = "Abgelaufen"
         else:
             retention_status = f"Verbleibend: {expiration_date - now}"

         # Changer-Status anzeigen
         changer_status = "Im Changer" if inchanger else "Nicht im Changer"

         # Ausgabe
         print(f"{volume:15} {status:8} {lastwritten_str} 
{retention_status:30} {changer_status}")

     # Verbindung schließen
     cur.close()
     conn.close()

if __name__ == "__main__":
     main()

--- code end ---

(forgive the german parts)


Anyone interested in working on such a script?
Any obvious flaws in my approach?

Thanks, regards, Stefan










_______________________________________________
Bacula-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bacula-users