Source code for eclypse.builders.infrastructure.generators.fat_tree

"""Fat-Tree infrastructure generator.

This module provides a factory function to instantiate a Fat-Tree network topology
commonly used in data center environments. The topology includes core, aggregation,
and edge switches, as well as hosts connected to edge switches.

The size and structure of the topology are determined by the `k` parameter, which must
be an even number. The number of pods is equal to `k`, and the total number of hosts
is `k^3 / 4`.

The implementation follows the definition from:
Mohammad Al-Fares et al. "A Scalable, Commodity Data Center Network Architecture."
ACM SIGCOMM CCR, 2008, https://dl.acm.org/doi/10.1145/1402958.1402967
"""

from __future__ import annotations

from typing import (
    TYPE_CHECKING,
)

from eclypse.graph import Infrastructure

if TYPE_CHECKING:
    from collections.abc import Callable

    import networkx as nx

    from eclypse.graph.assets import Asset
    from eclypse.utils.types import (
        InitPolicy,
        UpdatePolicies,
    )


[docs] def get_fat_tree( k: int, infrastructure_id: str = "fat_tree", update_policies: UpdatePolicies = None, node_assets: dict[str, Asset] | None = None, link_assets: dict[str, Asset] | None = None, include_default_assets: bool = False, strict: bool = False, resource_init: InitPolicy = "max", path_algorithm: Callable[[nx.Graph, str, str], list[str]] | None = None, seed: int | None = None, ) -> Infrastructure: """Factory for generating a Fat-Tree network topology. This function builds a symmetrical Fat-Tree structure with parameter `k`, used to simulate scalable data center topologies. The resulting Infrastructure includes core, aggregation, and edge switches, as well as connected hosts. Args: k (int): The Fat-Tree parameter, must be an even number. Determines the size and structure of the Fat-Tree topology. infrastructure_id (str): Unique ID for the infrastructure instance.\ Defaults to "fat_tree". update_policies (Callable | list[Callable] | None): Graph update policies.\ Defaults to None. node_assets (dict[str, Asset] | None): Default attributes for all nodes.\ Defaults to None. link_assets (dict[str, Asset] | None): Default attributes for all links.\ Defaults to None. include_default_assets (bool): Whether to include default assets. \ Defaults to False. strict (bool): If True, raises an error if the asset values are not \ consistent with their spaces. Defaults to False. resource_init (InitPolicy): Initialization policy for resources. \ Defaults to "max". path_algorithm (Callable[[nx.Graph, str, str], list[str]] | None): \ Algorithm to compute paths. Defaults to None. Defaults to None. seed (int | None): Seed for random number generation. Defaults to None. Returns: Infrastructure: A Fat-Tree topology with switches and hosts. """ if k % 2 != 0: raise ValueError(f"k must be an even number (got {k}) for a Fat-Tree topology.") infra = Infrastructure( infrastructure_id=infrastructure_id, update_policies=update_policies, node_assets=node_assets, edge_assets=link_assets, include_default_assets=include_default_assets, resource_init=resource_init, path_algorithm=path_algorithm, seed=seed, ) num_pods = k num_core_switches = (num_pods // 2) ** 2 num_agg_switches_per_pod = num_pods // 2 num_edge_switches_per_pod = num_pods // 2 num_hosts_per_edge = num_pods // 2 # Core switches for i in range(num_core_switches): core_id = f"core_{i}" infra.add_node(core_id, strict=strict) # Pods for pod in range(num_pods): # Aggregation switches agg_switches = [] for a in range(num_agg_switches_per_pod): agg_id = f"agg_{pod}_{a}" agg_switches.append(agg_id) infra.add_node(agg_id, strict=strict) # Edge switches + hosts for e in range(num_edge_switches_per_pod): edge_id = f"edge_{pod}_{e}" infra.add_node(edge_id, strict=strict) # Edge <-> Aggregation for agg_id in agg_switches: infra.add_edge(edge_id, agg_id, symmetric=True, strict=strict) # Hosts under edge for h in range(num_hosts_per_edge): host_id = f"host_{pod}_{e}_{h}" infra.add_node(host_id, strict=strict) infra.add_edge(host_id, edge_id, symmetric=True, strict=strict) # Aggregation <-> Core for i, agg_id in enumerate(agg_switches): for j in range(num_pods // 2): core_index = i * (num_pods // 2) + j core_id = f"core_{core_index}" infra.add_edge(agg_id, core_id, symmetric=True, strict=strict) return infra