From 0a86133da59261e7ebb3e5d465e2a619598e15e2 Mon Sep 17 00:00:00 2001 From: Gabriel Mougard Date: Thu, 7 Dec 2023 19:22:13 +0100 Subject: [PATCH] microcloud/preseed: Allow the creation of an underlay network in preseed mode Signed-off-by: Gabriel Mougard --- cmd/microcloud/main_init_preseed.go | 75 +++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/cmd/microcloud/main_init_preseed.go b/cmd/microcloud/main_init_preseed.go index 84a51e774..7f61e8c9c 100644 --- a/cmd/microcloud/main_init_preseed.go +++ b/cmd/microcloud/main_init_preseed.go @@ -38,6 +38,7 @@ type Preseed struct { type System struct { Name string `yaml:"name"` UplinkInterface string `yaml:"ovn_uplink_interface"` + UnderlayIP string `yaml:"underlay_ip"` Storage InitStorage `yaml:"storage"` } @@ -178,6 +179,7 @@ func (c *CmdControl) RunPreseed(cmd *cobra.Command, init bool) error { // validate validates the unmarshaled preseed input. func (p *Preseed) validate(name string, bootstrap bool) error { uplinkCount := 0 + underlayCount := 0 directCephCount := 0 directLocalCount := 0 localInit := false @@ -203,6 +205,15 @@ func (p *Preseed) validate(name string, bootstrap bool) error { uplinkCount++ } + if system.UnderlayIP != "" { + _, _, err := net.ParseCIDR(system.UnderlayIP) + if err != nil { + return fmt.Errorf("Invalid underlay IP: %w", err) + } + + underlayCount++ + } + if len(system.Storage.Ceph) > 0 { directCephCount++ } @@ -232,6 +243,11 @@ func (p *Preseed) validate(name string, bootstrap bool) error { return fmt.Errorf("Some systems are missing an uplink interface") } + containsUnderlay := underlayCount > 0 + if containsUnderlay && underlayCount < len(p.Systems) { + return fmt.Errorf("Some systems are missing an underlay interface") + } + containsCephStorage = directCephCount > 0 if containsCephStorage && directCephCount < 3 && len(p.Storage.Ceph) == 0 && bootstrap { return fmt.Errorf("At least 3 systems must specify ceph storage disks") @@ -449,10 +465,15 @@ func (p *Preseed) Parse(s *service.Handler, bootstrap bool) (map[string]InitSyst lxd := s.Services[types.LXD].(*service.LXDService) ifaceByPeer := map[string]string{} + ovnUnderlayNeeded := false for _, cfg := range p.Systems { if cfg.UplinkInterface != "" { ifaceByPeer[cfg.Name] = cfg.UplinkInterface } + + if cfg.UnderlayIP != "" { + ovnUnderlayNeeded = true + } } // If we have specified any part of OVN config, implicitly assume we want to set it up. @@ -477,10 +498,58 @@ func (p *Preseed) Parse(s *service.Handler, bootstrap bool) (map[string]InitSyst ifaceByPeer[peer] = nets[0].Name } } - } - if usingOVN && bootstrap && len(ifaceByPeer) < 3 { - return nil, fmt.Errorf("Failed to find at least 3 interfaces on 3 machines for OVN configuration") + if bootstrap && len(ifaceByPeer) < 3 { + return nil, fmt.Errorf("Failed to find at least 3 interfaces on 3 machines for OVN configuration") + } + + // Check the preseed underlay network configuration against the available ips. + if ovnUnderlayNeeded { + underlayNetworks, err := lxd.GetUnderlayInterfaces(context.Background(), bootstrap, infos) + if err != nil { + return nil, err + } + + if len(underlayNetworks) != len(p.Systems) { + return nil, fmt.Errorf("Failed to find all underlay IPs on the network") + } + + underlays := make(map[string]string, len(p.Systems)) + for _, sys := range p.Systems { + underlays[sys.Name] = sys.UnderlayIP + } + + underlayCount := 0 + for peer, networks := range underlayNetworks { + for _, net := range networks { + if len(net.Addresses) != 0 { + for _, cidrAddr := range net.Addresses { + if underlays[peer] == cidrAddr { + underlayCount = underlayCount + 1 + goto out + } + } + } + } + + out: + } + + if underlayCount != len(p.Systems) { + return nil, fmt.Errorf("Failed to find all underlay IPs on the network") + } + + // Apply the underlay IPs to the systems. + for peer, system := range systems { + ip, _, err := net.ParseCIDR(underlays[peer]) + if err != nil { + return nil, fmt.Errorf("Failed to parse underlay IP: %w", err) + } + + system.OVNGeneveAddr = ip.String() + systems[peer] = system + } + } } for peer, iface := range ifaceByPeer {