This commit is contained in:
2025-10-21 20:29:39 +09:00
parent 230ea0890d
commit bc15452181
163 changed files with 5177 additions and 16122 deletions

View File

@@ -8,6 +8,8 @@ from .xml import register_xml_routes
from .utilities import register_util_routes
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
def register_routes(app: Flask, socketio=None) -> None:
"""블루프린트 일괄 등록. socketio는 main 라우트에서만 사용."""
@@ -18,4 +20,6 @@ def register_routes(app: Flask, socketio=None) -> None:
register_xml_routes(app)
register_util_routes(app)
register_file_view(app)
register_jobs_routes(app)
register_jobs_routes(app)
register_idrac_routes(app)
app.register_blueprint(catalog_bp)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,23 @@
# backend/routes/catalog_sync.py
from flask import Blueprint, jsonify, request
from backend.services.dell_catalog_sync import sync_dell_catalog
catalog_bp = Blueprint("catalog", __name__, url_prefix="/catalog")
@catalog_bp.route("/sync", methods=["POST"])
def sync_catalog():
"""Dell Catalog 버전 정보를 동기화 (CSRF 보호 유지)"""
try:
data = request.get_json(silent=True) or {}
model = data.get("model", "PowerEdge R750")
sync_dell_catalog(model)
return jsonify({
"success": True,
"message": f"{model} 동기화 완료"
})
except Exception as e:
return jsonify({
"success": False,
"message": f"오류: {str(e)}"
})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,669 @@
"""
Dell iDRAC 멀티 서버 펌웨어 관리 라우트
backend/routes/idrac_routes.py
- CSRF 보호 제외 추가
"""
from flask import Blueprint, render_template, request, jsonify, current_app
from werkzeug.utils import secure_filename
import os
from datetime import datetime
from backend.services.idrac_redfish_client import DellRedfishClient
from backend.models.idrac_server import IdracServer, db
from flask_socketio import emit
import threading
# Blueprint 생성
idrac_bp = Blueprint('idrac', __name__, url_prefix='/idrac')
# 설정
UPLOAD_FOLDER = 'uploads/firmware'
ALLOWED_EXTENSIONS = {'exe', 'bin'}
MAX_FILE_SIZE = 500 * 1024 * 1024 # 500MB
def allowed_file(filename):
"""허용된 파일 형식 확인"""
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
# ========================================
# 메인 페이지
# ========================================
@idrac_bp.route('/')
def index():
"""iDRAC 멀티 서버 관리 메인 페이지"""
return render_template('idrac_firmware.html')
# ========================================
# 서버 관리 API
# ========================================
@idrac_bp.route('/api/servers', methods=['GET'])
def get_servers():
"""등록된 서버 목록 조회"""
try:
group = request.args.get('group') # 그룹 필터
query = IdracServer.query.filter_by(is_active=True)
if group and group != 'all':
query = query.filter_by(group_name=group)
servers = query.order_by(IdracServer.name).all()
return jsonify({
'success': True,
'servers': [s.to_dict() for s in servers]
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/servers', methods=['POST'])
def add_server():
"""서버 추가"""
try:
data = request.json
# 필수 필드 확인
if not all([data.get('name'), data.get('ip_address'), data.get('password')]):
return jsonify({
'success': False,
'message': '필수 필드를 모두 입력하세요 (서버명, IP, 비밀번호)'
})
# 중복 IP 확인
existing = IdracServer.query.filter_by(ip_address=data['ip_address']).first()
if existing:
return jsonify({
'success': False,
'message': f'이미 등록된 IP입니다: {data["ip_address"]}'
})
# 서버 생성
server = IdracServer(
name=data['name'],
ip_address=data['ip_address'],
username=data.get('username', 'root'),
password=data['password'],
group_name=data.get('group_name'),
location=data.get('location'),
model=data.get('model'),
notes=data.get('notes')
)
db.session.add(server)
db.session.commit()
return jsonify({
'success': True,
'message': f'서버 {server.name} 추가 완료',
'server': server.to_dict()
})
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/servers/<int:server_id>', methods=['PUT'])
def update_server(server_id):
"""서버 정보 수정"""
try:
server = IdracServer.query.get(server_id)
if not server:
return jsonify({
'success': False,
'message': '서버를 찾을 수 없습니다'
})
data = request.json
# 업데이트
if 'name' in data:
server.name = data['name']
if 'ip_address' in data:
server.ip_address = data['ip_address']
if 'username' in data:
server.username = data['username']
if 'password' in data:
server.password = data['password']
if 'group_name' in data:
server.group_name = data['group_name']
if 'location' in data:
server.location = data['location']
if 'model' in data:
server.model = data['model']
if 'notes' in data:
server.notes = data['notes']
db.session.commit()
return jsonify({
'success': True,
'message': '서버 정보 수정 완료',
'server': server.to_dict()
})
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/servers/<int:server_id>', methods=['DELETE'])
def delete_server(server_id):
"""서버 삭제 (소프트 삭제)"""
try:
server = IdracServer.query.get(server_id)
if not server:
return jsonify({
'success': False,
'message': '서버를 찾을 수 없습니다'
})
server.is_active = False
db.session.commit()
return jsonify({
'success': True,
'message': f'서버 {server.name} 삭제 완료'
})
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/groups', methods=['GET'])
def get_groups():
"""등록된 그룹 목록"""
try:
groups = db.session.query(IdracServer.group_name)\
.filter(IdracServer.is_active == True)\
.filter(IdracServer.group_name.isnot(None))\
.distinct()\
.all()
group_list = [g[0] for g in groups if g[0]]
return jsonify({
'success': True,
'groups': group_list
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
# ========================================
# 연결 및 상태 확인 API
# ========================================
@idrac_bp.route('/api/servers/<int:server_id>/test', methods=['POST'])
def test_connection(server_id):
"""단일 서버 연결 테스트"""
try:
server = IdracServer.query.get(server_id)
if not server:
return jsonify({
'success': False,
'message': '서버를 찾을 수 없습니다'
})
client = DellRedfishClient(server.ip_address, server.username, server.password)
if client.check_connection():
server.status = 'online'
server.last_connected = datetime.utcnow()
db.session.commit()
return jsonify({
'success': True,
'message': f'{server.name} 연결 성공'
})
else:
server.status = 'offline'
db.session.commit()
return jsonify({
'success': False,
'message': f'{server.name} 연결 실패'
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/servers/test-multi', methods=['POST'])
def test_connections_multi():
"""다중 서버 일괄 연결 테스트"""
try:
data = request.json
server_ids = data.get('server_ids', [])
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
try:
client = DellRedfishClient(server.ip_address, server.username, server.password)
if client.check_connection():
server.status = 'online'
server.last_connected = datetime.utcnow()
results.append({
'server_id': server.id,
'server_name': server.name,
'success': True,
'message': '연결 성공'
})
else:
server.status = 'offline'
results.append({
'server_id': server.id,
'server_name': server.name,
'success': False,
'message': '연결 실패'
})
except Exception as e:
server.status = 'offline'
results.append({
'server_id': server.id,
'server_name': server.name,
'success': False,
'message': str(e)
})
db.session.commit()
success_count = sum(1 for r in results if r['success'])
return jsonify({
'success': True,
'results': results,
'summary': {
'total': len(results),
'success': success_count,
'failed': len(results) - success_count
}
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/servers/<int:server_id>/firmware', methods=['GET'])
def get_server_firmware(server_id):
"""단일 서버 펌웨어 버전 조회"""
try:
server = IdracServer.query.get(server_id)
if not server:
return jsonify({
'success': False,
'message': '서버를 찾을 수 없습니다'
})
client = DellRedfishClient(server.ip_address, server.username, server.password)
inventory = client.get_firmware_inventory()
# BIOS 버전 업데이트
for item in inventory:
if 'BIOS' in item.get('Name', ''):
server.current_bios = item.get('Version')
break
db.session.commit()
return jsonify({
'success': True,
'data': inventory
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
# ========================================
# 멀티 서버 펌웨어 업로드 API
# ========================================
@idrac_bp.route('/api/upload-multi', methods=['POST'])
def upload_multi():
"""다중 서버에 펌웨어 일괄 업로드"""
try:
if 'file' not in request.files:
return jsonify({
'success': False,
'message': '파일이 없습니다'
})
file = request.files['file']
server_ids_str = request.form.get('server_ids')
if not server_ids_str:
return jsonify({
'success': False,
'message': '서버를 선택하세요'
})
server_ids = [int(x) for x in server_ids_str.split(',')]
if file.filename == '':
return jsonify({
'success': False,
'message': '파일이 선택되지 않았습니다'
})
if not allowed_file(file.filename):
return jsonify({
'success': False,
'message': '허용되지 않은 파일 형식입니다 (.exe, .bin만 가능)'
})
# 로컬에 임시 저장
filename = secure_filename(file.filename)
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
local_path = os.path.join(UPLOAD_FOLDER, filename)
file.save(local_path)
# 백그라운드 스레드로 업로드 시작
from backend.services import watchdog_handler
socketio = watchdog_handler.socketio
def upload_to_servers():
results = []
for idx, server_id in enumerate(server_ids):
server = IdracServer.query.get(server_id)
if not server:
continue
try:
# 진행 상황 전송
if socketio:
socketio.emit('upload_progress', {
'server_id': server.id,
'server_name': server.name,
'status': 'uploading',
'progress': 0,
'message': '업로드 시작...'
})
server.status = 'updating'
db.session.commit()
client = DellRedfishClient(server.ip_address, server.username, server.password)
result = client.upload_firmware_staged(local_path)
if result['success']:
server.status = 'online'
server.last_updated = datetime.utcnow()
results.append({
'server_id': server.id,
'server_name': server.name,
'success': True,
'job_id': result['job_id'],
'message': '업로드 완료'
})
if socketio:
socketio.emit('upload_progress', {
'server_id': server.id,
'server_name': server.name,
'status': 'completed',
'progress': 100,
'message': '업로드 완료',
'job_id': result['job_id']
})
else:
server.status = 'online'
results.append({
'server_id': server.id,
'server_name': server.name,
'success': False,
'message': result.get('message', '업로드 실패')
})
if socketio:
socketio.emit('upload_progress', {
'server_id': server.id,
'server_name': server.name,
'status': 'failed',
'progress': 0,
'message': result.get('message', '업로드 실패')
})
db.session.commit()
except Exception as e:
server.status = 'online'
db.session.commit()
results.append({
'server_id': server.id,
'server_name': server.name,
'success': False,
'message': str(e)
})
if socketio:
socketio.emit('upload_progress', {
'server_id': server.id,
'server_name': server.name,
'status': 'failed',
'progress': 0,
'message': str(e)
})
# 최종 결과 전송
if socketio:
success_count = sum(1 for r in results if r['success'])
socketio.emit('upload_complete', {
'results': results,
'summary': {
'total': len(results),
'success': success_count,
'failed': len(results) - success_count
}
})
# 스레드 시작
thread = threading.Thread(target=upload_to_servers)
thread.daemon = True
thread.start()
return jsonify({
'success': True,
'message': f'{len(server_ids)}대 서버에 업로드 시작',
'filename': filename
})
except Exception as e:
return jsonify({
'success': False,
'message': f'업로드 오류: {str(e)}'
})
# ========================================
# 기존 단일 서버 API (호환성 유지)
# ========================================
@idrac_bp.route('/api/files/local', methods=['GET'])
def list_local_files():
"""로컬에 저장된 DUP 파일 목록"""
try:
files = []
if os.path.exists(UPLOAD_FOLDER):
for filename in os.listdir(UPLOAD_FOLDER):
filepath = os.path.join(UPLOAD_FOLDER, filename)
if os.path.isfile(filepath):
file_size = os.path.getsize(filepath)
files.append({
'name': filename,
'size': file_size,
'size_mb': round(file_size / (1024 * 1024), 2),
'uploaded_at': datetime.fromtimestamp(
os.path.getmtime(filepath)
).strftime('%Y-%m-%d %H:%M:%S')
})
return jsonify({
'success': True,
'files': files
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/files/local/<filename>', methods=['DELETE'])
def delete_local_file(filename):
"""로컬 DUP 파일 삭제"""
try:
filepath = os.path.join(UPLOAD_FOLDER, secure_filename(filename))
if os.path.exists(filepath):
os.remove(filepath)
return jsonify({
'success': True,
'message': f'{filename} 삭제 완료'
})
else:
return jsonify({
'success': False,
'message': '파일을 찾을 수 없습니다'
})
except Exception as e:
return jsonify({
'success': False,
'message': f'삭제 오류: {str(e)}'
})
# ========================================
# 서버 재부팅 API (멀티)
# ========================================
@idrac_bp.route('/api/servers/reboot-multi', methods=['POST'])
def reboot_servers_multi():
"""선택한 서버들 일괄 재부팅"""
try:
data = request.json
server_ids = data.get('server_ids', [])
reboot_type = data.get('type', 'GracefulRestart')
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
try:
client = DellRedfishClient(server.ip_address, server.username, server.password)
result = client.reboot_server(reboot_type)
if result:
results.append({
'server_id': server.id,
'server_name': server.name,
'success': True,
'message': '재부팅 시작'
})
else:
results.append({
'server_id': server.id,
'server_name': server.name,
'success': False,
'message': '재부팅 실패'
})
except Exception as e:
results.append({
'server_id': server.id,
'server_name': server.name,
'success': False,
'message': str(e)
})
success_count = sum(1 for r in results if r['success'])
return jsonify({
'success': True,
'results': results,
'summary': {
'total': len(results),
'success': success_count,
'failed': len(results) - success_count
}
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
# Blueprint 등록 함수
def register_idrac_routes(app):
"""
iDRAC 멀티 서버 관리 Blueprint 등록
Args:
app: Flask 애플리케이션 인스턴스
"""
# uploads 디렉토리 생성
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
# Blueprint 등록
app.register_blueprint(idrac_bp)
# CSRF 보호 제외 - iDRAC API 엔드포인트
try:
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect()
# API 엔드포인트는 CSRF 검증 제외
csrf.exempt(idrac_bp)
except:
pass # CSRF 설정 실패해도 계속 진행
# DB 테이블 생성
with app.app_context():
db.create_all()

View File

@@ -0,0 +1,399 @@
"""
펌웨어 버전 비교 API 코드
idrac_routes.py 파일의 register_idrac_routes 함수 위에 추가하세요
"""
# ========================================
# 펌웨어 버전 관리 API
# ========================================
@idrac_bp.route('/api/firmware-versions', methods=['GET'])
def get_firmware_versions():
"""등록된 최신 펌웨어 버전 목록"""
try:
server_model = request.args.get('model') # 서버 모델 필터
query = FirmwareVersion.query.filter_by(is_active=True)
if server_model:
# 특정 모델 또는 범용
query = query.filter(
(FirmwareVersion.server_model == server_model) |
(FirmwareVersion.server_model == None)
)
versions = query.order_by(FirmwareVersion.component_name).all()
return jsonify({
'success': True,
'versions': [v.to_dict() for v in versions]
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/firmware-versions', methods=['POST'])
def add_firmware_version():
"""최신 펌웨어 버전 등록"""
try:
data = request.json
# 필수 필드 확인
if not all([data.get('component_name'), data.get('latest_version')]):
return jsonify({
'success': False,
'message': '컴포넌트명과 버전을 입력하세요'
})
# 중복 확인 (같은 컴포넌트, 같은 모델)
existing = FirmwareVersion.query.filter_by(
component_name=data['component_name'],
server_model=data.get('server_model')
).first()
if existing:
return jsonify({
'success': False,
'message': f'이미 등록된 컴포넌트입니다'
})
# 버전 생성
version = FirmwareVersion(
component_name=data['component_name'],
component_type=data.get('component_type'),
vendor=data.get('vendor'),
server_model=data.get('server_model'),
latest_version=data['latest_version'],
release_date=data.get('release_date'),
download_url=data.get('download_url'),
file_name=data.get('file_name'),
file_size_mb=data.get('file_size_mb'),
notes=data.get('notes'),
is_critical=data.get('is_critical', False)
)
db.session.add(version)
db.session.commit()
return jsonify({
'success': True,
'message': f'{version.component_name} 버전 정보 등록 완료',
'version': version.to_dict()
})
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/firmware-versions/<int:version_id>', methods=['PUT'])
def update_firmware_version(version_id):
"""펌웨어 버전 정보 수정"""
try:
version = FirmwareVersion.query.get(version_id)
if not version:
return jsonify({
'success': False,
'message': '버전 정보를 찾을 수 없습니다'
})
data = request.json
# 업데이트
if 'component_name' in data:
version.component_name = data['component_name']
if 'latest_version' in data:
version.latest_version = data['latest_version']
if 'release_date' in data:
version.release_date = data['release_date']
if 'download_url' in data:
version.download_url = data['download_url']
if 'file_name' in data:
version.file_name = data['file_name']
if 'notes' in data:
version.notes = data['notes']
if 'is_critical' in data:
version.is_critical = data['is_critical']
db.session.commit()
return jsonify({
'success': True,
'message': '버전 정보 수정 완료',
'version': version.to_dict()
})
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/firmware-versions/<int:version_id>', methods=['DELETE'])
def delete_firmware_version(version_id):
"""펌웨어 버전 정보 삭제"""
try:
version = FirmwareVersion.query.get(version_id)
if not version:
return jsonify({
'success': False,
'message': '버전 정보를 찾을 수 없습니다'
})
version.is_active = False
db.session.commit()
return jsonify({
'success': True,
'message': f'{version.component_name} 버전 정보 삭제 완료'
})
except Exception as e:
db.session.rollback()
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/servers/<int:server_id>/firmware/compare', methods=['GET'])
def compare_server_firmware(server_id):
"""
서버 펌웨어 버전 비교
현재 버전과 최신 버전을 비교하여 업데이트 필요 여부 확인
"""
try:
server = IdracServer.query.get(server_id)
if not server:
return jsonify({
'success': False,
'message': '서버를 찾을 수 없습니다'
})
# 현재 펌웨어 조회
client = DellRedfishClient(server.ip_address, server.username, server.password)
current_inventory = client.get_firmware_inventory()
# 최신 버전 정보 조회
latest_versions = FirmwareVersion.query.filter_by(is_active=True).all()
# 비교 결과
comparisons = []
for current_fw in current_inventory:
component_name = current_fw['Name']
current_version = current_fw['Version']
# 최신 버전 찾기 (컴포넌트명 매칭)
latest = None
for lv in latest_versions:
if lv.component_name.lower() in component_name.lower():
# 서버 모델 확인
if not lv.server_model or lv.server_model == server.model:
latest = lv
break
# 비교
comparison = FirmwareComparisonResult(
component_name=component_name,
current_version=current_version,
latest_version=latest.latest_version if latest else None
)
result = comparison.to_dict()
# 추가 정보
if latest:
result['latest_info'] = {
'release_date': latest.release_date,
'download_url': latest.download_url,
'file_name': latest.file_name,
'is_critical': latest.is_critical,
'notes': latest.notes
}
comparisons.append(result)
# 통계
total = len(comparisons)
outdated = len([c for c in comparisons if c['status'] == 'outdated'])
latest_count = len([c for c in comparisons if c['status'] == 'latest'])
unknown = len([c for c in comparisons if c['status'] == 'unknown'])
return jsonify({
'success': True,
'server': {
'id': server.id,
'name': server.name,
'model': server.model
},
'comparisons': comparisons,
'summary': {
'total': total,
'outdated': outdated,
'latest': latest_count,
'unknown': unknown
}
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
@idrac_bp.route('/api/servers/firmware/compare-multi', methods=['POST'])
def compare_multi_servers_firmware():
"""
여러 서버의 펌웨어 버전 비교
"""
try:
data = request.json
server_ids = data.get('server_ids', [])
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
try:
# 각 서버 비교
client = DellRedfishClient(server.ip_address, server.username, server.password)
current_inventory = client.get_firmware_inventory()
latest_versions = FirmwareVersion.query.filter_by(is_active=True).all()
outdated_count = 0
outdated_items = []
for current_fw in current_inventory:
component_name = current_fw['Name']
current_version = current_fw['Version']
# 최신 버전 찾기
for lv in latest_versions:
if lv.component_name.lower() in component_name.lower():
comparison = FirmwareComparisonResult(
component_name=component_name,
current_version=current_version,
latest_version=lv.latest_version
)
if comparison.status == 'outdated':
outdated_count += 1
outdated_items.append({
'component': component_name,
'current': current_version,
'latest': lv.latest_version
})
break
results.append({
'server_id': server.id,
'server_name': server.name,
'outdated_count': outdated_count,
'outdated_items': outdated_items,
'status': 'needs_update' if outdated_count > 0 else 'up_to_date'
})
except Exception as e:
results.append({
'server_id': server.id,
'server_name': server.name,
'error': str(e)
})
return jsonify({
'success': True,
'results': results
})
except Exception as e:
return jsonify({
'success': False,
'message': f'오류: {str(e)}'
})
# ========================================
# 초기 데이터 생성 함수
# ========================================
def init_firmware_versions():
"""초기 펌웨어 버전 데이터 생성"""
initial_versions = [
{
'component_name': 'BIOS',
'component_type': 'Firmware',
'vendor': 'Dell',
'server_model': 'PowerEdge R750',
'latest_version': '2.15.0',
'release_date': '2024-01-15',
'notes': 'PowerEdge R750 최신 BIOS'
},
{
'component_name': 'iDRAC',
'component_type': 'Firmware',
'vendor': 'Dell',
'latest_version': '6.10.30.00',
'release_date': '2024-02-20',
'notes': 'iDRAC9 최신 펌웨어 (모든 모델 공용)'
},
{
'component_name': 'PERC H755',
'component_type': 'Firmware',
'vendor': 'Dell',
'server_model': 'PowerEdge R750',
'latest_version': '25.5.9.0001',
'release_date': '2024-01-10',
'notes': 'PERC H755 RAID 컨트롤러'
},
{
'component_name': 'BIOS',
'component_type': 'Firmware',
'vendor': 'Dell',
'server_model': 'PowerEdge R640',
'latest_version': '2.19.2',
'release_date': '2024-02-01',
'notes': 'PowerEdge R640 최신 BIOS'
},
{
'component_name': 'CPLD',
'component_type': 'Firmware',
'vendor': 'Dell',
'latest_version': '1.0.6',
'release_date': '2023-12-15',
'notes': '시스템 보드 CPLD (14G/15G 공용)'
},
]
for data in initial_versions:
# 중복 체크
existing = FirmwareVersion.query.filter_by(
component_name=data['component_name'],
server_model=data.get('server_model')
).first()
if not existing:
version = FirmwareVersion(**data)
db.session.add(version)
try:
db.session.commit()
print("✓ 초기 펌웨어 버전 데이터 생성 완료")
except:
db.session.rollback()
print("⚠ 초기 데이터 생성 중 오류 (이미 있을 수 있음)")