/*
Bacula® - The Network Backup Solution
- Copyright (C) 2001-2007 Free Software Foundation Europe e.V.
+ Copyright (C) 2001-2012 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.
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.
- Bacula® is a registered trademark of John Walker.
+ Bacula® is a registered trademark of Kern Sibbald.
The licensor of Bacula is the Free Software Foundation Europe
(FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
Switzerland, email:ftf@fsfeurope.org.
* Kern Sibbald, March MMI
*
- * Version $Id$
*/
#include "bacula.h"
#include "dird.h"
-static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger);
+static int const dbglvl = 50; /* debug level */
+/* Set storage id if possible */
+void set_storageid_in_mr(STORE *store, MEDIA_DBR *mr)
+{
+ if (store != NULL) {
+ mr->StorageId = store->StorageId;
+ }
+}
/*
* Items needed:
* mr.PoolId must be set
+ * mr.ScratchPoolId could be set (used if create==true)
* jcr->wstore
* jcr->db
* jcr->pool
STORE *store = jcr->wstore;
bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
- Dmsg2(150, "find_next_vol_for_append: PoolId=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
+ Dmsg3(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s\n",
+ (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType);
/*
* If we are using an Autochanger, restrict Volume
* search to the Autochanger on the first pass
/*
* 1. Look for volume with "Append" status.
*/
+ set_storageid_in_mr(store, mr); /* put StorageId in new record */
ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
if (!ok) {
- Dmsg4(050, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
+ /*
+ * No volume found, apply algorithm
+ */
+ Dmsg4(dbglvl, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
ok, index, InChanger, mr->VolStatus);
/*
* 2. Try finding a recycled volume
*/
- ok = find_recycled_volume(jcr, InChanger, mr);
- Dmsg2(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
+ ok = find_recycled_volume(jcr, InChanger, mr, store);
+ set_storageid_in_mr(store, mr); /* put StorageId in new record */
+ Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
if (!ok) {
/*
* 3. Try recycling any purged volume
*/
- ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
+ ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
+ set_storageid_in_mr(store, mr); /* put StorageId in new record */
if (!ok) {
/*
* 4. Try pruning Volumes
*/
if (prune) {
- Dmsg0(150, "Call prune_volumes\n");
- prune_volumes(jcr, InChanger, mr);
+ Dmsg0(dbglvl, "Call prune_volumes\n");
+ prune_volumes(jcr, InChanger, mr, store);
}
- ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
- if (!ok) {
- Dmsg4(050, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
+ ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
+ set_storageid_in_mr(store, mr); /* put StorageId in new record */
+ if (!ok && create) {
+ Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
ok, index, InChanger, mr->VolStatus);
/*
* 5. Try pulling a volume from the Scratch pool
*/
- ok = get_scratch_volume(jcr, mr, InChanger);
+ ok = get_scratch_volume(jcr, InChanger, mr, store);
+ set_storageid_in_mr(store, mr); /* put StorageId in new record */
+ Dmsg4(dbglvl, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
+ ok, index, InChanger, mr->VolStatus);
}
/*
* If we are using an Autochanger and have not found
* a volume, retry looking for any volume.
*/
- if (InChanger) {
+ if (!ok && InChanger) {
InChanger = false;
- if (!ok) {
- continue; /* retry again accepting any volume */
- }
+ continue; /* retry again accepting any volume */
}
}
}
/*
* 6. Try "creating" a new Volume
*/
- ok = newVolume(jcr, mr);
+ ok = newVolume(jcr, mr, store);
}
/*
* Look at more drastic ways to find an Appendable Volume
*/
if (!ok && (jcr->pool->purge_oldest_volume ||
jcr->pool->recycle_oldest_volume)) {
- Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
+ Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
/* Find oldest volume to recycle */
+ set_storageid_in_mr(store, mr); /* update storage id */
ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
- Dmsg1(400, "Find oldest=%d\n", ok);
+ set_storageid_in_mr(store, mr); /* update storageid */
+ Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok);
if (ok && prune) {
UAContext *ua;
- Dmsg0(400, "Try purge.\n");
+ Dmsg0(dbglvl, "Try purge Volume.\n");
/*
* 7. Try to purging oldest volume only if not UA calling us.
*/
free_ua_context(ua);
if (ok) {
ok = recycle_volume(jcr, mr);
- Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
+ Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok);
}
}
}
}
- Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
+ Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
if (ok) {
/* If we can use the volume, check if it is expired */
if (has_volume_expired(jcr, mr)) {
break;
} /* end for loop */
db_unlock(jcr->db);
- Dmsg1(150, "return ok=%d find_next_vol\n", ok);
+ Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok);
return ok;
}
bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
{
bool expired = false;
+ char ed1[50];
/*
* Check limits and expirations if "Append" and it has been used
* i.e. mr->VolJobs > 0
if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
/* First handle Max Volume Bytes */
if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
- Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
- "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
+ Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
+ "Marking Volume \"%s\" as Full.\n"),
+ edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
expired = true;
/* Now see if Max Jobs written to volume */
} else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
- Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
- "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
+ Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
+ "Marking Volume \"%s\" as Used.\n"),
+ edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
+ Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
+ (uint32_t)jcr->JobId, mr->VolumeName);
bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
expired = true;
/* Now see if Max Files written to volume */
} else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
- Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
- "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
+ Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
+ "Marking Volume \"%s\" as Used.\n"),
+ edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
expired = true;
utime_t now = time(NULL);
/* See if Vol Use has expired */
if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
- Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
- "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
+ Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
+ "Marking Volume \"%s\" as Used.\n"),
+ edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
expired = true;
}
}
if (expired) {
/* Need to update media */
+ Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName);
+ set_storageid_in_mr(NULL, mr);
if (!db_update_media_record(jcr, jcr->db, mr)) {
Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
mr->VolumeName, db_strerror(jcr->db));
}
}
+ Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
return expired;
}
* it now possible to reuse it for the job that it is currently
* needed for?
*/
- if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
- && mr->Recycle && jcr->pool->recycle_current_volume
+ if (!mr->Recycle) {
+ *reason = _("volume has recycling disabled");
+ return;
+ }
+ /*
+ * Check retention period from last written, but recycle to within
+ * a minute to try to catch close calls ...
+ */
+ if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL)
+ && jcr->pool->recycle_current_volume
&& (strcmp(mr->VolStatus, "Full") == 0 ||
strcmp(mr->VolStatus, "Used") == 0)) {
/*
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
+bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
+ STORE *store)
{
- MEDIA_DBR smr;
+ MEDIA_DBR smr; /* for searching scratch pool */
POOL_DBR spr, pr;
bool ok = false;
bool found = false;
- char ed1[50], ed2[50];
/* Only one thread at a time can pull from the scratch pool */
P(mutex);
/*
* Get Pool record for Scratch Pool
+ * choose between ScratchPoolId and Scratch
+ * db_get_pool_record will first try ScratchPoolId,
+ * and then try the pool named Scratch
*/
memset(&spr, 0, sizeof(spr));
bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
+ spr.PoolId = mr->ScratchPoolId;
if (db_get_pool_record(jcr, jcr->db, &spr)) {
- memset(&smr, 0, sizeof(smr));
smr.PoolId = spr.PoolId;
if (InChanger) {
smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
* recycling any existing purged volumes, then
* try to take the oldest volume.
*/
+ set_storageid_in_mr(store, &smr); /* put StorageId in new record */
if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
found = true;
- } else if (find_recycled_volume(jcr, InChanger, &smr)) {
+ } else if (find_recycled_volume(jcr, InChanger, &smr, store)) {
found = true;
- } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) {
+ } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) {
found = true;
}
* that we can add a Volume.
*/
memset(&pr, 0, sizeof(pr));
- bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
+ bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
+
if (!db_get_pool_record(jcr, jcr->db, &pr)) {
Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
db_strerror(jcr->db));
goto bail_out;
}
+
+ /* Make sure there is room for another volume */
if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
- jcr->pool->hdr.name, pr.MaxVols);
+ jcr->pool->name(), pr.MaxVols);
goto bail_out;
}
- /* OK, now move Scratch Volume */
- db_lock(jcr->db);
- Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
- edit_int64(mr->PoolId, ed1),
- edit_int64(smr.MediaId, ed2));
- ok = db_sql_query(jcr->db, query.c_str(), NULL, NULL);
- db_unlock(jcr->db);
- if (!ok) {
- Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
- db_strerror(jcr->db));
- goto bail_out;
- }
- Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
- smr.VolumeName);
- /*
- * Get *full* media record to return as db_find_next_volume does
- * not return everything .
- */
- if (!db_get_media_record(jcr, jcr->db, mr)) {
- Jmsg(jcr, M_WARNING, 0, _("Unable to get Volume record: ERR=%s"),
- db_strerror(jcr->db));
- goto bail_out;
- }
+ mr->copy(&smr);
+ set_storageid_in_mr(store, mr);
+
/* Set default parameters from current pool */
set_pool_dbr_defaults_in_media_dbr(mr, &pr);
+
/*
* set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
- * we could have Recycled media,
+ * we could have Recycled media, also, we retain the old
+ * RecyclePoolId.
*/
bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
+ mr->RecyclePoolId = smr.RecyclePoolId;
+
if (!db_update_media_record(jcr, jcr->db, mr)) {
- Jmsg(jcr, M_WARNING, 0, _("Unable to update Volume record: ERR=%s"),
+ Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
db_strerror(jcr->db));
goto bail_out;
}
+
+ Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
+ mr->VolumeName);
+
ok = true;
}
}