]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add 'prune jobs/files all' command
authorEric Bollengier <eric@baculasystems.com>
Thu, 22 Nov 2018 17:07:08 +0000 (18:07 +0100)
committerKern Sibbald <kern@sibbald.com>
Mon, 8 Apr 2019 16:20:25 +0000 (18:20 +0200)
This new command will prune automatically all combinations of
Client/Pool found in the Job table. That will respect the safe pruning
algorithm, and will not prune jobs that are still needed to restore.

bacula/src/cats/bdb.h
bacula/src/cats/protos.h
bacula/src/cats/sql_get.c
bacula/src/cats/sql_list.c
bacula/src/dird/ua_cmds.c
bacula/src/dird/ua_prune.c

index 50b55402e8a838ee02658341c2db6e02bce9f48e..8761b746312051f1fefd332437d16056542a4c64 100644 (file)
@@ -243,6 +243,7 @@ public:
    bool bdb_get_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr);
    int  bdb_get_num_restoreobject_records(JCR *jcr, ROBJECT_DBR *rr);
    bool bdb_get_job_statistics(JCR *jcr, JOB_DBR *jr);
+   bool bdb_get_client_pool(JCR *jcr, alist *results);
 
 /* sql_list.c */
    void bdb_list_pool_records(JCR *jcr, POOL_DBR *pr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type);
index dd7844ccadeac439b647a78ce2abe227e38db825..3a541476b7b6eacd19b28839c135426291c3c25f 100644 (file)
@@ -106,7 +106,8 @@ void bdb_disable_batch_insert(bool disable);
 
 /* sql_get.c */
 void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr);
-
+#define db_get_client_pool(jcr, mdb, results)    \
+   mdb->bdb_get_client_pool(jcr, results)
 
 /* sql_create.c */
 #define db_create_path_record(jcr, mdb, ar) \
index 4ffe2ec3d488ac1572d434b7d6ceca12d13d47f6..377f46b6f5e01fe64565c2c71bc671c7ac35367a 100644 (file)
@@ -1754,4 +1754,47 @@ bail_out:
    return ok;
 }
 
+/*
+ * List Client/Pool associations. Return a list of Client/Pool in a alist.
+ * [0] Client
+ * [1] Pool
+ * [2] Client
+ * [3] Pool
+ * ...
+ */
+bool BDB::bdb_get_client_pool(JCR *jcr, alist *results)
+{
+   SQL_ROW row;
+   bool ret=false;
+   POOLMEM *where  = get_pool_memory(PM_MESSAGE);
+   POOLMEM *tmp    = get_pool_memory(PM_MESSAGE);
+   bdb_lock();
+
+   /* Build the WHERE part with the current ACLs if any */
+   pm_strcpy(where, get_acls(DB_ACL_BIT(DB_ACL_CLIENT)  |
+                             DB_ACL_BIT(DB_ACL_JOB)     |
+                             DB_ACL_BIT(DB_ACL_POOL),
+                             true));
+
+   Mmsg(cmd, "SELECT DISTINCT Client.Name, Pool.Name "
+        "FROM Job JOIN Client USING (ClientId) JOIN Pool USING (PoolId) %s",
+        where);
+
+   Dmsg1(100, "sql=%s\n", cmd);
+   if (QueryDB(jcr, cmd)) {
+      ret = true;
+
+      while ((row = sql_fetch_row()) != NULL) {
+         results->append(bstrdup(row[0])); /* append client */
+         results->append(bstrdup(row[1])); /* append pool */
+      }
+      sql_free_result();
+   }
+
+   bdb_unlock();
+   free_pool_memory(where);
+   free_pool_memory(tmp);
+   return ret;
+}
+
 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */
index 15cfe094820c2867d1c43bb95ee37d9ab9894bcd..8e998e190bd1c789cb9f447d7b79591ce99af721 100644 (file)
@@ -405,7 +405,6 @@ bail_out:
    bdb_unlock();
 }
 
