G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현 G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건) G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST) G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직 G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트 G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트 G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트 G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트 G-10: core/push_notify.py + routers/push.py + PushSubscription 모델 G-11: approvals 다중승인 (위임/서명/기한초과/마감연장) G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh 하네스: guardia-orchestrator 확장기능 Phase 반영 봇명령어: /sr /status /license /bulk 슬래시 명령어 추가 설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
182 lines
8.2 KiB
Bash
182 lines
8.2 KiB
Bash
#!/bin/bash
|
|
# ============================================================
|
|
# GUARDiA SM | db_oracle_sm.sh
|
|
# 대상: Oracle Database (11g/12c/19c/21c)
|
|
# 파라미터: ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
|
|
# ORACLE_SID=orcl ORACLE_USER=oracle
|
|
# DB_USER=system DB_PASS=oracle DB_CONN=localhost:1521/orcl
|
|
# LISTENER_PORT=1521 ARCHIVE_WARN=80
|
|
# ============================================================
|
|
set -euo pipefail
|
|
ORACLE_HOME=${ORACLE_HOME:-/opt/oracle/product/19c/dbhome_1}
|
|
ORACLE_SID=${ORACLE_SID:-orcl}
|
|
ORACLE_USER=${ORACLE_USER:-oracle}
|
|
DB_USER=${DB_USER:-system}
|
|
DB_PASS=${DB_PASS:-oracle}
|
|
DB_CONN=${DB_CONN:-"localhost:1521/${ORACLE_SID}"}
|
|
LISTENER_PORT=${LISTENER_PORT:-1521}
|
|
ARCHIVE_WARN=${ARCHIVE_WARN:-80}
|
|
OK="[OK]"; WARN="[WARN]"; CRIT="[CRIT]"
|
|
SEP="─────────────────────────────────────────"
|
|
RESULT=0
|
|
export ORACLE_HOME ORACLE_SID
|
|
export PATH="${ORACLE_HOME}/bin:${PATH}"
|
|
|
|
# sqlplus 헬퍼 (결과만 반환)
|
|
_sql() {
|
|
sqlplus -s "${DB_USER}/${DB_PASS}@${DB_CONN}" <<EOF 2>/dev/null
|
|
SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF TRIMOUT ON
|
|
$1
|
|
EXIT;
|
|
EOF
|
|
}
|
|
|
|
echo "======================================================"
|
|
echo " GUARDiA SM 점검 | Oracle DB | $(hostname -s)"
|
|
echo " 점검 시각: $(date '+%Y-%m-%d %H:%M:%S %Z')"
|
|
echo "======================================================"
|
|
|
|
# ── 1. 리스너 프로세스 ────────────────────────────────────
|
|
echo; echo "[$SEP] 1. 리스너 상태"
|
|
LSNR_PROC=$(pgrep -c "tnslsnr" 2>/dev/null || echo 0)
|
|
if [ "$LSNR_PROC" -gt 0 ]; then
|
|
echo " ${OK} 리스너 실행 중 (PID: $(pgrep tnslsnr | head -1))"
|
|
if command -v lsnrctl &>/dev/null; then
|
|
LSNR_STAT=$(lsnrctl status 2>/dev/null | grep -E "Alias|Status|Uptime|Version" | \
|
|
sed 's/^/ /' | head -6 || echo "")
|
|
echo "$LSNR_STAT"
|
|
fi
|
|
else
|
|
echo " ${CRIT} 리스너 프로세스 없음"
|
|
RESULT=2
|
|
fi
|
|
|
|
# ── 2. 포트 리스닝 ────────────────────────────────────────
|
|
echo; echo "[$SEP] 2. 포트 리스닝"
|
|
if ss -tlnp 2>/dev/null | grep -q ":${LISTENER_PORT} "; then
|
|
echo " ${OK} 포트 ${LISTENER_PORT} LISTEN"
|
|
else
|
|
echo " ${CRIT} 포트 ${LISTENER_PORT} LISTEN 없음"
|
|
RESULT=2
|
|
fi
|
|
|
|
# ── 3. DB 프로세스 + 인스턴스 상태 ──────────────────────
|
|
echo; echo "[$SEP] 3. 인스턴스 상태"
|
|
ORA_PROC=$(pgrep -c "ora_pmon_\|ora_smon_" 2>/dev/null || echo 0)
|
|
if [ "$ORA_PROC" -gt 0 ]; then
|
|
echo " ${OK} Oracle 백그라운드 프로세스 실행 중 (${ORA_PROC}개)"
|
|
else
|
|
echo " ${CRIT} Oracle 백그라운드 프로세스 없음"
|
|
RESULT=2
|
|
fi
|
|
|
|
# sqlplus 접속 테스트
|
|
if command -v sqlplus &>/dev/null && [ "$RESULT" -lt 2 ]; then
|
|
DB_STATUS=$(_sql "SELECT status FROM v\$instance;" 2>/dev/null | tr -d ' ')
|
|
if echo "$DB_STATUS" | grep -qi "OPEN"; then
|
|
echo " ${OK} DB 인스턴스 상태: OPEN"
|
|
else
|
|
echo " ${WARN} DB 인스턴스 상태: ${DB_STATUS}"
|
|
[ $RESULT -lt 1 ] && RESULT=1
|
|
fi
|
|
fi
|
|
|
|
# ── 4. 세션 현황 ─────────────────────────────────────────
|
|
echo; echo "[$SEP] 4. 세션 현황"
|
|
if command -v sqlplus &>/dev/null && [ "$RESULT" -lt 2 ]; then
|
|
SESSION_INFO=$(_sql "
|
|
SELECT 'TOTAL:'||count(*)||' ACTIVE:'||
|
|
sum(CASE WHEN status='ACTIVE' THEN 1 ELSE 0 END)||' INACTIVE:'||
|
|
sum(CASE WHEN status='INACTIVE' THEN 1 ELSE 0 END)
|
|
FROM v\$session WHERE type='USER';" 2>/dev/null | tr -d ' ')
|
|
echo " ${SESSION_INFO}"
|
|
# 세션 한도
|
|
MAX_SESS=$(_sql "SELECT value FROM v\$parameter WHERE name='sessions';" 2>/dev/null | tr -d ' ')
|
|
CURR_SESS=$(_sql "SELECT count(*) FROM v\$session;" 2>/dev/null | tr -d ' ')
|
|
echo " 세션 한도: ${MAX_SESS}, 현재: ${CURR_SESS}"
|
|
if [ -n "$MAX_SESS" ] && [ "$MAX_SESS" -gt 0 ]; then
|
|
SESS_PCT=$(( CURR_SESS * 100 / MAX_SESS ))
|
|
[ "$SESS_PCT" -ge 80 ] && echo " ${WARN} 세션 사용률 ${SESS_PCT}%" && [ $RESULT -lt 1 ] && RESULT=1
|
|
fi
|
|
fi
|
|
|
|
# ── 5. 테이블스페이스 사용률 ─────────────────────────────
|
|
echo; echo "[$SEP] 5. 테이블스페이스 사용률"
|
|
if command -v sqlplus &>/dev/null && [ "$RESULT" -lt 2 ]; then
|
|
TS_INFO=$(_sql "
|
|
SELECT df.tablespace_name,
|
|
round(df.total_mb,1)||'MB' AS total,
|
|
round(df.total_mb - fs.free_mb,1)||'MB' AS used,
|
|
round((df.total_mb - fs.free_mb) * 100 / df.total_mb,1)||'%' AS pct
|
|
FROM (SELECT tablespace_name, sum(bytes)/1048576 total_mb FROM dba_data_files
|
|
GROUP BY tablespace_name) df,
|
|
(SELECT tablespace_name, sum(bytes)/1048576 free_mb FROM dba_free_space
|
|
GROUP BY tablespace_name) fs
|
|
WHERE df.tablespace_name = fs.tablespace_name(+)
|
|
ORDER BY 4 DESC
|
|
FETCH FIRST 10 ROWS ONLY;" 2>/dev/null | sed 's/^/ /' || echo " 조회 실패")
|
|
echo "$TS_INFO"
|
|
# 90% 초과 체크
|
|
CRIT_TS=$(_sql "
|
|
SELECT count(*) FROM (
|
|
SELECT df.tablespace_name,
|
|
round((df.total_mb - nvl(fs.free_mb,0)) * 100 / df.total_mb,1) pct
|
|
FROM (SELECT tablespace_name, sum(bytes)/1048576 total_mb FROM dba_data_files
|
|
GROUP BY tablespace_name) df,
|
|
(SELECT tablespace_name, sum(bytes)/1048576 free_mb FROM dba_free_space
|
|
GROUP BY tablespace_name) fs
|
|
WHERE df.tablespace_name = fs.tablespace_name(+)
|
|
) WHERE pct >= 90;" 2>/dev/null | tr -d ' ' || echo 0)
|
|
[ "${CRIT_TS:-0}" -gt 0 ] && echo " ${CRIT} 테이블스페이스 90% 초과 ${CRIT_TS}개" && RESULT=2
|
|
fi
|
|
|
|
# ── 6. 아카이브 로그 사용률 ──────────────────────────────
|
|
echo; echo "[$SEP] 6. 아카이브 로그 사용률"
|
|
if command -v sqlplus &>/dev/null && [ "$RESULT" -lt 2 ]; then
|
|
ARCH_PCT=$(_sql "
|
|
SELECT round(space_used * 100 / space_limit,1)
|
|
FROM v\$recovery_file_dest;" 2>/dev/null | tr -d ' ' || echo "N/A")
|
|
if echo "$ARCH_PCT" | grep -qE "^[0-9]"; then
|
|
if [ "${ARCH_PCT%.*}" -ge 90 ]; then
|
|
echo " ${CRIT} FRA 사용률 ${ARCH_PCT}%"
|
|
RESULT=2
|
|
elif [ "${ARCH_PCT%.*}" -ge "$ARCHIVE_WARN" ]; then
|
|
echo " ${WARN} FRA 사용률 ${ARCH_PCT}%"
|
|
[ $RESULT -lt 1 ] && RESULT=1
|
|
else
|
|
echo " ${OK} FRA 사용률 ${ARCH_PCT}%"
|
|
fi
|
|
else
|
|
echo " FRA 정보 없음 (아카이브 모드 미사용 또는 미권한)"
|
|
fi
|
|
fi
|
|
|
|
# ── 7. 알람 로그 오류 (최근 100줄) ───────────────────────
|
|
echo; echo "[$SEP] 7. Alert 로그 오류"
|
|
ALERT_LOG="${ORACLE_HOME}/../diag/rdbms/${ORACLE_SID}/${ORACLE_SID}/trace/alert_${ORACLE_SID}.log"
|
|
if [ -r "$ALERT_LOG" ]; then
|
|
ERR_CNT=$(tail -200 "$ALERT_LOG" | grep -icE "ORA-[0-9]+|error" || echo 0)
|
|
echo " 최근 오류 수: ${ERR_CNT}"
|
|
tail -200 "$ALERT_LOG" | grep -iE "ORA-[0-9]+" | tail -5 | sed 's/^/ /' || true
|
|
[ "$ERR_CNT" -gt 5 ] && [ $RESULT -lt 1 ] && RESULT=1
|
|
else
|
|
# ADR 기반 경로 시도
|
|
for P in /opt/oracle/diag/rdbms/${ORACLE_SID}/${ORACLE_SID}/trace/alert_${ORACLE_SID}.log \
|
|
/u01/app/oracle/diag/rdbms/${ORACLE_SID}/${ORACLE_SID}/trace/alert_${ORACLE_SID}.log; do
|
|
[ -r "$P" ] && tail -200 "$P" | grep -iE "ORA-[0-9]+" | tail -5 | sed 's/^/ /' && break
|
|
done
|
|
echo " Alert 로그 경로 확인 필요: ${ALERT_LOG}"
|
|
fi
|
|
|
|
# ── 요약 ─────────────────────────────────────────────────
|
|
echo
|
|
echo "======================================================"
|
|
case $RESULT in
|
|
0) echo " 최종 결과: ${OK} Oracle DB 정상" ;;
|
|
1) echo " 최종 결과: ${WARN} 주의 항목 있음" ;;
|
|
2) echo " 최종 결과: ${CRIT} 즉시 조치 필요" ;;
|
|
esac
|
|
echo " 점검 완료: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
echo "======================================================"
|
|
exit $RESULT
|