167 lines
6.0 KiB
Python
167 lines
6.0 KiB
Python
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로 통일)
|
|
# 파일 내용 읽기 (LF로 통일)
|
|
# Monaco Editor에 원본 텍스트를 그대로 전달하기 위해 splitlines() 제거
|
|
# 파일 내용 읽기 (LF로 통일)
|
|
logger.info(f"Reading file1: {file1_path}")
|
|
content1 = file1_path.read_text(encoding="utf-8", errors="replace").replace("\r\n", "\n")
|
|
|
|
logger.info(f"Reading file2: {file2_path}")
|
|
content2 = file2_path.read_text(encoding="utf-8", errors="replace").replace("\r\n", "\n")
|
|
|
|
logger.info(f"Content1 length: {len(content1)}, Content2 length: {len(content2)}")
|
|
|
|
return render_template("scp_diff.html",
|
|
file1=file1_name,
|
|
file2=file2_name,
|
|
content1=content1,
|
|
content2=content2)
|
|
|
|
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/content/<path:filename>")
|
|
@login_required
|
|
def get_scp_content(filename):
|
|
"""
|
|
XML 파일 내용을 반환하는 API (Monaco Editor용)
|
|
"""
|
|
try:
|
|
safe_name = sanitize_preserve_unicode(filename)
|
|
path = Path(Config.XML_FOLDER) / safe_name
|
|
|
|
if not path.exists():
|
|
return "File not found", 404
|
|
|
|
# 텍스트로 읽어서 반환
|
|
content = path.read_text(encoding="utf-8", errors="replace").replace("\r\n", "\n")
|
|
return content, 200, {'Content-Type': 'text/plain; charset=utf-8'}
|
|
except Exception as e:
|
|
logger.error(f"Content read error: {e}")
|
|
return str(e), 500
|
|
|
|
@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"))
|