]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/next_vol.c
74238fc026e6238318bd4c6402a47918ec6ccab8
[bacula/bacula] / bacula / src / dird / next_vol.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *   Bacula Director -- next_vol -- handles finding the next
19  *    volume for append.  Split out of catreq.c August MMIII
20  *    catalog request from the Storage daemon.
21
22  *     Written by Kern Sibbald, March MMI
23  *
24  */
25
26 #include "bacula.h"
27 #include "dird.h"
28
29 static int const dbglvl = 50;   /* debug level */
30
31 /*
32  * We setup the StorageId if it is
33  *  an autochanger from the Storage and put it in
34  *  the media record.
35  * store == NULL => use existing StorageId
36  */
37 void set_storageid_in_mr(STORE *store, MEDIA_DBR *mr)
38 {
39    if (!store) {
40       return;
41    }
42    /* At this point we know store != NULL */
43    mr->StorageId = store->StorageId;
44 }
45
46 /*
47  *  Items needed:
48  *   mr.PoolId must be set
49  *   mr.ScratchPoolId could be set (used if create==true)
50  *   jcr->wstore
51  *   jcr->db
52  *   jcr->pool
53  *   MEDIA_DBR mr with PoolId set
54  *   create -- whether or not to create a new volume
55  */
56 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
57                                 bool create, bool prune)
58 {
59    int retry = 0;
60    bool ok;
61    bool InChanger;
62    STORE *store = jcr->wstore;
63
64    bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
65    Dmsg6(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d\n",
66          (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType, index,
67          create, prune);
68    /*
69     * If we are using an Autochanger, restrict Volume
70     *   search to the Autochanger on the first pass
71     */
72    InChanger = (store->autochanger)? true : false;
73
74    /*
75     * Find the Next Volume for Append
76     */
77    db_lock(jcr->db);
78    for ( ;; ) {
79       bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));  /* want only appendable volumes */
80       /*
81        *  1. Look for volume with "Append" status.
82        */
83       set_storageid_in_mr(store, mr);  /* put StorageId in new record */
84       ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr);
85
86       if (!ok) {
87          /*
88           * No volume found, apply algorithm
89           */
90          Dmsg4(dbglvl, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
91                ok, index, InChanger, mr->VolStatus);
92          /*
93           * 2. Try finding a recycled volume
94           */
95          ok = find_recycled_volume(jcr, InChanger, mr, store);
96          set_storageid_in_mr(store, mr);  /* put StorageId in new record */
97          Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten);
98          if (!ok) {
99             /*
100              * 3. Try recycling any purged volume
101              */
102             ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
103             set_storageid_in_mr(store, mr);  /* put StorageId in new record */
104             if (!ok) {
105                /*
106                 * 4. Try pruning Volumes
107                 */
108                if (prune) {
109                   Dmsg0(dbglvl, "Call prune_volumes\n");
110                   prune_volumes(jcr, InChanger, mr, store);
111                }
112                ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store);
113                set_storageid_in_mr(store, mr);  /* put StorageId in new record */
114                if (!ok && create) {
115                   Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n",
116                         ok, index, InChanger, mr->VolStatus);
117                   /*
118                    * 5. Try pulling a volume from the Scratch pool
119                    */
120                   ok = get_scratch_volume(jcr, InChanger, mr, store);
121                   set_storageid_in_mr(store, mr);  /* put StorageId in new record */
122                   Dmsg4(dbglvl, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n",
123                         ok, index, InChanger, mr->VolStatus);
124                }
125                /*
126                 * If we are using an Autochanger and have not found
127                 * a volume, retry looking for any volume.
128                 */
129                if (!ok && InChanger) {
130                   InChanger = false;
131                   continue;           /* retry again accepting any volume */
132                }
133             }
134          }
135
136
137          if (!ok && create) {
138             /*
139              * 6. Try "creating" a new Volume
140              */
141             ok = newVolume(jcr, mr, store);
142          }
143          /*
144           *  Look at more drastic ways to find an Appendable Volume
145           */
146          if (!ok && (jcr->pool->purge_oldest_volume ||
147                      jcr->pool->recycle_oldest_volume)) {
148             Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d",
149                 jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume);
150             /* Find oldest volume to recycle */
151             set_storageid_in_mr(store, mr);   /* update storage id */
152             ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr);
153             set_storageid_in_mr(store, mr);  /* update storageid */
154             Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok);
155             if (ok && prune) {
156                UAContext *ua;
157                Dmsg0(dbglvl, "Try purge Volume.\n");
158                /*
159                 * 7.  Try to purging oldest volume only if not UA calling us.
160                 */
161                ua = new_ua_context(jcr);
162                if (jcr->pool->purge_oldest_volume && create) {
163                   Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName);
164                   ok = purge_jobs_from_volume(ua, mr);
165                /*
166                 * 8. or try recycling the oldest volume
167                 */
168                } else if (jcr->pool->recycle_oldest_volume) {
169                   Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName);
170                   ok = prune_volume(ua, mr);
171                }
172                free_ua_context(ua);
173                if (ok) {
174                   ok = recycle_volume(jcr, mr);
175                   Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok);
176                }
177             }
178          }
179       }
180       Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
181       if (ok) {
182          /* If we can use the volume, check if it is expired */
183          if (has_volume_expired(jcr, mr)) {
184             if (retry++ < 200) {            /* sanity check */
185                continue;                    /* try again from the top */
186             } else {
187                Jmsg(jcr, M_ERROR, 0, _(
188 "We seem to be looping trying to find the next volume. I give up.\n"));
189             }
190          }
191       }
192       break;
193    } /* end for loop */
194    db_unlock(jcr->db);
195    Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok);
196    return ok;
197 }
198
199 /*
200  * Check if any time limits or use limits have expired
201  *   if so, set the VolStatus appropriately.
202  */
203 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
204 {
205    bool expired = false;
206    char ed1[50];
207    /*
208     * Check limits and expirations if "Append" and it has been used
209     * i.e. mr->VolJobs > 0
210     *
211     */
212    if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
213       /* First handle Max Volume Bytes */
214       if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
215          Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. "
216              "Marking Volume \"%s\" as Full.\n"),
217              edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName);
218          bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
219          expired = true;
220
221       /* Now see if Volume should only be used once */
222       } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
223          Jmsg(jcr, M_INFO, 0, _("Volume used once. "
224              "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
225          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
226          expired = true;
227
228       /* Now see if Max Jobs written to volume */
229       } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
230          Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. "
231              "Marking Volume \"%s\" as Used.\n"),
232              edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName);
233          Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs,
234                (uint32_t)jcr->JobId, mr->VolumeName);
235          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
236          expired = true;
237
238       /* Now see if Max Files written to volume */
239       } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
240          Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. "
241              "Marking Volume \"%s\" as Used.\n"),
242              edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName);
243          bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
244          expired = true;
245
246       /* Finally, check Use duration expiration */
247       } else if (mr->VolUseDuration > 0) {
248          utime_t now = time(NULL);
249          /* See if Vol Use has expired */
250          if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
251             Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. "
252                "Marking Volume \"%s\" as Used.\n"),
253                edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName);
254             bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
255             expired = true;
256          }
257       }
258    }
259    if (expired) {
260       /* Need to update media */
261       Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName);
262       set_storageid_in_mr(NULL, mr);
263       if (!db_update_media_record(jcr, jcr->db, mr)) {
264          Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
265               mr->VolumeName, db_strerror(jcr->db));
266       }
267    }
268    Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
269    return expired;
270 }
271
272 /*
273  * Try hard to recycle the current volume
274  *
275  *  Returns: on failure - reason = NULL
276  *           on success - reason - pointer to reason
277  */
278 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
279 {
280    int ok;
281
282    *reason = NULL;
283
284    /*  Check if a duration or limit has expired */
285    if (has_volume_expired(jcr, mr)) {
286       *reason = _("volume has expired");
287       /* Keep going because we may be able to recycle volume */
288    }
289
290    /*
291     * Now see if we can use the volume as is
292     */
293    if (strcmp(mr->VolStatus, "Append") == 0 ||
294        strcmp(mr->VolStatus, "Recycle") == 0) {
295       *reason = NULL;
296       return;
297    }
298
299    /*
300     * Check if the Volume is already marked for recycling
301     */
302    if (strcmp(mr->VolStatus, "Purged") == 0) {
303       if (recycle_volume(jcr, mr)) {
304          Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
305          *reason = NULL;
306          return;
307       } else {
308          /* In principle this shouldn't happen */
309          *reason = _("and recycling of current volume failed");
310          return;
311       }
312    }
313
314    /* At this point, the volume is not valid for writing */
315    *reason = _("but should be Append, Purged or Recycle");
316
317    /*
318     * What we're trying to do here is see if the current volume is
319     * "recyclable" - ie. if we prune all expired jobs off it, is
320     * it now possible to reuse it for the job that it is currently
321     * needed for?
322     */
323    if (!mr->Recycle) {
324       *reason = _("volume has recycling disabled");
325       return;
326    }
327    /*
328     * Check retention period from last written, but recycle to within
329     *   a minute to try to catch close calls ...
330     */
331    if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL)
332          && jcr->pool->recycle_current_volume
333          && (strcmp(mr->VolStatus, "Full") == 0 ||
334             strcmp(mr->VolStatus, "Used") == 0)) {
335       /*
336        * Attempt prune of current volume to see if we can
337        * recycle it for use.
338        */
339       UAContext *ua;
340
341       ua = new_ua_context(jcr);
342       ok = prune_volume(ua, mr);
343       free_ua_context(ua);
344
345       if (ok) {
346          /* If fully purged, recycle current volume */
347          if (recycle_volume(jcr, mr)) {
348             Jmsg(jcr, M_INFO, 0, _("Recycled current volume \"%s\"\n"), mr->VolumeName);
349             *reason = NULL;
350          } else {
351             *reason = _("but should be Append, Purged or Recycle (recycling of the "
352                "current volume failed)");
353          }
354       } else {
355          *reason = _("but should be Append, Purged or Recycle (cannot automatically "
356             "recycle current volume, as it still contains unpruned data "
357             "or the Volume Retention time has not expired.)");
358       }
359    }
360 }
361
362 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
363
364 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
365                         STORE *store)
366 {
367    MEDIA_DBR smr;                        /* for searching scratch pool */
368    POOL_DBR spr, pr;
369    bool ok = false;
370    bool found = false;
371
372    /* Only one thread at a time can pull from the scratch pool */
373    P(mutex);
374    /*
375     * Get Pool record for Scratch Pool
376     * choose between ScratchPoolId and Scratch
377     * db_get_pool_numvols will first try ScratchPoolId,
378     * and then try the pool named Scratch
379     */
380    memset(&spr, 0, sizeof(spr));
381    bstrncpy(spr.Name, "Scratch", sizeof(spr.Name));
382    spr.PoolId = mr->ScratchPoolId;
383    if (db_get_pool_record(jcr, jcr->db, &spr)) {
384       smr.PoolId = spr.PoolId;
385       bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus));  /* want only appendable volumes */
386       bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType));
387
388       /*
389        * If we do not find a valid Scratch volume, try
390        *  recycling any existing purged volumes, then
391        *  try to take the oldest volume.
392        */
393       set_storageid_in_mr(store, &smr);  /* put StorageId in new record */
394       if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) {
395          found = true;
396
397       } else if (find_recycled_volume(jcr, InChanger, &smr, store)) {
398          found = true;
399
400       } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) {
401          found = true;
402       }
403
404       if (found) {
405          POOL_MEM query(PM_MESSAGE);
406
407          /*
408           * Get pool record where the Scratch Volume will go to ensure
409           * that we can add a Volume.
410           */
411          memset(&pr, 0, sizeof(pr));
412          bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name));
413
414          if (!db_get_pool_numvols(jcr, jcr->db, &pr)) {
415             Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"),
416                  db_strerror(jcr->db));
417             goto bail_out;
418          }
419
420          /* Make sure there is room for another volume */
421          if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
422             Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"),
423                  jcr->pool->name(), pr.MaxVols);
424             goto bail_out;
425          }
426
427          mr->copy(&smr);
428          set_storageid_in_mr(store, mr);
429
430          /* Set default parameters from current pool */
431          set_pool_dbr_defaults_in_media_dbr(mr, &pr);
432
433          /*
434           * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append,
435           *   we could have Recycled media, also, we retain the old
436           *   RecyclePoolId.
437           */
438          bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus));
439          mr->RecyclePoolId = smr.RecyclePoolId;
440
441          if (!db_update_media_record(jcr, jcr->db, mr)) {
442             Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"),
443                  db_strerror(jcr->db));
444             goto bail_out;
445          }
446
447          Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"),
448               mr->VolumeName);
449
450          ok = true;
451       }
452    }
453 bail_out:
454    V(mutex);
455    return ok;
456 }