"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",
"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 "
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);
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
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) {
{ NT_("prune"), prunecmd, _("Prune expired records from catalog"),
NT_("files | jobs | pool=<pool> | client=<client-name> | volume=<volume-name> "), true},
- { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("volume=<vol>"), true},
+ { NT_("purge"), purgecmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [action=<action> devicetype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), 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=</path> client=<client> storage=<storage> bootstrap=<file>"
- "\n\tjobid=<jobid> done select all"), false},
+ "\n\tcomment=<text> jobid=<jobid> done select all"), false},
{ NT_("relabel"), relabel_cmd, _("Relabel a tape"),
NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
{ NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
{ NT_("run"), run_cmd, _("Run a job"),
NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
- "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tyes"), false}, /* need to be check */
+ "where=<directory-prefix>\n\twhen=<universal-time-specification>\n\tcomment=<text> yes"), false},
{ NT_("status"), status_cmd, _("Report status"),
NT_("all | dir=<dir-name> | director | client=<client-name> | storage=<storage-name> slots | days=nnn"), true},
/* 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 "
}
/* 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);
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; i<ua->argc; 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 ||
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);
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"),
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);
{"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},
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;
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