ZVOL Block Size Modifier: Fine-tuning for Optimal Performance
!!THIS IS BETA SOFTWARE!!
ZFS's ZVOLs, with their robustness and versatility, are a staple in many storage solutions. However, every system has room for enhancement. One such tweakable parameter in ZVOLs is the volblocksize. After its initial setup, traditional methods don't allow for this value to be altered. Yet, changing circumstances, performance metrics, or evolved best practices might shine a light on the need for such modification.
The ZVOL Block Size Modifier is here to facilitate this transformation, but with a twist: it creates a second copy of your chosen ZVOL with the new block size. This dual-copy approach ensures the integrity of your original data while providing an avenue for adjustments. However, an important implication of this method is the requirement for sufficient storage space. Before initiating the script, you must ensure there's ample free space to accommodate this duplicated data.
Understanding Block Sizes:
Using a block size that is too large for your typical data can lead to inefficiencies and fragmentation. For instance, if you have a ZVOL storing a database that typically writes data in 8K chunks, but you've set a 128K block size, you'll end up wasting space and potentially slowing down I/O operations.
Recommendation:
It's often advisable to test various block sizes in a controlled environment before deciding. Set up benchmarks that mirror your typical workload and monitor key metrics like I/O throughput, latency, and CPU usage across different block sizes. This empirical approach often provides the clearest indication of which size is optimal.
Lastly, always ensure you have regular backups of your data. Adjusting block sizes, while safe when done correctly, can introduce risks. Making changes to a production environment without backups is a risk not worth taking.
It's also worth noting, values that are not exposed in the TrueNAS UI, such as those greater than 128K or less than 4K may not work as expected.
Beginning the Journey: Selecting Your ZVOL
Upon starting the ZVOL Block Size Modifier, the script will helpfully present all your ZVOLs and their existing block sizes. From this lineup, simply identify the one you believe could benefit from adjustment.
The Heart of Customization: Determining the Size
Once you've made your pick, it's time for size determination. Whether you're leaning towards 4K, 128K, or any size in between, the choice of the new volblocksize is in your hands.
Tailoring the Environment: Cloning with Precision
With the specifications in place, the tool then embarks on the cloning process. It's about creating a bespoke environment for your data, so the script will forge a clone of your chosen ZVOL but with your decided block size.
Transition and Integrity: Migrating the Data
Following the cloning, the data migration phase swings into action. Content from your original ZVOL gets meticulously transferred to the newly crafted one. This step can be a test of patience, especially with substantial volumes of data, but the wait ensures the integrity and completeness of your information.
System Harmony: The Service Sync
To meld all changes seamlessly into the system, there's a service sync phase. The middlewared service undergoes a restart, ensuring the integration of the old with the new.
Reflecting on Origins: Deciding on the Original ZVOL
As the process nears completion, the script brings you to a decision point regarding the original ZVOL. You can opt to retain it, perhaps for backup or archival reasons, or you can choose to erase it, freeing up space and optimizing your storage.
A Word of Caution: While the script has been designed to manage your ZVOLs safely, operations at this level carry inherent risks. There's always the slim chance of data loss or anomalies. It's crucial to ensure you have up-to-date backups and have tested the process in a controlled environment before making adjustments to critical production data. Your data's safety is paramount, and proactive measures are the best defense.
Version v.0.03
Version v.0.02
Version v0.01
!!THIS IS BETA SOFTWARE!!
ZFS's ZVOLs, with their robustness and versatility, are a staple in many storage solutions. However, every system has room for enhancement. One such tweakable parameter in ZVOLs is the volblocksize. After its initial setup, traditional methods don't allow for this value to be altered. Yet, changing circumstances, performance metrics, or evolved best practices might shine a light on the need for such modification.
The ZVOL Block Size Modifier is here to facilitate this transformation, but with a twist: it creates a second copy of your chosen ZVOL with the new block size. This dual-copy approach ensures the integrity of your original data while providing an avenue for adjustments. However, an important implication of this method is the requirement for sufficient storage space. Before initiating the script, you must ensure there's ample free space to accommodate this duplicated data.
Understanding Block Sizes:
- In ZFS, the volblocksize for ZVOLs can range from 512 bytes to 128K.
- Warning: The default minimum block size is 8192. Setting volblocksize less than that size may have unintended consequences.
- To reduce wasted space a minimum volblocksize of 8192 is recommended.
- It defines the size of the data blocks used for the underlying storage.
- Sequential Workloads: If the I/O pattern is mostly sequential, such as in large file transfers, backups, or streaming, a larger block size (like 128K) can be efficient. Larger blocks mean fewer I/O operations for the same amount of data, making them more suited for sequential tasks.
- Random Workloads: If the I/O operations are mostly random, as seen in databases or virtualization environments, a smaller block size (like 8K or 16K) might be more appropriate. Smaller blocks reduce the risk of I/O amplification where reading or writing a small amount of data requires the system to handle a much larger block.
- HDDs: Traditional hard drives often benefit from larger block sizes due to their sequential read/write nature.
- SSDs: Solid-state drives can handle random access better than HDDs. Depending on the SSD's specifications and your workload, you might lean towards smaller block sizes.
Using a block size that is too large for your typical data can lead to inefficiencies and fragmentation. For instance, if you have a ZVOL storing a database that typically writes data in 8K chunks, but you've set a 128K block size, you'll end up wasting space and potentially slowing down I/O operations.
Recommendation:
It's often advisable to test various block sizes in a controlled environment before deciding. Set up benchmarks that mirror your typical workload and monitor key metrics like I/O throughput, latency, and CPU usage across different block sizes. This empirical approach often provides the clearest indication of which size is optimal.
Lastly, always ensure you have regular backups of your data. Adjusting block sizes, while safe when done correctly, can introduce risks. Making changes to a production environment without backups is a risk not worth taking.
It's also worth noting, values that are not exposed in the TrueNAS UI, such as those greater than 128K or less than 4K may not work as expected.
Beginning the Journey: Selecting Your ZVOL
Upon starting the ZVOL Block Size Modifier, the script will helpfully present all your ZVOLs and their existing block sizes. From this lineup, simply identify the one you believe could benefit from adjustment.
The Heart of Customization: Determining the Size
Once you've made your pick, it's time for size determination. Whether you're leaning towards 4K, 128K, or any size in between, the choice of the new volblocksize is in your hands.
Tailoring the Environment: Cloning with Precision
With the specifications in place, the tool then embarks on the cloning process. It's about creating a bespoke environment for your data, so the script will forge a clone of your chosen ZVOL but with your decided block size.
Transition and Integrity: Migrating the Data
Following the cloning, the data migration phase swings into action. Content from your original ZVOL gets meticulously transferred to the newly crafted one. This step can be a test of patience, especially with substantial volumes of data, but the wait ensures the integrity and completeness of your information.
System Harmony: The Service Sync
To meld all changes seamlessly into the system, there's a service sync phase. The middlewared service undergoes a restart, ensuring the integration of the old with the new.
Reflecting on Origins: Deciding on the Original ZVOL
As the process nears completion, the script brings you to a decision point regarding the original ZVOL. You can opt to retain it, perhaps for backup or archival reasons, or you can choose to erase it, freeing up space and optimizing your storage.
A Word of Caution: While the script has been designed to manage your ZVOLs safely, operations at this level carry inherent risks. There's always the slim chance of data loss or anomalies. It's crucial to ensure you have up-to-date backups and have tested the process in a controlled environment before making adjustments to critical production data. Your data's safety is paramount, and proactive measures are the best defense.
Version v.0.03
Code:
#!/bin/bash # Add this near the beginning of your script: declare -i clean_exit=0 # Define color functions color_output() { local color=$1 local text=$2 case $color in red) echo -e "\033[31m$text\033[0m" ;; green) echo -e "\033[32m$text\033[0m" ;; yellow) echo -e "\033[33m$text\033[0m" ;; blue) echo -e "\033[34m$text\033[0m" ;; purple) echo -e "\033[35m$text\033[0m" ;; cyan) echo -e "\033[36m$text\033[0m" ;; *) echo "$text" ;; esac } color_error() { echo "$(color_output red "$1")" } color_prompt() { echo "$(color_output blue "$1")" } bytes_to_human_readable() { local -i bytes=$1 if ((bytes > 1099511627776)); then echo $((bytes / 1099511627776))"T" elif ((bytes > 1073741824)); then echo $((bytes / 1073741824))"G" elif ((bytes > 1048576)); then echo $((bytes / 1048576))"M" elif ((bytes > 1024)); then echo $((bytes / 1024))"K" else echo $bytes"B" fi } cleanup() { if (( clean_exit == 0 )); then color_error "\nExiting early. Performing cleanup..." if [[ -n "$NEW_ZVOL_NAME" ]]; then zfs destroy "$NEW_ZVOL_NAME" 2>/dev/null fi fi } trap cleanup SIGINT SIGTERM SIGHUP tmp_error_log=$(mktemp) # Intro message echo "$(color_output yellow 'ZVOL Block Size Modifier v.03 - NickF')" echo "$(color_output cyan '- Larger volblocksize can help with mostly sequential workloads and will gain a compression efficiency')" echo "$(color_output green '- Smaller volblocksize can help with random workloads and minimize IO amplification, but will use more metadata and may have worse space efficiency')" echo "$(color_output red 'This program is free software under the terms of the GNU General Public License. It is provided WITHOUT WARRANTY. See <https://www.gnu.org/licenses/> for more details.')" read -p "Accept terms and proceed? [Y/N]: " choice echo "[DEBUG] User choice for accepting terms: $choice" if [[ "$choice" != [Yy] ]]; then exit 1 fi # Generate a list of existing ZVOLs zvols=($(zfs list -t volume -o name)) # Display the ZVOLs with aligned properties echo -e "\nAvailable ZVOLs:" for i in "${!zvols[@]}"; do if zfs list "${zvols[$i]}" &> /dev/null; then block_size=$(zfs get -H -o value volblocksize "${zvols[$i]}") provisioned_size=$(zfs get -H -o value volsize "${zvols[$i]}") used_space=$(zfs get -H -o value used "${zvols[$i]}") printf "[%d] %-50s Block Size: %-10s Used: %-10s Provisioned: %s\n" "$i" "${zvols[$i]}" "$block_size" "$used_space" "$provisioned_size" fi done # Get user's choice and validate read -p "Choose the zvol number you'd like to clone: " index echo "[DEBUG] User selected zvol number: $index" if [[ -z ${zvols[$index]} ]]; then color_error "Invalid zvol number." rm "$tmp_error_log" exit 1 fi OLD_ZVOL_NAME=${zvols[$index]} # Get the desired block size and validate read -p "Enter the new desired volblocksize (e.g. 8K, 16K, 32K): " NEW_BLOCK_SIZE echo "[DEBUG] User provided block size: $NEW_BLOCK_SIZE" if ! [[ $NEW_BLOCK_SIZE =~ ^[1-9][0-9]*[KkMmGgTt]$ ]]; then color_error "Invalid block size format." rm "$tmp_error_log" exit 1 fi NEW_ZVOL_NAME="${OLD_ZVOL_NAME}-${NEW_BLOCK_SIZE}" OLD_ZVOL_SIZE=$(zfs get -H -o value volsize "$OLD_ZVOL_NAME") # Convert OLD_ZVOL_SIZE to bytes for the calculation old_zvol_size_in_bytes=$(echo "$OLD_ZVOL_SIZE" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}') new_zvol_size_in_bytes=$(echo "$old_zvol_size_in_bytes" | awk '{print int($1 * 1.2)}') # Convert back to a human-readable size for zfs create (assuming GiB for simplicity) new_zvol_size_in_gib=$(( new_zvol_size_in_bytes / (1024**3) )) echo "[DEBUG] Attempting to create a new zvol with name: $NEW_ZVOL_NAME, block size: $NEW_BLOCK_SIZE, and size: ${new_zvol_size_in_gib}G." echo "INFO: New zvol will be created with a size 20% larger than the old one." zfs create -s -V "${new_zvol_size_in_gib}G" -b "$NEW_BLOCK_SIZE" "$NEW_ZVOL_NAME" 2>"$tmp_error_log" if [ $? -ne 0 ]; then color_error "Failed to create the new zvol. Here's the error:" cat "$tmp_error_log" rm "$tmp_error_log" exit 1 fi # Check the used space of the new ZVOL NEW_ZVOL_USED_SPACE=$(zfs get -H -o value used "$NEW_ZVOL_NAME") # Convert the used space to bytes for comparison NEW_ZVOL_USED_BYTES=$(echo "$NEW_ZVOL_USED_SPACE" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}') echo "INFO: Successfully created the new zvol: $NEW_ZVOL_NAME with the size: ${new_zvol_size_in_gib}G and block size: $NEW_BLOCK_SIZE." # After new ZVOL creation zpool_name=$(echo "$NEW_ZVOL_NAME" | cut -d'/' -f1) pool_status=$(zpool status -x "$zpool_name" | head -1) if [[ "$pool_status" != *"is healthy" ]]; then color_error "The zpool $zpool_name is in an unstable state: $pool_status. Exiting." rm "$tmp_error_log" exit 1 fi # Function to get the available space on the pool get_pool_avail_space_in_bytes() { local pool_name="$1" local avail_space avail_space=$(zfs list -H -o avail -r "$pool_name" | head -n 1) echo "$avail_space" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}' } # Function to get the size of the ZVOL in bytes get_zvol_size_in_bytes() { local zvol_name="$1" local zvol_size zvol_size=$(zfs get -H -o value used "$zvol_name") echo "$zvol_size" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}' } # Identify the pool name from ZVOL path. Assuming ZVOL is in format: pool_name/... POOL_NAME=$(echo "$OLD_ZVOL_NAME" | cut -d'/' -f1) # Check if there's enough space in the pool for the data transfer available_space_in_bytes=$(get_pool_avail_space_in_bytes "$POOL_NAME") old_zvol_size_in_bytes=$(echo "$OLD_ZVOL_SIZE" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}') if (( available_space_in_bytes < old_zvol_size_in_bytes )); then color_error "Not enough space in the zpool to proceed with the data transfer. Exiting." zfs destroy "$NEW_ZVOL_NAME" exit 1 fi echo "INFO: old_zvol_size_in_bytes = $old_zvol_size_in_bytes" echo "INFO: available_space_in_bytes = $available_space_in_bytes" # If there's enough space, proceed with the data transfer echo "$(color_output yellow "Starting data transfer from the old zvol to the new one. Depending on the ZVOL size, this process can take a while. Please be patient and don't interrupt the operation.")" dd if=/dev/zvol/"$OLD_ZVOL_NAME" of=/dev/zvol/"$NEW_ZVOL_NAME" bs="$NEW_BLOCK_SIZE" status=progress 2> errorlog.txt if [ $? -ne 0 ]; then color_error "Data transfer failed. Here's the error:" cat errorlog.txt rm errorlog.txt zfs destroy "$NEW_ZVOL_NAME" exit 1 else rm errorlog.txt # Removing errorlog.txt even if dd operation is successful fi # Check the used space of the new ZVOL NEW_ZVOL_USED_SPACE=$(zfs get -H -o value used "$NEW_ZVOL_NAME") MINIMUM_THRESHOLD=57344 # 56K in bytes if [ "$NEW_ZVOL_USED_BYTES" -lt "$MINIMUM_THRESHOLD" ]; then color_error "Data transfer might not have been successful. Used space on new ZVOL is suspiciously low." exit 1 fi # Confirmation for deletion echo "Do you want to delete the original zvol?" color_error "WARNING: This will permanently delete the original zvol!" read -p "Delete $OLD_ZVOL_NAME? [y/N]: " choice if [[ "$choice" == [Yy] ]]; then zfs destroy "$OLD_ZVOL_NAME" echo "$(color_output green 'Original zvol deleted successfully!')" else echo "$(color_output yellow 'Original zvol retained.')" fi # Restart the service if needed echo "$(color_output cyan 'Restarting the middlwared service can momentarily disrupt services relying on it. However, it might be necessary for changes to take effect in certain systems.')" read -p "Do you wish to restart the middlwared service now? [y/N]: " restart_choice if [[ "$restart_choice" == [Yy] ]]; then service middlwared restart echo "$(color_output green 'Middlwared service restarted successfully.')" fi # Summarize the actions echo "$(color_output green 'SUMMARY:')" echo "Original ZVOL: $OLD_ZVOL_NAME" echo "New ZVOL: $NEW_ZVOL_NAME" echo "Block Size Modified From: $(zfs get -H -o value volblocksize "$OLD_ZVOL_NAME") to: $NEW_BLOCK_SIZE" echo "Data Successfully Transferred: Yes" if [[ "$delete_choice" == [Yy] ]]; then echo "Original ZVOL Deleted: Yes" else echo "Original ZVOL Deleted: No" fi # Set clean_exit to 1 right before the script's successful termination message: echo "$(color_output green 'Operation completed successfully. Have a great day!')" clean_exit=1
Version v.0.02
Code:
#!/bin/bash # Add this near the beginning of your script: declare -i clean_exit=0 # Define color functions color_output() { local color=$1 local text=$2 case $color in red) echo -e "\033[31m$text\033[0m" ;; green) echo -e "\033[32m$text\033[0m" ;; yellow) echo -e "\033[33m$text\033[0m" ;; blue) echo -e "\033[34m$text\033[0m" ;; purple) echo -e "\033[35m$text\033[0m" ;; cyan) echo -e "\033[36m$text\033[0m" ;; *) echo "$text" ;; esac } color_error() { echo "$(color_output red "$1")" } color_prompt() { echo "$(color_output blue "$1")" } bytes_to_human_readable() { local -i bytes=$1 if ((bytes > 1099511627776)); then echo $((bytes / 1099511627776))"T" elif ((bytes > 1073741824)); then echo $((bytes / 1073741824))"G" elif ((bytes > 1048576)); then echo $((bytes / 1048576))"M" elif ((bytes > 1024)); then echo $((bytes / 1024))"K" else echo $bytes"B" fi } cleanup() { if (( clean_exit == 0 )); then color_error "\nExiting early. Performing cleanup..." if [[ -n "$NEW_ZVOL_NAME" ]]; then zfs destroy "$NEW_ZVOL_NAME" 2>/dev/null fi fi } trap cleanup SIGINT SIGTERM SIGHUP tmp_error_log=$(mktemp) # Intro message echo "$(color_output yellow 'ZVOL Block Size Modifier v.02 - NickF')" echo "$(color_output cyan '- Larger volblocksize can help with mostly sequential workloads and will gain a compression efficiency')" echo "$(color_output green '- Smaller volblocksize can help with random workloads and minimize IO amplification, but will use more metadata and may have worse space efficiency')" echo "$(color_output red 'This program is free software under the terms of the GNU General Public License. It is provided WITHOUT WARRANTY. See <https://www.gnu.org/licenses/> for more details.')" read -p "Accept terms and proceed? [Y/N]: " choice if [[ "$choice" != [Yy] ]]; then exit 1 fi # Generate a list of existing ZVOLs zvols=($(zfs list -t volume -o name)) # Display the ZVOLs with aligned properties echo -e "\nAvailable ZVOLs:" for i in "${!zvols[@]}"; do if zfs list "${zvols[$i]}" &> /dev/null; then block_size=$(zfs get -H -o value volblocksize "${zvols[$i]}") provisioned_size=$(zfs get -H -o value volsize "${zvols[$i]}") used_space=$(zfs get -H -o value used "${zvols[$i]}") printf "[%d] %-50s Block Size: %-10s Used: %-10s Provisioned: %s\n" "$i" "${zvols[$i]}" "$block_size" "$used_space" "$provisioned_size" fi done # Get user's choice and validate read -p "Choose the zvol number you'd like to clone: " index if [[ -z ${zvols[$index]} ]]; then color_error "Invalid zvol number." rm "$tmp_error_log" exit 1 fi OLD_ZVOL_NAME=${zvols[$index]} # Get the desired block size and validate read -p "Enter the new desired volblocksize (e.g. 8K, 16K, 32K): " NEW_BLOCK_SIZE if ! [[ $NEW_BLOCK_SIZE =~ ^[1-9][0-9]*[KkMmGgTt]$ ]]; then color_error "Invalid block size format." rm "$tmp_error_log" exit 1 fi NEW_ZVOL_NAME="${OLD_ZVOL_NAME}-${NEW_BLOCK_SIZE}" OLD_ZVOL_SIZE=$(zfs get -H -o value volsize "$OLD_ZVOL_NAME") # Convert OLD_ZVOL_SIZE to bytes for the calculation old_zvol_size_in_bytes=$(echo "$OLD_ZVOL_SIZE" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}') new_zvol_size_in_bytes=$(( old_zvol_size_in_bytes * 12 / 10 )) # Convert back to a human-readable size for zfs create (assuming GiB for simplicity) new_zvol_size_in_gib=$(( new_zvol_size_in_bytes / (1024**3) )) echo "INFO: Old zvol size was: $OLD_ZVOL_SIZE." echo "INFO: New zvol will be created with a size 20% larger than the old one." echo "INFO: Calculated new zvol size: ${new_zvol_size_in_gib}G." echo "Creating the new zvol named: $NEW_ZVOL_NAME with block size: $NEW_BLOCK_SIZE and size: ${new_zvol_size_in_gib}G." zfs create -s -V "${new_zvol_size_in_gib}G" -b "$NEW_BLOCK_SIZE" "$NEW_ZVOL_NAME" 2>"$tmp_error_log" if [ $? -ne 0 ]; then color_error "Failed to create the new zvol. Here's the error:" cat "$tmp_error_log" rm "$tmp_error_log" exit 1 fi echo "INFO: Successfully created the new zvol: $NEW_ZVOL_NAME with the size: ${new_zvol_size_in_gib}G and block size: $NEW_BLOCK_SIZE." # After new ZVOL creation zpool_name=$(echo "$NEW_ZVOL_NAME" | cut -d'/' -f1) pool_status=$(zpool status -x "$zpool_name" | head -1) if [[ "$pool_status" != *"is healthy" ]]; then color_error "The zpool $zpool_name is in an unstable state: $pool_status. Exiting." rm "$tmp_error_log" exit 1 fi # Function to get the available space on the pool get_pool_avail_space_in_bytes() { local pool_name="$1" local avail_space avail_space=$(zfs list -H -o avail -r "$pool_name" | head -n 1) echo "$avail_space" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}' } # Function to get the size of the ZVOL in bytes get_zvol_size_in_bytes() { local zvol_name="$1" local zvol_size zvol_size=$(zfs get -H -o value used "$zvol_name") echo "$zvol_size" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}' } # Identify the pool name from ZVOL path. Assuming ZVOL is in format: pool_name/... POOL_NAME=$(echo "$OLD_ZVOL_NAME" | cut -d'/' -f1) # Check if there's enough space in the pool for the data transfer available_space_in_bytes=$(get_pool_avail_space_in_bytes "$POOL_NAME") old_zvol_size_in_bytes=$(get_zvol_size_in_bytes "$OLD_ZVOL_NAME") if (( available_space_in_bytes < old_zvol_size_in_bytes )); then color_error "Not enough space in the zpool to proceed with the data transfer. Exiting." zfs destroy "$NEW_ZVOL_NAME" exit 1 fi echo "INFO: old_zvol_size_in_bytes = $old_zvol_size_in_bytes" echo "INFO: available_space_in_bytes = $available_space_in_bytes" # If there's enough space, proceed with the data transfer echo "$(color_output yellow "Starting data transfer from the old zvol to the new one. Depending on the ZVOL size, this process can take a while. Please be patient and don't interrupt the operation.")" dd if=/dev/zvol/"$OLD_ZVOL_NAME" of=/dev/zvol/"$NEW_ZVOL_NAME" bs="$NEW_BLOCK_SIZE" status=progress 2> errorlog.txt if [ $? -ne 0 ]; then color_error "Data transfer failed. Here's the error:" cat errorlog.txt rm errorlog.txt zfs destroy "$NEW_ZVOL_NAME" exit 1 fi # Check the used space of the new ZVOL NEW_ZVOL_USED_SPACE=$(zfs get -H -o value used "$NEW_ZVOL_NAME") # Convert the used space to bytes for comparison NEW_ZVOL_USED_BYTES=$(echo "$NEW_ZVOL_USED_SPACE" | awk '/K/ {print $1*1024} /M/ {print $1*1024^2} /G/ {print $1*1024^3} /T/ {print $1*1024^4}') MINIMUM_THRESHOLD=57344 # 56K in bytes if [ "$NEW_ZVOL_USED_BYTES" -lt "$MINIMUM_THRESHOLD" ]; then color_error "Data transfer might not have been successful. Used space on new ZVOL is suspiciously low." exit 1 fi # Confirmation for deletion echo "$(color_output yellow 'You are about to delete the original ZVOL which means the data will only exist on the new ZVOL with the modified block size. Make sure you have backups or you're certain about this action.')" read -p "Delete the original zvol ($OLD_ZVOL_NAME)? [y/N]: " delete_choice if [[ "$delete_choice" == [Yy] ]]; then zfs destroy "$OLD_ZVOL_NAME" echo "Original zvol $OLD_ZVOL_NAME has been deleted." else echo "$(color_output yellow 'Original ZVOL retained.')" fi # Restart the service if needed echo "$(color_output cyan 'Restarting the middlwared service can momentarily disrupt services relying on it. However, it might be necessary for changes to take effect in certain systems.')" read -p "Do you wish to restart the middlwared service now? [y/N]: " restart_choice if [[ "$restart_choice" == [Yy] ]]; then service middlwared restart echo "$(color_output green 'Middlwared service restarted successfully.')" fi # Summarize the actions echo "$(color_output green 'SUMMARY:')" echo "Original ZVOL: $OLD_ZVOL_NAME" echo "New ZVOL: $NEW_ZVOL_NAME" echo "Block Size Modified From: $(zfs get -H -o value volblocksize "$OLD_ZVOL_NAME") to: $NEW_BLOCK_SIZE" echo "Data Successfully Transferred: Yes" if [[ "$delete_choice" == [Yy] ]]; then echo "Original ZVOL Deleted: Yes" else echo "Original ZVOL Deleted: No" fi # Set clean_exit to 1 right before the script's successful termination message: echo "$(color_output green 'Operation completed successfully. Have a great day!')" clean_exit=1
Version v0.01
Code:
#!/bin/bash # Define color functions color_output() { local color=$1 local text=$2 case $color in red) echo -e "\033[31m$text\033[0m" ;; green) echo -e "\033[32m$text\033[0m" ;; yellow) echo -e "\033[33m$text\033[0m" ;; blue) echo -e "\033[34m$text\033[0m" ;; purple) echo -e "\033[35m$text\033[0m" ;; cyan) echo -e "\033[36m$text\033[0m" ;; *) echo "$text" ;; esac } color_error() { echo "$(color_output red "$1")" } cleanup() { color_error "\nCTRL+C detected. Exiting gracefully..." if [[ -n "$NEW_ZVOL_NAME" ]]; then zfs destroy "$NEW_ZVOL_NAME" 2>/dev/null fi exit 2 } trap cleanup SIGINT # Print the intro message echo "$(color_output yellow 'ZVOL Block Size Modifier v.01 - NickF')" echo "$(color_output cyan '- Larger volblocksize can help with mostly sequential workloads and will gain a compression efficiency')" echo "$(color_output green '- Smaller volblocksize can help with random workloads and minimize IO amplification, but will use more metadata and may have worse space efficiency')" echo "$(color_output red 'This program is free software under the terms of the GNU General Public License. It is provided WITHOUT WARRANTY. See <https://www.gnu.org/licenses/> for more details.')" read -p "Accept terms and proceed? [Y/N]: " choice if [[ "$choice" != [Yy] ]]; then exit 1 fi # Generate a list of existing ZVOLs and their block sizes zvols=($(zfs list -t volume -o name)) block_sizes=($(zfs get -H -o value volblocksize -t volume)) # Find the length of the longest zvol name max_length=0 for zvol in "${zvols[@]}"; do if [[ ${#zvol} -gt $max_length ]]; then max_length=${#zvol} fi done # Display the ZVOLs with aligned block sizes echo -e "\nAvailable ZVOLs:" for i in "${!zvols[@]}"; do printf "[%d] %-${max_length}s \t %s\n" "$i" "${zvols[$i]}" "${block_sizes[$i]}" done # Get the user's choice read -p "Choose the zvol number you'd like to clone: " index OLD_ZVOL_NAME=${zvols[$index]} OLD_ZVOL_SIZE=$(zfs get -H -o value volsize "$OLD_ZVOL_NAME") # Get the desired block size read -p "Enter the new desired volblocksize (e.g. 8K, 16K, 32K): " NEW_BLOCK_SIZE NEW_ZVOL_NAME="${OLD_ZVOL_NAME}-${NEW_BLOCK_SIZE}" echo "Creating the new zvol named: $NEW_ZVOL_NAME with block size: $NEW_BLOCK_SIZE." zfs create -s -V "$OLD_ZVOL_SIZE" -b "$NEW_BLOCK_SIZE" "$NEW_ZVOL_NAME" if [ $? -ne 0 ]; then color_error "Failed to create the new zvol. Exiting." exit 1 fi echo "$(color_output yellow 'Starting data transfer. This might take a while...')" dd if=/dev/zvol/"$OLD_ZVOL_NAME" of=/dev/zvol/"$NEW_ZVOL_NAME" bs="$NEW_BLOCK_SIZE" status=progress echo "Data transfer completed. Restarting the middlwared service..." service middlewared restart echo "Do you want to delete the original zvol?" color_error "WARNING: This will permanently delete the original zvol!" read -p "Delete $OLD_ZVOL_NAME? [Y/N]: " choice if [[ "$choice" == [Yy] ]]; then zfs destroy "$OLD_ZVOL_NAME" echo "$(color_output green 'Original zvol deleted successfully!')" else echo "$(color_output yellow 'Original zvol retained.')" fi echo "$(color_output green 'Script completed successfully!')"