2015-06-12 19:43:18 +00:00
package ipam
import (
2023-06-23 00:33:17 +00:00
"context"
2015-06-12 19:43:18 +00:00
"fmt"
"net"
2023-02-09 21:43:46 +00:00
"net/netip"
2023-02-07 15:25:52 +00:00
"strings"
2015-06-12 19:43:18 +00:00
2023-06-23 00:33:17 +00:00
"github.com/containerd/containerd/log"
2023-02-07 15:25:52 +00:00
"github.com/docker/docker/libnetwork/bitmap"
2021-04-06 00:24:47 +00:00
"github.com/docker/docker/libnetwork/ipamapi"
2023-02-09 21:43:46 +00:00
"github.com/docker/docker/libnetwork/ipbits"
2021-04-06 00:24:47 +00:00
"github.com/docker/docker/libnetwork/types"
2015-06-12 19:43:18 +00:00
)
const (
2015-09-22 20:20:55 +00:00
localAddressSpace = "LocalDefault"
globalAddressSpace = "GlobalDefault"
2015-06-12 19:43:18 +00:00
)
// Allocator provides per address space ipv4/ipv6 book keeping
type Allocator struct {
2023-01-24 23:49:24 +00:00
// The address spaces
2023-02-06 23:01:47 +00:00
local , global * addrSpace
2015-06-12 19:43:18 +00:00
}
// NewAllocator returns an instance of libnetwork ipam
2023-01-24 23:49:24 +00:00
func NewAllocator ( lcAs , glAs [ ] * net . IPNet ) ( * Allocator , error ) {
2023-02-09 21:43:46 +00:00
var (
a Allocator
err error
)
a . local , err = newAddrSpace ( lcAs )
if err != nil {
return nil , fmt . Errorf ( "could not construct local address space: %w" , err )
}
a . global , err = newAddrSpace ( glAs )
if err != nil {
return nil , fmt . Errorf ( "could not construct global address space: %w" , err )
}
return & a , nil
2015-10-05 11:24:44 +00:00
}
2023-02-09 21:43:46 +00:00
func newAddrSpace ( predefined [ ] * net . IPNet ) ( * addrSpace , error ) {
pdf := make ( [ ] netip . Prefix , len ( predefined ) )
for i , n := range predefined {
var ok bool
pdf [ i ] , ok = toPrefix ( n )
if ! ok {
return nil , fmt . Errorf ( "network at index %d (%v) is not in canonical form" , i , n )
}
2016-01-28 00:37:47 +00:00
}
2023-02-09 21:43:46 +00:00
return & addrSpace {
subnets : map [ netip . Prefix ] * PoolData { } ,
predefined : pdf ,
} , nil
2016-01-28 00:37:47 +00:00
}
2015-09-22 20:20:55 +00:00
// GetDefaultAddressSpaces returns the local and global default address spaces
func ( a * Allocator ) GetDefaultAddressSpaces ( ) ( string , string , error ) {
return localAddressSpace , globalAddressSpace , nil
}
2015-06-12 19:43:18 +00:00
2015-09-22 20:20:55 +00:00
// RequestPool returns an address pool along with its unique id.
2018-05-23 15:21:31 +00:00
// addressSpace must be a valid address space name and must not be the empty string.
2023-07-22 15:22:20 +00:00
// If requestedPool is the empty string then the default predefined pool for addressSpace will be used, otherwise pool must be a valid IP address and length in CIDR notation.
// If requestedSubPool is not empty, it must be a valid IP address and length in CIDR notation which is a sub-range of requestedPool.
// requestedSubPool must be empty if requestedPool is empty.
func ( a * Allocator ) RequestPool ( addressSpace , requestedPool , requestedSubPool string , _ map [ string ] string , v6 bool ) ( poolID string , pool * net . IPNet , meta map [ string ] string , err error ) {
log . G ( context . TODO ( ) ) . Debugf ( "RequestPool(%s, %s, %s, _, %t)" , addressSpace , requestedPool , requestedSubPool , v6 )
2016-05-25 22:02:44 +00:00
2023-07-22 13:35:08 +00:00
parseErr := func ( err error ) error {
2023-07-22 15:22:20 +00:00
return types . InternalErrorf ( "failed to parse pool request for address space %q pool %q subpool %q: %v" , addressSpace , requestedPool , requestedSubPool , err )
2015-06-12 19:43:18 +00:00
}
2015-10-04 01:51:53 +00:00
2023-02-07 17:56:44 +00:00
if addressSpace == "" {
2023-07-22 13:35:08 +00:00
return "" , nil , nil , parseErr ( ipamapi . ErrInvalidAddressSpace )
2023-02-07 17:56:44 +00:00
}
aSpace , err := a . getAddrSpace ( addressSpace )
if err != nil {
return "" , nil , nil , err
}
2023-07-22 15:22:20 +00:00
if requestedPool == "" && requestedSubPool != "" {
2023-07-22 13:35:08 +00:00
return "" , nil , nil , parseErr ( ipamapi . ErrInvalidSubPool )
}
2023-02-07 17:56:44 +00:00
2023-07-22 13:35:08 +00:00
k := PoolID { AddressSpace : addressSpace }
2023-07-22 15:22:20 +00:00
if requestedPool == "" {
2023-02-09 21:43:46 +00:00
k . Subnet , err = aSpace . allocatePredefinedPool ( v6 )
2023-02-07 15:25:52 +00:00
if err != nil {
return "" , nil , nil , err
}
2023-02-09 21:43:46 +00:00
return k . String ( ) , toIPNet ( k . Subnet ) , nil , nil
2023-02-07 17:56:44 +00:00
}
2023-07-22 15:22:20 +00:00
if k . Subnet , err = netip . ParsePrefix ( requestedPool ) ; err != nil {
2023-07-22 13:35:08 +00:00
return "" , nil , nil , parseErr ( ipamapi . ErrInvalidPool )
2023-02-07 17:56:44 +00:00
}
2016-05-25 22:02:44 +00:00
2023-07-22 15:22:20 +00:00
if requestedSubPool != "" {
k . ChildSubnet , err = netip . ParsePrefix ( requestedSubPool )
2023-02-07 15:25:52 +00:00
if err != nil {
2023-07-22 13:35:08 +00:00
return "" , nil , nil , parseErr ( ipamapi . ErrInvalidSubPool )
2016-05-25 22:02:44 +00:00
}
}
2023-02-09 21:43:46 +00:00
k . Subnet , k . ChildSubnet = k . Subnet . Masked ( ) , k . ChildSubnet . Masked ( )
2023-06-14 08:42:51 +00:00
// Prior to https://github.com/moby/moby/pull/44968, libnetwork would happily accept a ChildSubnet with a bigger
// mask than its parent subnet. In such case, it was producing IP addresses based on the parent subnet, and the
// child subnet was not allocated from the address pool. Following condition take care of restoring this behavior
// for networks created before upgrading to v24.0.
if k . ChildSubnet . IsValid ( ) && k . ChildSubnet . Bits ( ) < k . Subnet . Bits ( ) {
k . ChildSubnet = k . Subnet
}
2023-02-09 21:43:46 +00:00
err = aSpace . allocateSubnet ( k . Subnet , k . ChildSubnet )
2023-02-07 15:25:52 +00:00
if err != nil {
2015-09-22 20:20:55 +00:00
return "" , nil , nil , err
}
2015-10-05 11:24:44 +00:00
2023-02-09 21:43:46 +00:00
return k . String ( ) , toIPNet ( k . Subnet ) , nil , nil
2015-06-16 21:46:51 +00:00
}
2015-06-12 19:43:18 +00:00
2015-09-22 20:20:55 +00:00
// ReleasePool releases the address pool identified by the passed id
func ( a * Allocator ) ReleasePool ( poolID string ) error {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debugf ( "ReleasePool(%s)" , poolID )
2023-07-22 14:21:48 +00:00
k , err := PoolIDFromString ( poolID )
if err != nil {
2023-08-08 11:35:05 +00:00
return types . InvalidParameterErrorf ( "invalid pool id: %s" , poolID )
2015-09-22 20:20:55 +00:00
}
2015-08-12 02:22:38 +00:00
2015-10-05 11:24:44 +00:00
aSpace , err := a . getAddrSpace ( k . AddressSpace )
2015-10-04 01:51:53 +00:00
if err != nil {
return err
}
2023-02-09 21:43:46 +00:00
return aSpace . releaseSubnet ( k . Subnet , k . ChildSubnet )
2015-09-22 20:20:55 +00:00
}
2015-08-12 02:22:38 +00:00
2018-05-23 15:21:31 +00:00
// Given the address space, returns the local or global PoolConfig based on whether the
// address space is local or global. AddressSpace locality is registered with IPAM out of band.
2015-10-05 11:24:44 +00:00
func ( a * Allocator ) getAddrSpace ( as string ) ( * addrSpace , error ) {
2023-02-06 23:01:47 +00:00
switch as {
case localAddressSpace :
return a . local , nil
case globalAddressSpace :
return a . global , nil
2015-10-04 01:51:53 +00:00
}
2023-08-08 11:35:05 +00:00
return nil , types . InvalidParameterErrorf ( "cannot find address space %s" , as )
2015-10-04 01:51:53 +00:00
}
2023-02-09 21:43:46 +00:00
func newPoolData ( pool netip . Prefix ) * PoolData {
ones , bits := pool . Bits ( ) , pool . Addr ( ) . BitLen ( )
2015-10-09 03:04:13 +00:00
numAddresses := uint64 ( 1 << uint ( bits - ones ) )
2015-09-22 20:20:55 +00:00
2015-10-09 03:04:13 +00:00
// Allow /64 subnet
2023-02-09 21:43:46 +00:00
if pool . Addr ( ) . Is6 ( ) && numAddresses == 0 {
2015-10-09 03:04:13 +00:00
numAddresses --
}
2023-01-24 23:49:24 +00:00
// Generate the new address masks.
2023-02-07 15:25:52 +00:00
h := bitmap . New ( numAddresses )
2015-06-13 20:12:24 +00:00
2021-07-12 21:37:36 +00:00
// Pre-reserve the network address on IPv4 networks large
// enough to have one (i.e., anything bigger than a /31.
2023-02-09 21:43:46 +00:00
if ! ( pool . Addr ( ) . Is4 ( ) && numAddresses <= 2 ) {
2021-07-12 21:37:36 +00:00
h . Set ( 0 )
}
2015-06-12 19:43:18 +00:00
2021-07-12 21:37:36 +00:00
// Pre-reserve the broadcast address on IPv4 networks large
// enough to have one (i.e., anything bigger than a /31).
2023-02-09 21:43:46 +00:00
if pool . Addr ( ) . Is4 ( ) && numAddresses > 2 {
2015-10-22 19:58:45 +00:00
h . Set ( numAddresses - 1 )
}
2023-02-09 21:43:46 +00:00
return & PoolData { addrs : h , children : map [ netip . Prefix ] struct { } { } }
2015-06-12 19:43:18 +00:00
}
2023-02-06 23:01:47 +00:00
// getPredefineds returns the predefined subnets for the address space.
//
// It should not be called concurrently with any other method on the addrSpace.
2023-02-09 21:43:46 +00:00
func ( aSpace * addrSpace ) getPredefineds ( ) [ ] netip . Prefix {
2023-02-06 23:01:47 +00:00
i := aSpace . predefinedStartIndex
2018-05-30 19:32:11 +00:00
// defensive in case the list changed since last update
2023-02-06 23:01:47 +00:00
if i >= len ( aSpace . predefined ) {
2018-05-30 19:32:11 +00:00
i = 0
2015-06-12 19:43:18 +00:00
}
2023-02-06 23:01:47 +00:00
return append ( aSpace . predefined [ i : ] , aSpace . predefined [ : i ] ... )
2018-05-30 19:32:11 +00:00
}
2023-02-06 23:01:47 +00:00
// updatePredefinedStartIndex rotates the predefined subnet list by amt.
//
// It should not be called concurrently with any other method on the addrSpace.
func ( aSpace * addrSpace ) updatePredefinedStartIndex ( amt int ) {
i := aSpace . predefinedStartIndex + amt
if i < 0 || i >= len ( aSpace . predefined ) {
2018-05-30 19:32:11 +00:00
i = 0
}
2023-02-06 23:01:47 +00:00
aSpace . predefinedStartIndex = i
2015-09-22 20:20:55 +00:00
}
2015-06-12 19:43:18 +00:00
2023-02-09 21:43:46 +00:00
func ( aSpace * addrSpace ) allocatePredefinedPool ( ipV6 bool ) ( netip . Prefix , error ) {
2018-05-30 19:32:11 +00:00
aSpace . Lock ( )
2023-02-06 23:01:47 +00:00
defer aSpace . Unlock ( )
for i , nw := range aSpace . getPredefineds ( ) {
2023-02-09 21:43:46 +00:00
if ipV6 != nw . Addr ( ) . Is6 ( ) {
2015-09-22 20:20:55 +00:00
continue
}
2018-05-30 19:32:11 +00:00
// Checks whether pool has already been allocated
2023-02-09 21:43:46 +00:00
if _ , ok := aSpace . subnets [ nw ] ; ok {
2015-09-22 20:20:55 +00:00
continue
}
2018-05-30 19:32:11 +00:00
// Shouldn't be necessary, but check prevents IP collisions should
// predefined pools overlap for any reason.
2023-02-07 15:25:52 +00:00
if ! aSpace . contains ( nw ) {
2023-02-06 23:01:47 +00:00
aSpace . updatePredefinedStartIndex ( i + 1 )
2023-02-09 21:43:46 +00:00
err := aSpace . allocateSubnetL ( nw , netip . Prefix { } )
2023-02-07 15:25:52 +00:00
if err != nil {
2023-02-09 21:43:46 +00:00
return netip . Prefix { } , err
2023-02-07 15:25:52 +00:00
}
2023-02-09 21:43:46 +00:00
return nw , nil
2015-09-22 20:20:55 +00:00
}
2015-06-12 19:43:18 +00:00
}
2023-02-09 21:43:46 +00:00
v := 4
if ipV6 {
v = 6
}
return netip . Prefix { } , types . NotFoundErrorf ( "could not find an available, non-overlapping IPv%d address pool among the defaults to assign to the network" , v )
2015-06-12 19:43:18 +00:00
}
2015-09-22 20:20:55 +00:00
// RequestAddress returns an address from the specified pool ID
func ( a * Allocator ) RequestAddress ( poolID string , prefAddress net . IP , opts map [ string ] string ) ( * net . IPNet , map [ string ] string , error ) {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debugf ( "RequestAddress(%s, %v, %v)" , poolID , prefAddress , opts )
2023-07-22 14:21:48 +00:00
k , err := PoolIDFromString ( poolID )
if err != nil {
2023-08-08 11:35:05 +00:00
return nil , nil , types . InvalidParameterErrorf ( "invalid pool id: %s" , poolID )
2015-06-12 19:43:18 +00:00
}
2015-06-25 02:11:44 +00:00
2015-10-05 11:24:44 +00:00
aSpace , err := a . getAddrSpace ( k . AddressSpace )
2015-10-04 01:51:53 +00:00
if err != nil {
return nil , nil , err
}
2023-02-09 21:43:46 +00:00
var pref netip . Addr
if prefAddress != nil {
var ok bool
pref , ok = netip . AddrFromSlice ( prefAddress )
if ! ok {
2023-08-08 11:35:05 +00:00
return nil , nil , types . InvalidParameterErrorf ( "invalid preferred address: %v" , prefAddress )
2023-02-09 21:43:46 +00:00
}
}
p , err := aSpace . requestAddress ( k . Subnet , k . ChildSubnet , pref . Unmap ( ) , opts )
if err != nil {
return nil , nil , err
}
return & net . IPNet {
IP : p . AsSlice ( ) ,
Mask : net . CIDRMask ( k . Subnet . Bits ( ) , k . Subnet . Addr ( ) . BitLen ( ) ) ,
} , nil , nil
2023-02-07 15:25:52 +00:00
}
2015-10-04 01:51:53 +00:00
2023-02-09 21:43:46 +00:00
func ( aSpace * addrSpace ) requestAddress ( nw , sub netip . Prefix , prefAddress netip . Addr , opts map [ string ] string ) ( netip . Addr , error ) {
2015-10-05 11:24:44 +00:00
aSpace . Lock ( )
2023-02-07 15:25:52 +00:00
defer aSpace . Unlock ( )
2023-02-09 21:43:46 +00:00
p , ok := aSpace . subnets [ nw ]
2015-09-22 20:20:55 +00:00
if ! ok {
2023-02-09 21:43:46 +00:00
return netip . Addr { } , types . NotFoundErrorf ( "cannot find address pool for poolID:%v/%v" , nw , sub )
2015-06-12 19:43:18 +00:00
}
2015-06-25 02:11:44 +00:00
2023-02-09 21:43:46 +00:00
if prefAddress != ( netip . Addr { } ) && ! nw . Contains ( prefAddress ) {
return netip . Addr { } , ipamapi . ErrIPOutOfRange
2015-06-12 19:43:18 +00:00
}
2015-09-22 20:20:55 +00:00
2023-02-09 21:43:46 +00:00
if sub != ( netip . Prefix { } ) {
if _ , ok := p . children [ sub ] ; ! ok {
return netip . Addr { } , types . NotFoundErrorf ( "cannot find address pool for poolID:%v/%v" , nw , sub )
2023-02-07 15:25:52 +00:00
}
2015-06-25 02:11:44 +00:00
}
2017-06-01 02:41:21 +00:00
// In order to request for a serial ip address allocation, callers can pass in the option to request
// IP allocation serially or first available IP in the subnet
2023-02-07 15:25:52 +00:00
serial := opts [ ipamapi . AllocSerialPrefix ] == "true"
2023-02-09 21:43:46 +00:00
ip , err := getAddress ( nw , p . addrs , prefAddress , sub , serial )
2015-09-22 20:20:55 +00:00
if err != nil {
2023-02-09 21:43:46 +00:00
return netip . Addr { } , err
2015-06-25 02:11:44 +00:00
}
2015-09-22 20:20:55 +00:00
2023-02-09 21:43:46 +00:00
return ip , nil
2015-06-12 19:43:18 +00:00
}
2015-09-22 20:20:55 +00:00
// ReleaseAddress releases the address from the specified pool ID
func ( a * Allocator ) ReleaseAddress ( poolID string , address net . IP ) error {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debugf ( "ReleaseAddress(%s, %v)" , poolID , address )
2023-07-22 14:21:48 +00:00
k , err := PoolIDFromString ( poolID )
if err != nil {
2023-08-08 11:35:05 +00:00
return types . InvalidParameterErrorf ( "invalid pool id: %s" , poolID )
2015-09-22 20:20:55 +00:00
}
2015-06-12 19:43:18 +00:00
2015-10-05 11:24:44 +00:00
aSpace , err := a . getAddrSpace ( k . AddressSpace )
2015-10-04 01:51:53 +00:00
if err != nil {
return err
}
2023-02-09 21:43:46 +00:00
addr , ok := netip . AddrFromSlice ( address )
if ! ok {
2023-08-08 11:35:05 +00:00
return types . InvalidParameterErrorf ( "invalid address: %v" , address )
2023-02-09 21:43:46 +00:00
}
return aSpace . releaseAddress ( k . Subnet , k . ChildSubnet , addr . Unmap ( ) )
2023-02-07 15:25:52 +00:00
}
2023-02-09 21:43:46 +00:00
func ( aSpace * addrSpace ) releaseAddress ( nw , sub netip . Prefix , address netip . Addr ) error {
2015-10-05 11:24:44 +00:00
aSpace . Lock ( )
2023-02-07 15:25:52 +00:00
defer aSpace . Unlock ( )
2023-02-09 21:43:46 +00:00
p , ok := aSpace . subnets [ nw ]
2015-09-22 20:20:55 +00:00
if ! ok {
2023-02-09 21:43:46 +00:00
return types . NotFoundErrorf ( "cannot find address pool for %v/%v" , nw , sub )
2023-02-07 15:25:52 +00:00
}
2023-02-09 21:43:46 +00:00
if sub != ( netip . Prefix { } ) {
if _ , ok := p . children [ sub ] ; ! ok {
return types . NotFoundErrorf ( "cannot find address pool for poolID:%v/%v" , nw , sub )
2023-02-07 15:25:52 +00:00
}
2015-06-12 19:43:18 +00:00
}
2023-02-09 21:43:46 +00:00
if ! address . IsValid ( ) {
2023-08-08 11:35:05 +00:00
return types . InvalidParameterErrorf ( "invalid address" )
2015-06-12 19:43:18 +00:00
}
2023-02-09 21:43:46 +00:00
if ! nw . Contains ( address ) {
2015-10-07 03:29:30 +00:00
return ipamapi . ErrIPOutOfRange
}
2023-06-23 00:33:17 +00:00
defer log . G ( context . TODO ( ) ) . Debugf ( "Released address Address:%v Sequence:%s" , address , p . addrs )
2015-10-09 03:04:13 +00:00
2023-02-09 21:43:46 +00:00
return p . addrs . Unset ( hostID ( address , uint ( nw . Bits ( ) ) ) )
2015-06-12 19:43:18 +00:00
}
2023-02-09 21:43:46 +00:00
func getAddress ( base netip . Prefix , bitmask * bitmap . Bitmap , prefAddress netip . Addr , ipr netip . Prefix , serial bool ) ( netip . Addr , error ) {
2015-06-12 19:43:18 +00:00
var (
2015-10-09 03:04:13 +00:00
ordinal uint64
2015-06-24 22:02:08 +00:00
err error
2015-06-12 19:43:18 +00:00
)
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debugf ( "Request address PoolID:%v %s Serial:%v PrefAddress:%v " , base , bitmask , serial , prefAddress )
2015-09-22 20:20:55 +00:00
2021-05-28 00:15:56 +00:00
if bitmask . Unselected ( ) == 0 {
2023-02-09 21:43:46 +00:00
return netip . Addr { } , ipamapi . ErrNoAvailableIPs
2015-08-12 02:22:38 +00:00
}
2023-02-09 21:43:46 +00:00
if ipr == ( netip . Prefix { } ) && prefAddress == ( netip . Addr { } ) {
2017-06-01 02:41:21 +00:00
ordinal , err = bitmask . SetAny ( serial )
2023-02-09 21:43:46 +00:00
} else if prefAddress != ( netip . Addr { } ) {
ordinal = hostID ( prefAddress , uint ( base . Bits ( ) ) )
2015-08-12 02:22:38 +00:00
err = bitmask . Set ( ordinal )
2015-10-05 07:40:17 +00:00
} else {
2023-02-09 21:43:46 +00:00
start , end := subnetRange ( base , ipr )
ordinal , err = bitmask . SetAnyInRange ( start , end , serial )
2015-08-12 02:22:38 +00:00
}
2015-10-21 00:05:01 +00:00
switch err {
case nil :
// Convert IP ordinal for this subnet into IP address
2023-02-09 21:43:46 +00:00
return ipbits . Add ( base . Addr ( ) , ordinal , 0 ) , nil
2023-02-07 15:25:52 +00:00
case bitmap . ErrBitAllocated :
2023-02-09 21:43:46 +00:00
return netip . Addr { } , ipamapi . ErrIPAlreadyAllocated
2023-02-07 15:25:52 +00:00
case bitmap . ErrNoBitAvailable :
2023-02-09 21:43:46 +00:00
return netip . Addr { } , ipamapi . ErrNoAvailableIPs
2015-10-21 00:05:01 +00:00
default :
2023-02-09 21:43:46 +00:00
return netip . Addr { } , err
2015-06-12 19:43:18 +00:00
}
}
// DumpDatabase dumps the internal info
2015-09-22 20:20:55 +00:00
func ( a * Allocator ) DumpDatabase ( ) string {
2023-02-06 23:01:47 +00:00
aspaces := map [ string ] * addrSpace {
localAddressSpace : a . local ,
globalAddressSpace : a . global ,
2015-12-13 08:35:05 +00:00
}
2015-09-22 20:20:55 +00:00
2023-02-07 15:25:52 +00:00
var b strings . Builder
2023-02-06 23:01:47 +00:00
for _ , as := range [ ] string { localAddressSpace , globalAddressSpace } {
2023-02-07 15:25:52 +00:00
fmt . Fprintf ( & b , "\n### %s\n" , as )
b . WriteString ( aspaces [ as ] . DumpDatabase ( ) )
2015-10-04 01:51:53 +00:00
}
2023-02-07 15:25:52 +00:00
return b . String ( )
}
2015-06-12 19:43:18 +00:00
2023-02-07 15:25:52 +00:00
func ( aSpace * addrSpace ) DumpDatabase ( ) string {
aSpace . Lock ( )
defer aSpace . Unlock ( )
2015-10-05 11:24:44 +00:00
2023-02-07 15:25:52 +00:00
var b strings . Builder
for k , config := range aSpace . subnets {
fmt . Fprintf ( & b , "%v: %v\n" , k , config )
fmt . Fprintf ( & b , " Bitmap: %v\n" , config . addrs )
for k := range config . children {
fmt . Fprintf ( & b , " - Subpool: %v\n" , k )
}
}
return b . String ( )
2015-08-12 02:22:38 +00:00
}
2016-12-19 03:56:34 +00:00
// IsBuiltIn returns true for builtin drivers
func ( a * Allocator ) IsBuiltIn ( ) bool {
return true
}