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