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->wstore;
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 Dmsg4(100, "after find_next_vol index=%d ok=%d InChanger=%d Vstat=%s\n",
66 index, ok, InChanger, mr->VolStatus);
70 * 2. Try finding a recycled volume
72 ok = find_recycled_volume(jcr, InChanger, mr);
73 Dmsg2(100, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
76 * 3. Try recycling any purged volume
78 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
81 * 4. Try pruning Volumes
84 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
87 * 5. Try pulling a volume from the Scratch pool
89 ok = get_scratch_volume(jcr, mr, InChanger);
92 * If we are using an Autochanger and have not found
93 * a volume, retry looking for any volume.
98 continue; /* retry again accepting any volume */
107 * 6. Try "creating" a new Volume
109 ok = newVolume(jcr, mr);
112 * Look at more drastic ways to find an Appendable Volume
114 if (!ok && (jcr->pool->purge_oldest_volume ||
115 jcr->pool->recycle_oldest_volume)) {
116 Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
117 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
118 /* Find oldest volume to recycle */
119 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
120 Dmsg1(400, "Find oldest=%d\n", ok);
123 Dmsg0(400, "Try purge.\n");
125 * 7. Try to purging oldest volume only if not UA calling us.
127 ua = new_ua_context(jcr);
128 if (jcr->pool->purge_oldest_volume && create) {
129 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
130 ok = purge_jobs_from_volume(ua, mr);
132 * 8. or try recycling the oldest volume
134 } else if (jcr->pool->recycle_oldest_volume) {
135 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
136 ok = prune_volume(ua, mr);
140 ok = recycle_volume(jcr, mr);
141 Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
146 Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
148 /* If we can use the volume, check if it is expired */
149 if (has_volume_expired(jcr, mr)) {
150 if (retry++ < 200) { /* sanity check */
151 continue; /* try again from the top */
153 Jmsg(jcr, M_ERROR, 0, _(
154 "We seem to be looping trying to find the next volume. I give up.\n"));
165 * Check if any time limits or use limits have expired
166 * if so, set the VolStatus appropriately.
168 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
170 bool expired = false;
172 * Check limits and expirations if "Append" and it has been used
173 * i.e. mr->VolJobs > 0
176 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
177 /* First handle Max Volume Bytes */
178 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
179 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
180 "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
181 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
184 /* Now see if Volume should only be used once */
185 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
186 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
187 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
188 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
191 /* Now see if Max Jobs written to volume */
192 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
193 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
194 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
195 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
198 /* Now see if Max Files written to volume */
199 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
200 Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
201 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
202 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
205 /* Finally, check Use duration expiration */
206 } else if (mr->VolUseDuration > 0) {
207 utime_t now = time(NULL);
208 /* See if Vol Use has expired */
209 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
210 Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
211 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
212 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
218 /* Need to update media */
219 if (!db_update_media_record(jcr, jcr->db, mr)) {
220 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
221 mr->VolumeName, db_strerror(jcr->db));
228 * Try hard to recycle the current volume
230 * Returns: on failure - reason = NULL
231 * on success - reason - pointer to reason
233 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
239 /* Check if a duration or limit has expired */
240 if (has_volume_expired(jcr, mr)) {
241 *reason = _("volume has expired");
242 /* Keep going because we may be able to recycle volume */
246 * Now see if we can use the volume as is
248 if (strcmp(mr->VolStatus, "Append") == 0 ||
249 strcmp(mr->VolStatus, "Recycle") == 0) {
255 * Check if the Volume is already marked for recycling
257 if (strcmp(mr->VolStatus, "Purged") == 0) {
258 if (recycle_volume(jcr, mr)) {
259 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
263 /* In principle this shouldn't happen */
264 *reason = _("and recycling of current volume failed");
269 /* At this point, the volume is not valid for writing */
270 *reason = _("but should be Append, Purged or Recycle");
273 * What we're trying to do here is see if the current volume is
274 * "recyclable" - ie. if we prune all expired jobs off it, is
275 * it now possible to reuse it for the job that it is currently
278 if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
279 && mr->Recycle && jcr->pool->recycle_current_volume
280 && (strcmp(mr->VolStatus, "Full") == 0 ||
281 strcmp(mr->VolStatus, "Used") == 0)) {
283 * Attempt prune of current volume to see if we can
284 * recycle it for use.
288 ua = new_ua_context(jcr);
289 ok = prune_volume(ua, mr);
293 /* If fully purged, recycle current volume */
294 if (recycle_volume(jcr, mr)) {
295 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
298 *reason = _("but should be Append, Purged or Recycle (recycling of the "
299 "current volume failed)");
302 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
303 "recycle current volume, as it still contains unpruned data "
304 "or the Volume Retention time has not expired.)");
309 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
311 static bool get_scratch_volume(JCR *jcr, MEDIA_DBR *mr, bool InChanger)
316 char ed1[50], ed2[50];
318 /* Only one thread at a time can pull from the scratch pool */
321 * Get Pool record for Scratch Pool
323 memset(&spr, 0, sizeof(spr));
324 bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
325 if (db_get_pool_record(jcr, jcr->db, &spr)) {
326 memset(&smr, 0, sizeof(smr));
327 smr.PoolId = spr.PoolId;
329 smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */
331 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
332 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
333 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
334 POOL_MEM query(PM_MESSAGE);
337 * Get pool record where the Scratch Volume will go to ensure
338 * that we can add a Volume.
340 memset(&pr, 0, sizeof(pr));
341 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
342 if (!db_get_pool_record(jcr, jcr->db, &pr)) {
343 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
344 db_strerror(jcr->db));
347 if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
348 Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
349 jcr->pool->hdr.name, pr.MaxVols);
353 /* OK, now move Scratch Volume */
355 Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
356 edit_int64(mr->PoolId, ed1),
357 edit_int64(smr.MediaId, ed2));
358 ok = db_sql_query(jcr->db, query.c_str(), NULL, NULL);
361 Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
362 db_strerror(jcr->db));
365 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
367 /* Set new Pool Id in smr record, then copy it to mr */
368 smr.PoolId = mr->PoolId;
369 memcpy(mr, &smr, sizeof(MEDIA_DBR));
370 /* Set default parameters from current pool */
371 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
372 if (!db_update_media_record(jcr, jcr->db, mr)) {
373 Jmsg(jcr, M_WARNING, 0, _("Unable to update Volume record: ERR=%s"),
374 db_strerror(jcr->db));