From a9f6ca7cf88a221cc9258879125e6cb92ccfaddd Mon Sep 17 00:00:00 2001 From: Gabriel Mougard Date: Thu, 5 Dec 2024 11:45:32 +0100 Subject: [PATCH] cmd/microcloud: detect local net interface collisions within a member and global subnet collisions in a cluster of members * If a member have multiple network types going through the same interface, show a warning. * If multiple subnet types are detected to use the same subnet at a cluster level, show a warning. Signed-off-by: Gabriel Mougard --- cmd/microcloud/main_init.go | 74 +++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/cmd/microcloud/main_init.go b/cmd/microcloud/main_init.go index 94851554..ed828964 100644 --- a/cmd/microcloud/main_init.go +++ b/cmd/microcloud/main_init.go @@ -515,15 +515,81 @@ func validateGatewayNet(config map[string]string, ipPrefix string, cidrValidator } func (c *initConfig) validateSystems(s *service.Handler) (err error) { - for _, sys := range c.systems { + // subnetsCollisionMap maps a subnet CIDR notation to a map of subnet type to peer names. + // + // Example: + // { + // "10.0.1.0/24": { + // "OVN underlay": ["system1", "system2"], + // "Ceph cluster network": ["system3"] + // }, + // "10.0.2.0/24": { + // "Ceph public network": ["system1", "system2", "system3"], + // } + // } + // + // In this example, we have a collision for the subnet "10.0.1.0/24": + // - system1 and system2 are using it for the OVN underlay, whereas system3 is using it for the Ceph cluster network. + // Everything is fine for the subnet "10.0.2.0/24" as all systems are using it for the Ceph public network. + subnetsCollisionMap := make(map[string]map[string][]string) + for peer, sys := range c.systems { if sys.MicroCephInternalNetwork == nil || sys.OVNGeneveNetwork == nil { continue } - if sys.MicroCephInternalNetwork.Subnet.Contains(sys.OVNGeneveNetwork.IP) { - fmt.Printf("Warning: OVN underlay IP (%s) is shared with the Ceph cluster network (%s)\n", sys.OVNGeneveNetwork.IP.String(), sys.MicroCephInternalNetwork.Subnet.String()) + // We also check for interface collisions at a local level. + // This means that we check if the same interface is used for multiple networks in a member. + netTypeToNet := map[string]*Network{ + "Ceph cluster network": sys.MicroCephInternalNetwork, + "OVN underlay": sys.OVNGeneveNetwork, + } - break + // The public Ceph network is not always present (partially disaggregated setup) but we still + // wish to check for interface collisions between OVN and the Ceph cluster network. + // If it is present though, add it to the list of interfaces to check for collisions. + if sys.MicroCephPublicNetwork != nil { + netTypeToNet["Ceph public network"] = sys.MicroCephPublicNetwork + } + + // This is checking the local collisions (whether the same interface is used for multiple networks in a member). + for typeLeft, netLeft := range netTypeToNet { + for typeRight, netRight := range netTypeToNet { + // Skip self-comparison and already processed pairs + // (e.g. if we already compared "A vs B", we don't need to compare "B vs A"). + if typeLeft >= typeRight { + continue + } + + if netLeft.Interface == netRight.Interface { + fmt.Printf("Warning: %s is shared on the same network interface %q with the %s\n", typeLeft, netLeft.Interface, typeRight) + } + + // Populate the subnetsCollisionMap with the subnet type and the peer using it. + subnetTypeToPeers, ok := subnetsCollisionMap[netLeft.Subnet.String()] + if !ok { + subnetTypeToPeers = make(map[string][]string) + subnetTypeToPeers[typeLeft] = make([]string, 0) + subnetTypeToPeers[typeLeft] = append(subnetTypeToPeers[typeLeft], peer) + subnetsCollisionMap[netLeft.Subnet.String()] = subnetTypeToPeers + } else { + subnetTypeToPeers[typeLeft] = append(subnetTypeToPeers[typeLeft], peer) + } + } + } + } + + // Check for subnet collisions at the cluster level. + for subnet, subnetTypeToPeers := range subnetsCollisionMap { + // If there are multiple subnet types on the same subnet, we have a collision. + if len(subnetTypeToPeers) > 1 { + var sb strings.Builder + sb.WriteString("WARNING: Subnet collision detected:\n") + for subnetType, peers := range subnetTypeToPeers { + sb.WriteString(fmt.Sprintf("- Members %s are using the %q subnet for %s\n", strings.Join(peers, ", "), subnet, subnetType)) + } + + sb.WriteString("Please ensure that each subnet is used for a single network type.") + fmt.Printf("%s\n", sb.String()) } }