Initial project upload

This commit is contained in:
Kim.KANGHEE
2026-05-05 17:14:11 +09:00
commit 122b73d254
282 changed files with 72135 additions and 0 deletions
+185
View File
@@ -0,0 +1,185 @@
from flask import Blueprint, render_template, request, jsonify
from backend.models.user import db
from backend.models.idrac_server import IdracServer
from backend.models.server_config import ServerConfigSnapshot
from backend.services.idrac_redfish_client import DellRedfishClient
import concurrent.futures
import json
import hashlib
from datetime import datetime
server_config_bp = Blueprint('server_config', __name__, url_prefix='/server-config')
@server_config_bp.route('/')
def index():
"""서버 설정 분석 및 비교 페이지"""
return render_template('server_config.html')
def calculate_checksum(data):
"""JSON 데이터의 SHA256 체크섬 계산"""
json_str = json.dumps(data, sort_keys=True)
return hashlib.sha256(json_str.encode('utf-8')).hexdigest()
@server_config_bp.route('/api/sync', methods=['POST'])
def sync_configs():
"""
선택한 서버들의 설정을 iDRAC에서 가져와 DB에 저장 (동기화)
"""
try:
data = request.json
server_ids = data.get('server_ids', [])
categories = data.get('categories', ['bios', 'idrac', 'system'])
if not server_ids:
return jsonify({'success': False, 'message': '서버를 선택해주세요.'})
results = {'success': [], 'failed': []}
def process_server(server_id):
server = IdracServer.query.get(server_id)
if not server:
return {'id': server_id, 'status': 'failed', 'message': 'Server not found'}
client = DellRedfishClient(server.ip_address, server.username, server.password)
if not client.check_connection():
return {'id': server.id, 'name': server.name, 'status': 'failed', 'message': 'Connection failed'}
sync_results = []
# System Info
if 'system' in categories:
sys_data = client.get_system_info_detailed()
if sys_data:
save_snapshot(server.id, 'system', sys_data)
sync_results.append('system')
# BIOS Attributes
if 'bios' in categories:
bios_data = client.get_bios_attributes_all()
if bios_data:
save_snapshot(server.id, 'bios', bios_data)
sync_results.append('bios')
# iDRAC Attributes
if 'idrac' in categories:
idrac_data = client.get_idrac_attributes_all()
if idrac_data:
save_snapshot(server.id, 'idrac', idrac_data)
sync_results.append('idrac')
return {'id': server.id, 'name': server.name, 'status': 'success', 'synced': sync_results}
def save_snapshot(server_id, config_type, data):
# 기존 스냅샷 확인
snapshot = ServerConfigSnapshot.query.filter_by(
server_id=server_id,
config_type=config_type
).first()
new_hash = calculate_checksum(data)
if snapshot:
# 변경사항이 있는 경우에만 업데이트
if snapshot.hash_value != new_hash:
snapshot.data = data
snapshot.hash_value = new_hash
snapshot.updated_at = datetime.utcnow() # 명시적 업데이트 시간 갱신
else:
snapshot = ServerConfigSnapshot(
server_id=server_id,
config_type=config_type,
data=data,
hash_value=new_hash
)
db.session.add(snapshot)
# 트랜잭션은 개별 commit 혹은 묶어서 commit.
# 여기서는 스레드 안전성을 위해 함수 내에서는 session 조작만 하고
# 실제 commit은 메인 스레드나 별도 처리 필요하지만, Flask-SQLAlchemy는 scoped_session이므로
# 각 요청(스레드)마다 세션이 다를 수 있음.
# 하지만 ThreadPoolExecutor 사용 시 app context 문제가 발생할 수 있음.
# safe way: return data -> main thread saves. OR push app context.
# NOTE: For simplicity in this refactor with ThreadPoolExecutor,
# we will handle DB operations inside the thread but need app context.
# See below for implementation adjustment.
return True
# DB 저장을 위해 app context 필요
from flask import current_app
app = current_app._get_current_object()
def process_with_context(server_id):
with app.app_context():
try:
res = process_server(server_id)
db.session.commit() # Commit per server
return res
except Exception as e:
db.session.rollback()
return {'id': server_id, 'status': 'failed', 'message': str(e)}
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
future_to_id = {executor.submit(process_with_context, sid): sid for sid in server_ids}
for future in concurrent.futures.as_completed(future_to_id):
try:
res = future.result()
if res['status'] == 'success':
results['success'].append(res)
else:
results['failed'].append(res)
except Exception as e:
results['failed'].append({'error': str(e)})
return jsonify({
'success': True,
'results': results
})
except Exception as e:
return jsonify({'success': False, 'message': f"Sync Error: {str(e)}"})
@server_config_bp.route('/api/fetch', methods=['POST'])
def fetch_configs():
"""
DB에 저장된 서버 설정 스냅샷 조회
"""
try:
data = request.json
server_ids = data.get('server_ids', [])
categories = data.get('categories', ['bios', 'idrac', 'system'])
if not server_ids:
return jsonify({'success': False, 'message': '서버를 선택해주세요.'})
results = {}
for server_id in server_ids:
server = IdracServer.query.get(server_id)
if not server:
continue
server_data = {'id': server.id, 'name': server.name, 'ip': server.ip_address}
for cat in categories:
snapshot = ServerConfigSnapshot.query.filter_by(
server_id=server_id,
config_type=cat
).first()
if snapshot:
server_data[cat] = snapshot.data
server_data[f'{cat}_updated'] = snapshot.updated_at.isoformat()
else:
server_data[cat] = None
results[server_id] = server_data
return jsonify({
'success': True,
'results': results
})
except Exception as e:
return jsonify({'success': False, 'message': f"Fetch Error: {str(e)}"})