3 * Bacula Director -- next_vol -- handles finding the next
4 * volume for append. Split out of catreq.c August MMIII
5 * catalog request from the Storage daemon.
7 * Kern Sibbald, March MMI
12 Bacula® - The Network Backup Solution
14 Copyright (C) 2001-2006 Free Software Foundation Europe e.V.
16 The main author of Bacula is Kern Sibbald, with contributions from
17 many others, a complete list can be found in the file AUTHORS.
18 This program is Free Software; you can redistribute it and/or
19 modify it under the terms of version two of the GNU General Public
20 License as published by the Free Software Foundation plus additions
21 that are listed in the file LICENSE.
23 This program is distributed in the hope that it will be useful, but
24 WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, write to the Free Software
30 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33 Bacula® is a registered trademark of John Walker.
34 The licensor of Bacula is the Free Software Foundation Europe
35 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
36 Switzerland, email:ftf@fsfeurope.org.
42 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger);
47 * mr.PoolId must be set
51 * MEDIA_DBR mr (zeroed out)
52 * create -- whether or not to create a new volume
54 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
59 STORE *store = jcr->wstore;
61 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
62 Dmsg2(100, "CatReq FindMedia: PoolId=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
64 * If we are using an Autochanger, restrict Volume
65 * search to the Autochanger on the first pass
67 InChanger = store->autochanger;
69 * Find the Next Volume for Append
73 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
75 * 1. Look for volume with "Append" status.
77 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
78 Dmsg4(100, "after find_next_vol index=%d ok=%d InChanger=%d Vstat=%s\n",
79 index, ok, InChanger, mr->VolStatus);
83 * 2. Try finding a recycled volume
85 ok = find_recycled_volume(jcr, InChanger, mr);
86 Dmsg2(100, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
89 * 3. Try recycling any purged volume
91 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
94 * 4. Try pruning Volumes
97 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
100 * 5. Try pulling a volume from the Scratch pool
102 ok = get_scratch_volume(jcr, mr, InChanger);
105 * If we are using an Autochanger and have not found
106 * a volume, retry looking for any volume.
111 continue; /* retry again accepting any volume */
120 * 6. Try "creating" a new Volume
122 ok = newVolume(jcr, mr);
125 * Look at more drastic ways to find an Appendable Volume
127 if (!ok && (jcr->pool->purge_oldest_volume ||
128 jcr->pool->recycle_oldest_volume)) {
129 Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
130 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
131 /* Find oldest volume to recycle */
132 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
133 Dmsg1(400, "Find oldest=%d\n", ok);
136 Dmsg0(400, "Try purge.\n");
138 * 7. Try to purging oldest volume only if not UA calling us.
140 ua = new_ua_context(jcr);
141 if (jcr->pool->purge_oldest_volume && create) {
142 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
143 ok = purge_jobs_from_volume(ua, mr);
145 * 8. or try recycling the oldest volume
147 } else if (jcr->pool->recycle_oldest_volume) {
148 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
149 ok = prune_volume(ua, mr);
153 ok = recycle_volume(jcr, mr);
154 Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
159 Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
161 /* If we can use the volume, check if it is expired */
162 if (has_volume_expired(jcr, mr)) {
163 if (retry++ < 200) { /* sanity check */
164 continue; /* try again from the top */
166 Jmsg(jcr, M_ERROR, 0, _(
167 "We seem to be looping trying to find the next volume. I give up.\n"));
178 * Check if any time limits or use limits have expired
179 * if so, set the VolStatus appropriately.
181 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
183 bool expired = false;
185 * Check limits and expirations if "Append" and it has been used
186 * i.e. mr->VolJobs > 0
189 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
190 /* First handle Max Volume Bytes */
191 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
192 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
193 "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
194 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
197 /* Now see if Volume should only be used once */
198 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
199 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
200 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
201 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
204 /* Now see if Max Jobs written to volume */
205 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
206 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
207 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
208 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
211 /* Now see if Max Files written to volume */
212 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
213 Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
214 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
215 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
218 /* Finally, check Use duration expiration */
219 } else if (mr->VolUseDuration > 0) {
220 utime_t now = time(NULL);
221 /* See if Vol Use has expired */
222 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
223 Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
224 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
225 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
231 /* Need to update media */
232 if (!db_update_media_record(jcr, jcr->db, mr)) {
233 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
234 mr->VolumeName, db_strerror(jcr->db));
241 * Try hard to recycle the current volume
243 * Returns: on failure - reason = NULL
244 * on success - reason - pointer to reason
246 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
252 /* Check if a duration or limit has expired */
253 if (has_volume_expired(jcr, mr)) {
254 *reason = _("volume has expired");
255 /* Keep going because we may be able to recycle volume */
259 * Now see if we can use the volume as is
261 if (strcmp(mr->VolStatus, "Append") == 0 ||
262 strcmp(mr->VolStatus, "Recycle") == 0) {
268 * Check if the Volume is already marked for recycling
270 if (strcmp(mr->VolStatus, "Purged") == 0) {
271 if (recycle_volume(jcr, mr)) {
272 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
276 /* In principle this shouldn't happen */
277 *reason = _("and recycling of current volume failed");
282 /* At this point, the volume is not valid for writing */
283 *reason = _("but should be Append, Purged or Recycle");
286 * What we're trying to do here is see if the current volume is
287 * "recyclable" - ie. if we prune all expired jobs off it, is
288 * it now possible to reuse it for the job that it is currently
291 if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
292 && mr->Recycle && jcr->pool->recycle_current_volume
293 && (strcmp(mr->VolStatus, "Full") == 0 ||
294 strcmp(mr->VolStatus, "Used") == 0)) {
296 * Attempt prune of current volume to see if we can
297 * recycle it for use.
301 ua = new_ua_context(jcr);
302 ok = prune_volume(ua, mr);
306 /* If fully purged, recycle current volume */
307 if (recycle_volume(jcr, mr)) {
308 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
311 *reason = _("but should be Append, Purged or Recycle (recycling of the "
312 "current volume failed)");
315 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
316 "recycle current volume, as it still contains unpruned data "
317 "or the Volume Retention time has not expired.)");
322 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
324 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
330 char ed1[50], ed2[50];
332 /* Only one thread at a time can pull from the scratch pool */
335 * Get Pool record for Scratch Pool
337 memset(&spr, 0, sizeof(spr));
338 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
339 if (db_get_pool_record(jcr, jcr->db, &spr)) {
340 memset(&smr, 0, sizeof(smr));
341 smr.PoolId = spr.PoolId;
343 smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
345 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
346 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
349 * If we do not find a valid Scratch volume, try
350 * recycling any existing purged volumes, then
351 * try to take the oldest volume.
353 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
356 } else if (find_recycled_volume(jcr, InChanger, &smr)) {
359 } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) {
364 POOL_MEM query(PM_MESSAGE);
367 * Get pool record where the Scratch Volume will go to ensure
368 * that we can add a Volume.
370 memset(&pr, 0, sizeof(pr));
371 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
372 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
373 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
374 db_strerror(jcr->db));
377 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
378 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
379 jcr->pool->hdr.name, pr.MaxVols);
383 /* OK, now move Scratch Volume */
385 Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
386 edit_int64(mr->PoolId, ed1),
387 edit_int64(smr.MediaId, ed2));
388 ok = db_sql_query(jcr->db, query.c_str(), NULL, NULL);
391 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
392 db_strerror(jcr->db));
395 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
397 /* Set new Pool Id in smr record, then copy it to mr */
398 smr.PoolId = mr->PoolId;
399 memcpy(mr, &smr, sizeof(MEDIA_DBR));
400 /* Set default parameters from current pool */
401 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
402 /* set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
403 * we could have Recycled media */
404 bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
405 if (!db_update_media_record(jcr, jcr->db, mr)) {
406 Jmsg(jcr, M_WARNING, 0, _("Unable to update Volume record: ERR=%s"),
407 db_strerror(jcr->db));