-
 /*
  * List Job record(s) that match JOB_DBR
  *
index 10712262ab6a7deb856a1782ae3202062288cf3f..d19c1261c343423a67d1ec16305ba92a4e267452 100644 (file)
@@ -138,7 +138,9 @@ static struct cmdstruct commands[] = {                                      /* C
    NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
 
  { NT_("prune"),      prunecmd,      _("Prune expired records from catalog"),
-   NT_("files | jobs | pool=<pool> | snapshot  [client=<client-name>] | client=<client-name> | [ expired ] volume=<volume-name> "), true},
+   NT_("files | jobs | snapshot  [client=<client-name>] | client=<client-name> | \n"
+       "\t[expired] [all | allpools | allfrompool] [pool=<pool>] [mediatype=<type>] volume=<volume-name> [yes]"),
+   true},
 
  { NT_("purge"),      purge_cmd,     _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
  { NT_("quit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),              false},
index 063af642958ca27a42a5e5b352e2d1558455ed40..93be8e809271022e4c1f30a952e17029e494735e 100644 (file)
@@ -82,6 +82,87 @@ int file_delete_handler(void *ctx, int num_fields, char **row)
    return 0;
 }
 
+/* Prune jobs or files for all combinations of Client/Pool that we
+ * can find in the Job table. Doing so, the pruning will not prune a
+ * job that is needed to restore the client. As the command will detect
+ * all parameters automatically, it is very convenient to schedule it a
+ * couple of times per day.
+ */
+static int prune_all_clients_and_pools(UAContext *ua, int kw)
+{
+   alist results(owned_by_alist, 100);
+   POOL_MEM label;
+   CLIENT *client;
+   POOL *pool;
+
+   /* Get the combination of all Client/Pool in the Job table (respecting the ACLs) */
+   if (!db_get_client_pool(ua->jcr, ua->db, &results)) {
+      ua->error_msg(_("Unable to list Client/Pool. ERR=%s\n"), ua->db->errmsg);
+      return false;
+   }
+   while (!results.empty()) {
+      /* Each "record" is made of two values in results */
+      char *pool_s = (char *)results.pop();
+      char *client_s = (char *)results.pop();
+      Dmsg2(100, "Trying to prune %s/%s\n", client_s, pool_s);
+
+      if (!pool_s || !client_s) { /* Just in case */
+         ua->error_msg(_("Unable to list Client/Pool %s/%s\n"),
+                       NPRTB(client_s), NPRTB(pool_s));
+         bfree_and_null(pool_s);
+         bfree_and_null(client_s);
+         return false;
+      }
+
+      /* Make sure the client and the pool are still defined */
+      client = (CLIENT *)GetResWithName(R_CLIENT, client_s);
+      pool = (POOL *)GetResWithName(R_POOL, pool_s);
+      if (!client || !pool) {
+         Dmsg2(10, "Skip pruning of %s/%s, one resource is missing\n", client_s, pool_s);
+      }
+      free(client_s);
+      free(pool_s);
+      if (!client || !pool) {
+         continue;
+      }
+
+      /* Display correct messages and do the actual pruning */
+      if (kw == 0) {
+         ua->info_msg(_("Pruning Files for Client %s with Pool %s...\n"),
+                      client->name(), pool->name());
+         if (pool->FileRetention > 0) {
+            Mmsg(label, "Pool %s File", pool->name());
+            if (!confirm_retention(ua, &pool->FileRetention, label.c_str())) {
+               return false;
+            }
+         } else {
+            Mmsg(label, "Client %s File", client->name());
+            if (!confirm_retention(ua, &client->FileRetention, label.c_str())) {
+               return false;
+            }
+         }
+         prune_files(ua, client, pool);
+      }
+      if (kw == 1) {
+         ua->info_msg(_("Pruning Jobs for Client %s with Pool %s...\n"),
+                      client->name(), pool->name());
+         if (pool->JobRetention > 0) {
+            Mmsg(label, "Pool %s Job", pool->name());
+            if (!confirm_retention(ua, &pool->JobRetention, label.c_str())) {
+               return false;
+            }
+         } else {
+            Mmsg(label, "Client %s Job", client->name());
+            if (!confirm_retention(ua, &client->JobRetention, label.c_str())) {
+               return false;
+            }
+         }
+         prune_jobs(ua, client, pool, JT_BACKUP);
+      }
+   }
+   return true;
+}
+
 /*
  *   Prune records from database
  *
@@ -118,6 +199,11 @@ int prunecmd(UAContext *ua, const char *cmd)
       kw = do_keyword_prompt(ua, _("Choose item to prune"), keywords);
    }
 
+   /* prune files/jobs all (prune all Client/Pool automatically) */
+   if ((kw == 0 || kw == 1) && find_arg(ua, _("all")) > 0) {
+      return prune_all_clients_and_pools(ua, kw);
+   }
+
    switch (kw) {
    case 0:  /* prune files */
       /* We restrict the client list to ClientAcl, maybe something to change later */
@@ -293,8 +379,10 @@ int prune_files(UAContext *ua, CLIENT *client, POOL *pool)
    }
 
 //   edit_utime(now-period, ed1, sizeof(ed1));
-//   Jmsg(ua->jcr, M_INFO, 0, _("Begin pruning Jobs older than %s secs.\n"), ed1);
-   Jmsg(ua->jcr, M_INFO, 0, _("Begin pruning Files.\n"));
+//   Jmsg(ua->jcr, M_INFO, 0, _("Begin pruning Jobs older than %s secs.\n"), ed1)
+   if (ua->jcr->getJobType() != JT_CONSOLE) {
+      Jmsg(ua->jcr, M_INFO, 0, _("Begin pruning Files.\n"));
+   }
    /* Select Jobs -- for counting */
    Mmsg(query,
         "SELECT COUNT(1) FROM Job %s WHERE PurgedFiles=0 %s",
@@ -477,8 +565,10 @@ int prune_jobs(UAContext *ua, CLIENT *client, POOL *pool, int JobType)
       goto bail_out;
    }
 
-   edit_utime(period, ed1, sizeof(ed1));
-   Jmsg(ua->jcr, M_INFO, 0, _("Begin pruning Jobs older than %s.\n"), ed1);
+   if (ua->jcr->getJobType() != JT_CONSOLE) {
+      edit_utime(period, ed1, sizeof(ed1));
+      Jmsg(ua->jcr, M_INFO, 0, _("Begin pruning Jobs older than %s.\n"), ed1);
+   }
 
    del.max_ids = 100;
    del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);