From 7efc48f922a9d2ea1003e90d90e735755d84bcc9 Mon Sep 17 00:00:00 2001 From: Gabriel Mougard Date: Mon, 10 Feb 2025 10:57:33 +0100 Subject: [PATCH] cmd/microcloud: Add the detectCollisions function This function detect the local network interface collisions and the global subet collisions Signed-off-by: Gabriel Mougard --- cmd/microcloud/main_init.go | 126 ++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/cmd/microcloud/main_init.go b/cmd/microcloud/main_init.go index f2846c093..3dfd6a7c4 100644 --- a/cmd/microcloud/main_init.go +++ b/cmd/microcloud/main_init.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "os" + "sort" "strings" "sync" "time" @@ -551,6 +552,131 @@ func validateGatewayNet(config map[string]string, ipPrefix string, cidrValidator return ovnIPRanges, nil } +// detectCollisions checks for network collisions across systems. +// This tracks subnet usage across all systems using a global map and ensure collisions are +// detected when different systems use the same subnet for different network types. +// This also flag interface collisions within each individual system. +func detectCollisions(systems map[string]InitSystem) []string { + // 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]struct{}) + interfaceCollisionMap := make(map[string]map[string]map[string]struct{}) + subnetToGlobalTypes := make(map[string]map[string]struct{}) + + sortedKeys := func(set map[string]struct{}) []string { + keys := make([]string, 0, len(set)) + for k := range set { + keys = append(keys, k) + } + + sort.Strings(keys) + return keys + } + + for sysName, sys := range systems { + netTypeToNet := map[string]*Network{ + "Ceph cluster network": sys.MicroCephInternalNetwork, + "OVN underlay network": sys.OVNGeneveNetwork, + "MicroCloud internal network": sys.MicroCloudInternalNetwork, + } + + interfaceCollisionMap[sysName] = make(map[string]map[string]struct{}) + if sys.MicroCephPublicNetwork != nil { + netTypeToNet["Ceph public network"] = sys.MicroCephPublicNetwork + } + + // Track interface collisions (local to each system) + interfaceToTypes := make(map[string][]string) + for netType, net := range netTypeToNet { + if net == nil { + continue + } + + interfaceToTypes[net.Interface.Name] = append(interfaceToTypes[net.Interface.Name], netType) + } + + for iface, types := range interfaceToTypes { + if len(types) > 1 { + _, ok := interfaceCollisionMap[sysName][iface] + if !ok { + interfaceCollisionMap[sysName][iface] = make(map[string]struct{}) + } + + for _, t := range types { + interfaceCollisionMap[sysName][iface][t] = struct{}{} + } + } + } + + // Track subnets globally across systems + for netType, net := range netTypeToNet { + if net == nil { + continue + } + + if net.Subnet == nil { + continue + } + + subnetStr := net.Subnet.String() + _, exists := subnetToGlobalTypes[subnetStr] + if !exists { + subnetToGlobalTypes[subnetStr] = make(map[string]struct{}) + } + + subnetToGlobalTypes[subnetStr][netType] = struct{}{} + } + } + + // Build subnet collisions from global subnets + for subnet, types := range subnetToGlobalTypes { + if len(types) > 1 { + subnetsCollisionMap[subnet] = types + } + } + + warnings := make([]string, 0) + + // Format interface collisions + for sysName, ifaceTotypesSet := range interfaceCollisionMap { + for iface, typesSet := range ifaceTotypesSet { + types := sortedKeys(typesSet) + warnings = append(warnings, tui.Printf( + tui.Fmt{Arg: "%s sharing network interface %q on %q"}, + tui.Fmt{Arg: strings.Join(types, ", ")}, + tui.Fmt{Arg: iface, Color: tui.Yellow}, + tui.Fmt{Arg: sysName, Color: tui.Yellow}, + )) + } + } + + // Format subnet collisions + for subnet, typesSet := range subnetsCollisionMap { + types := sortedKeys(typesSet) + warnings = append(warnings, tui.Printf( + tui.Fmt{Arg: "%s sharing subnet %q"}, + tui.Fmt{Arg: strings.Join(types, ", ")}, + tui.Fmt{Arg: subnet, Color: tui.Yellow}, + )) + } + + return warnings +} + func (c *initConfig) validateSystems(s *service.Handler) (err error) { for _, sys := range c.systems { if sys.MicroCephInternalNetworkSubnet == "" || sys.OVNGeneveAddr == "" {