""" Dell iDRAC 멀티 서버 관리 라우트 backend/routes/idrac_routes.py - CSRF 보호 제외 추가 - 펌웨어 관리 기능 제거됨 (Server Config Analysis로 대체) """ from flask import Blueprint, request, jsonify, current_app from backend.services.idrac_redfish_client import DellRedfishClient from backend.models.idrac_server import IdracServer, db from datetime import datetime import threading # Blueprint 생성 idrac_bp = Blueprint('idrac', __name__, url_prefix='/idrac') # ======================================== # 페이지 라우트 # ======================================== @idrac_bp.route('/') def index(): """iDRAC 서버 관리 메인 페이지""" from flask import render_template return render_template('idrac_firmware.html') # ======================================== # 서버 관리 API (CRUD) # ======================================== @idrac_bp.route('/api/servers', methods=['GET']) def get_servers(): """서버 목록 조회""" try: group_filter = request.args.get('group') query = IdracServer.query.filter_by(is_active=True) if group_filter and group_filter != 'all': query = query.filter_by(group_name=group_filter) 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': str(e)}) @idrac_bp.route('/api/servers', methods=['POST']) def add_server(): """서버 추가""" try: data = request.json # 필수 필드 체크 if not all(k in data for k in ['name', 'ip_address', 'password']): return jsonify({'success': False, 'message': '필수 필드 누락'}) # 활성 서버 중복 체크 if IdracServer.query.filter_by(ip_address=data['ip_address'], is_active=True).first(): return jsonify({'success': False, 'message': '이미 등록된 IP입니다.'}) # Soft delete된 서버가 있는지 확인하고 재활성화 existing_server = IdracServer.query.filter_by(ip_address=data['ip_address'], is_active=False).first() if existing_server: # 기존 서버 재활성화 및 정보 업데이트 existing_server.is_active = True existing_server.name = data['name'] existing_server.username = data.get('username', 'root') existing_server.password = data['password'] existing_server.group_name = data.get('group_name') existing_server.model = data.get('model') existing_server.notes = data.get('notes') existing_server.status = 'registered' existing_server.updated_at = datetime.utcnow() db.session.commit() return jsonify({'success': True, 'message': '서버 재등록 완료', 'server': existing_server.to_dict()}) # 새 서버 생성 new_server = IdracServer( name=data['name'], ip_address=data['ip_address'], username=data.get('username', 'root'), password=data['password'], group_name=data.get('group_name'), model=data.get('model'), notes=data.get('notes') ) db.session.add(new_server) db.session.commit() return jsonify({'success': True, 'message': '서버 추가 완료', 'server': new_server.to_dict()}) except Exception as e: db.session.rollback() return jsonify({'success': False, 'message': str(e)}) @idrac_bp.route('/api/servers/', methods=['DELETE']) def delete_server(server_id): """서버 삭제 (Soft Delete)""" try: server = IdracServer.query.get(server_id) if not server: return jsonify({'success': False, 'message': '서버를 찾을 수 없습니다'}) # Soft delete: is_active를 False로 설정 # IP 주소는 유지하여 재등록 시 재활성화 가능 server.is_active = False server.status = 'deleted' db.session.commit() return jsonify({'success': True, 'message': '서버 삭제 완료'}) except Exception as e: db.session.rollback() return jsonify({'success': False, 'message': str(e)}) @idrac_bp.route('/api/groups', methods=['GET']) def get_groups(): """등록된 그룹 목록""" try: groups = db.session.query(IdracServer.group_name).filter( IdracServer.group_name != None, IdracServer.is_active == True ).distinct().all() return jsonify({ 'success': True, 'groups': [g[0] for g in groups if g[0]] }) except Exception as e: return jsonify({'success': False, 'message': str(e)}) # ======================================== # 연결 테스트 API # ======================================== @idrac_bp.route('/api/servers//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': '연결 성공'}) else: server.status = 'offline' db.session.commit() return jsonify({'success': False, 'message': '연결 실패'}) except Exception as e: return jsonify({'success': False, 'message': 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', []) results = [] for sid in server_ids: server = IdracServer.query.get(sid) 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': sid, 'success': True}) else: server.status = 'offline' results.append({'server_id': sid, 'success': False, 'message': 'Failed'}) except Exception as e: server.status = 'offline' results.append({'server_id': sid, 'success': False, 'message': str(e)}) db.session.commit() success = sum(1 for r in results if r['success']) return jsonify({ 'success': True, 'summary': {'success': success, 'failed': len(results)-success}, 'results': results }) except Exception as e: return jsonify({'success': False, 'message': 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') results = [] for sid in server_ids: server = IdracServer.query.get(sid) if not server: continue try: client = DellRedfishClient(server.ip_address, server.username, server.password) if client.reboot_server(reboot_type): results.append({'server_id': sid, 'success': True, 'message': 'Reboot initiated'}) else: results.append({'server_id': sid, 'success': False, 'message': 'Reboot failed'}) except Exception as e: results.append({'server_id': sid, 'success': False, 'message': str(e)}) success = sum(1 for r in results if r['success']) return jsonify({ 'success': True, 'summary': {'success': success, 'failed': len(results)-success}, 'results': results }) except Exception as e: return jsonify({'success': False, 'message': str(e)}) # ======================================== # Blueprint 등록 함수 # ======================================== def register_idrac_routes(app): """ idrac_routes 등록 및 초기화 """ # Blueprint 등록 app.register_blueprint(idrac_bp) # CSRF 제외 - app에 이미 설정된 csrf 인스턴스 사용 try: from flask_wtf.csrf import CSRFProtect # app에 이미 csrf가 설정되어 있으므로, 해당 인스턴스를 가져와서 exempt 설정 csrf = app.extensions.get('csrf') if csrf: csrf.exempt(idrac_bp) except Exception as e: app.logger.warning(f"CSRF exemption failed for idrac_bp: {e}") # DB 생성 with app.app_context(): db.create_all()