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-2005 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.
31 * mr.PoolId must be set
35 * MEDIA_DBR mr (zeroed out)
36 * create -- whether or not to create a new volume
38 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
43 STORE *store = jcr->store;
45 bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
46 Dmsg2(100, "CatReq FindMedia: Id=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType);
48 * If we are using an Autochanger, restrict Volume
49 * search to the Autochanger on the first pass
51 InChanger = store->autochanger;
53 * Find the Next Volume for Append
57 bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */
59 * 1. Look for volume with "Append" status.
61 ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
62 Dmsg2(100, "catreq after find_next_vol ok=%d FW=%d\n", ok, mr->FirstWritten);
65 * 2. Try finding a recycled volume
67 ok = find_recycled_volume(jcr, InChanger, mr);
68 Dmsg2(100, "find_recycled_volume %d FW=%d\n", ok, mr->FirstWritten);
71 * 3. Try recycling any purged volume
73 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
76 * 4. Try pruning Volumes
79 ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
83 continue; /* retry again accepting any volume */
93 char ed1[50], ed2[50];
95 * 5. Try pulling a volume from the Scratch pool
97 memset(&pr, 0, sizeof(pr));
98 bstrncpy(pr.Name, "Scratch", sizeof(pr.Name));
99 if (db_get_pool_record(jcr, jcr->db, &pr)) {
100 memset(&smr, 0, sizeof(smr));
101 smr.PoolId = pr.PoolId;
102 bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */
103 bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
104 if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
105 query = get_pool_memory(PM_MESSAGE);
107 Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s",
108 edit_int64(mr->PoolId, ed1),
109 edit_int64(smr.MediaId, ed2));
110 ok = db_sql_query(jcr->db, query, NULL, NULL);
112 Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
114 /* Set new Pool Id in smr record, then copy it to mr */
115 smr.PoolId = mr->PoolId;
116 memcpy(mr, &smr, sizeof(MEDIA_DBR));
117 memset(&pr, 0, sizeof(pr));
118 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
119 /* Set default parameters from current pool */
120 if (db_get_pool_record(jcr, jcr->db, &pr)) {
121 set_pool_dbr_defaults_in_media_dbr(mr, &pr);
122 if (!db_update_media_record(jcr, jcr->db, mr)) {
123 Jmsg(jcr, M_WARNING, 0, _("Unable to update Volume record: ERR=%s"),
124 db_strerror(jcr->db));
127 Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
128 db_strerror(jcr->db));
136 * 6. Try "creating" a new Volume
138 ok = newVolume(jcr, mr);
141 * Look at more drastic ways to find an Appendable Volume
143 if (!ok && (jcr->pool->purge_oldest_volume ||
144 jcr->pool->recycle_oldest_volume)) {
145 Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
146 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
147 /* Find oldest volume to recycle */
148 ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
149 Dmsg1(400, "Find oldest=%d\n", ok);
152 Dmsg0(400, "Try purge.\n");
154 * 7. Try to purging oldest volume only if not UA calling us.
156 ua = new_ua_context(jcr);
157 if (jcr->pool->purge_oldest_volume && create) {
158 Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
159 ok = purge_jobs_from_volume(ua, mr);
161 * 8. or try recycling the oldest volume
163 } else if (jcr->pool->recycle_oldest_volume) {
164 Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
165 ok = prune_volume(ua, mr);
169 ok = recycle_volume(jcr, mr);
170 Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
175 Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
177 /* If we can use the volume, check if it is expired */
178 if (has_volume_expired(jcr, mr)) {
179 if (retry++ < 200) { /* sanity check */
180 continue; /* try again from the top */
182 Jmsg(jcr, M_ERROR, 0, _(
183 "We seem to be looping trying to find the next volume. I give up.\n"));
194 * Check if any time limits or use limits have expired
195 * if so, set the VolStatus appropriately.
197 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
199 bool expired = false;
201 * Check limits and expirations if "Append" and it has been used
202 * i.e. mr->VolJobs > 0
205 if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
206 /* First handle Max Volume Bytes */
207 if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
208 Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
209 "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
210 bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
213 /* Now see if Volume should only be used once */
214 } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
215 Jmsg(jcr, M_INFO, 0, _("Volume used once. "
216 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
217 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
220 /* Now see if Max Jobs written to volume */
221 } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
222 Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
223 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
224 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
227 /* Now see if Max Files written to volume */
228 } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
229 Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
230 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
231 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
234 /* Finally, check Use duration expiration */
235 } else if (mr->VolUseDuration > 0) {
236 utime_t now = time(NULL);
237 /* See if Vol Use has expired */
238 if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
239 Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
240 "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
241 bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
247 /* Need to update media */
248 if (!db_update_media_record(jcr, jcr->db, mr)) {
249 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
250 mr->VolumeName, db_strerror(jcr->db));
257 * Try hard to recycle the current volume
259 * Returns: on failure - reason = NULL
260 * on success - reason - pointer to reason
262 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
268 /* Check if a duration or limit has expired */
269 if (has_volume_expired(jcr, mr)) {
270 *reason = _("volume has expired");
271 /* Keep going because we may be able to recycle volume */
275 * Now see if we can use the volume as is
277 if (strcmp(mr->VolStatus, "Append") == 0 ||
278 strcmp(mr->VolStatus, "Recycle") == 0) {
284 * Check if the Volume is already marked for recycling
286 if (strcmp(mr->VolStatus, "Purged") == 0) {
287 if (recycle_volume(jcr, mr)) {
288 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
292 /* In principle this shouldn't happen */
293 *reason = _("and recycling of current volume failed");
298 /* At this point, the volume is not valid for writing */
299 *reason = _("but should be Append, Purged or Recycle");
302 * What we're trying to do here is see if the current volume is
303 * "recyclable" - ie. if we prune all expired jobs off it, is
304 * it now possible to reuse it for the job that it is currently
307 if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
308 && mr->Recycle && jcr->pool->recycle_current_volume
309 && (strcmp(mr->VolStatus, "Full") == 0 ||
310 strcmp(mr->VolStatus, "Used") == 0)) {
312 * Attempt prune of current volume to see if we can
313 * recycle it for use.
317 ua = new_ua_context(jcr);
318 ok = prune_volume(ua, mr);
322 /* If fully purged, recycle current volume */
323 if (recycle_volume(jcr, mr)) {
324 Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
327 *reason = _("but should be Append, Purged or Recycle (recycling of the "
328 "current volume failed)");
331 *reason = _("but should be Append, Purged or Recycle (cannot automatically "
332 "recycle current volume, as it still contains unpruned data)");