From: Eric Bollengier Date: Thu, 22 Nov 2018 17:07:08 +0000 (+0100) Subject: Add 'prune jobs/files all' command X-Git-Tag: Release-9.4.3~52 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=02fa2d27c733c56d5db41d50c85773be2e43c325;p=thirdparty%2Fbacula.git Add 'prune jobs/files all' command 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. --- diff --git a/bacula/src/cats/bdb.h b/bacula/src/cats/bdb.h index 50b55402e..8761b7463 100644 --- a/bacula/src/cats/bdb.h +++ b/bacula/src/cats/bdb.h @@ -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); diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index dd7844cca..3a541476b 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -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) \ diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index 4ffe2ec3d..377f46b6f 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -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 */ diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index 15cfe0948..8e998e190 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -405,7 +405,6 @@ bail_out: bdb_unlock(); } - /* * List Job record(s) that match JOB_DBR * diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 10712262a..d19c1261c 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -138,7 +138,9 @@ static struct cmdstruct commands[] = { /* C NT_("storage= slot= drive= [ device= ] [ jobid= | job= ]"), false}, { NT_("prune"), prunecmd, _("Prune expired records from catalog"), - NT_("files | jobs | pool= | snapshot [client=] | client= | [ expired ] volume= "), true}, + NT_("files | jobs | snapshot [client=] | client= | \n" + "\t[expired] [all | allpools | allfrompool] [pool=] [mediatype=] volume= [yes]"), + true}, { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume= [mediatype= pool= allpools storage= drive=]"), true}, { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false}, diff --git a/bacula/src/dird/ua_prune.c b/bacula/src/dird/ua_prune.c index 063af6429..93be8e809 100644 --- a/bacula/src/dird/ua_prune.c +++ b/bacula/src/dird/ua_prune.c @@ -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);