X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fnext_vol.c;h=462d4550237e8e14cc04d0e105fd4197fe8c62a4;hb=928fa1c1a90e32611cdceecc19672945757af154;hp=52086651a1e2a103d18398b78eda42b889611c38;hpb=fce6a648a27ee86f6941a5085ec7909f6d43e725;p=bacula%2Fbacula diff --git a/bacula/src/dird/next_vol.c b/bacula/src/dird/next_vol.c index 52086651a1..462d455023 100644 --- a/bacula/src/dird/next_vol.c +++ b/bacula/src/dird/next_vol.c @@ -1,3 +1,30 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2001-2008 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version two of the GNU General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ /* * * Bacula Director -- next_vol -- handles finding the next @@ -8,25 +35,6 @@ * * Version $Id$ */ -/* - Copyright (C) 2001-2005 Kern Sibbald - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. - - */ #include "bacula.h" #include "dird.h" @@ -34,21 +42,25 @@ /* * Items needed: * mr.PoolId must be set - * jcr->store + * mr.StorageId should also be set + * mr.ScratchPoolId could be set (used if create==true) + * jcr->wstore * jcr->db * jcr->pool - * MEDIA_DBR mr (zeroed out) + * MEDIA_DBR mr with PoolId set * create -- whether or not to create a new volume */ -int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, bool create) +int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, + bool create, bool prune) { int retry = 0; bool ok; bool InChanger; - STORE *store = jcr->store; + STORE *store = jcr->wstore; bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType)); - Dmsg2(100, "CatReq FindMedia: Id=%d, MediaType=%s\n", (int)mr->PoolId, mr->MediaType); + Dmsg3(100, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s\n", + (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType); /* * If we are using an Autochanger, restrict Volume * search to the Autochanger on the first pass @@ -63,14 +75,16 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, bool create) /* * 1. Look for volume with "Append" status. */ - ok = db_find_next_volume(jcr, jcr->db, 1, InChanger, mr); - Dmsg2(100, "catreq after find_next_vol ok=%d FW=%d\n", ok, mr->FirstWritten); + ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr); + if (!ok) { + Dmsg4(150, "after find_next_vol ok=%d index=%d InChanger=%d Vstat=%s\n", + ok, index, InChanger, mr->VolStatus); /* * 2. Try finding a recycled volume */ ok = find_recycled_volume(jcr, InChanger, mr); - Dmsg2(100, "find_recycled_volume %d FW=%d\n", ok, mr->FirstWritten); + Dmsg2(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten); if (!ok) { /* * 3. Try recycling any purged volume @@ -80,54 +94,38 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, bool create) /* * 4. Try pruning Volumes */ - prune_volumes(jcr); - ok = recycle_oldest_purged_volume(jcr, InChanger, mr); - if (InChanger) { - InChanger = false; - if (!ok) { - continue; /* retry again accepting any volume */ - } + if (prune) { + Dmsg0(150, "Call prune_volumes\n"); + prune_volumes(jcr, InChanger, mr); } - Dmsg2(200, "find_recycled_volume2 %d FW=%d\n", ok, mr->FirstWritten); + ok = recycle_oldest_purged_volume(jcr, InChanger, mr); if (!ok && create) { + Dmsg4(150, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n", + ok, index, InChanger, mr->VolStatus); /* - * 5. Try "creating" a new Volume - */ - ok = newVolume(jcr, mr); + * 5. Try pulling a volume from the Scratch pool + */ + ok = get_scratch_volume(jcr, InChanger, mr); + Dmsg4(150, "after get scratch volume ok=%d index=%d InChanger=%d Vstat=%s\n", + ok, index, InChanger, mr->VolStatus); + } + /* + * If we are using an Autochanger and have not found + * a volume, retry looking for any volume. + */ + if (!ok && InChanger) { + InChanger = false; + continue; /* retry again accepting any volume */ } } } - if (!ok) { - MEDIA_DBR smr; - POOL_DBR pr; - POOLMEM *query; - char ed1[50], ed2[50]; + + if (!ok && create) { /* - * 6. Try pulling a volume from the Scratch pool - */ - memset(&pr, 0, sizeof(pr)); - bstrncpy(pr.Name, "Scratch", sizeof(pr.Name)); - if (db_get_pool_record(jcr, jcr->db, &pr)) { - memset(&smr, 0, sizeof(smr)); - smr.PoolId = pr.PoolId; - bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */ - bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType)); - if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) { - query = get_pool_memory(PM_MESSAGE); - db_lock(jcr->db); - Mmsg(query, "UPDATE Media SET PoolId=%s WHERE MediaId=%s", - edit_int64(mr->PoolId, ed1), - edit_int64(smr.MediaId, ed2)); - ok = db_sql_query(jcr->db, query, NULL, NULL); - db_unlock(jcr->db); - Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), - smr.VolumeName); - /* Set new Pool Id in smr record, then copy it to mr */ - smr.PoolId = mr->PoolId; - memcpy(mr, &smr, sizeof(MEDIA_DBR)); - } - } + * 6. Try "creating" a new Volume + */ + ok = newVolume(jcr, mr); } /* * Look at more drastic ways to find an Appendable Volume @@ -138,19 +136,19 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, bool create) jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume); /* Find oldest volume to recycle */ ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr); - Dmsg1(400, "Find oldest=%d\n", ok); - if (ok) { + Dmsg1(200, "Find oldest=%d Volume\n", ok); + if (ok && prune) { UAContext *ua; - Dmsg0(400, "Try purge.\n"); + Dmsg0(200, "Try purge Volume.\n"); /* - * 5. Try to purging oldest volume only if not UA calling us. + * 7. Try to purging oldest volume only if not UA calling us. */ ua = new_ua_context(jcr); if (jcr->pool->purge_oldest_volume && create) { Jmsg(jcr, M_INFO, 0, _("Purging oldest volume \"%s\"\n"), mr->VolumeName); ok = purge_jobs_from_volume(ua, mr); /* - * 5. or try recycling the oldest volume + * 8. or try recycling the oldest volume */ } else if (jcr->pool->recycle_oldest_volume) { Jmsg(jcr, M_INFO, 0, _("Pruning oldest volume \"%s\"\n"), mr->VolumeName); @@ -179,6 +177,7 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, bool create) break; } /* end for loop */ db_unlock(jcr->db); + Dmsg1(150, "return ok=%d find_next_vol\n", ok); return ok; } @@ -213,6 +212,8 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) { Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. " "Marking Volume \"%s\" as Used.\n"), mr->VolumeName); + Dmsg3(100, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs, + (uint32_t)jcr->JobId, mr->VolumeName); bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus)); expired = true; @@ -237,11 +238,13 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) } if (expired) { /* Need to update media */ + Dmsg1(150, "Vol=%s has expired update media record\n", mr->VolumeName); if (!db_update_media_record(jcr, jcr->db, mr)) { Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"), mr->VolumeName, db_strerror(jcr->db)); } } + Dmsg2(150, "Vol=%s expired=%d\n", mr->VolumeName, expired); return expired; } @@ -257,6 +260,11 @@ void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **r *reason = NULL; + if (!mr->Recycle) { + *reason = _("volume has recycling disabled"); + return; + } + /* Check if a duration or limit has expired */ if (has_volume_expired(jcr, mr)) { *reason = _("volume has expired"); @@ -321,7 +329,105 @@ void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **r } } else { *reason = _("but should be Append, Purged or Recycle (cannot automatically " - "recycle current volume, as it still contains unpruned data)"); + "recycle current volume, as it still contains unpruned data " + "or the Volume Retention time has not expired.)"); } } } + +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr) +{ + MEDIA_DBR smr; /* for searching scratch pool */ + POOL_DBR spr, pr; + bool ok = false; + bool found = false; + + /* Only one thread at a time can pull from the scratch pool */ + P(mutex); + /* + * Get Pool record for Scratch Pool + * choose between ScratchPoolId and Scratch + * db_get_pool_record will first try ScratchPoolId, + * and then try the pool named Scratch + */ + memset(&spr, 0, sizeof(spr)); + bstrncpy(spr.Name, "Scratch", sizeof(spr.Name)); + spr.PoolId = mr->ScratchPoolId; + if (db_get_pool_record(jcr, jcr->db, &spr)) { + memset(&smr, 0, sizeof(smr)); + smr.PoolId = spr.PoolId; + if (InChanger) { + smr.StorageId = mr->StorageId; /* want only Scratch Volumes in changer */ + } + bstrncpy(smr.VolStatus, "Append", sizeof(smr.VolStatus)); /* want only appendable volumes */ + bstrncpy(smr.MediaType, mr->MediaType, sizeof(smr.MediaType)); + + /* + * If we do not find a valid Scratch volume, try + * recycling any existing purged volumes, then + * try to take the oldest volume. + */ + if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) { + found = true; + + } else if (find_recycled_volume(jcr, InChanger, &smr)) { + found = true; + + } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) { + found = true; + } + + if (found) { + POOL_MEM query(PM_MESSAGE); + + /* + * Get pool record where the Scratch Volume will go to ensure + * that we can add a Volume. + */ + memset(&pr, 0, sizeof(pr)); + bstrncpy(pr.Name, jcr->pool->name(), sizeof(pr.Name)); + + if (!db_get_pool_record(jcr, jcr->db, &pr)) { + Jmsg(jcr, M_WARNING, 0, _("Unable to get Pool record: ERR=%s"), + db_strerror(jcr->db)); + goto bail_out; + } + + /* Make sure there is room for another volume */ + if (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) { + Jmsg(jcr, M_WARNING, 0, _("Unable add Scratch Volume, Pool \"%s\" full MaxVols=%d\n"), + jcr->pool->name(), pr.MaxVols); + goto bail_out; + } + + memcpy(mr, &smr, sizeof(MEDIA_DBR)); + + /* Set default parameters from current pool */ + set_pool_dbr_defaults_in_media_dbr(mr, &pr); + + /* + * set_pool_dbr_defaults_in_media_dbr set VolStatus to Append, + * we could have Recycled media, also, we retain the old + * RecyclePoolId. + */ + bstrncpy(mr->VolStatus, smr.VolStatus, sizeof(smr.VolStatus)); + mr->RecyclePoolId = smr.RecyclePoolId; + + if (!db_update_media_record(jcr, jcr->db, mr)) { + Jmsg(jcr, M_WARNING, 0, _("Failed to move Scratch Volume. ERR=%s\n"), + db_strerror(jcr->db)); + goto bail_out; + } + + Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), + mr->VolumeName); + + ok = true; + } + } +bail_out: + V(mutex); + return ok; +}