diff --git a/cmd/microcloud/main_init_test.go b/cmd/microcloud/main_init_test.go index 4cc8360f9..ea637c40c 100644 --- a/cmd/microcloud/main_init_test.go +++ b/cmd/microcloud/main_init_test.go @@ -1,9 +1,11 @@ package main import ( + "net" "testing" lxdAPI "github.com/canonical/lxd/shared/api" + "github.com/stretchr/testify/assert" "github.com/canonical/microcloud/microcloud/multicast" "github.com/canonical/microcloud/microcloud/service" @@ -226,3 +228,103 @@ func TestValidateSystemsMultiSystem(t *testing.T) { t.Fatalf("sys4 with conflicting management IP and ipv6.ovn.ranges passed validation") } } + +func newNetwork(ifaceName, ipStr string) *Network { + ip, subnet, _ := net.ParseCIDR(ipStr) + return &Network{ + Interface: ifaceName, + IP: ip, + Subnet: subnet, + } +} + +func TestValidateSystemsNetworkCollision(t *testing.T) { + tests := []struct { + name string + systems map[string]InitSystem + expectedWarnings []string + }{ + { + name: "no collisions", + systems: map[string]InitSystem{ + "system1": { + MicroCephPublicNetwork: newNetwork("eth0", "10.0.1.0/24"), + MicroCephInternalNetwork: newNetwork("eth1", "10.0.2.0/24"), + MicroCloudInternalNetwork: newNetwork("eth2", "10.0.3.0/24"), + OVNGeneveNetwork: newNetwork("eth3", "10.0.4.0/24"), + }, + "system2": { + MicroCephPublicNetwork: newNetwork("eth0", "10.1.1.0/24"), + MicroCephInternalNetwork: newNetwork("eth1", "10.1.2.0/24"), + MicroCloudInternalNetwork: newNetwork("eth2", "10.1.3.0/24"), + OVNGeneveNetwork: newNetwork("eth3", "10.1.4.0/24"), + }, + }, + expectedWarnings: nil, + }, + { + name: "single system interface collision", + systems: map[string]InitSystem{ + "system1": { + MicroCephInternalNetwork: newNetwork("eth0", "10.0.1.0/24"), + OVNGeneveNetwork: newNetwork("eth0", "10.0.2.0/24"), // Same interface + MicroCloudInternalNetwork: newNetwork("eth1", "10.0.3.0/24"), + }, + }, + expectedWarnings: []string{ + "- Ceph cluster network, OVN underlay sharing network interface \"eth0\"", + }, + }, + { + name: "cross-system subnet collision", + systems: map[string]InitSystem{ + "system1": { + MicroCephInternalNetwork: newNetwork("eth0", "10.0.1.0/24"), // Same subnet + OVNGeneveNetwork: newNetwork("eth1", "10.0.2.0/24"), + MicroCloudInternalNetwork: newNetwork("eth2", "10.0.3.0/24"), + }, + "system2": { + OVNGeneveNetwork: newNetwork("eth3", "10.0.1.0/24"), // Same subnet for different network type + MicroCloudInternalNetwork: newNetwork("eth4", "10.0.4.0/24"), + }, + }, + expectedWarnings: []string{ + "- Ceph cluster network, OVN underlay sharing subnet \"10.0.1.0/24\"", + }, + }, + { + name: "mixed interface and cross-subnet collision", + systems: map[string]InitSystem{ + "system1": { + MicroCephInternalNetwork: newNetwork("eth0", "10.0.1.0/24"), // Interface and subnet collision + OVNGeneveNetwork: newNetwork("eth0", "10.0.1.0/24"), // Same interface and subnet + MicroCloudInternalNetwork: newNetwork("eth1", "10.0.2.0/24"), + }, + "system2": { + OVNGeneveNetwork: newNetwork("eth2", "10.0.1.0/24"), // Same subnet globally + }, + }, + expectedWarnings: []string{ + "- Ceph cluster network, OVN underlay sharing network interface \"eth0\"", + "- Ceph cluster network, OVN underlay sharing subnet \"10.0.1.0/24\"", + }, + }, + { + name: "ignore systems with missing networks", + systems: map[string]InitSystem{ + "system1": { // Incomplete networks (skipped in checks) + OVNGeneveNetwork: newNetwork("eth0", "10.0.1.0/24"), + MicroCloudInternalNetwork: newNetwork("eth1", "10.0.2.0/24"), + }, + }, + expectedWarnings: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + warnings := detectCollisions(tt.systems) + assert.ElementsMatch(t, tt.expectedWarnings, warnings) + }) + } +}