Erstelle die Grundstruktur für die Ceph Max Storage Rechner-Anwendung mit Flask und HTMX. Füge Konfigurationsdateien, Routen, Modelle, Templates und statische Dateien hinzu. Implementiere grundlegende Funktionen zur Berechnung der Speichernutzung und zur PDF-Generierung. Integriere CSRF-Schutz und Logging. Stelle sicher, dass die Anwendung modular und wartbar ist.

This commit is contained in:
Samuel Müller
2025-03-26 08:40:32 +01:00
commit f00c1f48e9
26 changed files with 1744 additions and 0 deletions

View File

@ -0,0 +1,204 @@
def calculate_ceph_capacity(replication_type, replicas=3, k=0, m=0, nodes=None, min_size=2, storage_unit='GB'):
"""
Berechnet die maximal zulässige Speichernutzung für ein Ceph-Cluster unter Berücksichtigung von Nodeausfällen.
Parameter:
- replication_type: 'replication' oder 'erasure_coding'
- replicas: Anzahl der Replikate (Standard: 3)
- k: Anzahl der Datenchunks für EC
- m: Anzahl der Codierungschunks für EC
- nodes: Liste der Knoten mit Anzahl der OSDs und deren Größe
[{'osd_count': 4, 'osd_size_gb': 1000}, ...]
- min_size: Minimale Anzahl von Replikaten für I/O-Operationen (Standard: 2)
- storage_unit: 'GB' oder 'TB' (Standard: 'GB')
Returns:
- Dictionary mit max_usage_percent, max_usage_gb, max_usage_tb, raw_total und zusätzlichen Informationen zur Ausfallsicherheit
"""
if nodes is None or len(nodes) == 0:
return {
'max_usage_percent': 0,
'max_usage_gb': 0,
'max_usage_tb': 0,
'raw_total': 0,
'node_failure_tolerance': False,
'node_failure_info': 'Keine Nodes im Cluster',
'storage_unit': storage_unit
}
# Die Umrechnung von TB zu GB ist bereits im JavaScript-Code erfolgt
# Hier werden die Werte direkt verwendet
# Berechne den gesamten Rohspeicher und Informationen zu jedem Node
raw_total_gb = 0
node_capacities = []
for node_config in nodes:
osd_count = int(node_config.get('osd_count', 0))
osd_size_gb = float(node_config.get('osd_size_gb', 0))
node_capacity = osd_count * osd_size_gb
node_capacities.append(node_capacity)
raw_total_gb += node_capacity
# Größter Node (worst-case Szenario bei Ausfall)
largest_node_capacity = max(node_capacities) if node_capacities else 0
# Berechne die nutzbare Kapazität ohne Ausfall
if replication_type == 'replication':
# Bei Replikation ist der nutzbare Speicher = Rohspeicher / Anzahl der Replikate
usable_capacity_gb = raw_total_gb / replicas
else: # Erasure Coding
# Bei EC ist der nutzbare Speicher = Rohspeicher * (k / (k + m))
usable_capacity_gb = raw_total_gb * (k / (k + m))
# Die empfohlene maximale Auslastung für den Normalfall (ohne Ausfall)
max_recommended_usage_percent = 80
# Berechne die OSD-Auslastung nach der Formel x = (s × p) / (s + 1)
# wobei s = Anzahl der OSDs pro Server und p = Prozentsatz der Gesamtauslastung
osds_per_server = nodes[0].get('osd_count', 0) if nodes else 0
osd_usage_percent = (osds_per_server * max_recommended_usage_percent) / (osds_per_server + 1)
# Finde die größte OSD-Größe für die Berechnung der Kapazität nach OSD-Ausfall
largest_osd_size = max((node.get('osd_size_gb', 0) for node in nodes), default=0)
# Berechne die nutzbare Kapazität nach OSD-Ausfall
raw_after_osd_failure = raw_total_gb - largest_osd_size
if replication_type == 'replication':
usable_after_osd_failure = raw_after_osd_failure / replicas
else:
usable_after_osd_failure = raw_after_osd_failure * (k / (k + m))
# Berechne die maximale sichere Nutzung unter Berücksichtigung von OSD-Ausfall
max_usage_gb = min(
usable_capacity_gb * (osd_usage_percent / 100), # OSD-Auslastung basierend auf der Formel
usable_after_osd_failure * 0.8 # 80% der Kapazität nach OSD-Ausfall
)
max_usage_tb = max_usage_gb / 1024
# Berechnung der Ausfalltoleranz unter Berücksichtigung von min_size
if replication_type == 'replication':
max_failure_nodes = min(
len(nodes) - min_size, # Maximale Ausfälle basierend auf min_size
replicas - min_size # Maximale Ausfälle basierend auf Replikationsfaktor
)
else: # Erasure Coding
max_failure_nodes = min(
len(nodes) - (k + 1), # Mindestens k+1 Nodes müssen verfügbar bleiben
m # Maximale Anzahl von Codierungschunks die ausfallen können
)
# Sortiere die Nodes nach Größe absteigend für Worst-Case-Analyse
node_capacities_sorted = sorted(node_capacities, reverse=True)
# Kapazität nach Ausfall der größten N Nodes
raw_after_max_failures_gb = raw_total_gb
for i in range(min(max_failure_nodes, len(node_capacities_sorted))):
raw_after_max_failures_gb -= node_capacities_sorted[i]
# Nutzbare Kapazität nach maximalen tolerierbaren Ausfällen
if replication_type == 'replication':
usable_after_max_failures_gb = raw_after_max_failures_gb / min_size
else: # Erasure Coding
remaining_m = m - max_failure_nodes
usable_after_max_failures_gb = raw_after_max_failures_gb * (k / (k + remaining_m))
# Berechne die nutzbare Kapazität nach Ausfall des größten Nodes
raw_after_failure_gb = raw_total_gb - largest_node_capacity
if replication_type == 'replication':
usable_after_failure_gb = raw_after_failure_gb / min_size
else: # Erasure Coding
usable_after_failure_gb = raw_after_failure_gb * (k / (k + m))
# Prüfen, ob genügend Speicherplatz nach einem Nodeausfall vorhanden ist
node_failure_tolerance = True
# Prüfe die Mindestanforderungen für Nodes
if replication_type == 'replication':
if len(nodes) < min_size:
node_failure_tolerance = False
elif usable_after_failure_gb < max_usage_gb:
node_failure_tolerance = False
else: # Erasure Coding
if len(nodes) <= k:
node_failure_tolerance = False
elif usable_after_failure_gb < max_usage_gb:
node_failure_tolerance = False
# Für multiple Ausfälle prüfen
multi_failure_tolerance = False
if max_failure_nodes > 0:
multi_failure_tolerance = (
usable_after_max_failures_gb >= max_usage_gb and
len(nodes) > max_failure_nodes
)
# Maximale sichere Nutzung unter Berücksichtigung eines möglichen Nodeausfalls
safe_usage_percent = 0
safe_usage_gb = 0
safe_usage_tb = 0
node_failure_info = ""
if node_failure_tolerance:
safe_usage_percent = max_recommended_usage_percent
safe_usage_gb = max_usage_gb
safe_usage_tb = max_usage_tb
if multi_failure_tolerance and max_failure_nodes > 1:
node_failure_info = f"Der Cluster kann den Ausfall von bis zu {max_failure_nodes} Nodes tolerieren (min_size={min_size})."
else:
node_failure_info = f"Der Cluster kann einen Ausfall des größten Nodes tolerieren (min_size={min_size})."
else:
safe_usage_percent = round((usable_after_failure_gb / usable_capacity_gb) * 100 * 0.8)
safe_usage_gb = usable_after_failure_gb * 0.8
safe_usage_tb = safe_usage_gb / 1024
if len(nodes) <= (min_size if replication_type == 'replication' else k + m - min(m, 1)):
node_failure_info = f"KRITISCH: Zu wenige Nodes ({len(nodes)}) für die konfigurierte min_size={min_size}. "
node_failure_info += f"Mindestens {min_size + 1 if replication_type == 'replication' else k + m + 1 - min(m, 1)} Nodes benötigt."
else:
# Unit für die Anzeige
unit_display = "TB" if storage_unit == "TB" else "GB"
node_size_display = round(largest_node_capacity / 1024, 2) if storage_unit == "TB" else round(largest_node_capacity, 2)
node_failure_info = (f"WARNUNG: Der Cluster hat nicht genügend freie Kapazität, um einen Ausfall des größten Nodes "
f"({node_size_display} {unit_display}) zu tolerieren. "
f"Maximale sichere Nutzung: {safe_usage_percent}%")
# Berechnung für den Ausfall eines einzelnen OSD
osd_failure_tolerance = False
osd_failure_info = "Keine OSDs im Cluster"
if nodes and any(node.get('osd_count', 0) > 0 for node in nodes):
osd_failure_tolerance = usable_after_osd_failure >= max_usage_gb
# Unit für die Anzeige
unit_display = "TB" if storage_unit == "TB" else "GB"
osd_size_display = round(largest_osd_size / 1024, 2) if storage_unit == "TB" else round(largest_osd_size, 2)
osd_failure_info = f"Der Cluster kann den Ausfall des größten OSD tolerieren (min_size={min_size})." if osd_failure_tolerance else \
f"WARNUNG: Der Cluster hat nicht genügend freie Kapazität, um den Ausfall des größten OSD ({osd_size_display} {unit_display}) zu tolerieren."
# Rückgabewerte
result = {
'max_usage_percent': round(osd_usage_percent, 2),
'max_usage_gb': round(max_usage_gb if node_failure_tolerance else safe_usage_gb, 2),
'max_usage_tb': round(max_usage_tb if node_failure_tolerance else safe_usage_tb, 2),
'raw_total': round(raw_total_gb, 2),
'node_failure_tolerance': node_failure_tolerance,
'node_failure_info': node_failure_info,
'multi_failure_tolerance': multi_failure_tolerance,
'max_failure_nodes': max_failure_nodes,
'osd_failure_tolerance': osd_failure_tolerance,
'osd_failure_info': osd_failure_info,
'largest_node_gb': round(largest_node_capacity, 2),
'raw_after_failure_gb': round(raw_after_failure_gb, 2),
'usable_after_failure_gb': round(usable_after_failure_gb, 2),
'raw_after_max_failures_gb': round(raw_after_max_failures_gb, 2),
'usable_after_max_failures_gb': round(usable_after_max_failures_gb, 2),
'min_size': min_size,
'osds_per_server': osds_per_server,
'storage_unit': storage_unit
}
return result