#!/usr/bin/env bash # ============================================================================= # migrate.sh — 기존 누수 노드 → 누수+온습도+미세먼지(PMS7003) 결합 노드 원샷 마이그레이션 # # 대상: 이미 설치·하드닝(harden_pi.sh)까지 끝난 누수 전용/누수+온습도 Pi 4 노드 (Pi-A id1 / Pi-C id3). # SSH 는 꺼져 있으므로 현장에서 USB 로 이 폴더를 통째로 가져가 실행한다. # # 하는 일 (한 번에): # 1) 기존 누수 서비스 설치 경로 자동 탐지 (leak-sensor.service → 폴백 /home/pi/raspberry_pi4) # 2) 기존 config_local.py 에서 API_URL / API_KEY / DEVICE_LOCATION / sensor_id 자동 추출 # → pms7003/config_local.py 자동 생성 (동일 sensor_id·동일 위치 라벨, 10분 주기) # ★ 동일-id 통합 원칙: 미세먼지는 같은 노드 누수 센서와 같은 sensor_id 로 보고한다(SHT30 와 동일). # 3) /home/pi/pms7003 에 모듈 복사 → install.sh 실행 (debs/·wheelhouse/ 동봉 시 완전 오프라인) # (install.sh 가 UART 활성화: enable_uart=1 + 직렬콘솔 분리) # 4) systemd 서비스(pms7003-sensor) 등록·기동 + 권한 잠금(config_local.py 600) # 5) 검증: UART 장치(/dev/serial0)·서비스 상태. UART 첫 활성화면 재부팅 안내. # # 폐쇄망 준비물 (인터넷 되는 빌드 PC에서 먼저): # cd pms7003 && bash vendor.sh → debs/, wheelhouse/ 생성 → USB 에 그대로 포함 # # 실행 (노드 현장, 모니터+키보드): # sudo bash migrate.sh # # 서버 측 사전 작업(이미 되어 있으면 생략): # config.local.php 의 'METRIC_SENSOR_IDS' 에 이 노드의 sensor_id 포함 확인 # (미세먼지도 온습도와 같은 metric 파이프라인 · 같은 목록을 사용). # ============================================================================= set -euo pipefail [ "$(id -u)" -eq 0 ] || { echo "[ERROR] sudo 로 실행하세요: sudo bash migrate.sh"; exit 1; } cd "$(dirname "$0")" SERVICE_USER="${SERVICE_USER:-pi}" DEST_DIR="${DEST_DIR:-/home/${SERVICE_USER}/pms7003}" echo "==============================================" echo " 누수 노드 → 누수+미세먼지(PMS7003) 결합 마이그레이션" echo "==============================================" # ── 0) 사전 점검: 페이로드·오프라인 번들 ───────────────────────────────────── [ -d pms7003 ] || { echo "[ERROR] pms7003/ 페이로드 폴더가 없습니다. USB 복사가 불완전합니다."; exit 1; } if compgen -G "pms7003/debs/*.deb" >/dev/null && [ -d pms7003/wheelhouse ]; then echo "[INFO] 오프라인 번들(debs/·wheelhouse/) 확인 — 네트워크 없이 설치합니다." else echo "[WARN] 오프라인 번들이 없습니다. 폐쇄망 노드면 여기서 중단하고" echo " 빌드 PC에서 'cd pms7003 && bash vendor.sh' 후 USB 를 다시 만드세요." echo " (온라인 노드라면 Enter 로 계속 — 인터넷 설치 시도)" read -r -p "계속하려면 Enter, 중단은 Ctrl+C: " _ fi # ── 1) 기존 누수 설치 경로 탐지 ────────────────────────────────────────────── LEAK_DIR="${LEAK_DIR:-}" if [ -z "${LEAK_DIR}" ] && systemctl cat leak-sensor.service >/dev/null 2>&1; then LEAK_DIR="$(systemctl cat leak-sensor.service | sed -n 's/^WorkingDirectory=//p' | head -1)" fi if [ -z "${LEAK_DIR}" ] || [ ! -d "${LEAK_DIR}" ]; then LEAK_DIR="/home/${SERVICE_USER}/raspberry_pi4" fi if [ ! -f "${LEAK_DIR}/config_local.py" ]; then echo "[ERROR] 기존 누수 설치를 찾지 못했습니다 (${LEAK_DIR}/config_local.py 없음)." echo " LEAK_DIR=/실제/경로 sudo -E bash migrate.sh 로 경로를 지정하세요." exit 1 fi echo "[1/5] 기존 누수 설치 확인: ${LEAK_DIR}" # 이미 마이그레이션된 노드 보호(멱등): 서비스가 살아 있으면 재설치 여부 확인 if systemctl is-active --quiet pms7003-sensor 2>/dev/null; then echo "[WARN] pms7003-sensor 서비스가 이미 실행 중입니다. 계속하면 코드·설정을 갱신합니다." read -r -p "계속하려면 Enter, 중단은 Ctrl+C: " _ fi # ── 2) 기존 설정에서 값 추출 → pms7003 config_local.py 생성 ────────────────── echo "[2/5] 기존 config_local.py 에서 서버·센서 정보 추출..." # config_local.py 는 순수 변수 파일(외부 import 없음) — 안전하게 로드해 필드만 추출. mapfile -t CFG < <(python3 - "${LEAK_DIR}" <<'PYEOF' import sys, importlib.util d = sys.argv[1] spec = importlib.util.spec_from_file_location("leak_cfg", d + "/config_local.py") m = importlib.util.module_from_spec(spec) spec.loader.exec_module(m) sensors = getattr(m, "SENSORS", None) sid = sensors[0]["id"] if sensors else 1 print(m.API_URL) print(m.API_KEY) print(m.DEVICE_LOCATION) print(sid) PYEOF ) API_URL="${CFG[0]}"; API_KEY="${CFG[1]}"; DEVICE_LOCATION="${CFG[2]}"; SENSOR_ID="${CFG[3]}" case "${SENSOR_ID}" in (*[!0-9]*|'') echo "[ERROR] sensor_id 추출 실패: '${SENSOR_ID}'"; exit 1;; esac echo " sensor_id=${SENSOR_ID}, location='${DEVICE_LOCATION}' (API_KEY 는 표시하지 않음)" # ── 3) 모듈 복사 + config_local.py 생성 ────────────────────────────────────── echo "[3/5] ${DEST_DIR} 에 모듈 복사..." mkdir -p "${DEST_DIR}" cp -r pms7003/. "${DEST_DIR}/" # 시크릿 포함 파일은 생성 시점부터 소유자 전용(600) umask 077 cat > "${DEST_DIR}/config_local.py" < /etc/systemd/system/pms7003-sensor.service systemctl daemon-reload systemctl enable pms7003-sensor >/dev/null echo " systemd 유닛 등록 완료 (pms7003-sensor)" # ── 5) 기동·검증 ───────────────────────────────────────────────────────────── echo "[5/5] 기동·검증..." NEED_REBOOT=0 if [ ! -e /dev/serial0 ] && [ ! -e /dev/ttyAMA0 ]; then NEED_REBOOT=1 echo " [!] /dev/serial0 이 아직 없습니다 — UART 첫 활성화. 재부팅 후 서비스가 자동 시작됩니다." elif systemctl is-active --quiet "serial-getty@ttyAMA0" 2>/dev/null \ || systemctl is-active --quiet "serial-getty@ttyS0" 2>/dev/null; then # 직렬 로그인 콘솔이 아직 포트를 점유 중이면 재부팅 후에야 해제된다. NEED_REBOOT=1 echo " [!] 직렬 콘솔(serial-getty)이 아직 UART 를 점유 중입니다 — 재부팅 후 해제·자동 시작됩니다." else echo " UART 장치 확인 (/dev/serial0)" systemctl restart pms7003-sensor sleep 3 if systemctl is-active --quiet pms7003-sensor; then echo " pms7003-sensor 서비스 실행 중" else echo " [ERROR] 서비스 기동 실패 — journalctl -u pms7003-sensor -n 30 으로 확인하세요." exit 1 fi fi echo "" echo "==============================================" echo " 마이그레이션 완료 — 누수+미세먼지 결합 노드" echo "==============================================" echo " sensor_id : ${SENSOR_ID} (누수와 동일 id 통합 보고, metric_type=pms7003)" echo " 설치 경로 : ${DEST_DIR} · 보고 주기 10분" echo " 서버 확인 : 대시보드 실시간 현황에서 '${SENSOR_ID}번 센서' 카드에" echo " 누수·(온습도)·미세먼지 칩이 함께 보이면 정상." echo " 서버 설정 : config.local.php METRIC_SENSOR_IDS 에 ${SENSOR_ID} 포함 여부 확인." if [ "${NEED_REBOOT}" = "1" ]; then echo "" echo " ⚠️ 지금 'sudo reboot' 하세요 — UART 는 재부팅 후 활성화되고 서비스는 자동 시작됩니다." fi echo ""