]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/next_vol.c
- Get next volume from Scratch pool before creating a volume.
[bacula/bacula] / bacula / src / dird / next_vol.c
1 /*
2  *
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.
6
7  *     Kern Sibbald, March MMI
8  *
9  *   Version $Id$
10  */
11 /*
12    Copyright (C) 2001-2005 Kern Sibbald
13
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.
18
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.
23
24  */
25
26 #include "bacula.h"
27 #include "dird.h"
28
29 /*
30  *  Items needed:
31  *   mr.PoolId must be set
32  *   jcr->store
33  *   jcr->db
34  *   jcr->pool
35  *   MEDIA_DBR mr (zeroed out)
36  *   create -- whether or not to create a new volume
37  */
38 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create)
39 {
40    int retry = 0;
41    bool ok;
42    bool InChanger;
43    STORE *store = jcr->store;
44
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);
47    /*
48     * If we are using an Autochanger, restrict Volume
49     *   search to the Autochanger on the first pass
50     */
51    InChanger = store->autochanger;
52    /*
53     * Find the Next Volume for Append
54     */
55    db_lock(jcr->db);
56    for ( ;; ) {
57       bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));  /* want only appendable volumes */
58       /*
59        *  1. Look for volume with "Append" status.
60        */
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);
63       if (!ok) {
64          /*
65           * 2. Try finding a recycled volume
66           */
67          ok = find_recycled_volume(jcr, InChanger, mr);
68          Dmsg2(100, "find_recycled_volume %d FW=%d\n", ok, mr->FirstWritten);
69          if (!ok) {
70             /*
71              * 3. Try recycling any purged volume
72              */
73             ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
74             if (!ok) {
75                /*
76                 * 4. Try pruning Volumes
77                 */
78                prune_volumes(jcr);
79                ok = recycle_oldest_purged_volume(jcr, InChanger, mr);
80                if (InChanger) {
81                   InChanger = false;
82                   if (!ok) {
83                      continue;           /* retry again accepting any volume */
84                   }
85                }
86             }
87          }
88
89          if (!ok) {
90             MEDIA_DBR smr;
91             POOL_DBR pr;
92             POOLMEM *query;
93             char ed1[50], ed2[50];
94             /*
95              * 5. Try pulling a volume from the Scratch pool
96              */ 
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);
106                    db_lock(jcr->db);
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);  
111                    db_unlock(jcr->db);
112                    Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), 
113                         smr.VolumeName);
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));
125                       }
126                    } else {
127                       Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"), 
128                          db_strerror(jcr->db));
129                    }
130                 }
131              }
132          }
133
134          if (!ok && create) {
135             /*
136              * 6. Try "creating" a new Volume
137              */
138             ok = newVolume(jcr, mr);
139          }
140          /*
141           *  Look at more drastic ways to find an Appendable Volume
142           */
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);
150             if (ok) {
151                UAContext *ua;
152                Dmsg0(400, "Try purge.\n");
153                /*
154                 * 7.  Try to purging oldest volume only if not UA calling us.
155                 */
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);
160                /*
161                 * 8. or try recycling the oldest volume
162                 */
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);
166                }
167                free_ua_context(ua);
168                if (ok) {
169                   ok = recycle_volume(jcr, mr);
170                   Dmsg1(400, "Recycle after purge oldest=%d\n", ok);
171                }
172             }
173          }
174       }
175       Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
176       if (ok) {
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 */
181             } else {
182                Jmsg(jcr, M_ERROR, 0, _(
183 "We seem to be looping trying to find the next volume. I give up.\n"));
184             }
185          }
186       }
187       break;
188    } /* end for loop */
189    db_unlock(jcr->db);
190    return ok;
191 }
192
193 /*
194  * Check if any time limits or use limits have expired
195  *   if so, set the VolStatus appropriately.
196  */
197 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
198 {
199    bool expired = false;
200    /*
201     * Check limits and expirations if "Append" and it has been used
202     * i.e. mr->VolJobs > 0
203     *
204     */
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));
211          expired = true;
212
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));
218          expired = true;
219
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));
225          expired = true;
226
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));
232          expired = true;
233
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));
242             expired = true;
243          }
244       }
245    }
246    if (expired) {
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));
251       }
252    }
253    return expired;
254 }
255
256 /*
257  * Try hard to recycle the current volume
258  *
259  *  Returns: on failure - reason = NULL
260  *           on success - reason - pointer to reason
261  */
262 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
263 {
264    int ok;
265
266    *reason = NULL;
267
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 */
272    }
273
274    /*
275     * Now see if we can use the volume as is
276     */
277    if (strcmp(mr->VolStatus, "Append") == 0 ||
278        strcmp(mr->VolStatus, "Recycle") == 0) {
279       *reason = NULL;
280       return;
281    }
282
283    /*
284     * Check if the Volume is already marked for recycling
285     */
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);
289          *reason = NULL;
290          return;
291       } else {
292          /* In principle this shouldn't happen */
293          *reason = _("and recycling of current volume failed");
294          return;
295       }
296    }
297
298    /* At this point, the volume is not valid for writing */
299    *reason = _("but should be Append, Purged or Recycle");
300
301    /*
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
305     * needed for?
306     */
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)) {
311       /*
312        * Attempt prune of current volume to see if we can
313        * recycle it for use.
314        */
315       UAContext *ua;
316
317       ua = new_ua_context(jcr);
318       ok = prune_volume(ua, mr);
319       free_ua_context(ua);
320
321       if (ok) {
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);
325             *reason = NULL;
326          } else {
327             *reason = _("but should be Append, Purged or Recycle (recycling of the "
328                "current volume failed)");
329          }
330       } else {
331          *reason = _("but should be Append, Purged or Recycle (cannot automatically "
332             "recycle current volume, as it still contains unpruned data)");
333       }
334    }
335 }