update
This commit is contained in:
@@ -10,6 +10,7 @@ from .file_view import register_file_view
|
||||
from .jobs import register_jobs_routes
|
||||
from .idrac_routes import register_idrac_routes
|
||||
from .catalog_sync import catalog_bp
|
||||
from .scp_routes import scp_bp
|
||||
|
||||
def register_routes(app: Flask, socketio=None) -> None:
|
||||
"""블루프린트 일괄 등록. socketio는 main 라우트에서만 사용."""
|
||||
@@ -23,3 +24,4 @@ def register_routes(app: Flask, socketio=None) -> None:
|
||||
register_jobs_routes(app)
|
||||
register_idrac_routes(app)
|
||||
app.register_blueprint(catalog_bp)
|
||||
app.register_blueprint(scp_bp)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
backend/routes/__pycache__/scp_routes.cpython-312.pyc
Normal file
BIN
backend/routes/__pycache__/scp_routes.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -18,6 +18,11 @@ from flask import (
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from backend.models.user import User, db
|
||||
from backend.models.telegram_bot import TelegramBot
|
||||
try:
|
||||
from telegram import Bot
|
||||
except ImportError:
|
||||
Bot = None
|
||||
|
||||
admin_bp = Blueprint("admin", __name__)
|
||||
|
||||
@@ -124,3 +129,111 @@ def reset_password(user_id: int):
|
||||
flash("비밀번호 변경 중 오류가 발생했습니다.", "danger")
|
||||
|
||||
return redirect(url_for("admin.admin_panel"))
|
||||
|
||||
|
||||
# ▼▼▼ 시스템 설정 (텔레그램 봇 관리) ▼▼▼
|
||||
@admin_bp.route("/admin/settings", methods=["GET"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def settings():
|
||||
# 테이블 생성 확인 (임시)
|
||||
try:
|
||||
bots = TelegramBot.query.all()
|
||||
except Exception:
|
||||
db.create_all()
|
||||
bots = []
|
||||
|
||||
return render_template("admin_settings.html", bots=bots)
|
||||
|
||||
|
||||
@admin_bp.route("/admin/settings/bot/add", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def add_bot():
|
||||
name = request.form.get("name")
|
||||
token = request.form.get("token")
|
||||
chat_id = request.form.get("chat_id")
|
||||
desc = request.form.get("description")
|
||||
|
||||
# 알림 유형 (체크박스 다중 선택)
|
||||
notify_types = request.form.getlist("notify_types")
|
||||
notify_str = ",".join(notify_types) if notify_types else ""
|
||||
|
||||
if not (name and token and chat_id):
|
||||
flash("필수 항목(이름, 토큰, Chat ID)을 입력하세요.", "warning")
|
||||
return redirect(url_for("admin.settings"))
|
||||
|
||||
bot = TelegramBot(
|
||||
name=name,
|
||||
token=token,
|
||||
chat_id=chat_id,
|
||||
description=desc,
|
||||
is_active=True,
|
||||
notification_types=notify_str
|
||||
)
|
||||
db.session.add(bot)
|
||||
db.session.commit()
|
||||
flash("텔레그램 봇이 추가되었습니다.", "success")
|
||||
return redirect(url_for("admin.settings"))
|
||||
|
||||
|
||||
@admin_bp.route("/admin/settings/bot/edit/<int:bot_id>", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def edit_bot(bot_id):
|
||||
bot = db.session.get(TelegramBot, bot_id)
|
||||
if not bot:
|
||||
abort(404)
|
||||
|
||||
bot.name = request.form.get("name")
|
||||
bot.token = request.form.get("token")
|
||||
bot.chat_id = request.form.get("chat_id")
|
||||
bot.description = request.form.get("description")
|
||||
bot.is_active = request.form.get("is_active") == "on"
|
||||
|
||||
# 알림 유형 업데이트
|
||||
notify_types = request.form.getlist("notify_types")
|
||||
bot.notification_types = ",".join(notify_types) if notify_types else ""
|
||||
|
||||
db.session.commit()
|
||||
flash("봇 설정이 수정되었습니다.", "success")
|
||||
return redirect(url_for("admin.settings"))
|
||||
|
||||
|
||||
@admin_bp.route("/admin/settings/bot/delete/<int:bot_id>", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def delete_bot(bot_id):
|
||||
bot = db.session.get(TelegramBot, bot_id)
|
||||
if bot:
|
||||
db.session.delete(bot)
|
||||
db.session.commit()
|
||||
flash("봇이 삭제되었습니다.", "success")
|
||||
return redirect(url_for("admin.settings"))
|
||||
|
||||
|
||||
@admin_bp.route("/admin/settings/bot/test/<int:bot_id>", methods=["POST"])
|
||||
@login_required
|
||||
@admin_required
|
||||
def test_bot(bot_id):
|
||||
if not Bot:
|
||||
flash("python-telegram-bot 라이브러리가 설치되지 않았습니다.", "danger")
|
||||
return redirect(url_for("admin.settings"))
|
||||
|
||||
bot_obj = db.session.get(TelegramBot, bot_id)
|
||||
if not bot_obj:
|
||||
abort(404)
|
||||
|
||||
import asyncio
|
||||
|
||||
async def _send():
|
||||
bot = Bot(token=bot_obj.token)
|
||||
await bot.send_message(chat_id=bot_obj.chat_id, text="🔔 <b>테스트 메시지</b>\n이 메시지가 보이면 설정이 정상입니다.", parse_mode="HTML")
|
||||
|
||||
try:
|
||||
asyncio.run(_send())
|
||||
flash(f"'{bot_obj.name}' 봇으로 테스트 메시지를 보냈습니다.", "success")
|
||||
except Exception as e:
|
||||
flash(f"테스트 실패: {e}", "danger")
|
||||
|
||||
return redirect(url_for("admin.settings"))
|
||||
|
||||
@@ -3,8 +3,11 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import threading
|
||||
import asyncio
|
||||
import secrets
|
||||
from typing import Optional
|
||||
from urllib.parse import urlparse, urljoin
|
||||
from datetime import datetime
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
@@ -23,11 +26,13 @@ from backend.models.user import User, db
|
||||
|
||||
# ── (선택) Telegram: 미설정이면 조용히 패스
|
||||
try:
|
||||
from telegram import Bot
|
||||
from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from telegram.constants import ParseMode
|
||||
except Exception: # 라이브러리 미설치/미사용 환경
|
||||
Bot = None
|
||||
ParseMode = None
|
||||
InlineKeyboardButton = None
|
||||
InlineKeyboardMarkup = None
|
||||
|
||||
auth_bp = Blueprint("auth", __name__)
|
||||
|
||||
@@ -42,21 +47,162 @@ def _is_safe_url(target: str) -> bool:
|
||||
return (test.scheme in ("http", "https")) and (ref.netloc == test.netloc)
|
||||
|
||||
|
||||
def _notify(text: str) -> None:
|
||||
"""텔레그램 알림 (설정 없으면 바로 return)."""
|
||||
token = (current_app.config.get("TELEGRAM_BOT_TOKEN") or "").strip()
|
||||
chat_id = (current_app.config.get("TELEGRAM_CHAT_ID") or "").strip()
|
||||
if not (token and chat_id and Bot and ParseMode):
|
||||
return
|
||||
|
||||
def _send():
|
||||
def _notify(text: str, category: str = "system") -> None:
|
||||
"""
|
||||
텔레그램 알림 전송
|
||||
- DB(TelegramBot)에 등록된 활성 봇들에게 전송
|
||||
- category: 'auth', 'activity', 'system' 등
|
||||
"""
|
||||
try:
|
||||
from backend.models.telegram_bot import TelegramBot
|
||||
|
||||
# 앱 컨텍스트 안에서 실행되므로 바로 DB 접근 가능
|
||||
try:
|
||||
bot = Bot(token=token)
|
||||
bot.send_message(chat_id=chat_id, text=text, parse_mode=ParseMode.HTML)
|
||||
except Exception as e:
|
||||
current_app.logger.warning("Telegram send failed: %s", e)
|
||||
bots = TelegramBot.query.filter_by(is_active=True).all()
|
||||
except Exception:
|
||||
db.create_all()
|
||||
bots = []
|
||||
|
||||
threading.Thread(target=_send, daemon=True).start()
|
||||
# 1. DB에 봇이 없고, Config에 설정이 있는 경우 -> 자동 마이그레이션
|
||||
if not bots:
|
||||
cfg_token = (current_app.config.get("TELEGRAM_BOT_TOKEN") or "").strip()
|
||||
cfg_chat = (current_app.config.get("TELEGRAM_CHAT_ID") or "").strip()
|
||||
|
||||
if cfg_token and cfg_chat:
|
||||
new_bot = TelegramBot(
|
||||
name="기본 봇 (Config)",
|
||||
token=cfg_token,
|
||||
chat_id=cfg_chat,
|
||||
is_active=True,
|
||||
description="config.py 설정에서 자동 가져옴",
|
||||
notification_types="auth,activity,system"
|
||||
)
|
||||
db.session.add(new_bot)
|
||||
db.session.commit()
|
||||
bots = [new_bot]
|
||||
current_app.logger.info("Telegram: Config settings migrated to DB.")
|
||||
|
||||
if not bots:
|
||||
return
|
||||
|
||||
# 카테고리 필터링
|
||||
target_bots = []
|
||||
for b in bots:
|
||||
allowed = (b.notification_types or "auth,activity,system").split(",")
|
||||
if category in allowed:
|
||||
target_bots.append(b)
|
||||
|
||||
if not target_bots:
|
||||
return
|
||||
|
||||
if not (Bot and ParseMode):
|
||||
current_app.logger.warning("Telegram: Library not installed.")
|
||||
return
|
||||
|
||||
app = current_app._get_current_object()
|
||||
|
||||
async def _send_to_bot(bot_obj, msg):
|
||||
try:
|
||||
t_bot = Bot(token=bot_obj.token)
|
||||
await t_bot.send_message(chat_id=bot_obj.chat_id, text=msg, parse_mode=ParseMode.HTML)
|
||||
except Exception as e:
|
||||
app.logger.error(f"Telegram fail ({bot_obj.name}): {e}")
|
||||
|
||||
async def _send_all():
|
||||
tasks = [_send_to_bot(b, text) for b in target_bots]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
def _run_thread():
|
||||
try:
|
||||
asyncio.run(_send_all())
|
||||
except Exception as e:
|
||||
app.logger.error(f"Telegram async loop error: {e}")
|
||||
|
||||
threading.Thread(target=_run_thread, daemon=True).start()
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Telegram notification error: {e}")
|
||||
|
||||
|
||||
def _notify_with_buttons(text: str, buttons: list, category: str = "auth") -> None:
|
||||
"""
|
||||
텔레그램 알림 전송 (인라인 버튼 포함)
|
||||
- buttons: [(text, callback_data), ...] 형식의 리스트
|
||||
"""
|
||||
try:
|
||||
from backend.models.telegram_bot import TelegramBot
|
||||
|
||||
try:
|
||||
bots = TelegramBot.query.filter_by(is_active=True).all()
|
||||
except Exception:
|
||||
db.create_all()
|
||||
bots = []
|
||||
|
||||
if not bots:
|
||||
cfg_token = (current_app.config.get("TELEGRAM_BOT_TOKEN") or "").strip()
|
||||
cfg_chat = (current_app.config.get("TELEGRAM_CHAT_ID") or "").strip()
|
||||
|
||||
if cfg_token and cfg_chat:
|
||||
new_bot = TelegramBot(
|
||||
name="기본 봇 (Config)",
|
||||
token=cfg_token,
|
||||
chat_id=cfg_chat,
|
||||
is_active=True,
|
||||
description="config.py 설정에서 자동 가져옴",
|
||||
notification_types="auth,activity,system"
|
||||
)
|
||||
db.session.add(new_bot)
|
||||
db.session.commit()
|
||||
bots = [new_bot]
|
||||
|
||||
if not bots:
|
||||
return
|
||||
|
||||
target_bots = []
|
||||
for b in bots:
|
||||
allowed = (b.notification_types or "auth,activity,system").split(",")
|
||||
if category in allowed:
|
||||
target_bots.append(b)
|
||||
|
||||
if not target_bots:
|
||||
return
|
||||
|
||||
if not (Bot and ParseMode and InlineKeyboardButton and InlineKeyboardMarkup):
|
||||
current_app.logger.warning("Telegram: Library not installed.")
|
||||
return
|
||||
|
||||
# 인라인 키보드 생성
|
||||
keyboard = [[InlineKeyboardButton(btn[0], callback_data=btn[1]) for btn in buttons]]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
|
||||
app = current_app._get_current_object()
|
||||
|
||||
async def _send_to_bot(bot_obj, msg, markup):
|
||||
try:
|
||||
t_bot = Bot(token=bot_obj.token)
|
||||
await t_bot.send_message(
|
||||
chat_id=bot_obj.chat_id,
|
||||
text=msg,
|
||||
parse_mode=ParseMode.HTML,
|
||||
reply_markup=markup
|
||||
)
|
||||
except Exception as e:
|
||||
app.logger.error(f"Telegram fail ({bot_obj.name}): {e}")
|
||||
|
||||
async def _send_all():
|
||||
tasks = [_send_to_bot(b, text, reply_markup) for b in target_bots]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
def _run_thread():
|
||||
try:
|
||||
asyncio.run(_send_all())
|
||||
except Exception as e:
|
||||
app.logger.error(f"Telegram async loop error: {e}")
|
||||
|
||||
threading.Thread(target=_run_thread, daemon=True).start()
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Telegram notification with buttons error: {e}")
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
@@ -67,12 +213,28 @@ def register_auth_routes(app):
|
||||
app.register_blueprint(auth_bp)
|
||||
|
||||
@app.before_request
|
||||
def _touch_session():
|
||||
# 요청마다 세션 갱신(만료 슬라이딩) + 로그아웃 플래그 정리
|
||||
def _global_hooks():
|
||||
# 1. 세션 갱신 (요청마다 세션 타임아웃 연장)
|
||||
session.modified = True
|
||||
if current_user.is_authenticated and session.get("just_logged_out"):
|
||||
session.pop("just_logged_out", None)
|
||||
flash("세션이 만료되어 자동 로그아웃 되었습니다.", "info")
|
||||
|
||||
# 2. 활동 알림 (로그인된 사용자)
|
||||
if current_user.is_authenticated:
|
||||
# 정적 리소스 및 불필요한 경로 제외
|
||||
if request.endpoint == 'static':
|
||||
return
|
||||
|
||||
# 제외할 확장자/경로 (필요 시 추가)
|
||||
ignored_exts = ('.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.woff', '.woff2')
|
||||
if request.path.lower().endswith(ignored_exts):
|
||||
return
|
||||
|
||||
# 활동 내용 구성
|
||||
msg = (
|
||||
f"👣 <b>활동 감지</b>\n"
|
||||
f"👤 <code>{current_user.username}</code>\n"
|
||||
f"📍 <code>{request.method} {request.path}</code>"
|
||||
)
|
||||
_notify(msg, category="activity")
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
@@ -97,17 +259,36 @@ def register():
|
||||
current_app.logger.info("REGISTER: dup username %s", form.username.data)
|
||||
return render_template("register.html", form=form)
|
||||
|
||||
user = User(username=form.username.data, email=form.email.data, is_active=False)
|
||||
user.set_password(form.password.data) # passlib: 기본 Argon2id
|
||||
# 승인 토큰 생성
|
||||
approval_token = secrets.token_urlsafe(32)
|
||||
|
||||
user = User(
|
||||
username=form.username.data,
|
||||
email=form.email.data,
|
||||
is_active=False,
|
||||
is_approved=False,
|
||||
approval_token=approval_token
|
||||
)
|
||||
user.set_password(form.password.data)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
_notify(
|
||||
f"🆕 <b>신규 가입 요청</b>\n"
|
||||
f"📛 사용자: <code>{user.username}</code>\n"
|
||||
f"📧 이메일: <code>{user.email}</code>"
|
||||
# 텔레그램 알림 (인라인 버튼 포함)
|
||||
message = (
|
||||
f"🆕 <b>신규 가입 요청</b>\n\n"
|
||||
f"<EFBFBD> 사용자: <code>{user.username}</code>\n"
|
||||
f"📧 이메일: <code>{user.email}</code>\n"
|
||||
f"🕐 신청시간: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
)
|
||||
current_app.logger.info("REGISTER: created id=%s email=%s", user.id, user.email)
|
||||
|
||||
buttons = [
|
||||
("✅ 승인", f"approve_{approval_token}"),
|
||||
("❌ 거부", f"reject_{approval_token}")
|
||||
]
|
||||
|
||||
_notify_with_buttons(message, buttons, category="auth")
|
||||
|
||||
current_app.logger.info("REGISTER: created id=%s email=%s token=%s", user.id, user.email, approval_token[:10])
|
||||
flash("회원가입이 완료되었습니다. 관리자의 승인을 기다려주세요.", "success")
|
||||
return redirect(url_for("auth.login"))
|
||||
else:
|
||||
@@ -136,24 +317,28 @@ def login():
|
||||
current_app.logger.info("LOGIN: user not found")
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
pass_ok = user.check_password(form.password.data) # passlib verify(+자동 재해시)
|
||||
pass_ok = user.check_password(form.password.data)
|
||||
current_app.logger.info(
|
||||
"LOGIN: found id=%s active=%s pass_ok=%s",
|
||||
user.id, user.is_active, pass_ok
|
||||
"LOGIN: found id=%s active=%s approved=%s pass_ok=%s",
|
||||
user.id, user.is_active, user.is_approved, pass_ok
|
||||
)
|
||||
|
||||
if not pass_ok:
|
||||
flash("이메일 또는 비밀번호가 올바르지 않습니다.", "danger")
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
if not user.is_approved:
|
||||
flash("계정이 아직 승인되지 않았습니다. 관리자의 승인을 기다려주세요.", "warning")
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
if not user.is_active:
|
||||
flash("계정이 아직 승인되지 않았습니다.", "warning")
|
||||
flash("계정이 비활성화되었습니다. 관리자에게 문의하세요.", "warning")
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
# 성공
|
||||
login_user(user, remember=form.remember.data)
|
||||
session.permanent = True
|
||||
_notify(f"🔐 <b>로그인 성공</b>\n👤 <code>{user.username}</code>")
|
||||
_notify(f"🔐 <b>로그인 성공</b>\n👤 <code>{user.username}</code>", category="auth")
|
||||
current_app.logger.info("LOGIN: SUCCESS → redirect")
|
||||
|
||||
nxt = request.args.get("next")
|
||||
@@ -175,6 +360,7 @@ def login():
|
||||
def logout():
|
||||
if current_user.is_authenticated:
|
||||
current_app.logger.info("LOGOUT: user=%s", current_user.username)
|
||||
_notify(f"🚪 <b>로그아웃</b>\n👤 <code>{current_user.username}</code>", category="auth")
|
||||
logout_user()
|
||||
session["just_logged_out"] = True
|
||||
flash("정상적으로 로그아웃 되었습니다.", "success")
|
||||
return redirect(url_for("auth.login"))
|
||||
144
backend/routes/scp_routes.py
Normal file
144
backend/routes/scp_routes.py
Normal file
@@ -0,0 +1,144 @@
|
||||
from flask import Blueprint, render_template, request, jsonify, flash, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
import logging
|
||||
import difflib
|
||||
from pathlib import Path
|
||||
from config import Config
|
||||
from backend.services.redfish_client import RedfishClient
|
||||
from backend.routes.xml import sanitize_preserve_unicode
|
||||
|
||||
scp_bp = Blueprint("scp", __name__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@scp_bp.route("/scp/diff", methods=["GET"])
|
||||
@login_required
|
||||
def diff_scp():
|
||||
"""
|
||||
두 XML 파일의 차이점을 비교하여 보여줍니다.
|
||||
"""
|
||||
file1_name = request.args.get("file1")
|
||||
file2_name = request.args.get("file2")
|
||||
|
||||
if not file1_name or not file2_name:
|
||||
flash("비교할 두 파일을 선택해주세요.", "warning")
|
||||
return redirect(url_for("xml.xml_management"))
|
||||
|
||||
try:
|
||||
file1_path = Path(Config.XML_FOLDER) / sanitize_preserve_unicode(file1_name)
|
||||
file2_path = Path(Config.XML_FOLDER) / sanitize_preserve_unicode(file2_name)
|
||||
|
||||
if not file1_path.exists() or not file2_path.exists():
|
||||
flash("파일을 찾을 수 없습니다.", "danger")
|
||||
return redirect(url_for("xml.xml_management"))
|
||||
|
||||
# 파일 내용 읽기 (LF로 통일)
|
||||
content1 = file1_path.read_text(encoding="utf-8").replace("\r\n", "\n").splitlines()
|
||||
content2 = file2_path.read_text(encoding="utf-8").replace("\r\n", "\n").splitlines()
|
||||
|
||||
# Diff 생성
|
||||
diff = difflib.unified_diff(
|
||||
content1, content2,
|
||||
fromfile=file1_name,
|
||||
tofile=file2_name,
|
||||
lineterm=""
|
||||
)
|
||||
|
||||
diff_content = "\n".join(diff)
|
||||
|
||||
return render_template("scp_diff.html", file1=file1_name, file2=file2_name, diff_content=diff_content)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Diff error: {e}")
|
||||
flash(f"비교 중 오류가 발생했습니다: {str(e)}", "danger")
|
||||
return redirect(url_for("xml.xml_management"))
|
||||
|
||||
@scp_bp.route("/scp/export", methods=["POST"])
|
||||
@login_required
|
||||
def export_scp():
|
||||
"""
|
||||
iDRAC에서 설정을 내보내기 (Export)
|
||||
네트워크 공유 설정이 필요합니다.
|
||||
"""
|
||||
data = request.form
|
||||
target_ip = data.get("target_ip")
|
||||
username = data.get("username")
|
||||
password = data.get("password")
|
||||
|
||||
# Share Parameters
|
||||
share_ip = data.get("share_ip")
|
||||
share_name = data.get("share_name")
|
||||
share_user = data.get("share_user")
|
||||
share_pwd = data.get("share_pwd")
|
||||
filename = data.get("filename")
|
||||
|
||||
if not all([target_ip, username, password, share_ip, share_name, filename]):
|
||||
flash("필수 정보가 누락되었습니다.", "warning")
|
||||
return redirect(url_for("xml.xml_management"))
|
||||
|
||||
share_params = {
|
||||
"IPAddress": share_ip,
|
||||
"ShareName": share_name,
|
||||
"FileName": filename,
|
||||
"ShareType": "CIFS", # 기본값 CIFS
|
||||
"UserName": share_user,
|
||||
"Password": share_pwd
|
||||
}
|
||||
|
||||
try:
|
||||
with RedfishClient(target_ip, username, password) as client:
|
||||
job_id = client.export_system_configuration(share_params)
|
||||
if job_id:
|
||||
flash(f"내보내기 작업이 시작되었습니다. Job ID: {job_id}", "success")
|
||||
else:
|
||||
flash("작업을 시작했으나 Job ID를 받지 못했습니다.", "warning")
|
||||
except Exception as e:
|
||||
logger.error(f"Export failed: {e}")
|
||||
flash(f"내보내기 실패: {str(e)}", "danger")
|
||||
|
||||
return redirect(url_for("xml.xml_management"))
|
||||
|
||||
@scp_bp.route("/scp/import", methods=["POST"])
|
||||
@login_required
|
||||
def import_scp():
|
||||
"""
|
||||
iDRAC로 설정 가져오기 (Import/Deploy)
|
||||
"""
|
||||
data = request.form
|
||||
target_ip = data.get("target_ip")
|
||||
username = data.get("username")
|
||||
password = data.get("password")
|
||||
|
||||
# Share Parameters
|
||||
share_ip = data.get("share_ip")
|
||||
share_name = data.get("share_name")
|
||||
share_user = data.get("share_user")
|
||||
share_pwd = data.get("share_pwd")
|
||||
filename = data.get("filename")
|
||||
|
||||
import_mode = data.get("import_mode", "Replace")
|
||||
|
||||
if not all([target_ip, username, password, share_ip, share_name, filename]):
|
||||
flash("필수 정보가 누락되었습니다.", "warning")
|
||||
return redirect(url_for("xml.xml_management"))
|
||||
|
||||
share_params = {
|
||||
"IPAddress": share_ip,
|
||||
"ShareName": share_name,
|
||||
"FileName": filename,
|
||||
"ShareType": "CIFS",
|
||||
"UserName": share_user,
|
||||
"Password": share_pwd
|
||||
}
|
||||
|
||||
try:
|
||||
with RedfishClient(target_ip, username, password) as client:
|
||||
job_id = client.import_system_configuration(share_params, import_mode=import_mode)
|
||||
if job_id:
|
||||
flash(f"설정 적용(Import) 작업이 시작되었습니다. Job ID: {job_id}", "success")
|
||||
else:
|
||||
flash("작업을 시작했으나 Job ID를 받지 못했습니다.", "warning")
|
||||
except Exception as e:
|
||||
logger.error(f"Import failed: {e}")
|
||||
flash(f"설정 적용 실패: {str(e)}", "danger")
|
||||
|
||||
return redirect(url_for("xml.xml_management"))
|
||||
Reference in New Issue
Block a user