#define B_ANSI_LABEL 1
#define B_IBM_LABEL 2
+/*
+ * Actions on purge
+ */
+#define AOP_TRUNCATE 1
+
/* Size of File Address stored in STREAM_SPARSE_DATA. Do NOT change! */
#define SPARSE_FADDR_SIZE (sizeof(uint64_t))
int32_t AcceptAnyVolume; /* set to accept any volume sequence */
int32_t AutoPrune; /* set to prune automatically */
int32_t Recycle; /* default Vol recycle flag */
+ uint32_t ActionOnPurge; /* action on purge, e.g. truncate the disk volume */
utime_t VolRetention; /* retention period in seconds */
utime_t VolUseDuration; /* time in secs volume can be used */
uint32_t MaxVolJobs; /* Max Jobs on Volume */
uint64_t VolWriteTime; /* time spent writing volume */
utime_t VolRetention; /* Volume retention in seconds */
utime_t VolUseDuration; /* time in secs volume can be used */
+ uint32_t ActionOnPurge; /* action on purge, e.g. truncate the disk volume */
uint32_t MaxVolJobs; /* Max Jobs on Volume */
uint32_t MaxVolFiles; /* Max files on Volume */
int32_t Recycle; /* recycle yes/no */
Mmsg(mdb->cmd,
"SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
"AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
-"MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId FROM Pool WHERE Pool.PoolId=%s",
+"MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
+"ActionOnPurge FROM Pool WHERE Pool.PoolId=%s",
edit_int64(pdbr->PoolId, ed1));
} else { /* find by name */
Mmsg(mdb->cmd,
"SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,AcceptAnyVolume,"
"AutoPrune,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
-"MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId FROM Pool WHERE Pool.Name='%s'",
+"MaxVolBytes,PoolType,LabelType,LabelFormat,RecyclePoolId,ScratchPoolId,"
+"ActionOnPurge FROM Pool WHERE Pool.Name='%s'",
pdbr->Name);
}
if (QUERY_DB(jcr, mdb, mdb->cmd)) {
bstrncpy(pdbr->LabelFormat, row[16]!=NULL?row[16]:"", sizeof(pdbr->LabelFormat));
pdbr->RecyclePoolId = str_to_int64(row[17]);
pdbr->ScratchPoolId = str_to_int64(row[18]);
+ pdbr->ActionOnPurge = str_to_int32(row[19]);
ok = true;
}
}
"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 MediaId=%s",
edit_int64(mr->MediaId, ed1));
} else { /* find by name */
"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 VolumeName='%s'", mr->VolumeName);
}
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_int32(row[37]);
ok = true;
}
"AcceptAnyVolume=%d,VolRetention='%s',VolUseDuration='%s',"
"MaxVolJobs=%u,MaxVolFiles=%u,MaxVolBytes=%s,Recycle=%d,"
"AutoPrune=%d,LabelType=%d,LabelFormat='%s',RecyclePoolId=%s,"
-"ScratchPoolId=%s WHERE PoolId=%s",
+"ScratchPoolId=%s,ActionOnPurge=%d WHERE PoolId=%s",
pr->NumVols, pr->MaxVols, pr->UseOnce, pr->UseCatalog,
pr->AcceptAnyVolume, edit_uint64(pr->VolRetention, ed1),
edit_uint64(pr->VolUseDuration, ed2),
edit_uint64(pr->MaxVolBytes, ed3),
pr->Recycle, pr->AutoPrune, pr->LabelType,
pr->LabelFormat, edit_int64(pr->RecyclePoolId,ed5),
- edit_int64(pr->ScratchPoolId,ed6),ed4);
+ edit_int64(pr->ScratchPoolId,ed6),
+ pr->ActionOnPurge,
+ ed4);
stat = UPDATE_DB(jcr, mdb, mdb->cmd);
db_unlock(mdb);
return stat;
"Slot=%d,InChanger=%d,VolReadTime=%s,VolWriteTime=%s,VolParts=%d,"
"LabelType=%d,StorageId=%s,PoolId=%s,VolRetention=%s,VolUseDuration=%s,"
"MaxVolJobs=%d,MaxVolFiles=%d,Enabled=%d,LocationId=%s,"
- "ScratchPoolId=%s,RecyclePoolId=%s,RecycleCount=%d,Recycle=%d"
+ "ScratchPoolId=%s,RecyclePoolId=%s,RecycleCount=%d,Recycle=%d,ActionOnPurge=%d"
" WHERE VolumeName='%s'",
mr->VolJobs, mr->VolFiles, mr->VolBlocks, edit_uint64(mr->VolBytes, ed1),
mr->VolMounts, mr->VolErrors, mr->VolWrites,
mr->Enabled, edit_uint64(mr->LocationId, ed9),
edit_uint64(mr->ScratchPoolId, ed10),
edit_uint64(mr->RecyclePoolId, ed11),
- mr->RecycleCount,mr->Recycle,
+ mr->RecycleCount,mr->Recycle, mr->ActionOnPurge,
mr->VolumeName);
Dmsg1(400, "%s\n", mdb->cmd);
void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
+static void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass);
static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
{"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
{"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
{"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
+ {"actiononpurge", store_actiononpurge, ITEM(res_pool.action_on_purge), 0, 0, 0},
{"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
{"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
{"maximumvolumes", store_pint32, ITEM(res_pool.max_volumes), 0, 0, 0},
NPRT(res->res_pool.label_format));
sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
- sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
+ sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d ActionOnPurge=%d\n"),
res->res_pool.recycle_oldest_volume,
- res->res_pool.purge_oldest_volume);
+ res->res_pool.purge_oldest_volume,
+ res->res_pool.action_on_purge);
sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
res->res_pool.MaxVolJobs,
res->res_pool.MaxVolFiles,
}
}
+static void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass)
+{
+ uint32_t *destination = (uint32_t*)item->value;
+ lex_get_token(lc, T_NAME);
+ printf("got token %s\n", lc->str);
+ if (strcasecmp(lc->str, "truncate") == 0)
+ *destination = (*destination) | AOP_TRUNCATE;
+ else {
+ scan_err2(lc, _("Expected one of: %s, got: %s"), "Truncate", lc->str);
+ return;
+ }
+ scan_to_eol(lc);
+ set_bit(index, res_all.hdr.item_present);
+}
+
/*
* Store Device. Note, the resource is created upon the
* first reference. The details of the resource are obtained
bool recycle_current_volume; /* attempt recycle of current volume */
bool AutoPrune; /* default for pool auto prune */
bool Recycle; /* default for media recycle yes/no */
+ uint32_t action_on_purge; /* action on purge, e.g. truncate the disk volume */
POOL *RecyclePool; /* RecyclePool destination when media is purged */
POOL *ScratchPool; /* ScratchPool source when requesting media */
alist *CopyPool; /* List of copy pools */
mr->Recycle = pr->Recycle;
mr->VolRetention = pr->VolRetention;
mr->VolUseDuration = pr->VolUseDuration;
+ mr->ActionOnPurge = pr->ActionOnPurge;
mr->RecyclePoolId = pr->RecyclePoolId;
mr->MaxVolJobs = pr->MaxVolJobs;
mr->MaxVolFiles = pr->MaxVolFiles;
pr->MaxVolFiles = pool->MaxVolFiles;
pr->MaxVolBytes = pool->MaxVolBytes;
pr->AutoPrune = pool->AutoPrune;
+ pr->ActionOnPurge = pool->action_on_purge;
pr->Recycle = pool->Recycle;
if (pool->label_format) {
bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
return purged;
}
+static BSOCK *open_sd_bsock(UAContext *ua)
+{
+ STORE *store = ua->jcr->wstore;
+
+ if (!ua->jcr->store_bsock) {
+ ua->send_msg(_("Connecting to Storage daemon %s at %s:%d ...\n"),
+ store->name(), store->address, store->SDport);
+ if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
+ ua->error_msg(_("Failed to connect to Storage daemon.\n"));
+ return NULL;
+ }
+ }
+ return ua->jcr->store_bsock;
+}
+
/*
* IF volume status is Append, Full, Used, or Error, mark it Purged
* Purged volumes can then be recycled (if enabled).
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) {
+ sd->fsend("action_on_purge %s vol=%s action=%d",
+ ua->jcr->wstore->dev_name(),
+ mr->VolumeName,
+ mr->ActionOnPurge);
+
+ 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);
}
}
-
+static void update_vol_actiononpurge(UAContext *ua, char *val, MEDIA_DBR *mr)
+{
+ if (strcasecmp(val, "truncate") == 0)
+ mr->ActionOnPurge = AOP_TRUNCATE;
+ else mr->ActionOnPurge = 0;
+
+ if (!db_update_media_record(ua->jcr, ua->db, mr)) {
+ ua->error_msg(_("Error updating media record ActionOnPurge: ERR=%s"), db_strerror(ua->db));
+ } else {
+ ua->info_msg(_("New ActionOnPurge is: %d\n"), mr->ActionOnPurge);
+ }
+}
/*
* Update a media record -- allows you to change the
NT_("AllFromPool"), /* 11 !!! see below !!! */
NT_("Enabled"), /* 12 */
NT_("RecyclePool"), /* 13 */
+ NT_("ActionOnPurge"), /* 14 */
NULL };
#define AllFromPool 11 /* keep this updated with above */
case 13:
update_vol_recyclepool(ua, ua->argv[j], &mr);
break;
+ case 14:
+ update_vol_actiononpurge(ua, ua->argv[j], &mr);
+ break;
}
done = true;
}
add_prompt(ua, _("All Volumes from all Pools")); /* 13 */
add_prompt(ua, _("Enabled")), /* 14 */
add_prompt(ua, _("RecyclePool")), /* 15 */
- add_prompt(ua, _("Done")); /* 16 */
+ add_prompt(ua, _("Action On Purge")), /* 16 */
+ add_prompt(ua, _("Done")); /* 17 */
i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);
/* For All Volumes, All Volumes from Pool, and Done, we don't need
* a Volume record */
- if ( i != 12 && i != 13 && i != 16) {
+ if ( i != 12 && i != 13 && i != 17) {
if (!select_media_dbr(ua, &mr)) { /* Get Volume record */
return 0;
}
update_vol_recyclepool(ua, pr.Name, &mr);
return 1;
+ case 16:
+ ua->info_msg(_("Current ActionOnPurge is: %d\n"), mr.ActionOnPurge);
+ if (!get_cmd(ua, _("Enter new ActionOnPurge: (one of: Truncate, None) "))) {
+ return 0;
+ }
+
+ update_vol_actiononpurge(ua, ua->cmd, &mr);
+ break;
+
default: /* Done or error */
ua->info_msg(_("Selection terminated.\n"));
return 1;
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 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},
{"use storage=", use_cmd, 0},
{"run", run_cmd, 0},
// {"query", query_cmd, 0},
return true;
}
+/*
+ * 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.
+ *
+ */
+static bool action_on_purge_cmd(JCR *jcr)
+{
+ POOL_MEM devname;
+ POOL_MEM volumename;
+ BSOCK *dir = jcr->dir_bsock;
+ DEVICE *dev;
+ DCR *dcr;
+ int drive;
+ int action;
+
+ if (sscanf(dir->msg, "action_on_purge %127s vol=%s action=%d",
+ devname.c_str(), volumename.c_str(), &action) != 3) {
+ dir->fsend(_("3916 Error scanning action_on_purge command\n"));
+ goto done;
+ }
+
+ /* 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"));
+
+done:
+ dir->signal(BNET_EOD);
+ return true;
+}
+
/*
* Release command from Director. This rewinds the device and if
* configured does a offline and ensures that Bacula will