+(Appears on: +IPPoolSpec) +
+Field | +Description | +
---|---|
+allocatedTo + +string + + |
++ | +
+Range + + +Range + + + |
+
+
+(Members of |
+
@@ -685,6 +730,18 @@ string
allocatedRanges
allocatedIPs
allocatedRanges
allocatedIPs
instanceSubnet
dhcpAllocationStart
DHCPAllocationStart must be inside the SubNet range
+dhcpAllocationStop
DHCPAllocationStop must be inside the SubNet range
+instanceSubnetBitStep
InstanceSubnetBitStep indicates how many bites to allocate for each node DHCP range
allocationStart
staticAllocationStart
allocationStop
staticAllocationStop
(Appears on: +AllocatedRange, BuilderNetwork, IPPoolSpec)
diff --git a/pkg/api/v1/ippool_types.go b/pkg/api/v1/ippool_types.go index 491b2b4..c9227c4 100644 --- a/pkg/api/v1/ippool_types.go +++ b/pkg/api/v1/ippool_types.go @@ -26,9 +26,10 @@ import ( // within the subnet from which IPs can be allocated by IPAM, // and a set of IPs that are currently allocated already. type IPPoolSpec struct { - Subnet string `json:"subnet"` - Ranges []Range `json:"ranges"` - AllocatedIPs []AllocatedIP `json:"allocatedIPs"` + Subnet string `json:"subnet"` + Ranges []Range `json:"ranges"` + AllocatedRanges []AllocatedRange `json:"allocatedRanges,omitempty"` + AllocatedIPs []AllocatedIP `json:"allocatedIPs"` // MACPrefix defines the MAC prefix to use for VM mac addresses MACPrefix string `json:"macPrefix"` // NextMAC indicates the next MAC address (in sequence) that @@ -43,6 +44,11 @@ type AllocatedIP struct { AllocatedTo string `json:"allocatedTo"` } +type AllocatedRange struct { + AllocatedTo string `json:"allocatedTo"` + Range `json:",inline"` +} + // Range has (inclusive) bounds within a subnet from which IPs can be allocated type Range struct { Start string `json:"start"` diff --git a/pkg/api/v1/vino_types.go b/pkg/api/v1/vino_types.go index d0a9bd7..9eec06d 100644 --- a/pkg/api/v1/vino_types.go +++ b/pkg/api/v1/vino_types.go @@ -41,6 +41,8 @@ const ( VinoNetworkDataTemplateDefaultKey = "template" // VinoDefaultRootDeviceName is default root device for the underlying libvirt VM VinoDefaultRootDeviceName = "/dev/vda" + // VinoDefaultInstanceSubnetBitStep is the value for InstanceSubnetBitStep + VinoDefaultInstanceSubnetBitStep = 4 ) // Constants for BasicAuth @@ -97,14 +99,19 @@ type CPUConfiguration struct { // Network defines libvirt networks type Network struct { //Network Parameter defined - Name string `json:"name,omitempty"` - SubNet string `json:"subnet,omitempty"` - InstanceSubnet string `json:"instanceSubnet,omitempty"` - Type string `json:"type,omitempty"` - AllocationStart string `json:"allocationStart,omitempty"` - AllocationStop string `json:"allocationStop,omitempty"` - DNSServers []string `json:"dns_servers,omitempty"` - Routes []VMRoutes `json:"routes,omitempty"` + Name string `json:"name,omitempty"` + SubNet string `json:"subnet,omitempty"` + // DHCPAllocationStart must be inside the SubNet range + DHCPAllocationStart string `json:"dhcpAllocationStart,omitempty"` + // DHCPAllocationStop must be inside the SubNet range + DHCPAllocationStop string `json:"dhcpAllocationStop,omitempty"` + // InstanceSubnetBitStep indicates how many bites to allocate for each node DHCP range + InstanceSubnetBitStep int `json:"instanceSubnetBitStep,omitempty"` + Type string `json:"type,omitempty"` + StaticAllocationStart string `json:"staticAllocationStart,omitempty"` + StaticAllocationStop string `json:"staticAllocationStop,omitempty"` + DNSServers []string `json:"dns_servers,omitempty"` + Routes []VMRoutes `json:"routes,omitempty"` // MACPrefix defines the zero-padded MAC prefix to use for // VM mac addresses, and is the first address that will be // allocated sequentially to VMs in this network. diff --git a/pkg/api/v1/zz_generated.deepcopy.go b/pkg/api/v1/zz_generated.deepcopy.go index fac2701..bf80608 100644 --- a/pkg/api/v1/zz_generated.deepcopy.go +++ b/pkg/api/v1/zz_generated.deepcopy.go @@ -40,6 +40,22 @@ func (in *AllocatedIP) DeepCopy() *AllocatedIP { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AllocatedRange) DeepCopyInto(out *AllocatedRange) { + *out = *in + out.Range = in.Range +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllocatedRange. +func (in *AllocatedRange) DeepCopy() *AllocatedRange { + if in == nil { + return nil + } + out := new(AllocatedRange) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BMCCredentials) DeepCopyInto(out *BMCCredentials) { *out = *in @@ -273,6 +289,11 @@ func (in *IPPoolSpec) DeepCopyInto(out *IPPoolSpec) { *out = make([]Range, len(*in)) copy(*out, *in) } + if in.AllocatedRanges != nil { + in, out := &in.AllocatedRanges, &out.AllocatedRanges + *out = make([]AllocatedRange, len(*in)) + copy(*out, *in) + } if in.AllocatedIPs != nil { in, out := &in.AllocatedIPs, &out.AllocatedIPs *out = make([]AllocatedIP, len(*in)) diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 8b61a39..f60e4a9 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -17,6 +17,7 @@ package ipam import ( "context" "fmt" + "math" "net" "regexp" "strings" @@ -378,3 +379,100 @@ func (i *Ipam) getIPPools(ctx context.Context) (map[string]*vinov1.IPPoolSpec, e } return ippools, nil } + +func (i *Ipam) AllocateRange(ctx context.Context, + bitStep int, + host, macPrefix, start, stop, subnet string) (vinov1.Range, error) { + ipPool, err := i.getIPPoolWithRanges(ctx, bitStep, macPrefix, start, stop, subnet) + if err != nil { + return vinov1.Range{}, err + } + + result, err := chooseRange(host, ipPool) + if err != nil { + return vinov1.Range{}, err + } + return result, i.applyIPPool(ctx, *ipPool) +} + +func chooseRange(host string, ipPool *vinov1.IPPoolSpec) (vinov1.Range, error) { + const unallocated = -1 + freeIndex := unallocated + for i, r := range ipPool.AllocatedRanges { + if r.AllocatedTo == host { + return r.Range, nil + } else if r.AllocatedTo == "" && freeIndex == unallocated { + freeIndex = i + } + } + if freeIndex != unallocated { + ipPool.AllocatedRanges[freeIndex].AllocatedTo = host + } else { + return vinov1.Range{}, fmt.Errorf("No free ranges available for host %s", host) + } + return ipPool.AllocatedRanges[freeIndex].Range, nil +} + +func (i *Ipam) getIPPoolWithRanges(ctx context.Context, bitStep int, + macPrefix, start, stop, subnet string) (*vinov1.IPPoolSpec, error) { + ippools, err := i.getIPPools(ctx) + if err != nil { + return &vinov1.IPPoolSpec{}, err + } + logger := i.Log.WithValues("subnet", subnet) + + ippool, exists := ippools[subnet] + if !exists { + logger.Info("IPAM creating subnet") + _, err = macStringToInt(macPrefix) // mac format validation + if err != nil { + return &vinov1.IPPoolSpec{}, err + } + ippool = &vinov1.IPPoolSpec{ + Subnet: subnet, + Ranges: []vinov1.Range{}, + AllocatedIPs: []vinov1.AllocatedIP{}, + MACPrefix: macPrefix, + NextMAC: macPrefix, + } + ippools[subnet] = ippool + } + if len(ippool.AllocatedRanges) != 0 { + return ippool, nil + } + + ranges, err := generateRanges(start, stop, bitStep) + if err != nil { + return nil, err + } + ippool.AllocatedRanges = ranges + return ippool, nil +} + +func generateRanges(start, stop string, bitStep int) ([]vinov1.AllocatedRange, error) { + firstNetIPInt, err := ipStringToInt(start) + if err != nil { + return nil, err + } + + // support only IPv4, use 32 netmask + subnetEnd, err := ipStringToInt(stop) + if err != nil { + return nil, err + } + ranges := []vinov1.AllocatedRange{} + shift := uint64(math.Pow(2, float64(bitStep))) + + for start, end := firstNetIPInt, firstNetIPInt+shift; end-1 <= subnetEnd; { + fmt.Printf("start is %s, end is %s\n", intToIPv4String(start), intToIPv4String(subnetEnd)) + ranges = append(ranges, vinov1.AllocatedRange{ + Range: vinov1.Range{ + Start: intToIPv4String(start), + Stop: intToIPv4String(end - 1), + }, + }) + start += shift + end += shift + } + return ranges, nil +} diff --git a/pkg/managers/bmh.go b/pkg/managers/bmh.go index 75b4ffa..e301b10 100644 --- a/pkg/managers/bmh.go +++ b/pkg/managers/bmh.go @@ -169,7 +169,7 @@ func (r *BMHManager) createIpamNetworks(ctx context.Context, vino *vinov1.Vino) } func (r *BMHManager) createIpamNetwork(ctx context.Context, network vinov1.Network) error { - subnetRange, err := ipam.NewRange(network.AllocationStart, network.AllocationStop) + subnetRange, err := ipam.NewRange(network.StaticAllocationStart, network.StaticAllocationStop) if err != nil { return err } @@ -295,6 +295,21 @@ func (r *BMHManager) nodeNetworks(ctx context.Context, builderNetwork.Network.Routes[routeIndex].Gateway = bridgeIP } } + bitStep := network.InstanceSubnetBitStep + if bitStep == 0 { + bitStep = vinov1.VinoDefaultInstanceSubnetBitStep + } + r, err := r.Ipam.AllocateRange(ctx, + bitStep, + k8sNode.Name, + network.MACPrefix, + network.DHCPAllocationStart, + network.DHCPAllocationStop, + network.SubNet) + if err != nil { + return []vinov1.BuilderNetwork{}, err + } + builderNetwork.Range = r builderNetworks = append(builderNetworks, builderNetwork) } return builderNetworks, nil @@ -316,7 +331,7 @@ func (r *BMHManager) domainSpecificNetValues( for _, network := range networks { if network.Name == networkName { subnet = network.SubNet - subnetRange, err = ipam.NewRange(network.AllocationStart, network.AllocationStop) + subnetRange, err = ipam.NewRange(network.StaticAllocationStart, network.StaticAllocationStop) if err != nil { return networkTemplateValues{}, err } @@ -381,7 +396,7 @@ func (r *BMHManager) annotateNode(ctx context.Context, k8sNode *corev1.Node, vin func (r *BMHManager) getBridgeIPandMAC(ctx context.Context, network vinov1.Network, k8sNode *corev1.Node) (string, string, error) { - subnetRange, err := ipam.NewRange(network.AllocationStart, network.AllocationStop) + subnetRange, err := ipam.NewRange(network.StaticAllocationStart, network.StaticAllocationStop) if err != nil { return "", "", err } diff --git a/vino-builder/assets/playbooks/vino-builder.yaml b/vino-builder/assets/playbooks/vino-builder.yaml index 5084bfe..f72708e 100644 --- a/vino-builder/assets/playbooks/vino-builder.yaml +++ b/vino-builder/assets/playbooks/vino-builder.yaml @@ -39,11 +39,6 @@ - hosts: localhost tasks: - # generate libvirt definitions for storage, networks, and domains - - name: generate management network ip addresses - include_role: - name: ipam - # generate libvirt definitions for storage, networks, and domains - name: process libvirt definitions include_role: