]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
cloud: Fix #9714 introduce max_vol_parts_num variable to limit cloud volumes parts...
authornorbert.bizet <norbert.bizet@baculasystems.com>
Tue, 6 Dec 2022 16:50:07 +0000 (11:50 -0500)
committerEric Bollengier <eric@baculasystems.com>
Thu, 14 Sep 2023 11:57:00 +0000 (13:57 +0200)
bacula/src/stored/cloud_dev.c
bacula/src/stored/dev.h
bacula/src/stored/init_dev.c
bacula/src/stored/label.c
bacula/src/stored/record.h
bacula/src/stored/stored_conf.c
bacula/src/stored/stored_conf.h
regress/tests/cloud-maxvolpartnums-test [new file with mode: 0755]

index a98344ae7ac49436c55623c99abd8e16be4c3db4..538abe24de30fee135d9a00b785c2610dfb0d973 100644 (file)
@@ -2309,6 +2309,15 @@ bool cloud_dev::do_size_checks(DCR *dcr, DEV_BLOCK *block)
          return false;
       }
 
+      /* switch volume when max_vol_parts_num is set and reached. Do it before next part is openned to avoid small orphean part at the end of volume */
+      if ((max_vol_parts_num) > 0 && (part >= max_vol_parts_num)) {
+         
+         Dmsg2(dbglvl, "max_vol_parts_num = %d exceeded by partidx= %d. Calling terminate_writing_volume\n", max_vol_parts_num, part);
+         terminate_writing_volume(dcr);
+         dev_errno = ENOSPC;
+         return false;
+      }
+
       if (!open_next_part(dcr)) {
          return false;
       }
index 6acf154d0149c926919870670a2dbd591f4c26ca..12a6b616627c87691a1e4b358a688099736192ef 100644 (file)
@@ -334,6 +334,7 @@ public:
    uint64_t adata_addr;               /* Next adata write address */
 
    uint64_t max_part_size;            /* max part size */
+   uint64_t max_vol_parts_num;        /* Max number of parts in a cloud volume */
    uint64_t part_size;                /* current part size */
    uint32_t part;                     /* current part number (starts at 0) */
    /* state ST_FREESPACE_OK is set if free_space is valid */
index 769aa5fa5b8c171192e654dec6adeded16d3c2ac..53f2b678f3902649f024111d4e7ccfa10a0481e5 100644 (file)
@@ -317,8 +317,10 @@ void DEVICE::device_generic_init(JCR *jcr, DEVRES *device)
    dev->crypto_device_ctx = NULL;
    if (dev->is_tape()) { /* No parts on tapes */
       dev->max_part_size = 0;
+      dev->max_vol_parts_num = 0;
    } else {
       dev->max_part_size = device->max_part_size;
+      dev->max_vol_parts_num = device->max_vol_parts_num;
    }
 
 #ifndef DEVELOPER
