# lab_train_fakes.sh — lab_train.sh --dry-run が読む差し替え (単体では動かさない. 依頼 49) # # 本物の Pod を借りずに lab_train.sh の流れを最後まで流す: # - 書く場所 (DATA_ROOT・LAB_ROOT・checkpoints)・鍵を一時フォルダ LAB_DRY_ROOT (なければ作る) へ # - runpod_api / pod_ssh / pod_scp / Mac の hf download / 通知 / caffeinate を偽物にする # (呼ばれた中身を $LAB_DRY_ROOT/calls.log・ssh.log・scp.log・notify.log に残す) # - 待ちを短くする (進みを見るのは 1 秒おき) # どうなるかは LAB_FAKE_SCENARIO で決める (test_lab_train.sh が使う): # success (既定) 学習が最後まで終わる # crash 学習が終了コード 1 で落ちる # killed 学習のプロセスが終わりの印を書かずに消える # timeout 学習が終わらない (--limit-hours を小さくして使う) # stall 学習のログが長く書かれない # hf_fail Hugging Face に上げられない (Pod から scp で KIOXIA へ取る) # dl_mismatch Hugging Face から取ったものが Pod 側の一覧と合わない # gpu_busy A40 が借りられない (RTX A6000 で借りる) # all_busy どの GPU も借りられない # delete_fail Pod を消せない # delete_flaky 1 回目の DELETE だけ失敗する # late_finish 上限を超えた直後の確かめで, 学習が終わっている (消さずに持ち帰る) # wd_missing Pod 用の鍵が見つからず, Pod 側の見張りが働かない # list_empty Pod 側のチェックポイントの一覧が取れない # list_partial Pod 側の一覧の途中で find が失敗する (一部の行と終了コード 1) # late_start 学習を始めるのに手間取り, 見守りに入ったときにはもう上限を過ぎている (学習は終わっている) LAB_DRY_ROOT=${LAB_DRY_ROOT:-$(mktemp -d /tmp/lab_train_dry.XXXXXX)} LAB_FAKE_SCENARIO=${LAB_FAKE_SCENARIO:-success} FAKE_POD_ID=dryrunpod0001 echo "[DRY ] ドライラン: Pod は借りません. 書くのは $LAB_DRY_ROOT の中だけ (場合: $LAB_FAKE_SCENARIO)" if [ -d /Volumes/KIOXIA/r2r2r-data ]; then for f in "/Volumes/KIOXIA/r2r2r-data/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v3/mecharm_redblock_box_teach_v1_trim2_corr_v3_raw_episodes.tar" \ /Volumes/KIOXIA/r2r2r-data/lab/Log11/pod/add_trim_config.py "$RUNPOD_KEY_FILE" "$HF_TOKEN_FILE"; do echo "[DRY ] 本物の $(basename "$f"): $([ -s "$f" ] && echo ある || echo ない)" done fi DATA_ROOT="$LAB_DRY_ROOT/data" LAB_ROOT="$DATA_ROOT/lab"; LAB_CURRENT="$LAB_ROOT/.lab_current" RUNPOD_KEY_FILE="$LAB_DRY_ROOT/keys/runpod_api_key.txt"; HF_TOKEN_FILE="$LAB_DRY_ROOT/keys/hf_token.txt" POD_SSH_KEY="$LAB_DRY_ROOT/keys/id_ed25519" mkdir -p "$LAB_DRY_ROOT/keys" "$LAB_DRY_ROOT/fake" "$DATA_ROOT/lab/Log11/pod" \ "$DATA_ROOT/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v3" [ -s "$RUNPOD_KEY_FILE" ] || echo "DRY-RUNPOD-SECRET-KEY" > "$RUNPOD_KEY_FILE" [ -s "$HF_TOKEN_FILE" ] || echo "hf_DRYSECRETTOKEN" > "$HF_TOKEN_FILE" [ -s "$POD_SSH_KEY" ] || echo "dry-ssh-key" > "$POD_SSH_KEY" [ -s "$POD_SSH_KEY.pub" ] || echo "ssh-ed25519 AAAA dry" > "$POD_SSH_KEY.pub" [ -f "$DATA_ROOT/lab/Log11/pod/add_trim_config.py" ] || echo "# dry" > "$DATA_ROOT/lab/Log11/pod/add_trim_config.py" TARF="$DATA_ROOT/datasets/mecharm_redblock_box_teach_v1_trim2_corr_v3/mecharm_redblock_box_teach_v1_trim2_corr_v3_raw_episodes.tar" [ -f "$TARF" ] || echo "dry tar" > "$TARF" touch "$LAB_DRY_ROOT/calls.log" "$LAB_DRY_ROOT/ssh.log" "$LAB_DRY_ROOT/scp.log" CREATE_WAIT=0; SSH_WAIT=0; LOGIN_WAIT=0; GONE_WAIT=0; FETCH_WAIT=0 POLL_SEC=${LAB_DRY_POLL_SEC:-1} NOTIFY_TITLE="$NOTIFY_TITLE (ドライラン)" # 偽物のチェックポイント (Pod 側の一覧と同じ中身を Hugging Face / scp で取ったことにする) FAKE_CKPT_FILES=$'_CHECKPOINT_METADATA\t426\nassets/local/mecharm_redblock_box_teach_v1_trim2_corr_v3/norm_stats.json\t2120\nparams/_METADATA\t33052\nparams/d/0123abcd\t1048576\nparams/manifest.ocdbt\t118' fake_make_ckpt() { # fake_make_ckpt フォルダ [ずらす] → FAKE_CKPT_FILES のとおりのファイルを作る local d="$1" p n while IFS=$'\t' read -r p n; do [ "${2:-}" = shift ] && [ "$p" = params/manifest.ocdbt ] && n=$((n + 1)) mkdir -p "$d/$(dirname "$p")"; head -c "$n" /dev/zero > "$d/$p" done <<<"$FAKE_CKPT_FILES" } fake_n() { echo x >> "$LAB_DRY_ROOT/fake/$1.n"; wc -l < "$LAB_DRY_ROOT/fake/$1.n" | tr -d ' '; } runpod_api() { local m="$1" p="$2" data="${3:-}" gpu echo "$m $p" >> "$LAB_DRY_ROOT/calls.log" case "$m $p" in "POST /pods") gpu=$(jq -r '.gpuTypeIds[0]' <<<"$data"); echo "POST gpu=$gpu name=$(jq -r .name <<<"$data")" >> "$LAB_DRY_ROOT/calls.log" if [ "$LAB_FAKE_SCENARIO" = all_busy ] || { [ "$LAB_FAKE_SCENARIO" = gpu_busy ] && [ "$gpu" = "NVIDIA A40" ]; }; then echo '{"error":"There are no longer any instances available with the requested specifications."}'; RUNPOD_HTTP=500 else echo "{\"id\":\"$FAKE_POD_ID\",\"costPerHr\":0.44}"; RUNPOD_HTTP=200 echo "$gpu" > "$LAB_DRY_ROOT/fake/created" fi ;; "GET /pods") if [ -f "$LAB_DRY_ROOT/fake/created" ] && [ ! -f "$LAB_DRY_ROOT/fake/deleted" ]; then echo "[{\"id\":\"$FAKE_POD_ID\",\"name\":\"p2m-train-x\",\"desiredStatus\":\"RUNNING\",\"costPerHr\":0.44}]" else cat "$LAB_DRY_ROOT/fake/others.json" 2>/dev/null || echo '[]' fi RUNPOD_HTTP=200 ;; "GET /pods/"*) if [ "${p#/pods/}" = "$FAKE_POD_ID" ] && [ -f "$LAB_DRY_ROOT/fake/created" ] && [ ! -f "$LAB_DRY_ROOT/fake/deleted" ]; then echo "{\"id\":\"$FAKE_POD_ID\",\"desiredStatus\":\"RUNNING\",\"publicIp\":\"10.9.8.7\",\"portMappings\":{\"22\":40123}}" RUNPOD_HTTP=200 else echo '{"error":"pod not found"}'; RUNPOD_HTTP=404 fi ;; "DELETE /pods/"*) if [ "$LAB_FAKE_SCENARIO" = delete_fail ] || { [ "$LAB_FAKE_SCENARIO" = delete_flaky ] && [ "$(fake_n delete)" = 1 ]; }; then echo '{"error":"internal"}'; RUNPOD_HTTP=500 else echo '{}'; RUNPOD_HTTP=200; touch "$LAB_DRY_ROOT/fake/deleted"; fi ;; *) echo '{}'; RUNPOD_HTTP=404 ;; esac case "$RUNPOD_HTTP" in 2??) return 0 ;; *) return 1 ;; esac } pod_ssh() { printf '%s\n----\n' "$*" >> "$LAB_DRY_ROOT/ssh.log" cat > /dev/null [ -f "$LAB_DRY_ROOT/fake/deleted" ] && return 255 local n case "$*" in *": lt=watchdog"*) [ "$LAB_FAKE_SCENARIO" = wd_missing ] && echo "WATCHDOG run=1 pod=1 key=0 ctl=0" || echo "WATCHDOG run=1 pod=1 key=1 ctl=1" ;; *": lt=start_train"*) [ "$LAB_FAKE_SCENARIO" = late_start ] && sleep 9; echo STARTED ;; *": lt=status"*) n=$(fake_n status) if [ "$LAB_FAKE_SCENARIO" = late_start ]; then echo "STATUS run=0 exit=0 age=4 bar=1500 logstep=1400 loss=0.0215 ckpt=1499 cm=1"; return 0; fi if [ "$LAB_FAKE_SCENARIO" = late_finish ]; then # 確かめている間に上限を過ぎ, 学習も終わる sleep 9; echo "STATUS run=0 exit=0 age=4 bar=1500 logstep=1400 loss=0.0215 ckpt=1499 cm=1"; return 0 fi case "$LAB_FAKE_SCENARIO:$n" in crash:1|killed:1|stall:1|timeout:*|*:1) if [ "$LAB_FAKE_SCENARIO" = stall ]; then echo "STATUS run=1 exit=- age=99999 bar=40 logstep=- loss=- ckpt=- cm=0" else echo "STATUS run=1 exit=- age=3 bar=$((n * 100)) logstep=$((n * 100)) loss=0.0412 ckpt=- cm=0"; fi ;; crash:*) echo "STATUS run=0 exit=1 age=5 bar=650 logstep=600 loss=0.0399 ckpt=- cm=0" ;; killed:*) echo "STATUS run=0 exit=- age=5 bar=650 logstep=600 loss=0.0399 ckpt=- cm=0" ;; *) echo "STATUS run=0 exit=0 age=4 bar=1500 logstep=1400 loss=0.0215 ckpt=1499 cm=1" ;; esac ;; *": lt=list_ckpt"*|*": lt=ls_all"*) if [ "$LAB_FAKE_SCENARIO" = list_partial ] && [[ "$*" == *"lt=list_ckpt"* ]]; then sed 's/^/F\t/' <<<"$FAKE_CKPT_FILES" | head -2; echo "find: './params/d': Input/output error"; return 1 fi if [ -f "$LAB_DRY_ROOT/fake/status.n" ] && [ "$LAB_FAKE_SCENARIO" != crash ] && [ "$LAB_FAKE_SCENARIO" != list_empty ] && [ "$LAB_FAKE_SCENARIO" != killed ] \ && [ "$LAB_FAKE_SCENARIO" != timeout ] && [ "$LAB_FAKE_SCENARIO" != stall ]; then sed 's/^/F\t/' <<<"$FAKE_CKPT_FILES" fi ;; *"hf_upload_ckpt.py"*) if [ "$LAB_FAKE_SCENARIO" = hf_fail ]; then echo "HFUP error HfHubHTTPError: 401 Client Error"; return 6; fi echo "HFUP ok PixelstoMotors/pi05-mecharm-trim-corr-lora-v1 に 5 ファイルを上げ, 名前・バイト数が全部合うのを確かめた" ;; *"pod_prep_trim_corr.sh"*) echo "== 5. 準備できました (ドライラン)" ;; *"hf auth whoami"*) echo "user: PixelstoMotors (ドライラン)" ;; esac return 0 } pod_scp() { echo "$*" >> "$LAB_DRY_ROOT/scp.log" [ -f "$LAB_DRY_ROOT/fake/deleted" ] && return 1 local last="${!#}" case "$*" in *":/workspace/openpi/checkpoints/"*) fake_make_ckpt "$last" ;; *":/workspace/logs/watchdog.log"*) return 1 ;; # 見張りが働いていなければ, ないのがふつう *" root@"*":/workspace/logs/"*|"root@"*":/workspace/logs/"*) echo "dry log line (${1##*/})" > "$last" ;; esac return 0 } lab_have_hf_cli() { return 0; } lab_hf_download_mac() { echo "hf download $1 → $2" >> "$LAB_DRY_ROOT/calls.log" fake_make_ckpt "$2" "$([ "$LAB_FAKE_SCENARIO" = dl_mismatch ] && echo shift)" mkdir -p "$2/.cache/huggingface/download"; echo x > "$2/.cache/huggingface/download/x.metadata"; echo "*" > "$2/.gitattributes" echo x > "$2/._params"; echo x > "$2/.DS_Store" # KIOXIA (exFAT) が付けるもの } lab_notify() { echo "[NOTIFY] $1: $2" | tee -a "$LAB_DRY_ROOT/notify.log"; } lab_keep_awake() { echo "[MAC ] (ドライラン) caffeinate は使わない"; }