from flask import Blueprint, render_template, request, jsonify, send_file, current_app from app.utils.ceph_calculator import calculate_ceph_capacity from app.utils.pdf_generator import generate_pdf_report from io import BytesIO import logging import traceback logger = logging.getLogger(__name__) bp = Blueprint('main', __name__) @bp.route('/', methods=['GET']) def index(): return render_template('index.html') @bp.route('/calculate', methods=['POST']) def calculate(): try: data = request.json if not data: return jsonify({"error": "No data received"}), 400 replication_type = data.get('replication_type') # 'replication' or 'erasure_coding' if not replication_type: return jsonify({"error": "Replication type missing"}), 400 # Basic parameter validation try: replicas = int(data.get('replicas', 3)) # Default value: 3 replicas min_size = int(data.get('min_size', 2)) # Default value: 2 # For Erasure Coding k = int(data.get('k', 0)) if replication_type == 'erasure_coding' else 0 m = int(data.get('m', 0)) if replication_type == 'erasure_coding' else 0 # Nodes and OSDs nodes = data.get('nodes', []) if not nodes: return jsonify({"error": "No nodes defined"}), 400 # Validate node data format for node in nodes: if 'osd_count' not in node or 'osd_size_gb' not in node: return jsonify({"error": "Invalid node data format. Each node must have osd_count and osd_size_gb properties."}), 400 # Simple validity check if int(node.get('osd_count', 0)) <= 0 or float(node.get('osd_size_gb', 0)) <= 0: return jsonify({"error": "Invalid OSD data. Both count and size must be greater than 0."}), 400 # Storage unit storage_unit = data.get('storage_unit', 'GB') if storage_unit not in ['GB', 'TB']: storage_unit = 'GB' # Default fallback except (ValueError, TypeError) as e: return jsonify({"error": f"Invalid parameter: {str(e)}"}), 400 # Perform calculation result = calculate_ceph_capacity( replication_type=replication_type, replicas=replicas, k=k, m=m, nodes=nodes, min_size=min_size, storage_unit=storage_unit ) logger.info(f"Calculation performed successfully: {replication_type}, replicas={replicas}, nodes={len(nodes)}") return jsonify(result) except ValueError as e: logger.error(f"Validation error during calculation: {str(e)}") return jsonify({"error": str(e)}), 400 except Exception as e: logger.error(f"Error during calculation: {str(e)}\n{traceback.format_exc()}") return jsonify({"error": "An unexpected error occurred"}), 500 @bp.route('/generate-pdf', methods=['POST']) def generate_pdf(): try: data = request.get_json() if not data: return jsonify({"error": "No data received"}), 400 # Validate data if not data.get('replication_type'): return jsonify({"error": "Replication type missing"}), 400 if not data.get('nodes') or not isinstance(data['nodes'], list): return jsonify({"error": "Invalid node data"}), 400 if not data.get('result') or not isinstance(data['result'], dict): return jsonify({"error": "Invalid result data"}), 400 if not data['result'].get('raw_total'): return jsonify({"error": "No calculation results available"}), 400 # Generate PDF pdf_bytes = generate_pdf_report(data) if not pdf_bytes: return jsonify({"error": "PDF generation failed"}), 500 return send_file( BytesIO(pdf_bytes), mimetype='application/pdf', as_attachment=True, download_name='ceph-report.pdf' ) except ValueError as e: logger.error(f"Validation error during PDF generation: {str(e)}") return jsonify({"error": str(e)}), 400 except Exception as e: logger.error(f"Error during PDF generation: {str(e)}\n{traceback.format_exc()}") return jsonify({"error": "An unexpected error occurred"}), 500