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