114 lines
4.5 KiB
Python
114 lines
4.5 KiB
Python
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 |