index 8362a1581c267fdb6b82d03b02f6207e128d56cb..1f06bf539faf616de607286e8d9eaccce70c3a87 100644 (file)
@@ -837,6 +837,7 @@ void create_volume_header(DEVICE *dev, const char *VolName,
       dev->VolHdr.VerNum = BaculaS3CloudVersion;
       dev->VolHdr.BlockSize = dev->max_block_size;
       dev->VolHdr.MaxPartSize = dev->max_part_size;
+      dev->VolHdr.MaxVolPartsNum = dev->max_vol_parts_num;
    } else {
       bstrncpy(dev->VolHdr.Id, BaculaId, sizeof(dev->VolHdr.Id));
       dev->VolHdr.VerNum = BaculaTapeVersion;
index 71da5620cddc0c7cf1ef7c5754f308cf0fc6a939..59f7b6957e202f173567f2a9f240a1f880619e8c 100644 (file)
@@ -207,6 +207,7 @@ struct Volume_Label {
 
   /* For Cloud */
   uint64_t  MaxPartSize;              /* Maximum Part Size */
+  uint64_t  MaxVolPartsNum;           /* Maximum Num of parts in a volume */
 
   /* For Volume encryption */
   bool is_vol_encrypted;
index 01dda27e07384266d0e5f3585ea3d056c9dcfbbc..b0cde618d27c2fc2bc38788c11b8ef314ae762e6 100644 (file)
@@ -191,6 +191,7 @@ static RES_ITEM dev_items[] = {
    {"MaximumJobSpoolSize",   store_size64, ITEM(res_dev.max_job_spool_size), 0, 0, 0},
    {"DriveIndex",            store_pint32, ITEM(res_dev.drive_index), 0, 0, 0},
    {"MaximumPartSize",       store_size64, ITEM(res_dev.max_part_size), 0, ITEM_DEFAULT, 0},
+   {"MaximumVolumeParts", store_size64, ITEM(res_dev.max_vol_parts_num), 0, ITEM_DEFAULT, 0},
    {"MountPoint",            store_strname,ITEM(res_dev.mount_point), 0, 0, 0},
    {"MountCommand",          store_strname,ITEM(res_dev.mount_command), 0, 0, 0},
    {"UnmountCommand",        store_strname,ITEM(res_dev.unmount_command), 0, 0, 0},
index ebadb8b87f05f093482cd92b62b69436c243a2d6..1cdd6de2b4ab20665031b657360d6a145f343e6c 100644 (file)
@@ -266,6 +266,7 @@ public:
    int64_t max_job_spool_size;        /* Max spool size for any single job */
 
    int64_t max_part_size;             /* Max part size */
+   int64_t max_vol_parts_num;          /* Max number of parts in a cloud volume */
    char *mount_point;                 /* Mount point for require mount devices */
    char *mount_command;               /* Mount command */
    char *unmount_command;             /* Unmount command */
diff --git a/regress/tests/cloud-maxvolpartnums-test b/regress/tests/cloud-maxvolpartnums-test
new file mode 100755 (executable)
index 0000000..4197bc4
--- /dev/null
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+# Copyright (C) 2000-2021 Kern Sibbald
+# Copyright (C) 2021-2022 Bacula Systems SA
+# License: BSD 2-Clause; see file LICENSE-FOSS
+#
+#  Short elementary Volume span test case for debugging.
+#  Here we create at least two volumes and the data spans the volume.
+#
+
+TestName="cloud-maxvolpartnums-test"
+JobName=MaxVolPartNums
+. scripts/functions
+
+copy_test_confs
+
+FORCE_FILE_SET=${FORCE_FILE_SET:-"${cwd}/build"}
+echo "$FORCE_FILE_SET" >${cwd}/tmp/file-list
+
+MaximumVolumeParts=5
+MaximumPartSize=1000000
+$bperl -e 'add_attribute("$conf/bacula-sd.conf", "MaximumPartSize", "'$MaximumPartSize'", "Device")'
+$bperl -e 'add_attribute("$conf/bacula-sd.conf", "MaximumVolumeParts", "'$MaximumVolumeParts'", "Device")'
+#$bperl -e 'add_attribute("$conf/bacula-sd.conf", "TruncateCache", "AfterUpload", "Cloud")'
+
+cp ${cwd}/bin/bacula-dir.conf ${cwd}/tmp/1
+sed "s%# Simple Label Format%  Label Format%" ${cwd}/tmp/1 >${cwd}/bin/bacula-dir.conf
+
+change_jobname Simple $JobName
+start_test
+
+cat <<END_OF_SCRIPT >${cwd}/tmp/bconcmds
+@$out /dev/null
+messages
+@$out ${cwd}/tmp/log1.out
+status all
+list pools
+messages
+@#setdebug level=110 storage
+run job=$JobName level=Full storage=File yes
+wait
+list pools
+list volumes
+messages
+END_OF_SCRIPT
+
+run_bacula
+
+for v in ${tmp}/Backup-*; do
+   for p in ${v}/part.*; do
+      # check the part size (must be <= MaximumPartSize)
+      size=$(stat -c "%s" $p);
+      if [ "$size" -gt "$MaximumPartSize" ]; then
+         print_debug "ERROR: part $p is too big $size > $MaximumPartSize";
+         estat=1;
+      fi
+      # check the part index (must be <= MAX_PART)
+      extension="${p##*.}";
+      if [ "$extension" -gt "$MaximumVolumeParts" ]; then
+         print_debug "ERROR: part $p is out-of-range: $extension > $MaximumVolumeParts";
+         estat=2;
+      fi
+   done
+
+# Now truncate every volume from the cache
+volume=$(basename $v);
+
+cat <<END_OF_SCRIPT >${cwd}/tmp/bconcmds
+messages
+truncate cache volume=$volume storage=File
+wait
+messages
+END_OF_SCRIPT
+run_bconsole
+
+done # for each volume
+
+cat <<END_OF_DATA >${cwd}/tmp/bconcmds
+@# 
+@# now do a restore
+@#
+@$out ${cwd}/tmp/log2.out
+@#setdebug level=500 storage=File
+restore where=${cwd}/tmp/bacula-restores select storage=File
+unmark *
+mark *
+pwd
+done
+yes
+wait
+messages
+@$out $tmp/log31.out
+cloud list storage=File
+@$out $tmp/log3.out
+cloud list storage=File
+quit
+END_OF_DATA
+
+run_bconsole
+
+check_for_zombie_jobs storage=File
+stop_bacula
+
+check_two_logs
+# Check single file
+diff -ur ${cwd}/build/$file ${tmp}/bacula-restores${src}
+if test $? -ne 0; then
+   dstat=1
+fi
+#check_restore_diff
+end_test