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 Copyright (C) 2001-2006 Kern Sibbald
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 version 2 as amended with additional clauses defined in the
17 file LICENSE in the main source directory.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 the file LICENSE for additional details.
29 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger);
34 * mr.PoolId must be set
38 * MEDIA_DBR mr (zeroed out)
39 * create -- whether or not to create a new volume
41 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
46 STORE *store = jcr->store;
48 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
49 Dmsg2(100, "CatReq FindMedia: PoolId=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
51 * If we are using an Autochanger, restrict Volume
52 * search to the Autochanger on the first pass
54 InChanger = store->autochanger;
56 * Find the Next Volume for Append
60 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
62 * 1. Look for volume with "Append" status.
64 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
65 Dmsg2(100, "catreq after find_next_vol ok=%d FW=%d\n", ok, mr->FirstWritten);
68 * 2. Try finding a recycled volume
70 ok = find_recycled_volume(jcr, InChanger, mr);
71 Dmsg2(100, "find_recycled_volume %d FW=%d\n", ok, mr->FirstWritten);
74 * 3. Try recycling any purged volume
76 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
79 * 4. Try pruning Volumes
82 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
86 continue; /* retry again accepting any volume */
94 * 5. Try pulling a volume from the Scratch pool
96 ok = get_scratch_volume(jcr, mr, InChanger);
101 * 6. Try "creating" a new Volume
103 ok = newVolume(jcr, mr);
106 * Look at more drastic ways to find an Appendable Volume
108 if (!ok && (jcr->pool->purge_oldest_volume ||
109 jcr->pool->recycle_oldest_volume)) {
110 Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
111 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
112 /* Find oldest volume to recycle */
113 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
114 Dmsg1(400, "Find oldest=%d\n", ok);
117 Dmsg0(400, "Try purge.\n");
119 * 7. Try to purging oldest volume only if not UA calling us.
121 ua = new_ua_context(jcr);
122 if (jcr->pool->purge_oldest_volume && create) {
123 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
124 ok = purge_jobs_from_volume(ua, mr);
126 * 8. or try recycling the oldest volume
128 } else if (jcr->pool->recycle_oldest_volume) {
129 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
130 ok = prune_volume(ua, mr);
134 ok = recycle_volume(jcr, mr);
135 Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
140 Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
142 /* If we can use the volume, check if it is expired */
143 if (has_volume_expired(jcr, mr)) {
144 if (retry++ < 200) { /* sanity check */
145 continue; /* try again from the top */
147 Jmsg(jcr, M_ERROR, 0, _(
148 "We seem to be looping trying to find the next volume. I give up.\n"));
159 * Check if any time limits or use limits have expired
160 * if so, set the VolStatus appropriately.
162 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
164 bool expired = false;
166 * Check limits and expirations if "Append" and it has been used
167 * i.e. mr->VolJobs > 0
170 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
171 /* First handle Max Volume Bytes */
172 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
173 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
174 "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
175 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
178 /* Now see if Volume should only be used once */
179 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
180 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
181 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
182 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
185 /* Now see if Max Jobs written to volume */
186 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
187 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
188 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
189 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
192 /* Now see if Max Files written to volume */
193 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
194 Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
195 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
196 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
199 /* Finally, check Use duration expiration */
200 } else if (mr->VolUseDuration > 0) {
201 utime_t now = time(NULL);
202 /* See if Vol Use has expired */
203 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
204 Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
205 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
206 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
212 /* Need to update media */
213 if (!db_update_media_record(jcr, jcr->db, mr)) {
214 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
215 mr->VolumeName, db_strerror(jcr->db));
222 * Try hard to recycle the current volume
224 * Returns: on failure - reason = NULL
225 * on success - reason - pointer to reason
227 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
233 /* Check if a duration or limit has expired */
234 if (has_volume_expired(jcr, mr)) {
235 *reason = _("volume has expired");
236 /* Keep going because we may be able to recycle volume */
240 * Now see if we can use the volume as is
242 if (strcmp(mr->VolStatus, "Append") == 0 ||
243 strcmp(mr->VolStatus, "Recycle") == 0) {
249 * Check if the Volume is already marked for recycling
251 if (strcmp(mr->VolStatus, "Purged") == 0) {
252 if (recycle_volume(jcr, mr)) {
253 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
257 /* In principle this shouldn't happen */
258 *reason = _("and recycling of current volume failed");
263 /* At this point, the volume is not valid for writing */
264 *reason = _("but should be Append, Purged or Recycle");
267 * What we're trying to do here is see if the current volume is
268 * "recyclable" - ie. if we prune all expired jobs off it, is
269 * it now possible to reuse it for the job that it is currently
272 if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
273 && mr->Recycle && jcr->pool->recycle_current_volume
274 && (strcmp(mr->VolStatus, "Full") == 0 ||
275 strcmp(mr->VolStatus, "Used") == 0)) {
277 * Attempt prune of current volume to see if we can
278 * recycle it for use.
282 ua = new_ua_context(jcr);
283 ok = prune_volume(ua, mr);
287 /* If fully purged, recycle current volume */
288 if (recycle_volume(jcr, mr)) {
289 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
292 *reason = _("but should be Append, Purged or Recycle (recycling of the "
293 "current volume failed)");
296 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
297 "recycle current volume, as it still contains unpruned data)");
302 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
304 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
309 char ed1[50], ed2[50];
311 /* Only one thread at a time can pull from the scratch pool */
314 * Get pool record where the Scratch Volume will go
316 memset(&pr, 0, sizeof(pr));
317 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
318 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
319 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
320 db_strerror(jcr->db));
323 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
324 Jmsg(jcr, M_WARNING, 0, _("Unable to use Scratch Volume, Pool full MaxVols=%d\n"),
329 * Get Pool record for Scratch Pool
331 memset(&spr, 0, sizeof(spr));
332 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
333 if (db_get_pool_record(jcr, jcr->db, &spr)) {
334 memset(&smr, 0, sizeof(smr));
335 smr.PoolId = spr.PoolId;
336 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
337 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
338 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
339 POOL_MEM query(PM_MESSAGE);
341 Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
342 edit_int64(mr->PoolId, ed1),
343 edit_int64(smr.MediaId, ed2));
344 ok = db_sql_query(jcr->db, query.c_str(), NULL, NULL);
347 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
348 db_strerror(jcr->db));
351 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
353 /* Set new Pool Id in smr record, then copy it to mr */
354 smr.PoolId = mr->PoolId;
355 memcpy(mr, &smr, sizeof(MEDIA_DBR));
356 /* Set default parameters from current pool */
357 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
358 if (!db_update_media_record(jcr, jcr->db, mr)) {
359 Jmsg(jcr, M_WARNING, 0, _("Unable to update Volume record: ERR=%s"),
360 db_strerror(jcr->db));