#!/bin/bash
# DistCp Migration 검증 스크립트 (랜덤 샘플링 기능 추가)
# 사용법: ./verify_migration.sh <folder_list_file> <source_base> <target_base> [sample_count]
# 특징: 오류가 발생해도 중단하지 않고 끝까지 수행
set +e # 오류 발생 시에도 계속 진행
# 색상 정의
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 사용법 출력
usage() {
echo "Usage: $0 <folder_list_file> <source_base> <target_base> [sample_count]"
echo ""
echo "예시:"
echo " $0 /tmp/distcp_folders.txt hdfs://hdfsf.com/a/b/c s3a://y/z"
echo " $0 /tmp/distcp_folders.txt hdfs://hdfsf.com/a/b/c s3a://y/z 50 # 랜덤 50개만 검증"
echo ""
echo "매개변수:"
echo " sample_count: 랜덤 샘플 개수 (생략시 전체 검증)"
exit 1
}
# 매개변수 확인
if [ $# -lt 3 ]; then
usage
fi
FOLDER_LIST_FILE="$1"
SOURCE_BASE="$2"
TARGET_BASE="$3"
SAMPLE_COUNT="$4" # 선택적 매개변수
# 파일 존재 확인
if [ ! -f "$FOLDER_LIST_FILE" ]; then
echo -e "${RED}❌ 폴더 목록 파일을 찾을 수 없습니다: $FOLDER_LIST_FILE${NC}"
exit 1
fi
# 결과 변수 초기화
TOTAL_FOLDERS=0
MATCHED_FOLDERS=0
MISSING_TARGET_FOLDERS=0
MISSING_SOURCE_FOLDERS=0
COUNT_MISMATCH_FOLDERS=0
SIZE_MISMATCH_FOLDERS=0
ERROR_FOLDERS=0
SOURCE_TOTAL_SIZE=0
TARGET_TOTAL_SIZE=0
# 임시 파일 생성
TEMP_DIR="/tmp/distcp_verify_$$"
mkdir -p "$TEMP_DIR"
# 종료 시 정리
cleanup() {
rm -rf "$TEMP_DIR"
}
trap cleanup EXIT
# 로그 파일
LOG_FILE="$TEMP_DIR/verification.log"
MISSING_LOG="$TEMP_DIR/missing_folders.log"
MISMATCH_LOG="$TEMP_DIR/mismatch_folders.log"
ERROR_LOG="$TEMP_DIR/error_folders.log"
# 파일 크기를 읽기 쉬운 형태로 변환
format_size() {
local size=$1
if [ $size -eq 0 ]; then
echo "0 B"
return
fi
local units=("B" "KB" "MB" "GB" "TB")
local unit=0
local formatted_size=$size
while [ $formatted_size -ge 1024 ] && [ $unit -lt 4 ]; do
formatted_size=$((formatted_size / 1024))
unit=$((unit + 1))
done
echo "$formatted_size ${units[$unit]}"
}
# 폴더 정보 가져오기 (파일 개수, 총 크기) - 오류 처리 강화
get_folder_info() {
local folder_path="$1"
local info_file="$2"
# 초기값 설정
echo "exists=false" > "$info_file"
echo "file_count=0" >> "$info_file"
echo "total_size=0" >> "$info_file"
# hadoop fs -ls -R 실행 (타임아웃 적용)
if timeout 60 hadoop fs -ls -R "$folder_path" 2>/dev/null | grep "^-" > "$info_file.tmp" 2>/dev/null; then
# 파일이 있는지 확인
if [ -s "$info_file.tmp" ]; then
# 파일 개수
local file_count=$(wc -l < "$info_file.tmp" 2>/dev/null || echo "0")
# 총 크기 계산 (awk로 더 빠르게) - 오류 처리 추가
local total_size=$(awk '{sum += $5} END {print sum+0}' "$info_file.tmp" 2>/dev/null || echo "0")
# 결과 저장
echo "exists=true" > "$info_file"
echo "file_count=$file_count" >> "$info_file"
echo "total_size=$total_size" >> "$info_file"
rm -f "$info_file.tmp" 2>/dev/null
return 0
fi
fi
# 실패한 경우 임시 파일 정리
rm -f "$info_file.tmp" 2>/dev/null
return 1
}
# xxx 추출 함수
extract_xxx() {
local full_path="$1"
echo "${full_path##*/}"
}
# 타겟 경로 생성
build_target_path() {
local xxx="$1"
echo "${TARGET_BASE%/}/$xxx"
}
# 랜덤 샘플링 함수
sample_folders() {
local input_array=("$@")
local total_count=${#input_array[@]}
if [ -z "$SAMPLE_COUNT" ] || [ "$SAMPLE_COUNT" -ge "$total_count" ]; then
# 샘플링 없이 전체 사용
echo "${input_array[@]}"
return
fi
echo -e "${YELLOW}전체 ${total_count}개 폴더 중 랜덤 ${SAMPLE_COUNT}개 선택${NC}"
# 랜덤 인덱스 생성
local indices=()
for ((i=0; i<total_count; i++)); do
indices+=($i)
done
# Fisher-Yates 셔플 알고리즘
for ((i=total_count-1; i>0; i--)); do
local j=$((RANDOM % (i+1)))
local temp=${indices[$i]}
indices[$i]=${indices[$j]}
indices[$j]=$temp
done
# 선택된 폴더들 반환
local selected_folders=()
for ((i=0; i<SAMPLE_COUNT; i++)); do
local idx=${indices[$i]}
selected_folders+=("${input_array[$idx]}")
done
echo "${selected_folders[@]}"
}
# 단일 폴더 검증
verify_folder() {
local source_folder="$1"
local xxx=$(extract_xxx "$source_folder")
local target_folder=$(build_target_path "$xxx")
local source_info="$TEMP_DIR/source_$xxx.info"
local target_info="$TEMP_DIR/target_$xxx.info"
# 소스 폴더 정보 가져오기
if ! get_folder_info "$source_folder" "$source_info"; then
echo -e "${RED}❌ $xxx: 소스 폴더가 존재하지 않습니다${NC}"
echo "$xxx|missing_source|$source_folder|$target_folder|소스 폴더가 존재하지 않습니다" >> "$ERROR_LOG"
return 1
fi
# 타겟 폴더 정보 가져오기
if ! get_folder_info "$target_folder" "$target_info"; then
echo -e "${RED}❌ $xxx: 타겟 폴더가 존재하지 않습니다${NC}"
echo "$xxx|missing_target|$source_folder|$target_folder|타겟 폴더가 존재하지 않습니다" >> "$MISSING_LOG"
return 2
fi
# 정보 읽기
if [ ! -f "$source_info" ]; then
echo -e "${RED}❌ $xxx: 소스 정보 파일 생성 실패${NC}"
echo "$xxx|error|$source_folder|$target_folder|소스 정보 파일 생성 실패" >> "$ERROR_LOG"
return 1
fi
source "$source_info"
local source_count=$file_count
local source_size=$total_size
if [ ! -f "$target_info" ]; then
echo -e "${RED}❌ $xxx: 타겟 정보 파일 생성 실패${NC}"
echo "$xxx|error|$source_folder|$target_folder|타겟 정보 파일 생성 실패" >> "$ERROR_LOG"
return 1
fi
source "$target_info"
local target_count=$file_count
local target_size=$total_size
# 파일 개수 비교
if [ $source_count -ne $target_count ]; then
echo -e "${YELLOW}❌ $xxx: 파일 개수 불일치 (소스: $source_count, 타겟: $target_count)${NC}"
echo "$xxx|count_mismatch|$source_folder|$target_folder|파일 개수 불일치: 소스($source_count) vs 타겟($target_count)" >> "$MISMATCH_LOG"
return 3
fi
# 크기 비교
if [ $source_size -ne $target_size ]; then
echo -e "${YELLOW}❌ $xxx: 크기 불일치 (소스: $(format_size $source_size), 타겟: $(format_size $target_size))${NC}"
echo "$xxx|size_mismatch|$source_folder|$target_folder|크기 불일치: 소스($source_size) vs 타겟($target_size)" >> "$MISMATCH_LOG"
return 4
fi
# 성공
echo -e "${GREEN}✅ $xxx: 정상 (${source_count}개 파일, $(format_size $source_size))${NC}"
echo "$xxx|matched|$source_folder|$target_folder|정상" >> "$LOG_FILE"
return 0
}
# 메인 검증 함수
main_verification() {
echo -e "${BLUE}=== DistCp Migration 검증 시작 ===${NC}"
echo "폴더 목록: $FOLDER_LIST_FILE"
echo "소스 베이스: $SOURCE_BASE"
echo "타겟 베이스: $TARGET_BASE"
echo "검증 시작: $(date)"
echo "=" * 50
# 폴더 목록 읽기
local all_folders=()
while IFS= read -r line; do
line=$(echo "$line" | tr -d '\r' | xargs) # 공백 및 개행 제거
if [ -n "$line" ] && [[ ! "$line" =~ ^# ]]; then
all_folders+=("$line")
fi
done < "$FOLDER_LIST_FILE"
local total_folders_in_file=${#all_folders[@]}
echo "파일에서 읽은 전체 폴더 수: $total_folders_in_file"
# 샘플링 적용
local folder_list
if [ -n "$SAMPLE_COUNT" ] && [ "$SAMPLE_COUNT" -lt "$total_folders_in_file" ]; then
echo -e "${YELLOW}랜덤 샘플링: $SAMPLE_COUNT개 폴더 선택${NC}"
IFS=' ' read -ra folder_list <<< "$(sample_folders "${all_folders[@]}")"
# 선택된 폴더들을 로그에 저장
printf '%s\n' "${folder_list[@]}" > "$TEMP_DIR/selected_folders.txt"
echo "선택된 폴더 목록이 저장됨: $TEMP_DIR/selected_folders.txt"
else
echo "전체 폴더 검증 수행"
folder_list=("${all_folders[@]}")
fi
TOTAL_FOLDERS=${#folder_list[@]}
echo "실제 검증 대상 폴더: $TOTAL_FOLDERS"
echo
# 처음 5개 폴더 미리보기
echo "처음 5개 폴더 미리보기:"
for i in $(seq 0 4); do
if [ $i -lt ${#folder_list[@]} ]; then
local source_folder="${folder_list[$i]}"
local xxx=$(extract_xxx "$source_folder")
echo " $((i+1)). $xxx"
fi
done
echo
# 각 폴더 검증 (오류가 발생해도 계속 진행)
local processed=0
for source_folder in "${folder_list[@]}"; do
local xxx=$(extract_xxx "$source_folder")
# 폴더 검증 수행 (실패해도 계속 진행)
verify_folder "$source_folder"
local result=$?
# 결과에 따른 카운터 증가 (모든 경우를 처리)
case $result in
0) # 성공
MATCHED_FOLDERS=$((MATCHED_FOLDERS + 1))
;;
1) # 소스 누락 또는 기타 오류
if grep -q "missing_source" "$ERROR_LOG" 2>/dev/null && grep -q "$xxx" "$ERROR_LOG"; then
MISSING_SOURCE_FOLDERS=$((MISSING_SOURCE_FOLDERS + 1))
else
ERROR_FOLDERS=$((ERROR_FOLDERS + 1))
fi
;;
2) # 타겟 누락
MISSING_TARGET_FOLDERS=$((MISSING_TARGET_FOLDERS + 1))
;;
3) # 개수 불일치
COUNT_MISMATCH_FOLDERS=$((COUNT_MISMATCH_FOLDERS + 1))
;;
4) # 크기 불일치
SIZE_MISMATCH_FOLDERS=$((SIZE_MISMATCH_FOLDERS + 1))
;;
*) # 기타 오류
ERROR_FOLDERS=$((ERROR_FOLDERS + 1))
;;
esac
processed=$((processed + 1))
# 진행률 표시 (10개마다)
if [ $((processed % 10)) -eq 0 ] || [ $processed -eq $TOTAL_FOLDERS ]; then
local progress=$((processed * 100 / TOTAL_FOLDERS))
echo -e "${BLUE}진행률: $processed/$TOTAL_FOLDERS ($progress%) - 성공: $MATCHED_FOLDERS, 타겟누락: $MISSING_TARGET_FOLDERS, 개수불일치: $COUNT_MISMATCH_FOLDERS, 크기불일치: $SIZE_MISMATCH_FOLDERS, 오류: $ERROR_FOLDERS${NC}"
fi
# 잠깐 대기 (시스템 부하 감소)
sleep 0.1
done
echo ""
echo -e "${GREEN}모든 폴더 검증 완료!${NC}"
echo "처리된 폴더: $processed / $TOTAL_FOLDERS"
}
# 결과 보고서 생성
generate_report() {
local success_rate=0
if [ $TOTAL_FOLDERS -gt 0 ]; then
success_rate=$((MATCHED_FOLDERS * 100 / TOTAL_FOLDERS))
fi
echo
echo -e "${BLUE}=== Migration 검증 결과 보고서 ===${NC}"
echo "검증 완료 시간: $(date)"
echo
echo "=== 요약 ==="
echo "총 폴더 수: $TOTAL_FOLDERS"
echo "성공 폴더: $MATCHED_FOLDERS"
echo "타겟 누락: $MISSING_TARGET_FOLDERS"
echo "소스 누락: $MISSING_SOURCE_FOLDERS"
echo "파일 개수 불일치: $COUNT_MISMATCH_FOLDERS"
echo "크기 불일치: $SIZE_MISMATCH_FOLDERS"
echo "처리 오류: $ERROR_FOLDERS"
echo
echo "성공률: $success_rate%"
echo
# 문제가 있는 폴더들 표시
local total_issues=$((MISSING_TARGET_FOLDERS + MISSING_SOURCE_FOLDERS + COUNT_MISMATCH_FOLDERS + SIZE_MISMATCH_FOLDERS + ERROR_FOLDERS))
if [ $total_issues -gt 0 ]; then
echo -e "${RED}=== 문제가 있는 폴더들 ===${NC}"
if [ -f "$MISSING_LOG" ] && [ -s "$MISSING_LOG" ]; then
echo
echo "타겟 누락 폴더들 (최대 10개):"
head -10 "$MISSING_LOG" | while IFS='|' read -r xxx status source_path target_path error_msg; do
echo " - $xxx: $error_msg"
done
fi
if [ -f "$MISMATCH_LOG" ] && [ -s "$MISMATCH_LOG" ]; then
echo
echo "불일치 폴더들 (최대 10개):"
head -10 "$MISMATCH_LOG" | while IFS='|' read -r xxx status source_path target_path error_msg; do
echo " - $xxx: $error_msg"
done
fi
if [ -f "$ERROR_LOG" ] && [ -s "$ERROR_LOG" ]; then
echo
echo "오류 폴더들 (최대 10개):"
head -10 "$ERROR_LOG" | while IFS='|' read -r xxx status source_path target_path error_msg; do
echo " - $xxx: $error_msg"
done
fi
fi
echo
echo "=== 최종 판정 ==="
if [ $total_issues -eq 0 ]; then
echo -e "${GREEN}✅ 모든 폴더가 정상적으로 마이그레이션되었습니다!${NC}"
return 0
else
echo -e "${RED}❌ 총 $total_issues개 폴더에서 문제가 발견되었습니다.${NC}"
echo " - 타겟 누락: $MISSING_TARGET_FOLDERS"
echo " - 소스 누락: $MISSING_SOURCE_FOLDERS"
echo " - 파일 개수 불일치: $COUNT_MISMATCH_FOLDERS"
echo " - 크기 불일치: $SIZE_MISMATCH_FOLDERS"
echo " - 처리 오류: $ERROR_FOLDERS"
return 1
fi
}
# 상세 로그 저장
save_logs() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local log_dir="/tmp/distcp_verify_$timestamp"
mkdir -p "$log_dir"
# 로그 파일 복사
[ -f "$LOG_FILE" ] && cp "$LOG_FILE" "$log_dir/success.log"
[ -f "$MISSING_LOG" ] && cp "$MISSING_LOG" "$log_dir/missing.log"
[ -f "$MISMATCH_LOG" ] && cp "$MISMATCH_LOG" "$log_dir/mismatch.log"
[ -f "$ERROR_LOG" ] && cp "$ERROR_LOG" "$log_dir/error.log"
[ -f "$TEMP_DIR/selected_folders.txt" ] && cp "$TEMP_DIR/selected_folders.txt" "$log_dir/selected_folders.txt"
# 요약 정보 저장
cat > "$log_dir/summary.txt" << EOF
검증 완료 시간: $(date)
실제 검증 폴더 수: $TOTAL_FOLDERS
$([ -n "$SAMPLE_COUNT" ] && echo "샘플링 적용: $SAMPLE_COUNT개 랜덤 선택")
성공 폴더: $MATCHED_FOLDERS
타겟 누락: $MISSING_TARGET_FOLDERS
소스 누락: $MISSING_SOURCE_FOLDERS
파일 개수 불일치: $COUNT_MISMATCH_FOLDERS
크기 불일치: $SIZE_MISMATCH_FOLDERS
처리 오류: $ERROR_FOLDERS
성공률: $((MATCHED_FOLDERS * 100 / TOTAL_FOLDERS))%
EOF
echo
echo "상세 로그가 저장되었습니다: $log_dir"
}
# 메인 실행
main() {
main_verification
generate_report
local result=$?
save_logs
exit $result
}
# 스크립트 실행
main```