From c92777fac23f0732dbbd36e02f33f5f5d103569f Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Sat, 26 Jul 2025 18:29:11 +0530 Subject: [PATCH 01/21] adding script to migrate csi to cnsa k8s env Signed-off-by: badri-pathak --- tools/pv_migration_csi_to cnsa.bash | 206 ++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 tools/pv_migration_csi_to cnsa.bash diff --git a/tools/pv_migration_csi_to cnsa.bash b/tools/pv_migration_csi_to cnsa.bash new file mode 100644 index 000000000..28b0d7cb7 --- /dev/null +++ b/tools/pv_migration_csi_to cnsa.bash @@ -0,0 +1,206 @@ +#!/bin/bash + +# Usage: ./pv_migration_csi_to cnsa.bash /var/mnt/remote-sample + +# Migrates Spectrum Scale CSI PVs to updated volumeHandle paths + +set -euo pipefail + +# --- Help Function --- +help() { + echo "" + echo "Usage: $0 " + echo "" + echo "Example:" + echo " $0 /var/mnt/remote-sample" + echo "" + echo "Description:" + echo " This script migrates Storage Scale CSI PVs to use the new path prefix based on the new volumeHandle." + echo " It backs up PV/PVC definitions, updates the volumeHandle path, and recreates PVs/PVCs." + echo "" + exit 1 +} + +if [[ $# -ne 1 || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + help +fi + +# OLD_PATH_PREFIX="$1" +NEW_PATH_PREFIX="$1" + +# --- Initialize Counters and Lists --- +success_list=() +fail_list=() +skip_list=() + +success_count=0 +fail_count=0 +skip_count=0 + +main() { + # echo "Using old path prefix: $OLD_PATH_PREFIX" + echo "Using new path prefix: $NEW_PATH_PREFIX" + init + get_pv_list + for PV in $ALL_PVS; do + migrate_each "$PV" + done + final_summary +} + +init() { + LOG_FILE_DIR="migration_logs" + mkdir -p "$LOG_FILE_DIR" + LOG_FILE="$LOG_FILE_DIR/migration_$(date +%Y%m%d_%H%M%S).log" + + exec > >(tee "$LOG_FILE") 2>&1 + + echo "Logging to $LOG_FILE" + echo "Starting migration at: $(date)" + echo "" +} + +get_pv_list() { + ALL_PVS=$(kubectl get pv -o json | jq -r '.items[] | select(.spec.csi.driver == "spectrumscale.csi.ibm.com") | .metadata.name') + echo "Found $(echo "$ALL_PVS" | wc -l) PVs using spectrumscale.csi.ibm.com driver" +} + +migrate_each() { + PV="$1" + echo "--------------------------------------------------------------------------------" + echo "Processing PV: $PV" + + VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') + echo " Current volumeHandle: $VOLUME_HANDLE" + + if [[ "$VOLUME_HANDLE" == *"$NEW_PATH_PREFIX"* ]]; then + echo "Already migrated (volumeHandle contains new path). Skipping $PV" + ((skip_count++)) + skip_list+=("$PV") + return + fi + + PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') + PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') + + echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" + + if ! kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" >/dev/null 2>&1; then + echo "PVC $PVC_NAME not found in namespace $PVC_NAMESPACE, skipping." + fail_list+=("$PV") + ((fail_count++)) + return + fi + + # Extract key PV/PVC attributes + ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') + STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') + STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') + VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') + ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') + + + DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') + FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') + + # Construct new volumeHandle with updated path + OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') + # NEW_PATH=$(echo "$OLD_PATH" | sed "s|$OLD_PATH_PREFIX|$NEW_PATH_PREFIX|") + NEW_PATH="$NEW_PATH_PREFIX/$PV/$PV-data" + NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") + + echo "Updating volumeHandle to: $NEW_VOLUME_HANDLE" + + # Set PV reclaim policy to Retain + kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}' + + # Backup original PV/PVC + BACKUP_DIR="backup_pv_pvc/${PVC_NAME}" + mkdir -p "$BACKUP_DIR" + + echo "Taking backup of PV ${PV}" + kubectl get pv "$PV" -o yaml > "$BACKUP_DIR/$PV.yaml" + + echo "Taking backup of PVC ${PVC_NAME}" + kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o yaml > "$BACKUP_DIR/$PVC_NAME.yaml" + + echo "Backups stored under: $BACKUP_DIR" + + echo "Deleting PVC and PV..." + kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" + kubectl delete pv "$PV" + + echo "Recreating PV $PV with updated volumeHandle..." + + cat < 0 )); then + echo "Successful PVs: ${#success_list[@]}" + for pv in "${success_list[@]}"; do echo " - $pv"; done + echo "" + fi + + if (( ${#fail_list[@]} > 0 )); then + echo "Failed PVs: ${#fail_list[@]}" + for pv in "${fail_list[@]}"; do echo " - $pv"; done + echo "" + fi + + if (( ${#skip_list[@]} > 0 )); then + echo "Skipped PVs (already migrated): ${#skip_list[@]}" + for pv in "${skip_list[@]}"; do echo " - $pv"; done + echo "" + fi + + echo "Completed migration at: $(date)" +} + +main + +exit 0 From 700a6b87e22a87eabbd44e311c925d553e921ec3 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Wed, 30 Jul 2025 03:06:59 +0530 Subject: [PATCH 02/21] updated for multiple remotefs support Signed-off-by: badri-pathak --- ...ion_csi_to cnsa.bash => pv_migration_csi_to_cnsa.bash} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename tools/{pv_migration_csi_to cnsa.bash => pv_migration_csi_to_cnsa.bash} (96%) diff --git a/tools/pv_migration_csi_to cnsa.bash b/tools/pv_migration_csi_to_cnsa.bash similarity index 96% rename from tools/pv_migration_csi_to cnsa.bash rename to tools/pv_migration_csi_to_cnsa.bash index 28b0d7cb7..fa4ccaa2c 100644 --- a/tools/pv_migration_csi_to cnsa.bash +++ b/tools/pv_migration_csi_to_cnsa.bash @@ -1,6 +1,6 @@ #!/bin/bash -# Usage: ./pv_migration_csi_to cnsa.bash /var/mnt/remote-sample +# Usage: ./pv_migration_csi_to_cnsa.bash /var/mnt # Migrates Spectrum Scale CSI PVs to updated volumeHandle paths @@ -12,7 +12,7 @@ help() { echo "Usage: $0 " echo "" echo "Example:" - echo " $0 /var/mnt/remote-sample" + echo " $0 /var/mnt" echo "" echo "Description:" echo " This script migrates Storage Scale CSI PVs to use the new path prefix based on the new volumeHandle." @@ -98,7 +98,7 @@ migrate_each() { STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') - + VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') @@ -106,7 +106,7 @@ migrate_each() { # Construct new volumeHandle with updated path OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') # NEW_PATH=$(echo "$OLD_PATH" | sed "s|$OLD_PATH_PREFIX|$NEW_PATH_PREFIX|") - NEW_PATH="$NEW_PATH_PREFIX/$PV/$PV-data" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") echo "Updating volumeHandle to: $NEW_VOLUME_HANDLE" From ece04376625d7bb767827b948b6543136129b1d9 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Wed, 30 Jul 2025 22:19:26 +0530 Subject: [PATCH 03/21] added checks for persistentVolumeReclaimPolicy to Retain Signed-off-by: badri-pathak --- tools/pv_migration_csi_to_cnsa.bash | 71 ++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/tools/pv_migration_csi_to_cnsa.bash b/tools/pv_migration_csi_to_cnsa.bash index fa4ccaa2c..316fd0be9 100644 --- a/tools/pv_migration_csi_to_cnsa.bash +++ b/tools/pv_migration_csi_to_cnsa.bash @@ -2,7 +2,7 @@ # Usage: ./pv_migration_csi_to_cnsa.bash /var/mnt -# Migrates Spectrum Scale CSI PVs to updated volumeHandle paths +# Migrates Spectrum Scale CSI PVs to updated volumeHandle for CNSA set -euo pipefail @@ -25,7 +25,6 @@ if [[ $# -ne 1 || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then help fi -# OLD_PATH_PREFIX="$1" NEW_PATH_PREFIX="$1" # --- Initialize Counters and Lists --- @@ -38,7 +37,6 @@ fail_count=0 skip_count=0 main() { - # echo "Using old path prefix: $OLD_PATH_PREFIX" echo "Using new path prefix: $NEW_PATH_PREFIX" init get_pv_list @@ -70,7 +68,9 @@ migrate_each() { echo "--------------------------------------------------------------------------------" echo "Processing PV: $PV" - VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') + VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { + echo "Failed to get volumeHandle for PV: $PV"; ((fail_count++)); fail_list+=("$PV"); return; + } echo " Current volumeHandle: $VOLUME_HANDLE" if [[ "$VOLUME_HANDLE" == *"$NEW_PATH_PREFIX"* ]]; then @@ -80,8 +80,12 @@ migrate_each() { return fi - PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') - PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') + PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') || { + echo "Failed to get PVC name for PV: $PV"; ((fail_count++)); fail_list+=("$PV"); return; + } + PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') || { + echo "Failed to get PVC namespace for PV: $PV"; ((fail_count++)); fail_list+=("$PV"); return; + } echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" @@ -92,29 +96,46 @@ migrate_each() { return fi - # Extract key PV/PVC attributes - ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') - STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') - STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') - VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') - ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') - VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') + ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') || { + echo "Failed to get access modes."; ((fail_count++)); fail_list+=("$PV"); return; + } + STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') || { + echo "Failed to get storage size."; ((fail_count++)); fail_list+=("$PV"); return; + } + STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') || { + echo "Failed to get storage class."; ((fail_count++)); fail_list+=("$PV"); return; + } + VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') || { + echo "Failed to get volume mode."; ((fail_count++)); fail_list+=("$PV"); return; + } + ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') || { + echo "Failed to get volumeAttributes."; ((fail_count++)); fail_list+=("$PV"); return; + } + VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { + echo "Failed to get volBackendFs."; ((fail_count++)); fail_list+=("$PV"); return; + } + + DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { + echo "Failed to get driver."; ((fail_count++)); fail_list+=("$PV"); return; + } + FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { + echo "Failed to get fsType."; ((fail_count++)); fail_list+=("$PV"); return; + } - DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') - FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') - - # Construct new volumeHandle with updated path OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') - # NEW_PATH=$(echo "$OLD_PATH" | sed "s|$OLD_PATH_PREFIX|$NEW_PATH_PREFIX|") NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") echo "Updating volumeHandle to: $NEW_VOLUME_HANDLE" - # Set PV reclaim policy to Retain - kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}' + echo "Setting reclaim policy to Retain for PV: $PV" + if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then + echo "Failed to patch reclaim policy for PV: $PV" + ((fail_count++)) + fail_list+=("$PV") + return + fi - # Backup original PV/PVC BACKUP_DIR="backup_pv_pvc/${PVC_NAME}" mkdir -p "$BACKUP_DIR" @@ -126,6 +147,14 @@ migrate_each() { echo "Backups stored under: $BACKUP_DIR" + RECLAIM_POLICY=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') + if [[ "$RECLAIM_POLICY" != "Retain" ]]; then + echo "Reclaim policy for PV $PV is not 'Retain' (got: $RECLAIM_POLICY). Skipping migration." + ((fail_count++)) + fail_list+=("$PV") + return + fi + echo "Deleting PVC and PV..." kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" kubectl delete pv "$PV" From d8ba14257e41448030c318944f0578ddac156e5d Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Sun, 3 Aug 2025 05:23:59 +0530 Subject: [PATCH 04/21] updated script to handle all types of volumes Signed-off-by: badri-pathak --- ...csi_to_cnsa.bash => migration_pv_pvc.bash} | 180 ++++++++++++------ 1 file changed, 125 insertions(+), 55 deletions(-) rename tools/{pv_migration_csi_to_cnsa.bash => migration_pv_pvc.bash} (52%) diff --git a/tools/pv_migration_csi_to_cnsa.bash b/tools/migration_pv_pvc.bash similarity index 52% rename from tools/pv_migration_csi_to_cnsa.bash rename to tools/migration_pv_pvc.bash index 316fd0be9..aef2eb107 100644 --- a/tools/pv_migration_csi_to_cnsa.bash +++ b/tools/migration_pv_pvc.bash @@ -1,31 +1,51 @@ #!/bin/bash -# Usage: ./pv_migration_csi_to_cnsa.bash /var/mnt - -# Migrates Spectrum Scale CSI PVs to updated volumeHandle for CNSA +# Usage: ./migration_pv_pvc.bash --new_path_prefix /var/mnt +# Migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle. set -euo pipefail # --- Help Function --- help() { echo "" - echo "Usage: $0 " + echo "Usage: $0 --new_path_prefix " echo "" echo "Example:" - echo " $0 /var/mnt" + echo " $0 --new_path_prefix /var/mnt" echo "" echo "Description:" - echo " This script migrates Storage Scale CSI PVs to use the new path prefix based on the new volumeHandle." - echo " It backs up PV/PVC definitions, updates the volumeHandle path, and recreates PVs/PVCs." + echo " This script migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle." + echo " It supports parallel migration (max 10 at a time)." echo "" exit 1 } -if [[ $# -ne 1 || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then +# --- Argument Parsing --- +NEW_PATH_PREFIX="" +PARALLELISM=1 + +while [[ $# -gt 0 ]]; do + case "$1" in + --new_path_prefix) + shift + NEW_PATH_PREFIX="$1" + ;; + -h|--help) + help + ;; + *) + echo "Unknown argument: $1" + help + ;; + esac + shift +done + +if [[ -z "$NEW_PATH_PREFIX" ]]; then + echo "Missing required argument: --new_path_prefix" help fi -NEW_PATH_PREFIX="$1" # --- Initialize Counters and Lists --- success_list=() @@ -40,17 +60,28 @@ main() { echo "Using new path prefix: $NEW_PATH_PREFIX" init get_pv_list + TOTAL_PVS=$(echo "$ALL_PVS" | wc -l | xargs) + COUNT=1 for PV in $ALL_PVS; do + echo "" + echo "[$COUNT/$TOTAL_PVS] --------------------------------------------------------------------------------" + echo "Processing PV: $PV " migrate_each "$PV" + COUNT=$((COUNT + 1)) done final_summary } init() { - LOG_FILE_DIR="migration_logs" - mkdir -p "$LOG_FILE_DIR" - LOG_FILE="$LOG_FILE_DIR/migration_$(date +%Y%m%d_%H%M%S).log" + RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) + + BACKUP_PARENT_DIR="csi_migration_data" + mkdir -p "$BACKUP_PARENT_DIR" + + BACKUP_BASE_DIR="$BACKUP_PARENT_DIR/$RUN_TIMESTAMP" + mkdir -p "$BACKUP_BASE_DIR" + LOG_FILE="$BACKUP_BASE_DIR/migration.log" exec > >(tee "$LOG_FILE") 2>&1 echo "Logging to $LOG_FILE" @@ -65,93 +96,119 @@ get_pv_list() { migrate_each() { PV="$1" - echo "--------------------------------------------------------------------------------" - echo "Processing PV: $PV" + # echo "--------------------------------------------------------------------------------" + # echo "Processing PV: $PV" VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { - echo "Failed to get volumeHandle for PV: $PV"; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get volumeHandle for PV: $PV"; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } - echo " Current volumeHandle: $VOLUME_HANDLE" - - if [[ "$VOLUME_HANDLE" == *"$NEW_PATH_PREFIX"* ]]; then - echo "Already migrated (volumeHandle contains new path). Skipping $PV" - ((skip_count++)) - skip_list+=("$PV") - return - fi + echo "Current volumeHandle: $VOLUME_HANDLE" PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') || { - echo "Failed to get PVC name for PV: $PV"; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get PVC name for PV: $PV"; ((fail_count++)); fail_list+=("|$PV"); return; } PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') || { - echo "Failed to get PVC namespace for PV: $PV"; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get PVC namespace for PV: $PV"; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" if ! kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" >/dev/null 2>&1; then echo "PVC $PVC_NAME not found in namespace $PVC_NAMESPACE, skipping." - fail_list+=("$PV") + fail_list+=("$PVC_NAME|$PV") ((fail_count++)) return fi ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') || { - echo "Failed to get access modes."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get access modes."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') || { - echo "Failed to get storage size."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get storage size."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') || { - echo "Failed to get storage class."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get storage class."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') || { - echo "Failed to get volume mode."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get volume mode."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') || { - echo "Failed to get volumeAttributes."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get volumeAttributes."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { - echo "Failed to get volBackendFs."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get volBackendFs."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { - echo "Failed to get driver."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get driver."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { - echo "Failed to get fsType."; ((fail_count++)); fail_list+=("$PV"); return; + echo "Failed to get fsType."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; } OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + IFS=';' read -ra parts <<< "$VOLUME_HANDLE" + + if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then + echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" + + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then + echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" + PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then + echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + + elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then + echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" + NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#/ibm/}" + + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then + echo "Detected volumeHandle type: 0;3 (version2) : Shallow copy fileset" + NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#/ibm/}" + + else + echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" + fail_list+=("$PVC_NAME|$PV") + ((fail_count++)) + return + fi + NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") - echo "Updating volumeHandle to: $NEW_VOLUME_HANDLE" + if [[ "$VOLUME_HANDLE" == "$NEW_VOLUME_HANDLE" ]]; then + echo "Already migrated (volumeHandle matches target). Skipping $PV" + ((skip_count++)) + skip_list+=("$PVC_NAME|$PV") + return + fi + + echo "Updated volumeHandle: $NEW_VOLUME_HANDLE" echo "Setting reclaim policy to Retain for PV: $PV" if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then echo "Failed to patch reclaim policy for PV: $PV" ((fail_count++)) - fail_list+=("$PV") + fail_list+=("$PVC_NAME|$PV") return fi - BACKUP_DIR="backup_pv_pvc/${PVC_NAME}" + BACKUP_DIR="$BACKUP_BASE_DIR/${PVC_NAME}" mkdir -p "$BACKUP_DIR" - echo "Taking backup of PV ${PV}" - kubectl get pv "$PV" -o yaml > "$BACKUP_DIR/$PV.yaml" - - echo "Taking backup of PVC ${PVC_NAME}" - kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o yaml > "$BACKUP_DIR/$PVC_NAME.yaml" - - echo "Backups stored under: $BACKUP_DIR" + echo "Backing up PV and PVC..." + kubectl get pv "$PV" -o yaml > "$BACKUP_DIR/pv.yaml" + kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o yaml > "$BACKUP_DIR/pvc.yaml" RECLAIM_POLICY=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') if [[ "$RECLAIM_POLICY" != "Retain" ]]; then - echo "Reclaim policy for PV $PV is not 'Retain' (got: $RECLAIM_POLICY). Skipping migration." + echo "Reclaim policy is not Retain (got: $RECLAIM_POLICY), skipping." ((fail_count++)) - fail_list+=("$PV") + fail_list+=("$PVC_NAME|$PV") return fi @@ -159,8 +216,7 @@ migrate_each() { kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" kubectl delete pv "$PV" - echo "Recreating PV $PV with updated volumeHandle..." - + echo "Recreating PV and PVC..." cat < 0 )); then echo "Successful PVs: ${#success_list[@]}" - for pv in "${success_list[@]}"; do echo " - $pv"; done + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "--------------------------------------------------------------------------" + for entry in "${success_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done echo "" fi if (( ${#fail_list[@]} > 0 )); then echo "Failed PVs: ${#fail_list[@]}" - for pv in "${fail_list[@]}"; do echo " - $pv"; done + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "--------------------------------------------------------------------------" + for entry in "${fail_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done echo "" fi if (( ${#skip_list[@]} > 0 )); then echo "Skipped PVs (already migrated): ${#skip_list[@]}" - for pv in "${skip_list[@]}"; do echo " - $pv"; done + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "--------------------------------------------------------------------------" + for entry in "${skip_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done echo "" fi echo "Completed migration at: $(date)" } + main exit 0 From 1cfc50f1f07263766a3b23a680fb4ca66bca607e Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Mon, 4 Aug 2025 02:23:53 +0530 Subject: [PATCH 05/21] updated for snapshot and shallow copy Signed-off-by: badri-pathak --- tools/migration_pv_pvc.bash | 41 ++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/tools/migration_pv_pvc.bash b/tools/migration_pv_pvc.bash index aef2eb107..47104e2b7 100644 --- a/tools/migration_pv_pvc.bash +++ b/tools/migration_pv_pvc.bash @@ -1,6 +1,6 @@ #!/bin/bash -# Usage: ./migration_pv_pvc.bash --new_path_prefix /var/mnt +# Usage: ./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt # Migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle. set -euo pipefail @@ -13,16 +13,16 @@ help() { echo "Example:" echo " $0 --new_path_prefix /var/mnt" echo "" + echo " These must match the base mount point of Spectrum Scale on your nodes." + echo "" echo "Description:" echo " This script migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle." - echo " It supports parallel migration (max 10 at a time)." echo "" exit 1 } # --- Argument Parsing --- NEW_PATH_PREFIX="" -PARALLELISM=1 while [[ $# -gt 0 ]]; do case "$1" in @@ -46,6 +46,13 @@ if [[ -z "$NEW_PATH_PREFIX" ]]; then help fi +# TODO: Uncomment the following lines to enforce allowed path prefixes +# # Enforce allowed path prefixes +# if [[ "$NEW_PATH_PREFIX" != "/ibm" && "$NEW_PATH_PREFIX" != "/var/mnt" && "$NEW_PATH_PREFIX" != "/mnt" ]]; then +# echo "Error: Unsupported --new_path_prefix value: '$NEW_PATH_PREFIX'" +# echo "Allowed values are: /ibm, /var/mnt, /mnt" +# exit 1 +# fi # --- Initialize Counters and Lists --- success_list=() @@ -57,7 +64,14 @@ fail_count=0 skip_count=0 main() { - echo "Using new path prefix: $NEW_PATH_PREFIX" + echo "Using the new path prefix: $NEW_PATH_PREFIX" + echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." + echo "" + read -rp "Proceed with migration? (yes/y/Y to continue): " CONFIRM + if [[ "$CONFIRM" != "yes" && "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then + echo "Aborting migration." + exit 0 + fi init get_pv_list TOTAL_PVS=$(echo "$ALL_PVS" | wc -l | xargs) @@ -96,8 +110,6 @@ get_pv_list() { migrate_each() { PV="$1" - # echo "--------------------------------------------------------------------------------" - # echo "Processing PV: $PV" VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { echo "Failed to get volumeHandle for PV: $PV"; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; @@ -165,11 +177,13 @@ migrate_each() { elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" - NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#/ibm/}" + CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" + NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then echo "Detected volumeHandle type: 0;3 (version2) : Shallow copy fileset" - NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#/ibm/}" + CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" + NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" else echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" @@ -253,7 +267,7 @@ spec: EOF echo "Migration successful for PV: $PV and PVC: $PVC_NAME" - echo "--------------------------------------------------------------------------------" + echo "----------------------------------------------------------------------------------------" ((success_count++)) success_list+=("$PVC_NAME|$PV") } @@ -261,12 +275,12 @@ EOF final_summary() { echo "" echo "Migration Summary:" - echo "----------------------------" + echo "----------------------------------------------------------------------------------------------------------------------------" if (( ${#success_list[@]} > 0 )); then echo "Successful PVs: ${#success_list[@]}" printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "--------------------------------------------------------------------------" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" for entry in "${success_list[@]}"; do IFS='|' read -r pvc pv <<< "$entry" printf " %-80s | %s\n" "$pvc" "$pv" @@ -277,7 +291,7 @@ final_summary() { if (( ${#fail_list[@]} > 0 )); then echo "Failed PVs: ${#fail_list[@]}" printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "--------------------------------------------------------------------------" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" for entry in "${fail_list[@]}"; do IFS='|' read -r pvc pv <<< "$entry" printf " %-80s | %s\n" "$pvc" "$pv" @@ -288,7 +302,7 @@ final_summary() { if (( ${#skip_list[@]} > 0 )); then echo "Skipped PVs (already migrated): ${#skip_list[@]}" printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "--------------------------------------------------------------------------" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" for entry in "${skip_list[@]}"; do IFS='|' read -r pvc pv <<< "$entry" printf " %-80s | %s\n" "$pvc" "$pv" @@ -299,7 +313,6 @@ final_summary() { echo "Completed migration at: $(date)" } - main exit 0 From 62e0d3b9524620c076d72e4524e3b0a7c74ccc68 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Mon, 4 Aug 2025 19:21:32 +0530 Subject: [PATCH 06/21] added for static pv in dynamic Signed-off-by: badri-pathak --- tools/migration_pv_pvc.bash | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/migration_pv_pvc.bash b/tools/migration_pv_pvc.bash index 47104e2b7..34a5c2402 100644 --- a/tools/migration_pv_pvc.bash +++ b/tools/migration_pv_pvc.bash @@ -171,10 +171,26 @@ migrate_each() { PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and dependent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + fi + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" + + # Default path of dynamic fileset NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and independent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + fi + elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" From 3a8ef43e952d89c5319d87df69a16f3dd63a0b79 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Wed, 13 Aug 2025 12:19:15 +0530 Subject: [PATCH 07/21] fixed counter issue for bash v5 Signed-off-by: badri-pathak --- tools/migration_pv_pvc.bash | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tools/migration_pv_pvc.bash b/tools/migration_pv_pvc.bash index 34a5c2402..38012fa32 100644 --- a/tools/migration_pv_pvc.bash +++ b/tools/migration_pv_pvc.bash @@ -112,15 +112,15 @@ migrate_each() { PV="$1" VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { - echo "Failed to get volumeHandle for PV: $PV"; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get volumeHandle for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } echo "Current volumeHandle: $VOLUME_HANDLE" PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') || { - echo "Failed to get PVC name for PV: $PV"; ((fail_count++)); fail_list+=("|$PV"); return; + echo "Failed to get PVC name for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("|$PV"); return; } PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') || { - echo "Failed to get PVC namespace for PV: $PV"; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get PVC namespace for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" @@ -128,34 +128,34 @@ migrate_each() { if ! kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" >/dev/null 2>&1; then echo "PVC $PVC_NAME not found in namespace $PVC_NAMESPACE, skipping." fail_list+=("$PVC_NAME|$PV") - ((fail_count++)) + fail_count=$(expr $fail_count + 1) return fi ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') || { - echo "Failed to get access modes."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get access modes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') || { - echo "Failed to get storage size."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get storage size."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') || { - echo "Failed to get storage class."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get storage class."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') || { - echo "Failed to get volume mode."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get volume mode."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') || { - echo "Failed to get volumeAttributes."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get volumeAttributes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { - echo "Failed to get volBackendFs."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get volBackendFs."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { - echo "Failed to get driver."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get driver."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { - echo "Failed to get fsType."; ((fail_count++)); fail_list+=("$PVC_NAME|$PV"); return; + echo "Failed to get fsType."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') @@ -204,7 +204,7 @@ migrate_each() { else echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" fail_list+=("$PVC_NAME|$PV") - ((fail_count++)) + fail_count=$(expr $fail_count + 1) return fi @@ -212,7 +212,7 @@ migrate_each() { if [[ "$VOLUME_HANDLE" == "$NEW_VOLUME_HANDLE" ]]; then echo "Already migrated (volumeHandle matches target). Skipping $PV" - ((skip_count++)) + skip_count=$(expr $skip_count + 1) skip_list+=("$PVC_NAME|$PV") return fi @@ -222,7 +222,7 @@ migrate_each() { echo "Setting reclaim policy to Retain for PV: $PV" if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then echo "Failed to patch reclaim policy for PV: $PV" - ((fail_count++)) + fail_count=$(expr $fail_count + 1) fail_list+=("$PVC_NAME|$PV") return fi @@ -237,7 +237,7 @@ migrate_each() { RECLAIM_POLICY=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') if [[ "$RECLAIM_POLICY" != "Retain" ]]; then echo "Reclaim policy is not Retain (got: $RECLAIM_POLICY), skipping." - ((fail_count++)) + fail_count=$(expr $fail_count + 1) fail_list+=("$PVC_NAME|$PV") return fi @@ -284,7 +284,7 @@ EOF echo "Migration successful for PV: $PV and PVC: $PVC_NAME" echo "----------------------------------------------------------------------------------------" - ((success_count++)) + success_count=$(expr $success_count + 1) success_list+=("$PVC_NAME|$PV") } From 827f93e0a2e941abbfe1c3e95e119578464bb071 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Wed, 13 Aug 2025 12:31:04 +0530 Subject: [PATCH 08/21] restrict users for certain allowed path prefix. Signed-off-by: badri-pathak --- tools/migration_pv_pvc.bash | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/migration_pv_pvc.bash b/tools/migration_pv_pvc.bash index 38012fa32..a36f9b746 100644 --- a/tools/migration_pv_pvc.bash +++ b/tools/migration_pv_pvc.bash @@ -46,13 +46,13 @@ if [[ -z "$NEW_PATH_PREFIX" ]]; then help fi -# TODO: Uncomment the following lines to enforce allowed path prefixes -# # Enforce allowed path prefixes -# if [[ "$NEW_PATH_PREFIX" != "/ibm" && "$NEW_PATH_PREFIX" != "/var/mnt" && "$NEW_PATH_PREFIX" != "/mnt" ]]; then -# echo "Error: Unsupported --new_path_prefix value: '$NEW_PATH_PREFIX'" -# echo "Allowed values are: /ibm, /var/mnt, /mnt" -# exit 1 -# fi +#TODO: Uncomment the following lines to enforce allowed path prefixes +# Enforce allowed path prefixes +if [[ "$NEW_PATH_PREFIX" != "/ibm" && "$NEW_PATH_PREFIX" != "/var/mnt" && "$NEW_PATH_PREFIX" != "/mnt" ]]; then + echo "Error: Unsupported --new_path_prefix value: '$NEW_PATH_PREFIX'" + echo "Allowed values are: /ibm, /var/mnt, /mnt" + exit 1 +fi # --- Initialize Counters and Lists --- success_list=() From 9399b5df2c841a002cda6de58350bd3b593ce57f Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Mon, 18 Aug 2025 12:41:03 +0530 Subject: [PATCH 09/21] added csi to cnsa migration script Signed-off-by: badri-pathak --- tools/migration_csi_to_cnsa.bash | 337 +++++++++++++++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 tools/migration_csi_to_cnsa.bash diff --git a/tools/migration_csi_to_cnsa.bash b/tools/migration_csi_to_cnsa.bash new file mode 100644 index 000000000..de3a14b33 --- /dev/null +++ b/tools/migration_csi_to_cnsa.bash @@ -0,0 +1,337 @@ +#!/bin/bash + +# Usage: ./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt +# Migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle. + +set -euo pipefail + +# --- Help Function --- +help() { + echo "" + echo "Usage: $0 --new_path_prefix " + echo "" + echo "Example:" + echo " $0 --new_path_prefix /var/mnt" + echo "" + echo " These must match the base mount point of Spectrum Scale on your nodes." + echo "" + echo "Description:" + echo " This script migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle." + echo "" + exit 1 +} + +# --- Argument Parsing --- +NEW_PATH_PREFIX="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --new_path_prefix) + shift + NEW_PATH_PREFIX="$1" + ;; + -h|--help) + help + ;; + *) + echo "Unknown argument: $1" + help + ;; + esac + shift +done + +if [[ -z "$NEW_PATH_PREFIX" ]]; then + echo "Missing required argument: --new_path_prefix" + help +fi + +#TODO: Uncomment the following lines to enforce allowed path prefixes +# Enforce allowed path prefixes +if [[ "$NEW_PATH_PREFIX" != "/ibm" && "$NEW_PATH_PREFIX" != "/var/mnt" && "$NEW_PATH_PREFIX" != "/mnt" ]]; then + echo "Error: Unsupported --new_path_prefix value: '$NEW_PATH_PREFIX'" + echo "Allowed values are: /ibm, /var/mnt, /mnt" + exit 1 +fi + +# --- Initialize Counters and Lists --- +success_list=() +fail_list=() +skip_list=() + +success_count=0 +fail_count=0 +skip_count=0 + +main() { + echo "Using the new path prefix: $NEW_PATH_PREFIX" + echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." + echo "" + read -rp "Proceed with migration? (yes/y/Y to continue): " CONFIRM + if [[ "$CONFIRM" != "yes" && "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then + echo "Aborting migration." + exit 0 + fi + init + get_pv_list + TOTAL_PVS=$(echo "$ALL_PVS" | wc -l | xargs) + COUNT=1 + for PV in $ALL_PVS; do + echo "" + echo "[$COUNT/$TOTAL_PVS] --------------------------------------------------------------------------------" + echo "Processing PV: $PV " + migrate_each "$PV" + COUNT=$((COUNT + 1)) + done + final_summary +} + +init() { + RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) + + BACKUP_PARENT_DIR="csi_migration_data" + mkdir -p "$BACKUP_PARENT_DIR" + + BACKUP_BASE_DIR="$BACKUP_PARENT_DIR/$RUN_TIMESTAMP" + mkdir -p "$BACKUP_BASE_DIR" + + LOG_FILE="$BACKUP_BASE_DIR/migration.log" + exec > >(tee "$LOG_FILE") 2>&1 + + echo "Logging to $LOG_FILE" + echo "Starting migration at: $(date)" + echo "" +} + +get_pv_list() { + ALL_PVS=$(kubectl get pv -o json | jq -r '.items[] | select(.spec.csi.driver == "spectrumscale.csi.ibm.com") | .metadata.name') + echo "Found $(echo "$ALL_PVS" | wc -l) PVs using spectrumscale.csi.ibm.com driver" +} + +migrate_each() { + PV="$1" + + VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { + echo "Failed to get volumeHandle for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + echo "Current volumeHandle: $VOLUME_HANDLE" + + PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') || { + echo "Failed to get PVC name for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("|$PV"); return; + } + PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') || { + echo "Failed to get PVC namespace for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + + echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" + + if ! kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" >/dev/null 2>&1; then + echo "PVC $PVC_NAME not found in namespace $PVC_NAMESPACE, skipping." + fail_list+=("$PVC_NAME|$PV") + fail_count=$(expr $fail_count + 1) + return + fi + + ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') || { + echo "Failed to get access modes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') || { + echo "Failed to get storage size."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') || { + echo "Failed to get storage class."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') || { + echo "Failed to get volume mode."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') || { + echo "Failed to get volumeAttributes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { + echo "Failed to get volBackendFs."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { + echo "Failed to get driver."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { + echo "Failed to get fsType."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + RECLAIM_POLICY_ORIGINAL=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') || { + echo "Failed to get reclaimPolicy for PV $PV."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + + + OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') + IFS=';' read -ra parts <<< "$VOLUME_HANDLE" + + if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then + echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" + + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then + echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" + PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and dependent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + fi + + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then + echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" + + # Default path of dynamic fileset + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and independent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + fi + + elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then + echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" + CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" + NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" + + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then + echo "Detected volumeHandle type: 0;3 (version2) : Shallow copy fileset" + CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" + NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" + + else + echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" + fail_list+=("$PVC_NAME|$PV") + fail_count=$(expr $fail_count + 1) + return + fi + + NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") + + if [[ "$VOLUME_HANDLE" == "$NEW_VOLUME_HANDLE" ]]; then + echo "Already migrated (volumeHandle matches target). Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + fi + + echo "Updated volumeHandle: $NEW_VOLUME_HANDLE" + + echo "Setting reclaim policy to Retain for PV: $PV" + if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then + echo "Failed to patch reclaim policy for PV: $PV" + fail_count=$(expr $fail_count + 1) + fail_list+=("$PVC_NAME|$PV") + return + fi + + BACKUP_DIR="$BACKUP_BASE_DIR/${PVC_NAMESPACE}/${PVC_NAME}" + mkdir -p "$BACKUP_DIR" + + echo "Backing up PV and PVC..." + kubectl get pv "$PV" -o yaml > "$BACKUP_DIR/pv.yaml" + kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o yaml > "$BACKUP_DIR/pvc.yaml" + + RECLAIM_POLICY=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') + if [[ "$RECLAIM_POLICY" != "Retain" ]]; then + echo "Reclaim policy is not Retain (got: $RECLAIM_POLICY), skipping." + fail_count=$(expr $fail_count + 1) + fail_list+=("$PVC_NAME|$PV") + return + fi + + echo "Deleting PVC and PV..." + kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" + kubectl delete pv "$PV" + + echo "Recreating PV and PVC..." + cat < 0 )); then + echo "Successful PVs: ${#success_list[@]}" + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" + for entry in "${success_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done + echo "" + fi + + if (( ${#fail_list[@]} > 0 )); then + echo "Failed PVs: ${#fail_list[@]}" + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" + for entry in "${fail_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done + echo "" + fi + + if (( ${#skip_list[@]} > 0 )); then + echo "Skipped PVs (already migrated): ${#skip_list[@]}" + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" + for entry in "${skip_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done + echo "" + fi + + echo "Completed migration at: $(date)" +} + +main + +exit 0 From c03a6f99d0075237a97b47ba5d73c92be30d6a7c Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Mon, 18 Aug 2025 15:36:09 +0530 Subject: [PATCH 10/21] added readme files Signed-off-by: badri-pathak --- tools/volume_migration_scripts/README.md | 15 ++ .../README_migration_csi_to_cnsa.md | 142 ++++++++++++++++++ .../migration_csi_to_cnsa.bash | 8 +- .../migration_pv_pvc.bash | 0 4 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 tools/volume_migration_scripts/README.md create mode 100644 tools/volume_migration_scripts/README_migration_csi_to_cnsa.md rename tools/{ => volume_migration_scripts}/migration_csi_to_cnsa.bash (96%) rename tools/{ => volume_migration_scripts}/migration_pv_pvc.bash (100%) diff --git a/tools/volume_migration_scripts/README.md b/tools/volume_migration_scripts/README.md new file mode 100644 index 000000000..aa7cae0cc --- /dev/null +++ b/tools/volume_migration_scripts/README.md @@ -0,0 +1,15 @@ +# IBM Storage Scale CSI → Volume Migration Scripts + +This provides scripts to migrate existing IBM Storage Scale CSI PersistentVolumes (PVs) to the new compatible volumeHandles. + +## Scripts + +- [`migration_csi_to_cnsa.bash`](README_migration_csi_to_cnsa.md) + Migrates IBM Storage Scale CSI PersistentVolumes to the CNSA format by updating the volumeHandle path with the specified prefix. + +- [`migration_csi_to_cnsa.bash`](README_migration_csi_to_cnsa.md) + Migrates IBM Storage Scale CSI PersistentVolumes to the CNSA format by updating the volumeHandle path with the specified prefix. + +--- + +Each script has its **own README** with usage, inputs, outputs, and validation notes. diff --git a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md new file mode 100644 index 000000000..2e82dbecc --- /dev/null +++ b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md @@ -0,0 +1,142 @@ +# PV Migration Script – CSI → CNSA + +This script helps migrate existing Kubernetes **PersistentVolumes (PVs)** created under the **standalone CSI driver** to the **CNSA-compatible CSI driver** format. +It ensures that workloads continue to access their data seamlessly after migration. + +## Key Features + +- Preserves all **original PV properties**, including: + - Reclaim policy (e.g., `Retain`, `Delete`) + - Access modes (`ReadWriteOnce`, `ReadWriteMany`, etc.) + - Storage capacity + - Filesystem type (`fsType`) + - Labels, annotations, and other PV metadata + +- Only the **volumeHandle path segment** is updated to CNSA’s required format. +- Supports migration of PVs created from **different volume types** (fileset-based, CG, cache, static, dependent, independent). +- Generates **backup YAML files** before applying changes. +- Can be safely re-run if required (**idempotent migration**). + +## Why Migration is Required + +In the standalone **CSI driver** setup, PVs are created with a `volumeHandle` format tied to paths under **primary filesets**, different **volumeMounts**, or **consistency groups**. + +However, in a **CNSA setup**, this format becomes incompatible because: + +- The **primary fileset path** is no longer available. +- CNSA expects a **different mount path hierarchy** for PV data. +- PVs must reference a **common base mount point** where all IBM Storage Scale remote filesystems are mounted across CNSA Kubernetes worker nodes. + +- Without migration, existing workloads would **not be able to mount or access their data** after switching from standalone CSI to CNSA. + +This script updates PV definitions to use the new path structure while **preserving all other PV properties**. + + +## What is `--new_path_prefix`? + +The `--new_path_prefix` is the **base filesystem mount point** of the remotely mounted filesystems on the **local IBM Storage Scale instance running on your CNSA Kubernetes worker nodes**. + +It defines the **root filesystem path** under which all migrated PVs will be remapped. + +### Examples + +- `/var/mnt` – often used in CNSA-based installations +- `/ibm` – default mount point (commonly used in classic setups) +- `/mnt` – alternative mount point used in some deployments + +**Important:** +The prefix you provide must **exactly match how the filesystem is mounted** on all CNSA worker nodes. +If different nodes have different mount points, migration will **fail**. + + +## Example Transformation + +### Before (CSI standalone `volumeHandle`): +```text +volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/ibm/remotefs1/primary-fileset-remotefs1-475592072879187/.volumes/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1 +``` + +### After (CNSA-compatible `volumeHandle`): +```text +volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/var/mnt/remotefs1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1-data +``` + +### Key Points + +**Unchanged:** +- The identity portion of the handle (everything up to the last `;`). + +**Rewritten:** +- Only the **path segment after the last `;`**. + + +## Volume Type Variations + +The exact path suffix (e.g., `pvc-uuid-data`) may vary based on how the volume was originally created. +The script automatically detects and applies the correct mapping without user intervention. + + +## Migration Script Usage + +A helper script is provided to automate PV migration: + +```bash +# Usage +./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt +``` + +Where: + +- `` is a user-defined identifier for this migration run. +- `--new_path_prefix` specifies the **base mount point** of IBM Storage Scale filesystems on CNSA worker nodes. + Allowed values: `/ibm`, `/mnt`, `/var/mnt` + + +## Features of the Migration Script + +- ✅ Filters only PVs created with the **spectrumscale.csi.ibm.com** driver. +- ✅ Skips PVs already migrated (with a CNSA-compatible path in `volumeHandle`). +- ✅ **Backs up PVs and PVCs** before modification into a structured directory: + +``` +csi_migration_data/ +└── -/ + ├── migration.log + ├── / + │ └── / + │ ├── pvc.yaml + │ └── pv.yaml + └── ... +``` + +- ✅ Logs all actions, successes, skips, and failures into: + +``` +csi_migration_data/-/migration.log +``` + +- ✅ Summarizes **success, skipped, and failed** migrations at the end. +- ✅ Idempotent – safe to re-run if needed. + + +## Preserved PV Properties + +The script ensures that **all original PV configurations** are retained after migration. +The following fields are preserved: + +- **Capacity** (`spec.capacity.storage`) +- **AccessModes** (`spec.accessModes`) +- **PersistentVolumeReclaimPolicy** (`spec.persistentVolumeReclaimPolicy`) +- **StorageClassName** +- **CSI driver details** (fsType, volumeAttributes, nodeStageSecrets, etc.) +- **PVC binding information** (safely re-created to preserve claim references) + +- Only the **`volumeHandle` path** is modified to meet CNSA expectations. + + +## Notes and Limitations + +- The **filesystem names** (e.g., `remotefs1`) must remain identical between standalone CSI and CNSA deployments. +- The provided `--new_path_prefix` must reflect the **actual base mount point** of IBM Storage Scale on all CNSA **worker nodes**. +- The script does **not delete or recreate volumes** on IBM Storage Scale; it only updates Kubernetes PV metadata. +- Existing workloads must be restarted to pick up new PV mount paths after migration. diff --git a/tools/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash similarity index 96% rename from tools/migration_csi_to_cnsa.bash rename to tools/volume_migration_scripts/migration_csi_to_cnsa.bash index de3a14b33..3cdb55824 100644 --- a/tools/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -1,7 +1,7 @@ #!/bin/bash -# Usage: ./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt -# Migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle. +# Usage: ./migration_csi_to_cnsa.bash --new_path_prefix /var/mnt +# Migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix. set -euo pipefail @@ -13,10 +13,10 @@ help() { echo "Example:" echo " $0 --new_path_prefix /var/mnt" echo "" - echo " These must match the base mount point of Spectrum Scale on your nodes." + echo " These must match the base mount point of IBM Storage Scale on your nodes." echo "" echo "Description:" - echo " This script migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle." + echo " This script migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix" echo "" exit 1 } diff --git a/tools/migration_pv_pvc.bash b/tools/volume_migration_scripts/migration_pv_pvc.bash similarity index 100% rename from tools/migration_pv_pvc.bash rename to tools/volume_migration_scripts/migration_pv_pvc.bash From 8182bf1748a4b0acc1877d8b78a01325acaca07b Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Mon, 18 Aug 2025 17:19:57 +0530 Subject: [PATCH 11/21] added into previous script also. Signed-off-by: badri-pathak --- .../migration_pv_pvc.bash | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/volume_migration_scripts/migration_pv_pvc.bash b/tools/volume_migration_scripts/migration_pv_pvc.bash index a36f9b746..3cdb55824 100644 --- a/tools/volume_migration_scripts/migration_pv_pvc.bash +++ b/tools/volume_migration_scripts/migration_pv_pvc.bash @@ -1,7 +1,7 @@ #!/bin/bash -# Usage: ./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt -# Migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle. +# Usage: ./migration_csi_to_cnsa.bash --new_path_prefix /var/mnt +# Migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix. set -euo pipefail @@ -13,10 +13,10 @@ help() { echo "Example:" echo " $0 --new_path_prefix /var/mnt" echo "" - echo " These must match the base mount point of Spectrum Scale on your nodes." + echo " These must match the base mount point of IBM Storage Scale on your nodes." echo "" echo "Description:" - echo " This script migrates Storage Scale CSI PVs to use the new path prefix in the volumeHandle." + echo " This script migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix" echo "" exit 1 } @@ -150,13 +150,16 @@ migrate_each() { VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { echo "Failed to get volBackendFs."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } - DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { echo "Failed to get driver."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { echo "Failed to get fsType."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; } + RECLAIM_POLICY_ORIGINAL=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') || { + echo "Failed to get reclaimPolicy for PV $PV."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') IFS=';' read -ra parts <<< "$VOLUME_HANDLE" @@ -227,7 +230,7 @@ migrate_each() { return fi - BACKUP_DIR="$BACKUP_BASE_DIR/${PVC_NAME}" + BACKUP_DIR="$BACKUP_BASE_DIR/${PVC_NAMESPACE}/${PVC_NAME}" mkdir -p "$BACKUP_DIR" echo "Backing up PV and PVC..." @@ -256,7 +259,7 @@ spec: capacity: storage: $STORAGE accessModes: $(echo "$ACCESS_MODES" | jq '.') - persistentVolumeReclaimPolicy: Delete + persistentVolumeReclaimPolicy: $RECLAIM_POLICY_ORIGINAL storageClassName: $STORAGE_CLASS csi: driver: $DRIVER From c12a37620d30fc402b153437cf51328181cf91a0 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Mon, 18 Aug 2025 18:24:42 +0530 Subject: [PATCH 12/21] added cnsa primaryfs removal script Signed-off-by: badri-pathak --- tools/volume_migration_scripts/README.md | 4 +- .../README_migration_cnsa_primary_removal.md | 142 +++++++ .../migration_cnsa_primary_removal.bash | 374 ++++++++++++++++++ .../migration_csi_to_cnsa.bash | 4 +- 4 files changed, 520 insertions(+), 4 deletions(-) create mode 100644 tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md create mode 100644 tools/volume_migration_scripts/migration_cnsa_primary_removal.bash diff --git a/tools/volume_migration_scripts/README.md b/tools/volume_migration_scripts/README.md index aa7cae0cc..23e0b6397 100644 --- a/tools/volume_migration_scripts/README.md +++ b/tools/volume_migration_scripts/README.md @@ -7,8 +7,8 @@ This provides scripts to migrate existing IBM Storage Scale CSI PersistentVolume - [`migration_csi_to_cnsa.bash`](README_migration_csi_to_cnsa.md) Migrates IBM Storage Scale CSI PersistentVolumes to the CNSA format by updating the volumeHandle path with the specified prefix. -- [`migration_csi_to_cnsa.bash`](README_migration_csi_to_cnsa.md) - Migrates IBM Storage Scale CSI PersistentVolumes to the CNSA format by updating the volumeHandle path with the specified prefix. +- [`migration_cnsa_primary_removal.bash`](README_migration_cnsa_primary_removal.md) + Migrates IBM Storage Scale PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix after primary removal --- diff --git a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md new file mode 100644 index 000000000..2e82dbecc --- /dev/null +++ b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md @@ -0,0 +1,142 @@ +# PV Migration Script – CSI → CNSA + +This script helps migrate existing Kubernetes **PersistentVolumes (PVs)** created under the **standalone CSI driver** to the **CNSA-compatible CSI driver** format. +It ensures that workloads continue to access their data seamlessly after migration. + +## Key Features + +- Preserves all **original PV properties**, including: + - Reclaim policy (e.g., `Retain`, `Delete`) + - Access modes (`ReadWriteOnce`, `ReadWriteMany`, etc.) + - Storage capacity + - Filesystem type (`fsType`) + - Labels, annotations, and other PV metadata + +- Only the **volumeHandle path segment** is updated to CNSA’s required format. +- Supports migration of PVs created from **different volume types** (fileset-based, CG, cache, static, dependent, independent). +- Generates **backup YAML files** before applying changes. +- Can be safely re-run if required (**idempotent migration**). + +## Why Migration is Required + +In the standalone **CSI driver** setup, PVs are created with a `volumeHandle` format tied to paths under **primary filesets**, different **volumeMounts**, or **consistency groups**. + +However, in a **CNSA setup**, this format becomes incompatible because: + +- The **primary fileset path** is no longer available. +- CNSA expects a **different mount path hierarchy** for PV data. +- PVs must reference a **common base mount point** where all IBM Storage Scale remote filesystems are mounted across CNSA Kubernetes worker nodes. + +- Without migration, existing workloads would **not be able to mount or access their data** after switching from standalone CSI to CNSA. + +This script updates PV definitions to use the new path structure while **preserving all other PV properties**. + + +## What is `--new_path_prefix`? + +The `--new_path_prefix` is the **base filesystem mount point** of the remotely mounted filesystems on the **local IBM Storage Scale instance running on your CNSA Kubernetes worker nodes**. + +It defines the **root filesystem path** under which all migrated PVs will be remapped. + +### Examples + +- `/var/mnt` – often used in CNSA-based installations +- `/ibm` – default mount point (commonly used in classic setups) +- `/mnt` – alternative mount point used in some deployments + +**Important:** +The prefix you provide must **exactly match how the filesystem is mounted** on all CNSA worker nodes. +If different nodes have different mount points, migration will **fail**. + + +## Example Transformation + +### Before (CSI standalone `volumeHandle`): +```text +volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/ibm/remotefs1/primary-fileset-remotefs1-475592072879187/.volumes/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1 +``` + +### After (CNSA-compatible `volumeHandle`): +```text +volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/var/mnt/remotefs1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1-data +``` + +### Key Points + +**Unchanged:** +- The identity portion of the handle (everything up to the last `;`). + +**Rewritten:** +- Only the **path segment after the last `;`**. + + +## Volume Type Variations + +The exact path suffix (e.g., `pvc-uuid-data`) may vary based on how the volume was originally created. +The script automatically detects and applies the correct mapping without user intervention. + + +## Migration Script Usage + +A helper script is provided to automate PV migration: + +```bash +# Usage +./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt +``` + +Where: + +- `` is a user-defined identifier for this migration run. +- `--new_path_prefix` specifies the **base mount point** of IBM Storage Scale filesystems on CNSA worker nodes. + Allowed values: `/ibm`, `/mnt`, `/var/mnt` + + +## Features of the Migration Script + +- ✅ Filters only PVs created with the **spectrumscale.csi.ibm.com** driver. +- ✅ Skips PVs already migrated (with a CNSA-compatible path in `volumeHandle`). +- ✅ **Backs up PVs and PVCs** before modification into a structured directory: + +``` +csi_migration_data/ +└── -/ + ├── migration.log + ├── / + │ └── / + │ ├── pvc.yaml + │ └── pv.yaml + └── ... +``` + +- ✅ Logs all actions, successes, skips, and failures into: + +``` +csi_migration_data/-/migration.log +``` + +- ✅ Summarizes **success, skipped, and failed** migrations at the end. +- ✅ Idempotent – safe to re-run if needed. + + +## Preserved PV Properties + +The script ensures that **all original PV configurations** are retained after migration. +The following fields are preserved: + +- **Capacity** (`spec.capacity.storage`) +- **AccessModes** (`spec.accessModes`) +- **PersistentVolumeReclaimPolicy** (`spec.persistentVolumeReclaimPolicy`) +- **StorageClassName** +- **CSI driver details** (fsType, volumeAttributes, nodeStageSecrets, etc.) +- **PVC binding information** (safely re-created to preserve claim references) + +- Only the **`volumeHandle` path** is modified to meet CNSA expectations. + + +## Notes and Limitations + +- The **filesystem names** (e.g., `remotefs1`) must remain identical between standalone CSI and CNSA deployments. +- The provided `--new_path_prefix` must reflect the **actual base mount point** of IBM Storage Scale on all CNSA **worker nodes**. +- The script does **not delete or recreate volumes** on IBM Storage Scale; it only updates Kubernetes PV metadata. +- Existing workloads must be restarted to pick up new PV mount paths after migration. diff --git a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash new file mode 100644 index 000000000..c493dc491 --- /dev/null +++ b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash @@ -0,0 +1,374 @@ +#!/bin/bash + +# Usage: ./migration_cnsa_primary_removal.bash --new_path_prefix /var/mnt +# Migrates IBM Storage Scale PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix after primary removal. + +set -euo pipefail + +# --- Help Function --- +help() { + echo "" + echo "Usage: $0 --new_path_prefix " + echo "" + echo "Example:" + echo " $0 --new_path_prefix /var/mnt" + echo "" + echo " These must match the base mount point of IBM Storage Scale on your nodes." + echo "" + echo "Description:" + echo " This script migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix after primary removal." + echo "" + exit 1 +} + +# --- Argument Parsing --- +NEW_PATH_PREFIX="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --new_path_prefix) + shift + NEW_PATH_PREFIX="$1" + ;; + -h|--help) + help + ;; + *) + echo "Unknown argument: $1" + help + ;; + esac + shift +done + +if [[ -z "$NEW_PATH_PREFIX" ]]; then + echo "Missing required argument: --new_path_prefix" + help +fi + +#TODO: Uncomment the following lines to enforce allowed path prefixes +# Enforce allowed path prefixes +if [[ "$NEW_PATH_PREFIX" != "/ibm" && "$NEW_PATH_PREFIX" != "/var/mnt" && "$NEW_PATH_PREFIX" != "/mnt" ]]; then + echo "Error: Unsupported --new_path_prefix value: '$NEW_PATH_PREFIX'" + echo "Allowed values are: /ibm, /var/mnt, /mnt" + exit 1 +fi + +# --- Initialize Counters and Lists --- +success_list=() +fail_list=() +skip_list=() + +success_count=0 +fail_count=0 +skip_count=0 + +main() { + echo "Using the new path prefix: $NEW_PATH_PREFIX" + echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." + echo "" + read -rp "Proceed with migration? (yes/y/Y to continue): " CONFIRM + if [[ "$CONFIRM" != "yes" && "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then + echo "Aborting migration." + exit 0 + fi + init + get_pv_list + TOTAL_PVS=$(echo "$ALL_PVS" | wc -l | xargs) + COUNT=1 + for PV in $ALL_PVS; do + echo "" + echo "[$COUNT/$TOTAL_PVS] --------------------------------------------------------------------------------" + echo "Processing PV: $PV " + migrate_each "$PV" + COUNT=$((COUNT + 1)) + done + final_summary +} + +init() { + RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) + + BACKUP_PARENT_DIR="csi_migration_data" + mkdir -p "$BACKUP_PARENT_DIR" + + BACKUP_BASE_DIR="$BACKUP_PARENT_DIR/$RUN_TIMESTAMP" + mkdir -p "$BACKUP_BASE_DIR" + + LOG_FILE="$BACKUP_BASE_DIR/migration.log" + exec > >(tee "$LOG_FILE") 2>&1 + + echo "Logging to $LOG_FILE" + echo "Starting migration at: $(date)" + echo "" +} + +get_pv_list() { + ALL_PVS=$(kubectl get pv -o json | jq -r '.items[] | select(.spec.csi.driver == "spectrumscale.csi.ibm.com") | .metadata.name') + echo "Found $(echo "$ALL_PVS" | wc -l) PVs using spectrumscale.csi.ibm.com driver" +} + +migrate_each() { + PV="$1" + + VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { + echo "Failed to get volumeHandle for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + echo "Current volumeHandle: $VOLUME_HANDLE" + + PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') || { + echo "Failed to get PVC name for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("|$PV"); return; + } + PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') || { + echo "Failed to get PVC namespace for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + + echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" + + if ! kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" >/dev/null 2>&1; then + echo "PVC $PVC_NAME not found in namespace $PVC_NAMESPACE, skipping." + fail_list+=("$PVC_NAME|$PV") + fail_count=$(expr $fail_count + 1) + return + fi + + ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') || { + echo "Failed to get access modes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') || { + echo "Failed to get storage size."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') || { + echo "Failed to get storage class."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') || { + echo "Failed to get volume mode."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') || { + echo "Failed to get volumeAttributes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { + echo "Failed to get volBackendFs."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { + echo "Failed to get driver."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { + echo "Failed to get fsType."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + RECLAIM_POLICY_ORIGINAL=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') || { + echo "Failed to get reclaimPolicy for PV $PV."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; + } + + + OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') + IFS=';' read -ra parts <<< "$VOLUME_HANDLE" + + if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then + echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" + if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" + fi + else + echo "For this volume primary migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + fi + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then + echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" + if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then + PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and dependent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + fi + fi + else + echo "For this volume primary migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + fi + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" && "${parts[-1]}" == *"/.volumes/"* ]]; then + echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" + if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then + # Default path of dynamic fileset + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and independent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + fi + fi + else + echo "For this volume primary migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + fi + elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then + echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" + echo "For Consistency group fileset migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then + echo "Detected volumeHandle type: 0;3 : Shallow copy fileset" + echo "For Shallow copy fileset migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + + elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "3" ]]; then + echo "Detected volumeHandle type: 1;3 version-2: Shallow copy fileset" + echo "For Shallow copy fileset migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + + elif [[ "${parts[0]}" == "2" && "${parts[1]}" == "2" ]]; then + echo "Detected volumeHandle type: 2;2 : Cache fileset" + echo "For Cache fileset migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + else + echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" + fail_list+=("$PVC_NAME|$PV") + fail_count=$(expr $fail_count + 1) + return + fi + + NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") + + if [[ "$VOLUME_HANDLE" == "$NEW_VOLUME_HANDLE" ]]; then + echo "Already migrated (volumeHandle matches target). Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + fi + + echo "Updated volumeHandle: $NEW_VOLUME_HANDLE" + + echo "Setting reclaim policy to Retain for PV: $PV" + if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then + echo "Failed to patch reclaim policy for PV: $PV" + fail_count=$(expr $fail_count + 1) + fail_list+=("$PVC_NAME|$PV") + return + fi + + BACKUP_DIR="$BACKUP_BASE_DIR/${PVC_NAMESPACE}/${PVC_NAME}" + mkdir -p "$BACKUP_DIR" + + echo "Backing up PV and PVC..." + kubectl get pv "$PV" -o yaml > "$BACKUP_DIR/pv.yaml" + kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o yaml > "$BACKUP_DIR/pvc.yaml" + + RECLAIM_POLICY=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') + if [[ "$RECLAIM_POLICY" != "Retain" ]]; then + echo "Reclaim policy is not Retain (got: $RECLAIM_POLICY), skipping." + fail_count=$(expr $fail_count + 1) + fail_list+=("$PVC_NAME|$PV") + return + fi + + echo "Deleting PVC and PV..." + kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" + kubectl delete pv "$PV" + + echo "Recreating PV and PVC..." + cat < 0 )); then + echo "Successful PVs: ${#success_list[@]}" + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" + for entry in "${success_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done + echo "" + fi + + if (( ${#fail_list[@]} > 0 )); then + echo "Failed PVs: ${#fail_list[@]}" + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" + for entry in "${fail_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done + echo "" + fi + + if (( ${#skip_list[@]} > 0 )); then + echo "Skipped PVs (already migrated): ${#skip_list[@]}" + printf " %-80s | %s\n" "PVC Name" "PV Name" + printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" + for entry in "${skip_list[@]}"; do + IFS='|' read -r pvc pv <<< "$entry" + printf " %-80s | %s\n" "$pvc" "$pv" + done + echo "" + fi + + echo "Completed migration at: $(date)" +} + +main + +exit 0 diff --git a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash index 3cdb55824..1e00dfd69 100644 --- a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -199,8 +199,8 @@ migrate_each() { CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then - echo "Detected volumeHandle type: 0;3 (version2) : Shallow copy fileset" + elif { [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]] || [[ "${parts[0]}" == "1" && "${parts[1]}" == "3" ]]; }; then + echo "Detected volumeHandle type: 0;3 (version1) or 1;3 (version2): Shallow copy fileset" CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" From 23740cb04694118fd57ad9ec7944f1d371334c5c Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Tue, 19 Aug 2025 01:01:08 +0530 Subject: [PATCH 13/21] Adding script to handle csi pv migration after primaryfs removal Signed-off-by: badri-pathak --- tools/volume_migration_scripts/README.md | 5 +- .../README_migration_csi_primary_removal.md | 112 ++++++++++++++++ ...ash => migration_csi_primary_removal.bash} | 123 +++++++++--------- 3 files changed, 181 insertions(+), 59 deletions(-) create mode 100644 tools/volume_migration_scripts/README_migration_csi_primary_removal.md rename tools/volume_migration_scripts/{migration_pv_pvc.bash => migration_csi_primary_removal.bash} (75%) diff --git a/tools/volume_migration_scripts/README.md b/tools/volume_migration_scripts/README.md index 23e0b6397..ce32f8e31 100644 --- a/tools/volume_migration_scripts/README.md +++ b/tools/volume_migration_scripts/README.md @@ -7,8 +7,11 @@ This provides scripts to migrate existing IBM Storage Scale CSI PersistentVolume - [`migration_csi_to_cnsa.bash`](README_migration_csi_to_cnsa.md) Migrates IBM Storage Scale CSI PersistentVolumes to the CNSA format by updating the volumeHandle path with the specified prefix. +- [`migration_csi_primary_removal.bash`](README_migration_csi_primary_removal.md) + Migrates IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal. + - [`migration_cnsa_primary_removal.bash`](README_migration_cnsa_primary_removal.md) - Migrates IBM Storage Scale PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix after primary removal + Migrates IBM Storage Scale PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix after primary removal. --- diff --git a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md new file mode 100644 index 000000000..9571cb818 --- /dev/null +++ b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md @@ -0,0 +1,112 @@ + +# PV Migration Script – CSI (Primary → Actual Fileset Mount) + +This script helps migrate existing Kubernetes **PersistentVolumes (PVs)** that were originally created when the **primary fs and fileset** was enabled, to a format that uses the **actual fileset mount path** after the **primary has been removed**. +It ensures that workloads continue to access their data seamlessly after migration. + +## Key Features + +- Preserves all **original PV properties**, including: + - Reclaim policy (e.g., `Retain`, `Delete`) + - Access modes (`ReadWriteOnce`, `ReadWriteMany`, etc.) + - Storage capacity + - Filesystem type (`fsType`) + - Labels, annotations, and other PV metadata + +- Only the **volumeHandle path segment** is updated to reflect the **actual fileset mount path**. +- Supports migration of PVs across **different fileset types** (independent, dependent, static, cache, CG, etc.). +- Generates **backup YAML files** before applying changes. +- Can be safely re-run if required (**idempotent migration**). + +## Why Migration is Required + +When the **primary fs and fileset** was enabled, PVs were created under paths tied to the **primary fs and fileset hierarchy**. +Now that **primary is removed**, these paths are invalid: + +- PVs must be mounted at the **actual fileset mount path** in the Storage Scale filesystem. +- Without migration, existing workloads would **not be able to mount or access their data**. + +This script updates PV definitions to point to the correct fileset paths while **preserving all other PV properties**. + +## Example Transformation + +### Before (PV created with **primary** enabled): +```text +volumeHandle: 0;2;13009550825755318848;A3D56F10:9BC12E30;;pvc-3b1a-49d3-89e1-51f607b91234;/ibm/remotefs1/primary-remotefs1-123456789/.volumes/pvc-3b1a-49d3-89e1-51f607b91234 +``` + +### After (Migrated to **actual fileset mount path**): +```text +volumeHandle: 0;2;13009550825755318848;A3D56F10:9BC12E30;;pvc-3b1a-49d3-89e1-51f607b91234;/var/mnt/remotefs1/fs1-pvc-3b1a-49d3-89e1-51f607b91234/pvc-3b1a-49d3-89e1-51f607b91234-data +``` + +### Key Points + +**Unchanged:** +- The identity portion of the handle (everything up to the last `;`). + +**Rewritten:** +- Only the **path segment after the last `;`**, now pointing to the actual fileset mount path. + +## Volume Type Variations + +The exact path suffix (e.g., `pvc-uuid-data`) may vary depending on how the volume was originally created. +The script automatically detects and applies the correct mapping without user intervention. + +## Migration Script Usage + +A helper script is provided to automate PV migration: + +```bash +# Usage +./pv_migration_csi_primary_removed.bash +``` + +- No arguments are required. +- The script automatically detects the **actual fileset mount path** for each PV and updates accordingly. + +## Features of the Migration Script + +- ✅ Filters only PVs created with the **spectrumscale.csi.ibm.com** driver. +- ✅ Skips PVs already migrated (with an actual fileset mount path in `volumeHandle`). +- ✅ **Backs up PVs and PVCs** before modification into a structured directory: + +``` +csi_migration_data/ +└── migration-/ + ├── migration.log + ├── / + │ └── / + │ ├── pvc.yaml + │ └── pv.yaml + └── ... +``` + +- ✅ Logs all actions, successes, skips, and failures into: + +``` +csi_migration_data/migration-/migration.log +``` + +- ✅ Summarizes **success, skipped, and failed** migrations at the end. +- ✅ Idempotent – safe to re-run if needed. + +## Preserved PV Properties + +The script ensures that **all original PV configurations** are retained after migration. +The following fields are preserved: + +- **Capacity** (`spec.capacity.storage`) +- **AccessModes** (`spec.accessModes`) +- **PersistentVolumeReclaimPolicy** (`spec.persistentVolumeReclaimPolicy`) +- **StorageClassName** +- **CSI driver details** (fsType, volumeAttributes, nodeStageSecrets, etc.) +- **PVC binding information** (safely re-created to preserve claim references) + +- Only the **`volumeHandle` path** is modified to reflect the actual fileset mount. + +## Notes and Limitations + +- The **filesystem names** (e.g., `remotefs1`) must remain identical between pre-primary and post-primary removal deployments. +- The script does **not delete or recreate volumes** on IBM Storage Scale; it only updates Kubernetes PV metadata. +- Existing workloads must be restarted to pick up new PV mount paths after migration. diff --git a/tools/volume_migration_scripts/migration_pv_pvc.bash b/tools/volume_migration_scripts/migration_csi_primary_removal.bash similarity index 75% rename from tools/volume_migration_scripts/migration_pv_pvc.bash rename to tools/volume_migration_scripts/migration_csi_primary_removal.bash index 3cdb55824..e4c1f0b8f 100644 --- a/tools/volume_migration_scripts/migration_pv_pvc.bash +++ b/tools/volume_migration_scripts/migration_csi_primary_removal.bash @@ -1,35 +1,24 @@ #!/bin/bash -# Usage: ./migration_csi_to_cnsa.bash --new_path_prefix /var/mnt -# Migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix. +# Usage: ./migration_csi_primary_removal.bash +# Migrates IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal. set -euo pipefail # --- Help Function --- help() { echo "" - echo "Usage: $0 --new_path_prefix " - echo "" - echo "Example:" - echo " $0 --new_path_prefix /var/mnt" - echo "" - echo " These must match the base mount point of IBM Storage Scale on your nodes." + echo "Usage: $0" echo "" echo "Description:" - echo " This script migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix" + echo " This script migrates IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal." echo "" exit 1 } # --- Argument Parsing --- -NEW_PATH_PREFIX="" - while [[ $# -gt 0 ]]; do case "$1" in - --new_path_prefix) - shift - NEW_PATH_PREFIX="$1" - ;; -h|--help) help ;; @@ -41,19 +30,6 @@ while [[ $# -gt 0 ]]; do shift done -if [[ -z "$NEW_PATH_PREFIX" ]]; then - echo "Missing required argument: --new_path_prefix" - help -fi - -#TODO: Uncomment the following lines to enforce allowed path prefixes -# Enforce allowed path prefixes -if [[ "$NEW_PATH_PREFIX" != "/ibm" && "$NEW_PATH_PREFIX" != "/var/mnt" && "$NEW_PATH_PREFIX" != "/mnt" ]]; then - echo "Error: Unsupported --new_path_prefix value: '$NEW_PATH_PREFIX'" - echo "Allowed values are: /ibm, /var/mnt, /mnt" - exit 1 -fi - # --- Initialize Counters and Lists --- success_list=() fail_list=() @@ -64,8 +40,7 @@ fail_count=0 skip_count=0 main() { - echo "Using the new path prefix: $NEW_PATH_PREFIX" - echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." + echo "Starting migration of IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal" echo "" read -rp "Proceed with migration? (yes/y/Y to continue): " CONFIRM if [[ "$CONFIRM" != "yes" && "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then @@ -162,47 +137,79 @@ migrate_each() { OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') + CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" IFS=';' read -ra parts <<< "$VOLUME_HANDLE" if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" - + if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" + fi + else + echo "For this volume primary migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + fi elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" - PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" - - # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') - if [[ "$existingVolume" == "yes" ]]; then - echo "Static Fileset volume and dependent fileset" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then + PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and dependent fileset" + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + fi fi - - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then + else + echo "For this volume primary migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + fi + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" && "${parts[-1]}" == *"/.volumes/"* ]]; then echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" - - # Default path of dynamic fileset - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" - - # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') - if [[ "$existingVolume" == "yes" ]]; then - echo "Static Fileset volume and independent fileset" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then + # Default path of dynamic fileset + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + + # Check if existingVolume is set to yes for static fileset volumes + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + if [[ "$existingVolume" == "yes" ]]; then + echo "Static Fileset volume and independent fileset" + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + fi + fi + else + echo "For this volume primary migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return fi - elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" - CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" - NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" + echo "For Consistency group fileset migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then - echo "Detected volumeHandle type: 0;3 (version2) : Shallow copy fileset" - CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" - NEW_PATH="$NEW_PATH_PREFIX/${OLD_PATH#$CURRENT_PREFIX/}" + echo "Detected volumeHandle type: 0;3 : Shallow copy fileset" + echo "For Shallow copy fileset migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return + + elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "3" ]]; then + echo "Detected volumeHandle type: 1;3 version-2: Shallow copy fileset" + echo "For Shallow copy fileset migration is not required. Skipping $PV" + skip_count=$(expr $skip_count + 1) + skip_list+=("$PVC_NAME|$PV") + return else echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" From e2d8644ee4bff91618a34ca4ac530e1bf41ea671 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Tue, 19 Aug 2025 01:37:04 +0530 Subject: [PATCH 14/21] added pre-req checks for jq and kubectl Signed-off-by: badri-pathak --- .../README_migration_cnsa_primary_removal.md | 13 +++++++++++++ .../README_migration_csi_primary_removal.md | 14 ++++++++++++++ .../README_migration_csi_to_cnsa.md | 13 +++++++++++++ .../migration_cnsa_primary_removal.bash | 12 ++++++++++++ .../migration_csi_primary_removal.bash | 12 ++++++++++++ .../migration_csi_to_cnsa.bash | 12 ++++++++++++ 6 files changed, 76 insertions(+) diff --git a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md index 2e82dbecc..7cea0b042 100644 --- a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md @@ -75,6 +75,19 @@ volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d The exact path suffix (e.g., `pvc-uuid-data`) may vary based on how the volume was originally created. The script automatically detects and applies the correct mapping without user intervention. +## Prerequisites + +Before running the migration script, ensure the following tools are installed and available in your `$PATH`: + +- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects +- **jq** – for JSON parsing and manipulation of Kubernetes API responses + +You can verify installation with: + +```bash +kubectl version --client +jq --version +``` ## Migration Script Usage diff --git a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md index 9571cb818..a6ed4699c 100644 --- a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md @@ -53,6 +53,20 @@ volumeHandle: 0;2;13009550825755318848;A3D56F10:9BC12E30;;pvc-3b1a-49d3-89e1-51f The exact path suffix (e.g., `pvc-uuid-data`) may vary depending on how the volume was originally created. The script automatically detects and applies the correct mapping without user intervention. +## Prerequisites + +Before running the migration script, ensure the following tools are installed and available in your `$PATH`: + +- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects +- **jq** – for JSON parsing and manipulation of Kubernetes API responses + +You can verify installation with: + +```bash +kubectl version --client +jq --version +``` + ## Migration Script Usage A helper script is provided to automate PV migration: diff --git a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md index 2e82dbecc..7cea0b042 100644 --- a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md +++ b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md @@ -75,6 +75,19 @@ volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d The exact path suffix (e.g., `pvc-uuid-data`) may vary based on how the volume was originally created. The script automatically detects and applies the correct mapping without user intervention. +## Prerequisites + +Before running the migration script, ensure the following tools are installed and available in your `$PATH`: + +- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects +- **jq** – for JSON parsing and manipulation of Kubernetes API responses + +You can verify installation with: + +```bash +kubectl version --client +jq --version +``` ## Migration Script Usage diff --git a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash index c493dc491..e8f7e37f1 100644 --- a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash @@ -64,6 +64,7 @@ fail_count=0 skip_count=0 main() { + check_prerequisites echo "Using the new path prefix: $NEW_PATH_PREFIX" echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." echo "" @@ -86,6 +87,17 @@ main() { final_summary } +check_prerequisites() { + echo "Checking prerequisites..." + for cmd in kubectl jq; do + if ! command -v $cmd &>/dev/null; then + echo "Error: '$cmd' is required but not installed or not in PATH." >&2 + exit 1 + fi + done + echo "All prerequisites met." +} + init() { RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) diff --git a/tools/volume_migration_scripts/migration_csi_primary_removal.bash b/tools/volume_migration_scripts/migration_csi_primary_removal.bash index e4c1f0b8f..01db0c407 100644 --- a/tools/volume_migration_scripts/migration_csi_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_csi_primary_removal.bash @@ -40,6 +40,7 @@ fail_count=0 skip_count=0 main() { + check_prerequisites echo "Starting migration of IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal" echo "" read -rp "Proceed with migration? (yes/y/Y to continue): " CONFIRM @@ -61,6 +62,17 @@ main() { final_summary } +check_prerequisites() { + echo "Checking prerequisites..." + for cmd in kubectl jq; do + if ! command -v $cmd &>/dev/null; then + echo "Error: '$cmd' is required but not installed or not in PATH." >&2 + exit 1 + fi + done + echo "All prerequisites met." +} + init() { RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) diff --git a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash index 1e00dfd69..83d4ac263 100644 --- a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -64,6 +64,7 @@ fail_count=0 skip_count=0 main() { + check_prerequisites echo "Using the new path prefix: $NEW_PATH_PREFIX" echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." echo "" @@ -86,6 +87,17 @@ main() { final_summary } +check_prerequisites() { + echo "Checking prerequisites..." + for cmd in kubectl jq; do + if ! command -v $cmd &>/dev/null; then + echo "Error: '$cmd' is required but not installed or not in PATH." >&2 + exit 1 + fi + done + echo "All prerequisites met." +} + init() { RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) From fd3310f2a3631e737e8de289e56781ec66069469 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Tue, 19 Aug 2025 01:57:20 +0530 Subject: [PATCH 15/21] updating readme files Signed-off-by: badri-pathak --- .../README_migration_cnsa_primary_removal.md | 88 +++++++------------ .../README_migration_csi_primary_removal.md | 2 +- .../README_migration_csi_to_cnsa.md | 3 +- 3 files changed, 32 insertions(+), 61 deletions(-) diff --git a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md index 7cea0b042..9b9fc881a 100644 --- a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md @@ -1,7 +1,8 @@ -# PV Migration Script – CSI → CNSA -This script helps migrate existing Kubernetes **PersistentVolumes (PVs)** created under the **standalone CSI driver** to the **CNSA-compatible CSI driver** format. -It ensures that workloads continue to access their data seamlessly after migration. +# PV Migration Script – CNSA (Primary → Actual Fileset Mount) + +This script helps migrate existing Kubernetes **PersistentVolumes (PVs)** in a **CNSA environment** that were originally created when the **primary fileset option** was enabled. +It updates PVs to use the **actual fileset mount path** on CNSA worker nodes after the primary has been removed, ensuring workloads continue to access their data seamlessly. ## Key Features @@ -12,53 +13,31 @@ It ensures that workloads continue to access their data seamlessly after migrati - Filesystem type (`fsType`) - Labels, annotations, and other PV metadata -- Only the **volumeHandle path segment** is updated to CNSA’s required format. -- Supports migration of PVs created from **different volume types** (fileset-based, CG, cache, static, dependent, independent). +- Only the **volumeHandle path segment** is updated based on the **fileset type** and **prefix**. +- Supports migration of PVs across **different fileset types** (independent, dependent, static, cache, CG, etc.). - Generates **backup YAML files** before applying changes. - Can be safely re-run if required (**idempotent migration**). ## Why Migration is Required -In the standalone **CSI driver** setup, PVs are created with a `volumeHandle` format tied to paths under **primary filesets**, different **volumeMounts**, or **consistency groups**. - -However, in a **CNSA setup**, this format becomes incompatible because: - -- The **primary fileset path** is no longer available. -- CNSA expects a **different mount path hierarchy** for PV data. -- PVs must reference a **common base mount point** where all IBM Storage Scale remote filesystems are mounted across CNSA Kubernetes worker nodes. - -- Without migration, existing workloads would **not be able to mount or access their data** after switching from standalone CSI to CNSA. - -This script updates PV definitions to use the new path structure while **preserving all other PV properties**. - - -## What is `--new_path_prefix`? - -The `--new_path_prefix` is the **base filesystem mount point** of the remotely mounted filesystems on the **local IBM Storage Scale instance running on your CNSA Kubernetes worker nodes**. +When the **primary fileset** was enabled, PVs were created under paths tied to the **primary fileset hierarchy**. +Now that **primary is removed**, these paths are invalid: -It defines the **root filesystem path** under which all migrated PVs will be remapped. - -### Examples - -- `/var/mnt` – often used in CNSA-based installations -- `/ibm` – default mount point (commonly used in classic setups) -- `/mnt` – alternative mount point used in some deployments - -**Important:** -The prefix you provide must **exactly match how the filesystem is mounted** on all CNSA worker nodes. -If different nodes have different mount points, migration will **fail**. +- PVs must be mounted at the **actual fileset mount path** on CNSA worker nodes. +- Without migration, workloads would **not be able to mount or access their data**. +This script updates PV definitions to point to the correct CNSA paths while **preserving all other PV properties**. ## Example Transformation -### Before (CSI standalone `volumeHandle`): +### Before (PV created with primary enabled): ```text volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/ibm/remotefs1/primary-fileset-remotefs1-475592072879187/.volumes/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1 ``` -### After (CNSA-compatible `volumeHandle`): +### After (PV updated to actual fileset mount path with prefix `/var/mnt`): ```text -volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/var/mnt/remotefs1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1-data +volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/var/mnt/remotefs1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1/pvc-26946b2b-4c0d-9f77-606a444094c1-data ``` ### Key Points @@ -67,22 +46,21 @@ volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d - The identity portion of the handle (everything up to the last `;`). **Rewritten:** -- Only the **path segment after the last `;`**. - +- Only the **path segment after the last `;`**, now using the **prefix** and correct fileset mapping for CNSA. ## Volume Type Variations -The exact path suffix (e.g., `pvc-uuid-data`) may vary based on how the volume was originally created. -The script automatically detects and applies the correct mapping without user intervention. +The exact path suffix (e.g., `pvc-uuid-data`) may vary depending on how the volume was originally created. +The script automatically detects and applies the correct mapping for each fileset type. ## Prerequisites -Before running the migration script, ensure the following tools are installed and available in your `$PATH`: +Ensure the following tools are installed and available in your `$PATH`: -- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects +- **kubectl** – to interact with the CNSA Kubernetes cluster and fetch/update PV/PVC objects - **jq** – for JSON parsing and manipulation of Kubernetes API responses -You can verify installation with: +Verify installation with: ```bash kubectl version --client @@ -91,29 +69,26 @@ jq --version ## Migration Script Usage -A helper script is provided to automate PV migration: +Run the script to automatically migrate PVs: ```bash # Usage -./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt +./migration_cnsa_primary_removal.bash --new_path_prefix /var/mnt ``` Where: -- `` is a user-defined identifier for this migration run. -- `--new_path_prefix` specifies the **base mount point** of IBM Storage Scale filesystems on CNSA worker nodes. - Allowed values: `/ibm`, `/mnt`, `/var/mnt` - +- `--new_path_prefix` specifies the **base mount point** for all IBM Storage Scale filesystems on CNSA worker nodes (e.g., `/var/mnt`, `/ibm`, `/mnt`). ## Features of the Migration Script - ✅ Filters only PVs created with the **spectrumscale.csi.ibm.com** driver. -- ✅ Skips PVs already migrated (with a CNSA-compatible path in `volumeHandle`). +- ✅ Skips PVs already migrated (with an actual fileset mount path in `volumeHandle`). - ✅ **Backs up PVs and PVCs** before modification into a structured directory: ``` csi_migration_data/ -└── -/ +└── / ├── migration.log ├── / │ └── / @@ -125,17 +100,15 @@ csi_migration_data/ - ✅ Logs all actions, successes, skips, and failures into: ``` -csi_migration_data/-/migration.log +csi_migration_data//migration.log ``` - ✅ Summarizes **success, skipped, and failed** migrations at the end. - ✅ Idempotent – safe to re-run if needed. - ## Preserved PV Properties -The script ensures that **all original PV configurations** are retained after migration. -The following fields are preserved: +The script ensures that **all original PV configurations** are retained after migration: - **Capacity** (`spec.capacity.storage`) - **AccessModes** (`spec.accessModes`) @@ -144,12 +117,11 @@ The following fields are preserved: - **CSI driver details** (fsType, volumeAttributes, nodeStageSecrets, etc.) - **PVC binding information** (safely re-created to preserve claim references) -- Only the **`volumeHandle` path** is modified to meet CNSA expectations. - +- Only the **`volumeHandle` path** is modified to reflect the actual fileset mount with the provided **`--new_path_prefix`**. ## Notes and Limitations -- The **filesystem names** (e.g., `remotefs1`) must remain identical between standalone CSI and CNSA deployments. -- The provided `--new_path_prefix` must reflect the **actual base mount point** of IBM Storage Scale on all CNSA **worker nodes**. +- The **filesystem names** (e.g., `remotefs1`) must remain identical between pre-primary and post-primary removal deployments. +- The provided `--new_path_prefix` must reflect the **actual base mount point** of IBM Storage Scale on all CNSA worker nodes. - The script does **not delete or recreate volumes** on IBM Storage Scale; it only updates Kubernetes PV metadata. - Existing workloads must be restarted to pick up new PV mount paths after migration. diff --git a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md index a6ed4699c..a98d76321 100644 --- a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md @@ -73,7 +73,7 @@ A helper script is provided to automate PV migration: ```bash # Usage -./pv_migration_csi_primary_removed.bash +./migration_csi_primary_removal.bash ``` - No arguments are required. diff --git a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md index 7cea0b042..d19da9f0c 100644 --- a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md +++ b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md @@ -95,12 +95,11 @@ A helper script is provided to automate PV migration: ```bash # Usage -./pv_migration_csi_to_cnsa.bash --new_path_prefix /var/mnt +./migration_csi_to_cnsa.bash --new_path_prefix /var/mnt ``` Where: -- `` is a user-defined identifier for this migration run. - `--new_path_prefix` specifies the **base mount point** of IBM Storage Scale filesystems on CNSA worker nodes. Allowed values: `/ibm`, `/mnt`, `/var/mnt` From 5cf61798352cefbcdccfe1e4d5d9663d480f8ea6 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Wed, 20 Aug 2025 13:17:33 +0530 Subject: [PATCH 16/21] doc update and added patch to remove attacher finalizer when pods and pvcs are already deleted. Signed-off-by: badri-pathak --- .../README_migration_cnsa_primary_removal.md | 2 ++ .../README_migration_csi_primary_removal.md | 2 ++ .../README_migration_csi_to_cnsa.md | 2 ++ .../migration_cnsa_primary_removal.bash | 8 ++++++++ .../migration_csi_primary_removal.bash | 8 ++++++++ tools/volume_migration_scripts/migration_csi_to_cnsa.bash | 8 ++++++++ 6 files changed, 30 insertions(+) diff --git a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md index 9b9fc881a..e5775c54c 100644 --- a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md @@ -57,6 +57,8 @@ The script automatically detects and applies the correct mapping for each filese Ensure the following tools are installed and available in your `$PATH`: +- **Existing workloads or application pods must be deleted in the existing cluster before installation of new CNSA** +- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed - **kubectl** – to interact with the CNSA Kubernetes cluster and fetch/update PV/PVC objects - **jq** – for JSON parsing and manipulation of Kubernetes API responses diff --git a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md index a98d76321..c416a6440 100644 --- a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md @@ -57,6 +57,8 @@ The script automatically detects and applies the correct mapping without user in Before running the migration script, ensure the following tools are installed and available in your `$PATH`: +- **Existing workloads or application pods must be deleted in the existing CSI cluster before the new installation** +- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed - **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects - **jq** – for JSON parsing and manipulation of Kubernetes API responses diff --git a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md index d19da9f0c..70b760b60 100644 --- a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md +++ b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md @@ -79,6 +79,8 @@ The script automatically detects and applies the correct mapping without user in Before running the migration script, ensure the following tools are installed and available in your `$PATH`: +- **Existing workloads or application pods must be deleted in the existing CSI cluster before installation of CNSA** +- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed - **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects - **jq** – for JSON parsing and manipulation of Kubernetes API responses diff --git a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash index e8f7e37f1..ee4a268c7 100644 --- a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash @@ -296,6 +296,14 @@ migrate_each() { echo "Deleting PVC and PV..." kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" + + finalizer="external-attacher/spectrumscale-csi-ibm-com" + + # Check if PV has Spectrum Scale external-attacher finalizer + if kubectl get pv "${PV}" -o json | jq -e ".metadata.finalizers | index(\"${finalizer}\")" >/dev/null; then + echo "Removing ${finalizer} finalizer from PV ${PV} before deletion..." + kubectl patch pv "${PV}" -p '{"metadata":{"finalizers":["kubernetes.io/pv-protection"]}}' + fi kubectl delete pv "$PV" echo "Recreating PV and PVC..." diff --git a/tools/volume_migration_scripts/migration_csi_primary_removal.bash b/tools/volume_migration_scripts/migration_csi_primary_removal.bash index 01db0c407..3ab9c0fc0 100644 --- a/tools/volume_migration_scripts/migration_csi_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_csi_primary_removal.bash @@ -266,6 +266,14 @@ migrate_each() { echo "Deleting PVC and PV..." kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" + + finalizer="external-attacher/spectrumscale-csi-ibm-com" + + # Check if PV has Spectrum Scale external-attacher finalizer + if kubectl get pv "${PV}" -o json | jq -e ".metadata.finalizers | index(\"${finalizer}\")" >/dev/null; then + echo "Removing ${finalizer} finalizer from PV ${PV} before deletion..." + kubectl patch pv "${PV}" -p '{"metadata":{"finalizers":["kubernetes.io/pv-protection"]}}' + fi kubectl delete pv "$PV" echo "Recreating PV and PVC..." diff --git a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash index 83d4ac263..2ef4ba109 100644 --- a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -259,6 +259,14 @@ migrate_each() { echo "Deleting PVC and PV..." kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" + + finalizer="external-attacher/spectrumscale-csi-ibm-com" + + # Check if PV has Spectrum Scale external-attacher finalizer + if kubectl get pv "${PV}" -o json | jq -e ".metadata.finalizers | index(\"${finalizer}\")" >/dev/null; then + echo "Removing ${finalizer} finalizer from PV ${PV} before deletion..." + kubectl patch pv "${PV}" -p '{"metadata":{"finalizers":["kubernetes.io/pv-protection"]}}' + fi kubectl delete pv "$PV" echo "Recreating PV and PVC..." From d11c6309deeed97172355be1473363e58a945875 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Wed, 20 Aug 2025 13:53:30 +0530 Subject: [PATCH 17/21] fixed for dependent with parent as root fset Signed-off-by: badri-pathak --- .../migration_cnsa_primary_removal.bash | 21 +++++++++++++----- .../migration_csi_primary_removal.bash | 22 ++++++++++++++----- .../migration_csi_to_cnsa.bash | 22 ++++++++++++++----- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash index ee4a268c7..f20890435 100644 --- a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash @@ -191,14 +191,25 @@ migrate_each() { elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') - # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and dependent fileset" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + else + echo "parentFileset is empty or 'root', using default path" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + fi + else + echo "Dynamic Fileset volume and dependent fileset" + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + else + echo "parentFileset is empty or 'root', using default path" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + fi fi fi else diff --git a/tools/volume_migration_scripts/migration_csi_primary_removal.bash b/tools/volume_migration_scripts/migration_csi_primary_removal.bash index 3ab9c0fc0..4758b8a3f 100644 --- a/tools/volume_migration_scripts/migration_csi_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_csi_primary_removal.bash @@ -167,14 +167,26 @@ migrate_each() { elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" - # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') + if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and dependent fileset" - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + else + echo "parentFileset is empty or 'root', using default path" + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + fi + else + echo "Dynamic Fileset volume and dependent fileset" + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + else + echo "parentFileset is empty or 'root', using default path" + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + fi fi fi else diff --git a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash index 2ef4ba109..38620faa2 100644 --- a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -183,14 +183,26 @@ migrate_each() { elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" - PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset"') - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" - # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') + if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and dependent fileset" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" + else + echo "parentFileset is empty or 'root', using default path" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + fi + else + echo "Dynamic Fileset volume and dependent fileset" + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" + else + echo "parentFileset is empty or 'root', using default path" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + fi fi elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then From f753478c1f326b40f888a28549475a6c8dda4826 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Wed, 20 Aug 2025 14:10:19 +0530 Subject: [PATCH 18/21] improvements around dependent Signed-off-by: badri-pathak --- .../migration_cnsa_primary_removal.bash | 9 +++------ .../migration_csi_primary_removal.bash | 9 +++------ .../volume_migration_scripts/migration_csi_to_cnsa.bash | 2 +- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash index f20890435..54319e7d1 100644 --- a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash @@ -181,7 +181,6 @@ migrate_each() { if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" - fi else echo "For this volume primary migration is not required. Skipping $PV" skip_count=$(expr $skip_count + 1) @@ -211,26 +210,24 @@ migrate_each() { NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" fi fi - fi else echo "For this volume primary migration is not required. Skipping $PV" skip_count=$(expr $skip_count + 1) skip_list+=("$PVC_NAME|$PV") return fi - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" && "${parts[-1]}" == *"/.volumes/"* ]]; then + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then # Default path of dynamic fileset NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and independent fileset" NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" fi - fi else echo "For this volume primary migration is not required. Skipping $PV" skip_count=$(expr $skip_count + 1) @@ -280,7 +277,7 @@ migrate_each() { return fi - echo "Updated volumeHandle: $NEW_VOLUME_HANDLE" + echo "Updated volumeHandle for $PV: $NEW_VOLUME_HANDLE" echo "Setting reclaim policy to Retain for PV: $PV" if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then diff --git a/tools/volume_migration_scripts/migration_csi_primary_removal.bash b/tools/volume_migration_scripts/migration_csi_primary_removal.bash index 4758b8a3f..3075159a7 100644 --- a/tools/volume_migration_scripts/migration_csi_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_csi_primary_removal.bash @@ -157,7 +157,6 @@ migrate_each() { if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" - fi else echo "For this volume primary migration is not required. Skipping $PV" skip_count=$(expr $skip_count + 1) @@ -188,26 +187,24 @@ migrate_each() { NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" fi fi - fi else echo "For this volume primary migration is not required. Skipping $PV" skip_count=$(expr $skip_count + 1) skip_list+=("$PVC_NAME|$PV") return fi - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" && "${parts[-1]}" == *"/.volumes/"* ]]; then + elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then # Default path of dynamic fileset NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and independent fileset" NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" fi - fi else echo "For this volume primary migration is not required. Skipping $PV" skip_count=$(expr $skip_count + 1) @@ -251,7 +248,7 @@ migrate_each() { return fi - echo "Updated volumeHandle: $NEW_VOLUME_HANDLE" + echo "Updated volumeHandle for $PV: $NEW_VOLUME_HANDLE" echo "Setting reclaim policy to Retain for PV: $PV" if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then diff --git a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash index 38620faa2..0f8b46d66 100644 --- a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -244,7 +244,7 @@ migrate_each() { return fi - echo "Updated volumeHandle: $NEW_VOLUME_HANDLE" + echo "Updated volumeHandle for $PV: $NEW_VOLUME_HANDLE" echo "Setting reclaim policy to Retain for PV: $PV" if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then From d101c1ea7b77c359bf9679d204020cd37f2417c0 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Thu, 21 Aug 2025 14:47:05 +0530 Subject: [PATCH 19/21] added volDirPath and check for attacher finalizer Signed-off-by: badri-pathak --- .../README_migration_cnsa_primary_removal.md | 32 +++++++++--------- .../README_migration_csi_primary_removal.md | 33 ++++++++++--------- .../README_migration_csi_to_cnsa.md | 32 +++++++++--------- .../migration_cnsa_primary_removal.bash | 24 ++++++++++---- .../migration_csi_primary_removal.bash | 24 ++++++++++---- .../migration_csi_to_cnsa.bash | 26 +++++++++++---- 6 files changed, 107 insertions(+), 64 deletions(-) diff --git a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md index e5775c54c..a9cd7ac92 100644 --- a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md @@ -28,6 +28,23 @@ Now that **primary is removed**, these paths are invalid: This script updates PV definitions to point to the correct CNSA paths while **preserving all other PV properties**. +## Prerequisites + +- **Delete any existing workloads or application pods attached to the PVCs that will be migrated** from the existing CSI cluster **before installing CNSA.** + +Before running the migration script, ensure the following tools are installed and available in your `$PATH`: + +- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed +- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects +- **jq** – for JSON parsing and manipulation of Kubernetes API responses + +You can verify installation with: + +```bash +kubectl version --client +jq --version +``` + ## Example Transformation ### Before (PV created with primary enabled): @@ -53,21 +70,6 @@ volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d The exact path suffix (e.g., `pvc-uuid-data`) may vary depending on how the volume was originally created. The script automatically detects and applies the correct mapping for each fileset type. -## Prerequisites - -Ensure the following tools are installed and available in your `$PATH`: - -- **Existing workloads or application pods must be deleted in the existing cluster before installation of new CNSA** -- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed -- **kubectl** – to interact with the CNSA Kubernetes cluster and fetch/update PV/PVC objects -- **jq** – for JSON parsing and manipulation of Kubernetes API responses - -Verify installation with: - -```bash -kubectl version --client -jq --version -``` ## Migration Script Usage diff --git a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md index c416a6440..afb5a3166 100644 --- a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md +++ b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md @@ -28,6 +28,24 @@ Now that **primary is removed**, these paths are invalid: This script updates PV definitions to point to the correct fileset paths while **preserving all other PV properties**. +## Prerequisites + +- **Delete any existing workloads or application pods attached to the PVCs that will be migrated** from the existing CSI cluster **before installing CNSA.** + +Before running the migration script, ensure the following tools are installed and available in your `$PATH`: + +- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed +- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects +- **jq** – for JSON parsing and manipulation of Kubernetes API responses + +You can verify installation with: + +```bash +kubectl version --client +jq --version +``` + + ## Example Transformation ### Before (PV created with **primary** enabled): @@ -53,21 +71,6 @@ volumeHandle: 0;2;13009550825755318848;A3D56F10:9BC12E30;;pvc-3b1a-49d3-89e1-51f The exact path suffix (e.g., `pvc-uuid-data`) may vary depending on how the volume was originally created. The script automatically detects and applies the correct mapping without user intervention. -## Prerequisites - -Before running the migration script, ensure the following tools are installed and available in your `$PATH`: - -- **Existing workloads or application pods must be deleted in the existing CSI cluster before the new installation** -- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed -- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects -- **jq** – for JSON parsing and manipulation of Kubernetes API responses - -You can verify installation with: - -```bash -kubectl version --client -jq --version -``` ## Migration Script Usage diff --git a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md index 70b760b60..2b24b986d 100644 --- a/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md +++ b/tools/volume_migration_scripts/README_migration_csi_to_cnsa.md @@ -32,6 +32,23 @@ However, in a **CNSA setup**, this format becomes incompatible because: This script updates PV definitions to use the new path structure while **preserving all other PV properties**. +## Prerequisites + +- **Delete any existing workloads or application pods attached to the PVCs that will be migrated** from the existing CSI cluster **before installing CNSA.** + +Before running the migration script, ensure the following tools are installed and available in your `$PATH`: + +- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed +- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects +- **jq** – for JSON parsing and manipulation of Kubernetes API responses + +You can verify installation with: + +```bash +kubectl version --client +jq --version +``` + ## What is `--new_path_prefix`? The `--new_path_prefix` is the **base filesystem mount point** of the remotely mounted filesystems on the **local IBM Storage Scale instance running on your CNSA Kubernetes worker nodes**. @@ -75,21 +92,6 @@ volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d The exact path suffix (e.g., `pvc-uuid-data`) may vary based on how the volume was originally created. The script automatically detects and applies the correct mapping without user intervention. -## Prerequisites - -Before running the migration script, ensure the following tools are installed and available in your `$PATH`: - -- **Existing workloads or application pods must be deleted in the existing CSI cluster before installation of CNSA** -- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed -- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects -- **jq** – for JSON parsing and manipulation of Kubernetes API responses - -You can verify installation with: - -```bash -kubectl version --client -jq --version -``` ## Migration Script Usage diff --git a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash index 54319e7d1..12c97ec5c 100644 --- a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash @@ -179,7 +179,7 @@ migrate_each() { if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" else echo "For this volume primary migration is not required. Skipping $PV" @@ -192,6 +192,7 @@ migrate_each() { if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and dependent fileset" @@ -203,7 +204,10 @@ migrate_each() { fi else echo "Dynamic Fileset volume and dependent fileset" - if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" && -n "$VOL_DIR_BASE_PATH" ]]; then + echo "Using parentFileset with volDirBasePath" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PARENT_FILESET/$PV/$PV-data" + elif [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" else echo "parentFileset is empty or 'root', using default path" @@ -222,11 +226,16 @@ migrate_each() { # Default path of dynamic fileset NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') + # Check if existingVolume is set to yes for static fileset volumes existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and independent fileset" NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + elif [[ -n "$VOL_DIR_BASE_PATH" ]]; then + echo "Dynamic Fileset volume with volDirBasePath and independent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV/$PV-data" fi else echo "For this volume primary migration is not required. Skipping $PV" @@ -305,12 +314,15 @@ migrate_each() { echo "Deleting PVC and PV..." kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" +# Remove external-attacher finalizer from PV if it exists finalizer="external-attacher/spectrumscale-csi-ibm-com" + index=$(kubectl get pv "${PV}" -o json | jq -r \ + ".metadata.finalizers | to_entries | map(select(.value==\"${finalizer}\")) | .[0].key") - # Check if PV has Spectrum Scale external-attacher finalizer - if kubectl get pv "${PV}" -o json | jq -e ".metadata.finalizers | index(\"${finalizer}\")" >/dev/null; then - echo "Removing ${finalizer} finalizer from PV ${PV} before deletion..." - kubectl patch pv "${PV}" -p '{"metadata":{"finalizers":["kubernetes.io/pv-protection"]}}' + if [[ -n "$index" && "$index" != "null" ]]; then + echo "Removing ${finalizer} finalizer from PV ${PV}..." + kubectl patch pv "${PV}" --type=json \ + -p="[ { \"op\": \"remove\", \"path\": \"/metadata/finalizers/${index}\" } ]" fi kubectl delete pv "$PV" diff --git a/tools/volume_migration_scripts/migration_csi_primary_removal.bash b/tools/volume_migration_scripts/migration_csi_primary_removal.bash index 3075159a7..3593cfccc 100644 --- a/tools/volume_migration_scripts/migration_csi_primary_removal.bash +++ b/tools/volume_migration_scripts/migration_csi_primary_removal.bash @@ -155,7 +155,7 @@ migrate_each() { if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" else echo "For this volume primary migration is not required. Skipping $PV" @@ -169,6 +169,7 @@ migrate_each() { PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and dependent fileset" @@ -180,7 +181,10 @@ migrate_each() { fi else echo "Dynamic Fileset volume and dependent fileset" - if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" && -n "$VOL_DIR_BASE_PATH" ]]; then + echo "Using parentFileset with volDirBasePath" + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PARENT_FILESET/$PV/$PV-data" + elif [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" else echo "parentFileset is empty or 'root', using default path" @@ -199,11 +203,16 @@ migrate_each() { # Default path of dynamic fileset NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') + # Check if existingVolume is set to yes for static fileset volumes existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and independent fileset" NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + elif [[ -n "$VOL_DIR_BASE_PATH" ]]; then + echo "Dynamic Fileset volume with volDirBasePath and independent fileset" + NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV/$PV-data" fi else echo "For this volume primary migration is not required. Skipping $PV" @@ -276,12 +285,15 @@ migrate_each() { echo "Deleting PVC and PV..." kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" +# Remove external-attacher finalizer from PV if it exists finalizer="external-attacher/spectrumscale-csi-ibm-com" + index=$(kubectl get pv "${PV}" -o json | jq -r \ + ".metadata.finalizers | to_entries | map(select(.value==\"${finalizer}\")) | .[0].key") - # Check if PV has Spectrum Scale external-attacher finalizer - if kubectl get pv "${PV}" -o json | jq -e ".metadata.finalizers | index(\"${finalizer}\")" >/dev/null; then - echo "Removing ${finalizer} finalizer from PV ${PV} before deletion..." - kubectl patch pv "${PV}" -p '{"metadata":{"finalizers":["kubernetes.io/pv-protection"]}}' + if [[ -n "$index" && "$index" != "null" ]]; then + echo "Removing ${finalizer} finalizer from PV ${PV}..." + kubectl patch pv "${PV}" --type=json \ + -p="[ { \"op\": \"remove\", \"path\": \"/metadata/finalizers/${index}\" } ]" fi kubectl delete pv "$PV" diff --git a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash index 0f8b46d66..a2d82444d 100644 --- a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -178,7 +178,7 @@ migrate_each() { if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath"') + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then @@ -186,6 +186,7 @@ migrate_each() { PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and dependent fileset" @@ -197,7 +198,11 @@ migrate_each() { fi else echo "Dynamic Fileset volume and dependent fileset" - if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" && -n "$VOL_DIR_BASE_PATH" ]]; then + echo "Using parentFileset with volDirBasePath" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PARENT_FILESET/$PV/$PV-data" + elif [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then + echo "Using parentFileset without volDirBasePath" NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" else echo "parentFileset is empty or 'root', using default path" @@ -211,11 +216,15 @@ migrate_each() { # Default path of dynamic fileset NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" + VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume"') + existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') if [[ "$existingVolume" == "yes" ]]; then echo "Static Fileset volume and independent fileset" NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" + elif [[ -n "$VOL_DIR_BASE_PATH" ]]; then + echo "Dynamic Fileset volume with volDirBasePath and independent fileset" + NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV/$PV-data" fi elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then @@ -272,12 +281,15 @@ migrate_each() { echo "Deleting PVC and PV..." kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" +# Remove external-attacher finalizer from PV if it exists finalizer="external-attacher/spectrumscale-csi-ibm-com" + index=$(kubectl get pv "${PV}" -o json | jq -r \ + ".metadata.finalizers | to_entries | map(select(.value==\"${finalizer}\")) | .[0].key") - # Check if PV has Spectrum Scale external-attacher finalizer - if kubectl get pv "${PV}" -o json | jq -e ".metadata.finalizers | index(\"${finalizer}\")" >/dev/null; then - echo "Removing ${finalizer} finalizer from PV ${PV} before deletion..." - kubectl patch pv "${PV}" -p '{"metadata":{"finalizers":["kubernetes.io/pv-protection"]}}' + if [[ -n "$index" && "$index" != "null" ]]; then + echo "Removing ${finalizer} finalizer from PV ${PV}..." + kubectl patch pv "${PV}" --type=json \ + -p="[ { \"op\": \"remove\", \"path\": \"/metadata/finalizers/${index}\" } ]" fi kubectl delete pv "$PV" From 97c4108b8391c1892b9cb32bc92a018a839c37e3 Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Sat, 23 Aug 2025 02:13:01 +0530 Subject: [PATCH 20/21] logging improvement Signed-off-by: badri-pathak --- tools/volume_migration_scripts/migration_csi_to_cnsa.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash index a2d82444d..857519982 100644 --- a/tools/volume_migration_scripts/migration_csi_to_cnsa.bash +++ b/tools/volume_migration_scripts/migration_csi_to_cnsa.bash @@ -64,6 +64,7 @@ fail_count=0 skip_count=0 main() { + init check_prerequisites echo "Using the new path prefix: $NEW_PATH_PREFIX" echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." @@ -73,7 +74,6 @@ main() { echo "Aborting migration." exit 0 fi - init get_pv_list TOTAL_PVS=$(echo "$ALL_PVS" | wc -l | xargs) COUNT=1 From 0a3cdf38a0109e2930c3b5316f4e8fd6c0a1defc Mon Sep 17 00:00:00 2001 From: badri-pathak Date: Sat, 23 Aug 2025 02:30:38 +0530 Subject: [PATCH 21/21] move to another pr 1375 Signed-off-by: badri-pathak --- .../README_migration_cnsa_primary_removal.md | 131 ------ .../README_migration_csi_primary_removal.md | 131 ------ .../migration_cnsa_primary_removal.bash | 414 ------------------ .../migration_csi_primary_removal.bash | 385 ---------------- 4 files changed, 1061 deletions(-) delete mode 100644 tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md delete mode 100644 tools/volume_migration_scripts/README_migration_csi_primary_removal.md delete mode 100644 tools/volume_migration_scripts/migration_cnsa_primary_removal.bash delete mode 100644 tools/volume_migration_scripts/migration_csi_primary_removal.bash diff --git a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md b/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md deleted file mode 100644 index a9cd7ac92..000000000 --- a/tools/volume_migration_scripts/README_migration_cnsa_primary_removal.md +++ /dev/null @@ -1,131 +0,0 @@ - -# PV Migration Script – CNSA (Primary → Actual Fileset Mount) - -This script helps migrate existing Kubernetes **PersistentVolumes (PVs)** in a **CNSA environment** that were originally created when the **primary fileset option** was enabled. -It updates PVs to use the **actual fileset mount path** on CNSA worker nodes after the primary has been removed, ensuring workloads continue to access their data seamlessly. - -## Key Features - -- Preserves all **original PV properties**, including: - - Reclaim policy (e.g., `Retain`, `Delete`) - - Access modes (`ReadWriteOnce`, `ReadWriteMany`, etc.) - - Storage capacity - - Filesystem type (`fsType`) - - Labels, annotations, and other PV metadata - -- Only the **volumeHandle path segment** is updated based on the **fileset type** and **prefix**. -- Supports migration of PVs across **different fileset types** (independent, dependent, static, cache, CG, etc.). -- Generates **backup YAML files** before applying changes. -- Can be safely re-run if required (**idempotent migration**). - -## Why Migration is Required - -When the **primary fileset** was enabled, PVs were created under paths tied to the **primary fileset hierarchy**. -Now that **primary is removed**, these paths are invalid: - -- PVs must be mounted at the **actual fileset mount path** on CNSA worker nodes. -- Without migration, workloads would **not be able to mount or access their data**. - -This script updates PV definitions to point to the correct CNSA paths while **preserving all other PV properties**. - -## Prerequisites - -- **Delete any existing workloads or application pods attached to the PVCs that will be migrated** from the existing CSI cluster **before installing CNSA.** - -Before running the migration script, ensure the following tools are installed and available in your `$PATH`: - -- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed -- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects -- **jq** – for JSON parsing and manipulation of Kubernetes API responses - -You can verify installation with: - -```bash -kubectl version --client -jq --version -``` - -## Example Transformation - -### Before (PV created with primary enabled): -```text -volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/ibm/remotefs1/primary-fileset-remotefs1-475592072879187/.volumes/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1 -``` - -### After (PV updated to actual fileset mount path with prefix `/var/mnt`): -```text -volumeHandle: 0;2;13009550825755318848;9A7B0B0A:68891B40;;pvc-26946b2b-b18a-4c0d-9f77-606a444094c1;/var/mnt/remotefs1/pvc-26946b2b-b18a-4c0d-9f77-606a444094c1/pvc-26946b2b-4c0d-9f77-606a444094c1-data -``` - -### Key Points - -**Unchanged:** -- The identity portion of the handle (everything up to the last `;`). - -**Rewritten:** -- Only the **path segment after the last `;`**, now using the **prefix** and correct fileset mapping for CNSA. - -## Volume Type Variations - -The exact path suffix (e.g., `pvc-uuid-data`) may vary depending on how the volume was originally created. -The script automatically detects and applies the correct mapping for each fileset type. - - -## Migration Script Usage - -Run the script to automatically migrate PVs: - -```bash -# Usage -./migration_cnsa_primary_removal.bash --new_path_prefix /var/mnt -``` - -Where: - -- `--new_path_prefix` specifies the **base mount point** for all IBM Storage Scale filesystems on CNSA worker nodes (e.g., `/var/mnt`, `/ibm`, `/mnt`). - -## Features of the Migration Script - -- ✅ Filters only PVs created with the **spectrumscale.csi.ibm.com** driver. -- ✅ Skips PVs already migrated (with an actual fileset mount path in `volumeHandle`). -- ✅ **Backs up PVs and PVCs** before modification into a structured directory: - -``` -csi_migration_data/ -└── / - ├── migration.log - ├── / - │ └── / - │ ├── pvc.yaml - │ └── pv.yaml - └── ... -``` - -- ✅ Logs all actions, successes, skips, and failures into: - -``` -csi_migration_data//migration.log -``` - -- ✅ Summarizes **success, skipped, and failed** migrations at the end. -- ✅ Idempotent – safe to re-run if needed. - -## Preserved PV Properties - -The script ensures that **all original PV configurations** are retained after migration: - -- **Capacity** (`spec.capacity.storage`) -- **AccessModes** (`spec.accessModes`) -- **PersistentVolumeReclaimPolicy** (`spec.persistentVolumeReclaimPolicy`) -- **StorageClassName** -- **CSI driver details** (fsType, volumeAttributes, nodeStageSecrets, etc.) -- **PVC binding information** (safely re-created to preserve claim references) - -- Only the **`volumeHandle` path** is modified to reflect the actual fileset mount with the provided **`--new_path_prefix`**. - -## Notes and Limitations - -- The **filesystem names** (e.g., `remotefs1`) must remain identical between pre-primary and post-primary removal deployments. -- The provided `--new_path_prefix` must reflect the **actual base mount point** of IBM Storage Scale on all CNSA worker nodes. -- The script does **not delete or recreate volumes** on IBM Storage Scale; it only updates Kubernetes PV metadata. -- Existing workloads must be restarted to pick up new PV mount paths after migration. diff --git a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md b/tools/volume_migration_scripts/README_migration_csi_primary_removal.md deleted file mode 100644 index afb5a3166..000000000 --- a/tools/volume_migration_scripts/README_migration_csi_primary_removal.md +++ /dev/null @@ -1,131 +0,0 @@ - -# PV Migration Script – CSI (Primary → Actual Fileset Mount) - -This script helps migrate existing Kubernetes **PersistentVolumes (PVs)** that were originally created when the **primary fs and fileset** was enabled, to a format that uses the **actual fileset mount path** after the **primary has been removed**. -It ensures that workloads continue to access their data seamlessly after migration. - -## Key Features - -- Preserves all **original PV properties**, including: - - Reclaim policy (e.g., `Retain`, `Delete`) - - Access modes (`ReadWriteOnce`, `ReadWriteMany`, etc.) - - Storage capacity - - Filesystem type (`fsType`) - - Labels, annotations, and other PV metadata - -- Only the **volumeHandle path segment** is updated to reflect the **actual fileset mount path**. -- Supports migration of PVs across **different fileset types** (independent, dependent, static, cache, CG, etc.). -- Generates **backup YAML files** before applying changes. -- Can be safely re-run if required (**idempotent migration**). - -## Why Migration is Required - -When the **primary fs and fileset** was enabled, PVs were created under paths tied to the **primary fs and fileset hierarchy**. -Now that **primary is removed**, these paths are invalid: - -- PVs must be mounted at the **actual fileset mount path** in the Storage Scale filesystem. -- Without migration, existing workloads would **not be able to mount or access their data**. - -This script updates PV definitions to point to the correct fileset paths while **preserving all other PV properties**. - -## Prerequisites - -- **Delete any existing workloads or application pods attached to the PVCs that will be migrated** from the existing CSI cluster **before installing CNSA.** - -Before running the migration script, ensure the following tools are installed and available in your `$PATH`: - -- The script should be run in the context of the cluster where Spectrum Scale CSI is deployed -- **kubectl** – to interact with the Kubernetes cluster and fetch/update PV/PVC objects -- **jq** – for JSON parsing and manipulation of Kubernetes API responses - -You can verify installation with: - -```bash -kubectl version --client -jq --version -``` - - -## Example Transformation - -### Before (PV created with **primary** enabled): -```text -volumeHandle: 0;2;13009550825755318848;A3D56F10:9BC12E30;;pvc-3b1a-49d3-89e1-51f607b91234;/ibm/remotefs1/primary-remotefs1-123456789/.volumes/pvc-3b1a-49d3-89e1-51f607b91234 -``` - -### After (Migrated to **actual fileset mount path**): -```text -volumeHandle: 0;2;13009550825755318848;A3D56F10:9BC12E30;;pvc-3b1a-49d3-89e1-51f607b91234;/var/mnt/remotefs1/fs1-pvc-3b1a-49d3-89e1-51f607b91234/pvc-3b1a-49d3-89e1-51f607b91234-data -``` - -### Key Points - -**Unchanged:** -- The identity portion of the handle (everything up to the last `;`). - -**Rewritten:** -- Only the **path segment after the last `;`**, now pointing to the actual fileset mount path. - -## Volume Type Variations - -The exact path suffix (e.g., `pvc-uuid-data`) may vary depending on how the volume was originally created. -The script automatically detects and applies the correct mapping without user intervention. - - -## Migration Script Usage - -A helper script is provided to automate PV migration: - -```bash -# Usage -./migration_csi_primary_removal.bash -``` - -- No arguments are required. -- The script automatically detects the **actual fileset mount path** for each PV and updates accordingly. - -## Features of the Migration Script - -- ✅ Filters only PVs created with the **spectrumscale.csi.ibm.com** driver. -- ✅ Skips PVs already migrated (with an actual fileset mount path in `volumeHandle`). -- ✅ **Backs up PVs and PVCs** before modification into a structured directory: - -``` -csi_migration_data/ -└── migration-/ - ├── migration.log - ├── / - │ └── / - │ ├── pvc.yaml - │ └── pv.yaml - └── ... -``` - -- ✅ Logs all actions, successes, skips, and failures into: - -``` -csi_migration_data/migration-/migration.log -``` - -- ✅ Summarizes **success, skipped, and failed** migrations at the end. -- ✅ Idempotent – safe to re-run if needed. - -## Preserved PV Properties - -The script ensures that **all original PV configurations** are retained after migration. -The following fields are preserved: - -- **Capacity** (`spec.capacity.storage`) -- **AccessModes** (`spec.accessModes`) -- **PersistentVolumeReclaimPolicy** (`spec.persistentVolumeReclaimPolicy`) -- **StorageClassName** -- **CSI driver details** (fsType, volumeAttributes, nodeStageSecrets, etc.) -- **PVC binding information** (safely re-created to preserve claim references) - -- Only the **`volumeHandle` path** is modified to reflect the actual fileset mount. - -## Notes and Limitations - -- The **filesystem names** (e.g., `remotefs1`) must remain identical between pre-primary and post-primary removal deployments. -- The script does **not delete or recreate volumes** on IBM Storage Scale; it only updates Kubernetes PV metadata. -- Existing workloads must be restarted to pick up new PV mount paths after migration. diff --git a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash b/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash deleted file mode 100644 index 12c97ec5c..000000000 --- a/tools/volume_migration_scripts/migration_cnsa_primary_removal.bash +++ /dev/null @@ -1,414 +0,0 @@ -#!/bin/bash - -# Usage: ./migration_cnsa_primary_removal.bash --new_path_prefix /var/mnt -# Migrates IBM Storage Scale PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix after primary removal. - -set -euo pipefail - -# --- Help Function --- -help() { - echo "" - echo "Usage: $0 --new_path_prefix " - echo "" - echo "Example:" - echo " $0 --new_path_prefix /var/mnt" - echo "" - echo " These must match the base mount point of IBM Storage Scale on your nodes." - echo "" - echo "Description:" - echo " This script migrates IBM Storage Scale CSI PersistentVolumes to CNSA format by updating the volumeHandle path with the specified prefix after primary removal." - echo "" - exit 1 -} - -# --- Argument Parsing --- -NEW_PATH_PREFIX="" - -while [[ $# -gt 0 ]]; do - case "$1" in - --new_path_prefix) - shift - NEW_PATH_PREFIX="$1" - ;; - -h|--help) - help - ;; - *) - echo "Unknown argument: $1" - help - ;; - esac - shift -done - -if [[ -z "$NEW_PATH_PREFIX" ]]; then - echo "Missing required argument: --new_path_prefix" - help -fi - -#TODO: Uncomment the following lines to enforce allowed path prefixes -# Enforce allowed path prefixes -if [[ "$NEW_PATH_PREFIX" != "/ibm" && "$NEW_PATH_PREFIX" != "/var/mnt" && "$NEW_PATH_PREFIX" != "/mnt" ]]; then - echo "Error: Unsupported --new_path_prefix value: '$NEW_PATH_PREFIX'" - echo "Allowed values are: /ibm, /var/mnt, /mnt" - exit 1 -fi - -# --- Initialize Counters and Lists --- -success_list=() -fail_list=() -skip_list=() - -success_count=0 -fail_count=0 -skip_count=0 - -main() { - check_prerequisites - echo "Using the new path prefix: $NEW_PATH_PREFIX" - echo "The new volume handle volumePath will be: $NEW_PATH_PREFIX//..." - echo "" - read -rp "Proceed with migration? (yes/y/Y to continue): " CONFIRM - if [[ "$CONFIRM" != "yes" && "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then - echo "Aborting migration." - exit 0 - fi - init - get_pv_list - TOTAL_PVS=$(echo "$ALL_PVS" | wc -l | xargs) - COUNT=1 - for PV in $ALL_PVS; do - echo "" - echo "[$COUNT/$TOTAL_PVS] --------------------------------------------------------------------------------" - echo "Processing PV: $PV " - migrate_each "$PV" - COUNT=$((COUNT + 1)) - done - final_summary -} - -check_prerequisites() { - echo "Checking prerequisites..." - for cmd in kubectl jq; do - if ! command -v $cmd &>/dev/null; then - echo "Error: '$cmd' is required but not installed or not in PATH." >&2 - exit 1 - fi - done - echo "All prerequisites met." -} - -init() { - RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) - - BACKUP_PARENT_DIR="csi_migration_data" - mkdir -p "$BACKUP_PARENT_DIR" - - BACKUP_BASE_DIR="$BACKUP_PARENT_DIR/$RUN_TIMESTAMP" - mkdir -p "$BACKUP_BASE_DIR" - - LOG_FILE="$BACKUP_BASE_DIR/migration.log" - exec > >(tee "$LOG_FILE") 2>&1 - - echo "Logging to $LOG_FILE" - echo "Starting migration at: $(date)" - echo "" -} - -get_pv_list() { - ALL_PVS=$(kubectl get pv -o json | jq -r '.items[] | select(.spec.csi.driver == "spectrumscale.csi.ibm.com") | .metadata.name') - echo "Found $(echo "$ALL_PVS" | wc -l) PVs using spectrumscale.csi.ibm.com driver" -} - -migrate_each() { - PV="$1" - - VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { - echo "Failed to get volumeHandle for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - echo "Current volumeHandle: $VOLUME_HANDLE" - - PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') || { - echo "Failed to get PVC name for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("|$PV"); return; - } - PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') || { - echo "Failed to get PVC namespace for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - - echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" - - if ! kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" >/dev/null 2>&1; then - echo "PVC $PVC_NAME not found in namespace $PVC_NAMESPACE, skipping." - fail_list+=("$PVC_NAME|$PV") - fail_count=$(expr $fail_count + 1) - return - fi - - ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') || { - echo "Failed to get access modes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') || { - echo "Failed to get storage size."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') || { - echo "Failed to get storage class."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') || { - echo "Failed to get volume mode."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') || { - echo "Failed to get volumeAttributes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { - echo "Failed to get volBackendFs."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { - echo "Failed to get driver."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { - echo "Failed to get fsType."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - RECLAIM_POLICY_ORIGINAL=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') || { - echo "Failed to get reclaimPolicy for PV $PV."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - - - OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') - IFS=';' read -ra parts <<< "$VOLUME_HANDLE" - - if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then - echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" - if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" - else - echo "For this volume primary migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then - echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" - if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') - - if [[ "$existingVolume" == "yes" ]]; then - echo "Static Fileset volume and dependent fileset" - if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" - else - echo "parentFileset is empty or 'root', using default path" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" - fi - else - echo "Dynamic Fileset volume and dependent fileset" - if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" && -n "$VOL_DIR_BASE_PATH" ]]; then - echo "Using parentFileset with volDirBasePath" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PARENT_FILESET/$PV/$PV-data" - elif [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" - else - echo "parentFileset is empty or 'root', using default path" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" - fi - fi - else - echo "For this volume primary migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then - echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" - if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - # Default path of dynamic fileset - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" - - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') - - # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') - if [[ "$existingVolume" == "yes" ]]; then - echo "Static Fileset volume and independent fileset" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" - elif [[ -n "$VOL_DIR_BASE_PATH" ]]; then - echo "Dynamic Fileset volume with volDirBasePath and independent fileset" - NEW_PATH="$NEW_PATH_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV/$PV-data" - fi - else - echo "For this volume primary migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then - echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" - echo "For Consistency group fileset migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then - echo "Detected volumeHandle type: 0;3 : Shallow copy fileset" - echo "For Shallow copy fileset migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - - elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "3" ]]; then - echo "Detected volumeHandle type: 1;3 version-2: Shallow copy fileset" - echo "For Shallow copy fileset migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - - elif [[ "${parts[0]}" == "2" && "${parts[1]}" == "2" ]]; then - echo "Detected volumeHandle type: 2;2 : Cache fileset" - echo "For Cache fileset migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - else - echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" - fail_list+=("$PVC_NAME|$PV") - fail_count=$(expr $fail_count + 1) - return - fi - - NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") - - if [[ "$VOLUME_HANDLE" == "$NEW_VOLUME_HANDLE" ]]; then - echo "Already migrated (volumeHandle matches target). Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - - echo "Updated volumeHandle for $PV: $NEW_VOLUME_HANDLE" - - echo "Setting reclaim policy to Retain for PV: $PV" - if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then - echo "Failed to patch reclaim policy for PV: $PV" - fail_count=$(expr $fail_count + 1) - fail_list+=("$PVC_NAME|$PV") - return - fi - - BACKUP_DIR="$BACKUP_BASE_DIR/${PVC_NAMESPACE}/${PVC_NAME}" - mkdir -p "$BACKUP_DIR" - - echo "Backing up PV and PVC..." - kubectl get pv "$PV" -o yaml > "$BACKUP_DIR/pv.yaml" - kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o yaml > "$BACKUP_DIR/pvc.yaml" - - RECLAIM_POLICY=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') - if [[ "$RECLAIM_POLICY" != "Retain" ]]; then - echo "Reclaim policy is not Retain (got: $RECLAIM_POLICY), skipping." - fail_count=$(expr $fail_count + 1) - fail_list+=("$PVC_NAME|$PV") - return - fi - - echo "Deleting PVC and PV..." - kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" - -# Remove external-attacher finalizer from PV if it exists - finalizer="external-attacher/spectrumscale-csi-ibm-com" - index=$(kubectl get pv "${PV}" -o json | jq -r \ - ".metadata.finalizers | to_entries | map(select(.value==\"${finalizer}\")) | .[0].key") - - if [[ -n "$index" && "$index" != "null" ]]; then - echo "Removing ${finalizer} finalizer from PV ${PV}..." - kubectl patch pv "${PV}" --type=json \ - -p="[ { \"op\": \"remove\", \"path\": \"/metadata/finalizers/${index}\" } ]" - fi - kubectl delete pv "$PV" - - echo "Recreating PV and PVC..." - cat < 0 )); then - echo "Successful PVs: ${#success_list[@]}" - printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" - for entry in "${success_list[@]}"; do - IFS='|' read -r pvc pv <<< "$entry" - printf " %-80s | %s\n" "$pvc" "$pv" - done - echo "" - fi - - if (( ${#fail_list[@]} > 0 )); then - echo "Failed PVs: ${#fail_list[@]}" - printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" - for entry in "${fail_list[@]}"; do - IFS='|' read -r pvc pv <<< "$entry" - printf " %-80s | %s\n" "$pvc" "$pv" - done - echo "" - fi - - if (( ${#skip_list[@]} > 0 )); then - echo "Skipped PVs (already migrated): ${#skip_list[@]}" - printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" - for entry in "${skip_list[@]}"; do - IFS='|' read -r pvc pv <<< "$entry" - printf " %-80s | %s\n" "$pvc" "$pv" - done - echo "" - fi - - echo "Completed migration at: $(date)" -} - -main - -exit 0 diff --git a/tools/volume_migration_scripts/migration_csi_primary_removal.bash b/tools/volume_migration_scripts/migration_csi_primary_removal.bash deleted file mode 100644 index 3593cfccc..000000000 --- a/tools/volume_migration_scripts/migration_csi_primary_removal.bash +++ /dev/null @@ -1,385 +0,0 @@ -#!/bin/bash - -# Usage: ./migration_csi_primary_removal.bash -# Migrates IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal. - -set -euo pipefail - -# --- Help Function --- -help() { - echo "" - echo "Usage: $0" - echo "" - echo "Description:" - echo " This script migrates IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal." - echo "" - exit 1 -} - -# --- Argument Parsing --- -while [[ $# -gt 0 ]]; do - case "$1" in - -h|--help) - help - ;; - *) - echo "Unknown argument: $1" - help - ;; - esac - shift -done - -# --- Initialize Counters and Lists --- -success_list=() -fail_list=() -skip_list=() - -success_count=0 -fail_count=0 -skip_count=0 - -main() { - check_prerequisites - echo "Starting migration of IBM Storage Scale CSI PersistentVolumes to a new format by updating the volumeHandle path with the specified prefix after primary removal" - echo "" - read -rp "Proceed with migration? (yes/y/Y to continue): " CONFIRM - if [[ "$CONFIRM" != "yes" && "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then - echo "Aborting migration." - exit 0 - fi - init - get_pv_list - TOTAL_PVS=$(echo "$ALL_PVS" | wc -l | xargs) - COUNT=1 - for PV in $ALL_PVS; do - echo "" - echo "[$COUNT/$TOTAL_PVS] --------------------------------------------------------------------------------" - echo "Processing PV: $PV " - migrate_each "$PV" - COUNT=$((COUNT + 1)) - done - final_summary -} - -check_prerequisites() { - echo "Checking prerequisites..." - for cmd in kubectl jq; do - if ! command -v $cmd &>/dev/null; then - echo "Error: '$cmd' is required but not installed or not in PATH." >&2 - exit 1 - fi - done - echo "All prerequisites met." -} - -init() { - RUN_TIMESTAMP=$(date +%Y%m%d_%H%M%S) - - BACKUP_PARENT_DIR="csi_migration_data" - mkdir -p "$BACKUP_PARENT_DIR" - - BACKUP_BASE_DIR="$BACKUP_PARENT_DIR/$RUN_TIMESTAMP" - mkdir -p "$BACKUP_BASE_DIR" - - LOG_FILE="$BACKUP_BASE_DIR/migration.log" - exec > >(tee "$LOG_FILE") 2>&1 - - echo "Logging to $LOG_FILE" - echo "Starting migration at: $(date)" - echo "" -} - -get_pv_list() { - ALL_PVS=$(kubectl get pv -o json | jq -r '.items[] | select(.spec.csi.driver == "spectrumscale.csi.ibm.com") | .metadata.name') - echo "Found $(echo "$ALL_PVS" | wc -l) PVs using spectrumscale.csi.ibm.com driver" -} - -migrate_each() { - PV="$1" - - VOLUME_HANDLE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.volumeHandle}') || { - echo "Failed to get volumeHandle for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - echo "Current volumeHandle: $VOLUME_HANDLE" - - PVC_NAME=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.name}') || { - echo "Failed to get PVC name for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("|$PV"); return; - } - PVC_NAMESPACE=$(kubectl get pv "$PV" -o jsonpath='{.spec.claimRef.namespace}') || { - echo "Failed to get PVC namespace for PV: $PV"; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - - echo "Preparing to migrate PVC: $PVC_NAME in namespace $PVC_NAMESPACE" - - if ! kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" >/dev/null 2>&1; then - echo "PVC $PVC_NAME not found in namespace $PVC_NAMESPACE, skipping." - fail_list+=("$PVC_NAME|$PV") - fail_count=$(expr $fail_count + 1) - return - fi - - ACCESS_MODES=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq '.spec.accessModes') || { - echo "Failed to get access modes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - STORAGE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o json | jq -r '.spec.resources.requests.storage') || { - echo "Failed to get storage size."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - STORAGE_CLASS=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.storageClassName}') || { - echo "Failed to get storage class."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - VOLUME_MODE=$(kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o jsonpath='{.spec.volumeMode}') || { - echo "Failed to get volume mode."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - ATTRS=$(kubectl get pv "$PV" -o json | jq '.spec.csi.volumeAttributes') || { - echo "Failed to get volumeAttributes."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - VOL_BACKEND_FS=$(kubectl get pv "$PV" -o json | jq -r '.spec.csi.volumeAttributes.volBackendFs') || { - echo "Failed to get volBackendFs."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - DRIVER=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.driver}') || { - echo "Failed to get driver."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - FSTYPE=$(kubectl get pv "$PV" -o jsonpath='{.spec.csi.fsType}') || { - echo "Failed to get fsType."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - RECLAIM_POLICY_ORIGINAL=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') || { - echo "Failed to get reclaimPolicy for PV $PV."; fail_count=$(expr $fail_count + 1); fail_list+=("$PVC_NAME|$PV"); return; - } - - - OLD_PATH=$(echo "$VOLUME_HANDLE" | awk -F';' '{print $NF}') - CURRENT_PREFIX="${OLD_PATH%%/$VOL_BACKEND_FS/*}" - IFS=';' read -ra parts <<< "$VOLUME_HANDLE" - - if [[ "${parts[0]}" == "0" && "${parts[1]}" == "0" ]]; then - echo "Detected volumeHandle type: 0;0 (volDirBasePath) : Lightweight volume" - if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV" - else - echo "For this volume primary migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "1" ]]; then - echo "Detected volumeHandle type: 0;1 (parentFileset) : Fileset volume and dependent fileset" - if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - - PARENT_FILESET=$(echo "$ATTRS" | jq -r '."parentFileset" // empty') - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') - - if [[ "$existingVolume" == "yes" ]]; then - echo "Static Fileset volume and dependent fileset" - if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PVC_NAME" - else - echo "parentFileset is empty or 'root', using default path" - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" - fi - else - echo "Dynamic Fileset volume and dependent fileset" - if [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" && -n "$VOL_DIR_BASE_PATH" ]]; then - echo "Using parentFileset with volDirBasePath" - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PARENT_FILESET/$PV/$PV-data" - elif [[ -n "$PARENT_FILESET" && "$PARENT_FILESET" != "root" ]]; then - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PARENT_FILESET/$PV/$PV-data" - else - echo "parentFileset is empty or 'root', using default path" - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" - fi - fi - else - echo "For this volume primary migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "2" ]]; then - echo "Detected volumeHandle type: 0;2 (fileset) : Fileset volume and independent fileset" - if [[ "${parts[-1]}" == *"/.volumes/"* ]]; then - # Default path of dynamic fileset - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PV/$PV-data" - - VOL_DIR_BASE_PATH=$(echo "$ATTRS" | jq -r '."volDirBasePath" // empty') - - # Check if existingVolume is set to yes for static fileset volumes - existingVolume=$(echo "$ATTRS" | jq -r '."existingVolume" // empty') - if [[ "$existingVolume" == "yes" ]]; then - echo "Static Fileset volume and independent fileset" - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$PVC_NAME" - elif [[ -n "$VOL_DIR_BASE_PATH" ]]; then - echo "Dynamic Fileset volume with volDirBasePath and independent fileset" - NEW_PATH="$CURRENT_PREFIX/$VOL_BACKEND_FS/$VOL_DIR_BASE_PATH/$PV/$PV-data" - fi - else - echo "For this volume primary migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "1" ]]; then - echo "Detected volumeHandle type: 1;1 (version2) : Consistency group fileset" - echo "For Consistency group fileset migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - - elif [[ "${parts[0]}" == "0" && "${parts[1]}" == "3" ]]; then - echo "Detected volumeHandle type: 0;3 : Shallow copy fileset" - echo "For Shallow copy fileset migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - - elif [[ "${parts[0]}" == "1" && "${parts[1]}" == "3" ]]; then - echo "Detected volumeHandle type: 1;3 version-2: Shallow copy fileset" - echo "For Shallow copy fileset migration is not required. Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - - else - echo "Unknown volumeHandle type: ${parts[0]};${parts[1]} — skipping migration for PV: $PV" - fail_list+=("$PVC_NAME|$PV") - fail_count=$(expr $fail_count + 1) - return - fi - - NEW_VOLUME_HANDLE=$(echo "$VOLUME_HANDLE" | sed "s|$OLD_PATH|$NEW_PATH|") - - if [[ "$VOLUME_HANDLE" == "$NEW_VOLUME_HANDLE" ]]; then - echo "Already migrated (volumeHandle matches target). Skipping $PV" - skip_count=$(expr $skip_count + 1) - skip_list+=("$PVC_NAME|$PV") - return - fi - - echo "Updated volumeHandle for $PV: $NEW_VOLUME_HANDLE" - - echo "Setting reclaim policy to Retain for PV: $PV" - if ! kubectl patch pv "$PV" --type=merge -p '{"spec": {"persistentVolumeReclaimPolicy": "Retain"}}'; then - echo "Failed to patch reclaim policy for PV: $PV" - fail_count=$(expr $fail_count + 1) - fail_list+=("$PVC_NAME|$PV") - return - fi - - BACKUP_DIR="$BACKUP_BASE_DIR/${PVC_NAMESPACE}/${PVC_NAME}" - mkdir -p "$BACKUP_DIR" - - echo "Backing up PV and PVC..." - kubectl get pv "$PV" -o yaml > "$BACKUP_DIR/pv.yaml" - kubectl get pvc "$PVC_NAME" -n "$PVC_NAMESPACE" -o yaml > "$BACKUP_DIR/pvc.yaml" - - RECLAIM_POLICY=$(kubectl get pv "$PV" -o jsonpath='{.spec.persistentVolumeReclaimPolicy}') - if [[ "$RECLAIM_POLICY" != "Retain" ]]; then - echo "Reclaim policy is not Retain (got: $RECLAIM_POLICY), skipping." - fail_count=$(expr $fail_count + 1) - fail_list+=("$PVC_NAME|$PV") - return - fi - - echo "Deleting PVC and PV..." - kubectl delete pvc "$PVC_NAME" -n "$PVC_NAMESPACE" - -# Remove external-attacher finalizer from PV if it exists - finalizer="external-attacher/spectrumscale-csi-ibm-com" - index=$(kubectl get pv "${PV}" -o json | jq -r \ - ".metadata.finalizers | to_entries | map(select(.value==\"${finalizer}\")) | .[0].key") - - if [[ -n "$index" && "$index" != "null" ]]; then - echo "Removing ${finalizer} finalizer from PV ${PV}..." - kubectl patch pv "${PV}" --type=json \ - -p="[ { \"op\": \"remove\", \"path\": \"/metadata/finalizers/${index}\" } ]" - fi - kubectl delete pv "$PV" - - echo "Recreating PV and PVC..." - cat < 0 )); then - echo "Successful PVs: ${#success_list[@]}" - printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" - for entry in "${success_list[@]}"; do - IFS='|' read -r pvc pv <<< "$entry" - printf " %-80s | %s\n" "$pvc" "$pv" - done - echo "" - fi - - if (( ${#fail_list[@]} > 0 )); then - echo "Failed PVs: ${#fail_list[@]}" - printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" - for entry in "${fail_list[@]}"; do - IFS='|' read -r pvc pv <<< "$entry" - printf " %-80s | %s\n" "$pvc" "$pv" - done - echo "" - fi - - if (( ${#skip_list[@]} > 0 )); then - echo "Skipped PVs (already migrated): ${#skip_list[@]}" - printf " %-80s | %s\n" "PVC Name" "PV Name" - printf " %s\n" "----------------------------------------------------------------------------------------------------------------------------" - for entry in "${skip_list[@]}"; do - IFS='|' read -r pvc pv <<< "$entry" - printf " %-80s | %s\n" "$pvc" "$pv" - done - echo "" - fi - - echo "Completed migration at: $(date)" -} - -main - -exit 0