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