X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fua_purge.c;h=81c7ed493b798336a8e9721fc3d6d1a8d911a99b;hb=b7d55c258d091d7be881e0fb413c10accd1dd5d6;hp=282853f05ce810d9863d51e2d4949a9fbd4183e8;hpb=c3e33e826799df3edf6fddd964d11da94348a8b2;p=bacula%2Fbacula diff --git a/bacula/src/dird/ua_purge.c b/bacula/src/dird/ua_purge.c index 282853f05c..81c7ed493b 100644 --- a/bacula/src/dird/ua_purge.c +++ b/bacula/src/dird/ua_purge.c @@ -1,12 +1,12 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2002-2008 Free Software Foundation Europe e.V. + Copyright (C) 2002-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public + modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. @@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -35,7 +35,6 @@ * * Kern Sibbald, February MMII * - * Version $Id$ */ #include "bacula.h" @@ -44,6 +43,7 @@ /* Forward referenced functions */ static int purge_files_from_client(UAContext *ua, CLIENT *client); static int purge_jobs_from_client(UAContext *ua, CLIENT *client); +static int action_on_purge_cmd(UAContext *ua, const char *cmd); static const char *select_jobsfiles_from_client = "SELECT JobId FROM Job " @@ -138,6 +138,11 @@ int purgecmd(UAContext *ua, const char *cmd) } /* Volume */ case 2: + /* Perform ActionOnPurge (action=truncate) */ + if (find_arg(ua, "action") >= 0) { + return action_on_purge_cmd(ua, ua->cmd); + } + while ((i=find_arg(ua, NT_("volume"))) >= 0) { if (select_media_dbr(ua, &mr)) { purge_jobs_from_volume(ua, &mr, /*force*/true); @@ -382,26 +387,14 @@ void upgrade_copies(UAContext *ua, char *jobs) POOL_MEM query(PM_MESSAGE); db_lock(ua->db); + /* Do it in two times for mysql */ - Mmsg(query, "CREATE TEMPORARY TABLE cpy_tmp AS " - "SELECT MIN(JobId) AS JobId FROM Job " /* Choose the oldest job */ - "WHERE Type='%c' " - "AND ( PriorJobId IN (%s) " - "OR " - " PriorJobId IN ( " - "SELECT PriorJobId " - "FROM Job " - "WHERE JobId IN (%s) " - " AND Type='B' " - ") " - ") " - "GROUP BY PriorJobId ", /* one result per copy */ - JT_JOB_COPY, jobs, jobs); + Mmsg(query, uap_upgrade_copies_oldest_job[db_get_type_index(ua->db)], JT_JOB_COPY, jobs, jobs); db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); Dmsg1(050, "Upgrade copies Log sql=%s\n", query.c_str()); /* Now upgrade first copy to Backup */ - Mmsg(query, "UPDATE Job SET Type='B' " /* JT_JOB_COPY => JT_BACKUP */ + Mmsg(query, "UPDATE Job SET Type='B' " /* JT_JOB_COPY => JT_BACKUP */ "WHERE JobId IN ( SELECT JobId FROM cpy_tmp )"); db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); @@ -430,6 +423,14 @@ void purge_jobs_from_catalog(UAContext *ua, char *jobs) db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); Dmsg1(050, "Delete Log sql=%s\n", query.c_str()); + Mmsg(query, "DELETE FROM RestoreObject WHERE JobId IN (%s)", jobs); + db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); + Dmsg1(050, "Delete RestoreObject sql=%s\n", query.c_str()); + + Mmsg(query, "DELETE FROM PathVisibility WHERE JobId IN (%s)", jobs); + db_sql_query(ua->db, query.c_str(), NULL, (void *)NULL); + Dmsg1(050, "Delete PathVisibility sql=%s\n", query.c_str()); + upgrade_copies(ua, jobs); /* Now remove the Job record itself */ @@ -449,12 +450,11 @@ void purge_files_from_volume(UAContext *ua, MEDIA_DBR *mr ) bool purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr, bool force) { POOL_MEM query(PM_MESSAGE); - struct del_ctx del; + db_list_ctx lst; + char *jobids=NULL; int i; bool purged = false; bool stat; - JOB_DBR jr; - char ed1[50]; stat = strcmp(mr->VolStatus, "Append") == 0 || strcmp(mr->VolStatus, "Full") == 0 || @@ -467,42 +467,34 @@ bool purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr, bool force) return 0; } - memset(&jr, 0, sizeof(jr)); - memset(&del, 0, sizeof(del)); - del.max_ids = 1000; - del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids); - /* * Check if he wants to purge a single jobid */ i = find_arg_with_value(ua, "jobid"); - if (i >= 0) { - del.num_ids = 1; - del.JobId[0] = str_to_int64(ua->argv[i]); + if (i >= 0 && is_a_number_list(ua->argv[i])) { + jobids = ua->argv[i]; } else { /* * Purge ALL JobIds */ - Mmsg(query, "SELECT DISTINCT JobId FROM JobMedia WHERE MediaId=%s", - edit_int64(mr->MediaId, ed1)); - if (!db_sql_query(ua->db, query.c_str(), file_delete_handler, (void *)&del)) { + if (!db_get_volume_jobids(ua->jcr, ua->db, mr, &lst)) { ua->error_msg("%s", db_strerror(ua->db)); Dmsg0(050, "Count failed\n"); goto bail_out; } + jobids = lst.list; } - purge_job_list_from_catalog(ua, del); + if (*jobids) { + purge_jobs_from_catalog(ua, jobids); + } - ua->info_msg(_("%d File%s on Volume \"%s\" purged from catalog.\n"), del.num_del, - del.num_del==1?"":"s", mr->VolumeName); + ua->info_msg(_("%d File%s on Volume \"%s\" purged from catalog.\n"), + lst.count, lst.count<=1?"":"s", mr->VolumeName); purged = is_volume_purged(ua, mr, force); bail_out: - if (del.JobId) { - free(del.JobId); - } return purged; } @@ -537,7 +529,7 @@ bool is_volume_purged(UAContext *ua, MEDIA_DBR *mr, bool force) /* If purged, mark it so */ cnt.count = 0; - Mmsg(query, "SELECT count(*) FROM JobMedia WHERE MediaId=%s", + Mmsg(query, "SELECT 1 FROM JobMedia WHERE MediaId=%s LIMIT 1", edit_int64(mr->MediaId, ed1)); if (!db_sql_query(ua->db, query.c_str(), del_count_handler, (void *)&cnt)) { ua->error_msg("%s", db_strerror(ua->db)); @@ -571,13 +563,199 @@ static BSOCK *open_sd_bsock(UAContext *ua) return ua->jcr->store_bsock; } +/* + * Called here to send the appropriate commands to the SD + * to do truncate on purge. + */ +static void do_truncate_on_purge(UAContext *ua, MEDIA_DBR *mr, + char *pool, char *storage, + int drive, BSOCK *sd) +{ + int dvd; + bool ok=false; + uint64_t VolBytes = 0; + + /* TODO: Return if not mr->Recyle ? */ + if (!mr->Recycle) { + return; + } + + /* Do it only if action on purge = truncate is set */ + if (!(mr->ActionOnPurge & ON_PURGE_TRUNCATE)) { + return; + } + /* + * Send the command to truncate the volume after purge. If this feature + * is disabled for the specific device, this will be a no-op. + */ + + /* Protect us from spaces */ + bash_spaces(mr->VolumeName); + bash_spaces(mr->MediaType); + bash_spaces(pool); + bash_spaces(storage); + + /* Do it by relabeling the Volume, which truncates it */ + sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s " + "MediaType=%s Slot=%d drive=%d\n", + storage, + mr->VolumeName, mr->VolumeName, + pool, mr->MediaType, mr->Slot, drive); + + unbash_spaces(mr->VolumeName); + unbash_spaces(mr->MediaType); + unbash_spaces(pool); + unbash_spaces(storage); + + /* Send relabel command, and check for valid response */ + while (sd->recv() >= 0) { + ua->send_msg("%s", sd->msg); + if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes, &dvd) == 2) { + ok = true; + } + } + + if (ok) { + mr->VolBytes = VolBytes; + mr->VolFiles = 0; + if (!db_update_media_record(ua->jcr, ua->db, mr)) { + ua->error_msg(_("Can't update volume size in the catalog\n")); + } + ua->send_msg(_("The volume \"%s\" has been truncated\n"), mr->VolumeName); + } else { + ua->warning_msg(_("Unable to truncate volume \"%s\"\n"), mr->VolumeName); + } +} + +/* + * Implement Bacula bconsole command purge action + * purge action= pool= volume= storage= devicetype= + */ +static int action_on_purge_cmd(UAContext *ua, const char *cmd) +{ + bool allpools = false; + int drive = -1; + int nb = 0; + uint32_t *results = NULL; + const char *action = "all"; + STORE *store = NULL; + POOL *pool = NULL; + MEDIA_DBR mr; + POOL_DBR pr; + BSOCK *sd = NULL; + + memset(&pr, 0, sizeof(pr)); + memset(&mr, 0, sizeof(mr)); + + /* Look at arguments */ + for (int i=1; iargc; i++) { + if (strcasecmp(ua->argk[i], NT_("allpools")) == 0) { + allpools = true; + + } else if (strcasecmp(ua->argk[i], NT_("volume")) == 0 + && is_name_valid(ua->argv[i], NULL)) { + bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName)); + + } else if (strcasecmp(ua->argk[i], NT_("devicetype")) == 0 + && ua->argv[i]) { + bstrncpy(mr.MediaType, ua->argv[i], sizeof(mr.MediaType)); + + } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) { + drive = atoi(ua->argv[i]); + + } else if (strcasecmp(ua->argk[i], NT_("action")) == 0 + && is_name_valid(ua->argv[i], NULL)) { + action=ua->argv[i]; + } + } + + /* Choose storage */ + ua->jcr->wstore = store = get_storage_resource(ua, false); + if (!store) { + goto bail_out; + } + mr.StorageId = store->StorageId; + + if (!open_db(ua)) { + Dmsg0(100, "Can't open db\n"); + goto bail_out; + } + + if (!allpools) { + /* force pool selection */ + pool = get_pool_resource(ua); + if (!pool) { + Dmsg0(100, "Can't get pool resource\n"); + goto bail_out; + } + bstrncpy(pr.Name, pool->name(), sizeof(pr.Name)); + if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { + Dmsg0(100, "Can't get pool record\n"); + goto bail_out; + } + mr.PoolId = pr.PoolId; + } + + /* + * Look for all Purged volumes that can be recycled, are enabled and + * have more the 10,000 bytes. + */ + mr.Recycle = 1; + mr.Enabled = 1; + mr.VolBytes = 10000; + bstrncpy(mr.VolStatus, "Purged", sizeof(mr.VolStatus)); + if (!db_get_media_ids(ua->jcr, ua->db, &mr, &nb, &results)) { + Dmsg0(100, "No results from db_get_media_ids\n"); + goto bail_out; + } + + if (!nb) { + ua->send_msg(_("No Volumes found to perform %s action.\n"), action); + goto bail_out; + } + + if ((sd=open_sd_bsock(ua)) == NULL) { + Dmsg0(100, "Can't open connection to sd\n"); + goto bail_out; + } + + /* + * Loop over the candidate Volumes and actually truncate them + */ + for (int i=0; i < nb; i++) { + memset(&mr, 0, sizeof(mr)); + mr.MediaId = results[i]; + if (db_get_media_record(ua->jcr, ua->db, &mr)) { + /* TODO: ask for drive and change Pool */ + if (!strcasecmp("truncate", action) || !strcasecmp("all", action)) { + do_truncate_on_purge(ua, &mr, pr.Name, store->dev_name(), drive, sd); + } + } else { + Dmsg1(0, "Can't find MediaId=%lld\n", (uint64_t) mr.MediaId); + } + } + +bail_out: + close_db(ua); + if (sd) { + sd->signal(BNET_TERMINATE); + sd->close(); + ua->jcr->store_bsock = NULL; + } + ua->jcr->wstore = NULL; + if (results) { + free(results); + } + + return 1; +} + /* * IF volume status is Append, Full, Used, or Error, mark it Purged * Purged volumes can then be recycled (if enabled). */ bool mark_media_purged(UAContext *ua, MEDIA_DBR *mr) { - char dev_name[MAX_NAME_LENGTH]; JCR *jcr = ua->jcr; if (strcmp(mr->VolStatus, "Append") == 0 || strcmp(mr->VolStatus, "Full") == 0 || @@ -587,37 +765,9 @@ bool mark_media_purged(UAContext *ua, MEDIA_DBR *mr) if (!db_update_media_record(jcr, ua->db, mr)) { return false; } - - if (mr->ActionOnPurge > 0) { - /* Send the command to truncate the volume after purge. If this feature - * is disabled for the specific device, this will be a no-op. - */ - BSOCK *sd; - if ((sd=open_sd_bsock(ua)) != NULL) { - bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name)); - bash_spaces(dev_name); - bash_spaces(mr->VolumeName); - sd->fsend("action_on_purge %s vol=%s action=%d", - dev_name, - mr->VolumeName, - mr->ActionOnPurge); - unbash_spaces(mr->VolumeName); - while (sd->recv() >= 0) { - ua->send_msg("%s", sd->msg); - } - - sd->signal(BNET_TERMINATE); - sd->close(); - ua->jcr->store_bsock = NULL; - } else { - ua->error_msg(_("Could not connect to storage daemon")); - return false; - } - } - pm_strcpy(jcr->VolumeName, mr->VolumeName); generate_job_event(jcr, "VolumePurged"); - generate_plugin_event(jcr, bEventVolumePurged); + generate_plugin_event(jcr, bDirEventVolumePurged); /* * If the RecyclePool is defined, move the volume there */ @@ -643,6 +793,7 @@ bool mark_media_purged(UAContext *ua, MEDIA_DBR *mr) ua->error_msg("%s", db_strerror(ua->db)); } } + /* Send message to Job report, if it is a *real* job */ if (jcr && jcr->JobId > 0) { Jmsg(jcr, M_INFO, 0, _("All records pruned from Volume \"%s\"; marking it \"Purged\"\n"),