X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fnext_vol.c;h=f960000bb3aedfb7051942b5199921b73190454e;hb=e173d6d190d8cdb1fff798c3aa40988fe83590dd;hp=904f4cf0cab4e79eed6b0b1f1620d21f30789ccc;hpb=922f3d8c0b48617a0686f70bc3935e3c0f7e4b0f;p=bacula%2Fbacula diff --git a/bacula/src/dird/next_vol.c b/bacula/src/dird/next_vol.c index 904f4cf0ca..f960000bb3 100644 --- a/bacula/src/dird/next_vol.c +++ b/bacula/src/dird/next_vol.c @@ -1,55 +1,103 @@ /* - 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(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. */ /* - * * Bacula Director -- next_vol -- handles finding the next * volume for append. Split out of catreq.c August MMIII * catalog request from the Storage daemon. - - * Kern Sibbald, March MMI * - * Version $Id$ + * Kern Sibbald, March MMI */ #include "bacula.h" #include "dird.h" +static int const dbglvl = 50; /* debug level */ + +/* + * We setup the StorageId or StorageId group if it is + * an autochanger from the Storage and put it in + * the media record. + * store == NULL => use existing StorageId + */ +void set_storageid_in_mr(STORE *store, MEDIA_DBR *mr) +{ + if (store == NULL) { + /* Just use the plain (single) StorageId */ + mr->sid_group = edit_int64(mr->StorageId, mr->sid); + return; + } + + /* At this point we know store != NULL */ + mr->StorageId = store->StorageId; + /* Get to the parent of the autochanger (if any) */ + if (store->changer) { + store = store->changer; + mr->StorageId = store->StorageId; + } + /* Go to the master shared storage head (if any) */ + if (store->shared_storage && store->shared_storage->ac_group) { + store = store->shared_storage; + } + /* If it is an autochanger we should have an ac_group */ + if (store->autochanger && store->ac_group) { + /* Note we keep the StorageId of the local autochanger */ + mr->sid_group = store->ac_group; + } else { + /* Otherwise, we just use the plain (single) StorageId */ + mr->sid_group = edit_int64(mr->StorageId, mr->sid); + } +} + +static void add_volume_to_exclude_list(JCR *jcr, int index, MEDIA_DBR *mr) +{ + char ed1[50]; + if (index == 1) { + *jcr->next_vol_list = 0; + + } else if (*jcr->next_vol_list) { + pm_strcat(jcr->next_vol_list, ","); + } + pm_strcat(jcr->next_vol_list, edit_int64(mr->MediaId, ed1)); + + /* The list is valid only in find_next_volume_for_append() */ + mr->exclude_list = NULL; +} + +static void set_volume_to_exclude_list(JCR *jcr, int index, MEDIA_DBR *mr) +{ + if (index == 1) { + *jcr->next_vol_list = 0; + } + mr->exclude_list = jcr->next_vol_list; +} + /* * Items needed: * mr.PoolId must be set - * mr.StorageId should also be set + * mr.ScratchPoolId could be set (used if create==true) * jcr->wstore * jcr->db * jcr->pool * 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, int index, +int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create, bool prune) { int retry = 0; @@ -58,13 +106,18 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, STORE *store = jcr->wstore; bstrncpy(mr->MediaType, store->media_type, sizeof(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); + Dmsg6(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d\n", + (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType, index, + create, prune); /* * If we are using an Autochanger, restrict Volume * search to the Autochanger on the first pass */ - InChanger = store->autochanger; + InChanger = (store->autochanger)? true : false; + + /* Make sure we don't send two times the same volume in the same session */ + set_volume_to_exclude_list(jcr, index, mr); + /* * Find the Next Volume for Append */ @@ -74,43 +127,51 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, /* * 1. Look for volume with "Append" status. */ + set_storageid_in_mr(store, mr); /* put StorageId in new record */ 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", + /* + * No volume found, apply algorithm + */ + Dmsg4(dbglvl, "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(150, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten); + ok = find_recycled_volume(jcr, InChanger, mr, store); + set_storageid_in_mr(store, mr); /* put StorageId in new record */ + Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten); if (!ok) { /* * 3. Try recycling any purged volume */ - ok = recycle_oldest_purged_volume(jcr, InChanger, mr); + ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store); + set_storageid_in_mr(store, mr); /* put StorageId in new record */ if (!ok) { /* * 4. Try pruning Volumes */ if (prune) { - Dmsg0(150, "Call prune_volumes\n"); - prune_volumes(jcr, InChanger, mr); + Dmsg0(dbglvl, "Call prune_volumes\n"); + prune_volumes(jcr, InChanger, mr, store); } - ok = recycle_oldest_purged_volume(jcr, InChanger, mr); + ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store); + set_storageid_in_mr(store, mr); /* put StorageId in new record */ if (!ok && create) { - Dmsg4(150, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n", + Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n", ok, index, InChanger, mr->VolStatus); /* * 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 = get_scratch_volume(jcr, InChanger, mr, store); + set_storageid_in_mr(store, mr); /* put StorageId in new record */ + Dmsg4(dbglvl, "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. + * a volume, retry looking for any volume. */ if (!ok && InChanger) { InChanger = false; @@ -124,21 +185,23 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, /* * 6. Try "creating" a new Volume */ - ok = newVolume(jcr, mr); + ok = newVolume(jcr, mr, store); } /* * Look at more drastic ways to find an Appendable Volume */ if (!ok && (jcr->pool->purge_oldest_volume || jcr->pool->recycle_oldest_volume)) { - Dmsg2(200, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d", + Dmsg2(dbglvl, "No next volume found. PurgeOldest=%d\n RecyleOldest=%d", jcr->pool->purge_oldest_volume, jcr->pool->recycle_oldest_volume); /* Find oldest volume to recycle */ + set_storageid_in_mr(store, mr); /* update storage id */ ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr); - Dmsg1(200, "Find oldest=%d Volume\n", ok); + set_storageid_in_mr(store, mr); /* update storageid */ + Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok); if (ok && prune) { UAContext *ua; - Dmsg0(200, "Try purge Volume.\n"); + Dmsg0(dbglvl, "Try purge Volume.\n"); /* * 7. Try to purging oldest volume only if not UA calling us. */ @@ -156,12 +219,12 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, free_ua_context(ua); if (ok) { ok = recycle_volume(jcr, mr); - Dmsg1(400, "Recycle after purge oldest=%d\n", ok); + Dmsg1(dbglvl, "Recycle after purge oldest=%d\n", ok); } } } } - Dmsg2(100, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten); + Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten); if (ok) { /* If we can use the volume, check if it is expired */ if (has_volume_expired(jcr, mr)) { @@ -176,7 +239,12 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, break; } /* end for loop */ db_unlock(jcr->db); - Dmsg1(150, "return ok=%d find_next_vol\n", ok); + Dmsg1(dbglvl, "return ok=%d find_next_vol\n", ok); + + /* We keep the record of all previous volumes requested */ + if (ok) { + add_volume_to_exclude_list(jcr, index, mr);; + } return ok; } @@ -187,6 +255,7 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) { bool expired = false; + char ed1[50]; /* * Check limits and expirations if "Append" and it has been used * i.e. mr->VolJobs > 0 @@ -195,8 +264,9 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) { /* First handle Max Volume Bytes */ if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) { - Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. " - "Marking Volume \"%s\" as Full.\n"), mr->VolumeName); + Jmsg(jcr, M_INFO, 0, _("Max Volume bytes=%s exceeded. " + "Marking Volume \"%s\" as Full.\n"), + edit_uint64_with_commas(mr->MaxVolBytes, ed1), mr->VolumeName); bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus)); expired = true; @@ -209,17 +279,19 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) /* Now see if Max Jobs written to volume */ } 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, + Jmsg(jcr, M_INFO, 0, _("Max Volume jobs=%s exceeded. " + "Marking Volume \"%s\" as Used.\n"), + edit_uint64_with_commas(mr->MaxVolJobs, ed1), mr->VolumeName); + Dmsg3(dbglvl, "MaxVolJobs=%d JobId=%d Vol=%s\n", mr->MaxVolJobs, (uint32_t)jcr->JobId, mr->VolumeName); bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus)); expired = true; /* Now see if Max Files written to volume */ } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) { - Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. " - "Marking Volume \"%s\" as Used.\n"), mr->VolumeName); + Jmsg(jcr, M_INFO, 0, _("Max Volume files=%s exceeded. " + "Marking Volume \"%s\" as Used.\n"), + edit_uint64_with_commas(mr->MaxVolFiles, ed1), mr->VolumeName); bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus)); expired = true; @@ -228,8 +300,9 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) utime_t now = time(NULL); /* See if Vol Use has expired */ if (mr->VolUseDuration <= (now - mr->FirstWritten)) { - Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. " - "Marking Volume \"%s\" as Used.\n"), mr->VolumeName); + Jmsg(jcr, M_INFO, 0, _("Max configured use duration=%s sec. exceeded. " + "Marking Volume \"%s\" as Used.\n"), + edit_uint64_with_commas(mr->VolUseDuration, ed1), mr->VolumeName); bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus)); expired = true; } @@ -237,13 +310,14 @@ 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); + Dmsg1(dbglvl, "Vol=%s has expired update media record\n", mr->VolumeName); + set_storageid_in_mr(NULL, mr); 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); + Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired); return expired; } @@ -259,14 +333,12 @@ 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"); + if (!mr->Recycle) { /* cannot recycle */ + return; + } /* Keep going because we may be able to recycle volume */ } @@ -303,8 +375,16 @@ void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **r * it now possible to reuse it for the job that it is currently * needed for? */ - if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL) - && mr->Recycle && jcr->pool->recycle_current_volume + if (!mr->Recycle) { + *reason = _("volume has recycling disabled"); + return; + } + /* + * Check retention period from last written, but recycle to within + * a minute to try to catch close calls ... + */ + if ((mr->LastWritten + mr->VolRetention - 60) < (utime_t)time(NULL) + && jcr->pool->recycle_current_volume && (strcmp(mr->VolStatus, "Full") == 0 || strcmp(mr->VolStatus, "Used") == 0)) { /* @@ -336,7 +416,8 @@ void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **r static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr) +bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, + STORE *store) { MEDIA_DBR smr; /* for searching scratch pool */ POOL_DBR spr, pr; @@ -345,17 +426,17 @@ bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr) /* 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_numvols 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)); @@ -364,32 +445,33 @@ bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr) * recycling any existing purged volumes, then * try to take the oldest volume. */ + set_storageid_in_mr(store, &smr); /* put StorageId in new record */ if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) { found = true; - } else if (find_recycled_volume(jcr, InChanger, &smr)) { + } else if (find_recycled_volume(jcr, InChanger, &smr, store)) { found = true; - } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr)) { + } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) { 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"), + if (!db_get_pool_numvols(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"), @@ -397,7 +479,8 @@ bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr) goto bail_out; } - memcpy(mr, &smr, sizeof(MEDIA_DBR)); + mr->copy(&smr); + set_storageid_in_mr(store, mr); /* Set default parameters from current pool */ set_pool_dbr_defaults_in_media_dbr(mr, &pr); @@ -416,9 +499,10 @@ bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr) goto bail_out; } - Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from 'Scratch' pool.\n"), - mr->VolumeName); - + Jmsg(jcr, M_INFO, 0, _("Using Volume \"%s\" from '%s' %spool.\n"), + mr->VolumeName, spr.Name, + ((strcmp(spr.Name, "Scratch") == 0) ? "" : "Scratch ")); + ok = true; } }