From b5871b08b1e7e59f83b7549ab6ee5fe5d8615aed Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Thu, 4 Feb 2010 15:02:41 +0100 Subject: [PATCH] Backport truncate on purge from 5.1.x --- bacula/src/cats/sql_find.c | 5 +- bacula/src/cats/sql_get.c | 48 ++++++++- bacula/src/dird/ua_cmds.c | 6 +- bacula/src/dird/ua_purge.c | 209 +++++++++++++++++++++++++++++++------ bacula/src/stored/dircmd.c | 55 +++------- 5 files changed, 238 insertions(+), 85 deletions(-) diff --git a/bacula/src/cats/sql_find.c b/bacula/src/cats/sql_find.c index 827f0cb00d..e492035a51 100644 --- a/bacula/src/cats/sql_find.c +++ b/bacula/src/cats/sql_find.c @@ -331,7 +331,7 @@ db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId," "Enabled,LocationId,RecycleCount,InitialWrite," - "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime " + "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge " "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full'," "'Recycle','Purged','Used','Append') AND Enabled=1 " "ORDER BY LastWritten LIMIT 1", @@ -356,7 +356,7 @@ db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," "EndFile,EndBlock,VolParts,LabelType,LabelDate,StorageId," "Enabled,LocationId,RecycleCount,InitialWrite," - "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime " + "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge " "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 " "AND VolStatus='%s' " "%s " @@ -437,6 +437,7 @@ db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr mr->RecyclePoolId = str_to_int64(row[34]); mr->VolReadTime = str_to_int64(row[35]); mr->VolWriteTime = str_to_int64(row[36]); + mr->ActionOnPurge = str_to_int64(row[37]); sql_free_result(mdb); diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index 4e34e73eb5..6582a15370 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -848,10 +848,10 @@ int db_get_num_media_records(JCR *jcr, B_DB *mdb) return stat; } - /* * This function returns a list of all the Media record ids for - * the current Pool with the correct Media Type. + * the current Pool, the correct Media Type, Recyle, Enabled, StorageId, VolBytes + * VolumeName if specified * The caller must free ids if non-NULL. * * Returns false: on failure @@ -864,12 +864,50 @@ bool db_get_media_ids(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr, int *num_ids, uint32_t uint32_t *id; char ed1[50]; bool ok = false; + char buf[MAX_NAME_LENGTH*3]; /* Can contain MAX_NAME_LENGTH*2+1 + AND ....='' */ + char esc[MAX_NAME_LENGTH*2+1]; db_lock(mdb); *ids = NULL; - Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE PoolId=%s " - " AND MediaType='%s'", - edit_int64(mr->PoolId, ed1), mr->MediaType); + + Mmsg(mdb->cmd, "SELECT DISTINCT MediaId FROM Media WHERE Recycle=%d AND Enabled=%d ", + mr->Recycle, mr->Enabled); + + if (*mr->MediaType) { + db_escape_string(jcr, mdb, esc, mr->MediaType, strlen(mr->MediaType)); + bsnprintf(buf, sizeof(buf), "AND MediaType='%s' ", esc); + pm_strcat(mdb->cmd, buf); + } + + if (mr->StorageId) { + bsnprintf(buf, sizeof(buf), "AND StorageId=%s ", edit_uint64(mr->StorageId, ed1)); + pm_strcat(mdb->cmd, buf); + } + + if (mr->PoolId) { + bsnprintf(buf, sizeof(buf), "AND PoolId=%s ", edit_uint64(mr->PoolId, ed1)); + pm_strcat(mdb->cmd, buf); + } + + if (mr->VolBytes) { + bsnprintf(buf, sizeof(buf), "AND VolBytes > %s ", edit_uint64(mr->VolBytes, ed1)); + pm_strcat(mdb->cmd, buf); + } + + if (*mr->VolumeName) { + db_escape_string(jcr, mdb, esc, mr->VolumeName, strlen(mr->VolumeName)); + bsnprintf(buf, sizeof(buf), "AND VolumeName = '%s' ", esc); + pm_strcat(mdb->cmd, buf); + } + + if (*mr->VolStatus) { + db_escape_string(jcr, mdb, esc, mr->VolStatus, strlen(mr->VolStatus)); + bsnprintf(buf, sizeof(buf), "AND VolStatus = '%s' ", esc); + pm_strcat(mdb->cmd, buf); + } + + Dmsg1(100, "q=%s\n", mdb->cmd); + if (QUERY_DB(jcr, mdb, mdb->cmd)) { *num_ids = sql_num_rows(mdb); if (*num_ids > 0) { diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 34d25595c6..6e93c0267d 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -151,13 +151,13 @@ static struct cmdstruct commands[] = { /* C { NT_("prune"), prunecmd, _("Prune expired records from catalog"), NT_("files | jobs | pool= | client= | volume= "), true}, - { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("volume="), true}, + { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume= [action= devicetype= pool= allpools storage= drive=]"), true}, { NT_("python"), python_cmd, _("Python control commands"), NT_(""), false}, { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false}, { NT_("query"), querycmd, _("Query catalog"), NT_(""), false}, { NT_("restore"), restore_cmd, _("Restore files"), NT_("where= client= storage= bootstrap=" - "\n\tjobid= done select all"), false}, + "\n\tcomment= jobid= done select all"), false}, { NT_("relabel"), relabel_cmd, _("Relabel a tape"), NT_("storage= oldvolume=\n\tvolume= pool="), false}, @@ -166,7 +166,7 @@ static struct cmdstruct commands[] = { /* C { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true}, { NT_("run"), run_cmd, _("Run a job"), NT_("job= client=\n\tfileset= level=\n\tstorage=" - "where=\n\twhen=\n\tyes"), false}, /* need to be check */ + "where=\n\twhen=\n\tcomment= yes"), false}, { NT_("status"), status_cmd, _("Report status"), NT_("all | dir= | director | client= | storage= slots | days=nnn"), true}, diff --git a/bacula/src/dird/ua_purge.c b/bacula/src/dird/ua_purge.c index c031febc12..c78f83a969 100644 --- a/bacula/src/dird/ua_purge.c +++ b/bacula/src/dird/ua_purge.c @@ -44,6 +44,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 aop_cmd(UAContext *ua, const char *cmd); static const char *select_jobsfiles_from_client = "SELECT JobId FROM Job " @@ -138,6 +139,11 @@ int purgecmd(UAContext *ua, const char *cmd) } /* Volume */ case 2: + /* Perform ActionOnPurge (action=truncate) */ + if (find_arg(ua, "action") >= 0) { + return aop_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); @@ -571,13 +577,182 @@ static BSOCK *open_sd_bsock(UAContext *ua) return ua->jcr->store_bsock; } +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; + } + + if (mr->ActionOnPurge & AOP_TRUNCATE) { + /* 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); + + 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); + + 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); + } + } +} + +/* purge action= pool= volume= storage= devicetype= */ +static int aop_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 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 && ua->argv[i]) { + 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 && ua->argv[i]) { + 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; + } + + 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 volume founds to perform %s action(s)\n"), action); + goto bail_out; + } + + if ((sd=open_sd_bsock(ua)) == NULL) { + Dmsg0(100, "Can't open connection to sd\n"); + goto bail_out; + } + + 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 +762,6 @@ bool mark_media_purged(UAContext *ua, MEDIA_DBR *mr) if (!db_update_media_record(jcr, ua->db, mr)) { return false; } - -/* Code currently disabled */ -#if 0 - 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; - } - } -#endif - pm_strcpy(jcr->VolumeName, mr->VolumeName); generate_job_event(jcr, "VolumePurged"); generate_plugin_event(jcr, bEventVolumePurged); @@ -646,6 +790,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"), diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index c9b74b000f..8cf6ac8d09 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -83,7 +83,7 @@ static bool setdebug_cmd(JCR *jcr); static bool cancel_cmd(JCR *cjcr); static bool mount_cmd(JCR *jcr); static bool unmount_cmd(JCR *jcr); -static bool action_on_purge_cmd(JCR *jcr); +//static bool action_on_purge_cmd(JCR *jcr); static bool bootstrap_cmd(JCR *jcr); static bool changer_cmd(JCR *sjcr); static bool do_label(JCR *jcr, int relabel); @@ -119,7 +119,7 @@ static struct s_cmds cmds[] = { {"status", status_cmd, 1}, {".status", qstatus_cmd, 1}, {"unmount", unmount_cmd, 0}, - {"action_on_purge", action_on_purge_cmd, 0}, +// {"action_on_purge", action_on_purge_cmd, 0}, {"use storage=", use_cmd, 0}, {"run", run_cmd, 0}, // {"query", query_cmd, 0}, @@ -875,29 +875,27 @@ static bool unmount_cmd(JCR *jcr) return true; } +#if 0 /* * The truncate command will recycle a volume. The director can call this * after purging a volume so that disk space will not be wasted. Only useful * for File Storage, of course. * + * + * It is currently disabled */ static bool action_on_purge_cmd(JCR *jcr) { + BSOCK *dir = jcr->dir_bsock; + char devname[MAX_NAME_LENGTH]; char volumename[MAX_NAME_LENGTH]; - BSOCK *dir = jcr->dir_bsock; - DEVICE *dev; - DCR *dcr; int action; - /* Currently disabled */ - dir->fsend(_("3916 Feature action_on_purge currently disabled\n")); - -#if 0 /* TODO: Need to find a free device and ask for slot to the director */ if (sscanf(dir->msg, "action_on_purge %127s vol=%127s action=%d", - devname.c_str(), volumename.c_str(), &action)!= 5) + devname, volumename, &action)!= 5) { dir->fsend(_("3916 Error scanning action_on_purge command\n")); goto done; @@ -906,45 +904,16 @@ static bool action_on_purge_cmd(JCR *jcr) unbash_spaces(devname); /* Check if action is correct */ - switch (action) { - case AOP_TRUNCTATE: - break; - default: - dir->fsend(_("3919 Bad ActionOnPurge\n")); - goto done; - } + if (action & AOP_TRUNCTATE) { - /* TODO: Ask for Volume information - * - check recycle - * - find slot - */ - - /* FIXME: autochanger, drive = 0? how can we avoid that? we only work on - * files - */ - if ((dcr = find_device(jcr, devname, 0)) == NULL) { - dir->fsend(_("3999 Device \"%s\" not found or could not be opened.\n"), devname.c_str()); - goto done; - } - - dev = dcr->dev; - - /* Store the VolumeName for opening and re-labeling the volume */ - bstrncpy(dcr->VolumeName, volumename.c_str(), sizeof(dcr->VolumeName)); - bstrncpy(dev->VolHdr.VolumeName, volumename.c_str(), sizeof(dev->VolHdr.VolumeName)); - - /* Re-write the label with the recycle flag */ - if (rewrite_volume_label(dcr, true)) { - dir->fsend(_("3917 Volume recycled\n")); - } else { - dir->fsend(_("3918 Recycle failed\n")); - } -#endif + } + /* ... */ done: dir->signal(BNET_EOD); return true; } +#endif /* * Release command from Director. This rewinds the device and if -- 2.39.5