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