Skip to content

Commit c463327

Browse files
authored
Merge pull request moby#50917 from corhere/ipam-allocation-info-localscope
api, daemon: report IPAM status for local-scope networks
2 parents 2e12287 + 3f86797 commit c463327

File tree

28 files changed

+1001
-85
lines changed

28 files changed

+1001
-85
lines changed

api/swagger.yaml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2574,6 +2574,21 @@ definitions:
25742574
type: ServiceInfo
25752575
hints:
25762576
nullable: false
2577+
Status:
2578+
description: >
2579+
provides runtime information about the network
2580+
such as the number of allocated IPs.
2581+
$ref: "#/definitions/NetworkStatus"
2582+
2583+
NetworkStatus:
2584+
description: >
2585+
provides runtime information about the network
2586+
such as the number of allocated IPs.
2587+
type: "object"
2588+
x-go-name: Status
2589+
properties:
2590+
IPAM:
2591+
$ref: "#/definitions/IPAMStatus"
25772592

25782593
ServiceInfo:
25792594
x-nullable: false
@@ -2685,6 +2700,46 @@ definitions:
26852700
additionalProperties:
26862701
type: "string"
26872702

2703+
IPAMStatus:
2704+
type: "object"
2705+
x-nullable: false
2706+
x-omitempty: false
2707+
properties:
2708+
Subnets:
2709+
type: "object"
2710+
additionalProperties:
2711+
$ref: "#/definitions/SubnetStatus"
2712+
example:
2713+
"172.16.0.0/16":
2714+
IPsInUse: 3
2715+
DynamicIPsAvailable: 65533
2716+
"2001:db8:abcd:0012::0/96":
2717+
IPsInUse: 5
2718+
DynamicIPsAvailable: 4294967291
2719+
x-go-type:
2720+
type: SubnetStatuses
2721+
kind: map
2722+
2723+
SubnetStatus:
2724+
type: "object"
2725+
x-nullable: false
2726+
x-omitempty: false
2727+
properties:
2728+
IPsInUse:
2729+
description: >
2730+
Number of IP addresses in the subnet that are in use or reserved and
2731+
are therefore unavailable for allocation, saturating at 2<sup>64</sup> - 1.
2732+
type: integer
2733+
format: uint64
2734+
x-omitempty: false
2735+
DynamicIPsAvailable:
2736+
description: >
2737+
Number of IP addresses within the network's IPRange for the subnet
2738+
that are available for allocation, saturating at 2<sup>64</sup> - 1.
2739+
type: integer
2740+
format: uint64
2741+
x-omitempty: false
2742+
26882743
EndpointResource:
26892744
type: "object"
26902745
description: >

api/types/network/inspect.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/types/network/ipam.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ type IPAMConfig struct {
2222
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
2323
}
2424

25+
type SubnetStatuses = map[netip.Prefix]SubnetStatus
26+
2527
type ipFamily string
2628

2729
const (

api/types/network/ipam_status.go

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/types/network/status.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/types/network/subnet_status.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package uint128
2+
3+
import (
4+
"encoding/binary"
5+
"math/bits"
6+
)
7+
8+
type Uint128 struct{ hi, lo uint64 }
9+
10+
func From16(b [16]byte) Uint128 {
11+
return Uint128{
12+
hi: binary.BigEndian.Uint64(b[:8]),
13+
lo: binary.BigEndian.Uint64(b[8:]),
14+
}
15+
}
16+
17+
func From(x, y uint64) Uint128 {
18+
return Uint128{hi: x, lo: y}
19+
}
20+
21+
func (x Uint128) Add(y Uint128) Uint128 {
22+
lo, carry := bits.Add64(x.lo, y.lo, 0)
23+
hi, _ := bits.Add64(x.hi, y.hi, carry)
24+
return Uint128{hi: hi, lo: lo}
25+
}
26+
27+
func (x Uint128) Sub(y Uint128) Uint128 {
28+
lo, carry := bits.Sub64(x.lo, y.lo, 0)
29+
hi, _ := bits.Sub64(x.hi, y.hi, carry)
30+
return Uint128{hi: hi, lo: lo}
31+
}
32+
33+
func (x Uint128) Lsh(n uint) Uint128 {
34+
if n > 64 {
35+
return Uint128{hi: x.lo << (n - 64)}
36+
}
37+
return Uint128{
38+
hi: x.hi<<n | x.lo>>(64-n),
39+
lo: x.lo << n,
40+
}
41+
}
42+
43+
func (x Uint128) Rsh(n uint) Uint128 {
44+
if n > 64 {
45+
return Uint128{lo: x.hi >> (n - 64)}
46+
}
47+
return Uint128{
48+
hi: x.hi >> n,
49+
lo: x.lo>>n | x.hi<<(64-n),
50+
}
51+
}
52+
53+
func (x Uint128) And(y Uint128) Uint128 {
54+
return Uint128{hi: x.hi & y.hi, lo: x.lo & y.lo}
55+
}
56+
57+
func (x Uint128) Not() Uint128 {
58+
return Uint128{hi: ^x.hi, lo: ^x.lo}
59+
}
60+
61+
func (x Uint128) Fill16(a *[16]byte) {
62+
binary.BigEndian.PutUint64(a[:8], x.hi)
63+
binary.BigEndian.PutUint64(a[8:], x.lo)
64+
}
65+
66+
func (x Uint128) Uint64() uint64 {
67+
return x.lo
68+
}
69+
70+
func (x Uint128) Uint64Sat() uint64 {
71+
if x.hi != 0 {
72+
return ^uint64(0)
73+
}
74+
return x.lo
75+
}

daemon/libnetwork/ipamapi/contract.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"net"
66
"net/netip"
77

8+
"github.com/moby/moby/api/types/network"
89
"github.com/moby/moby/v2/daemon/libnetwork/types"
910
)
1011

@@ -57,6 +58,12 @@ type Ipam interface {
5758
IsBuiltIn() bool
5859
}
5960

61+
type PoolStatuser interface {
62+
Ipam
63+
// Status returns the operational status of the specified IPAM pool.
64+
PoolStatus(poolID string) (network.SubnetStatus, error)
65+
}
66+
6067
type PoolRequest struct {
6168
// AddressSpace is a mandatory field which denotes which block of pools
6269
// should be used to make the allocation. This value is opaque, and only

daemon/libnetwork/ipams/defaultipam/address_space.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
"sync"
88

99
"github.com/containerd/log"
10+
"github.com/moby/moby/api/types/network"
1011
"github.com/moby/moby/v2/daemon/libnetwork/internal/netiputil"
12+
"github.com/moby/moby/v2/daemon/libnetwork/internal/uint128"
1113
"github.com/moby/moby/v2/daemon/libnetwork/ipamapi"
1214
"github.com/moby/moby/v2/daemon/libnetwork/ipamutils"
1315
"github.com/moby/moby/v2/daemon/libnetwork/ipbits"
@@ -369,3 +371,24 @@ func (aSpace *addrSpace) releaseAddress(nw, sub netip.Prefix, address netip.Addr
369371

370372
return p.addrs.Remove(address)
371373
}
374+
375+
func (aSpace *addrSpace) allocationStatus(nw, ipr netip.Prefix) (network.SubnetStatus, error) {
376+
aSpace.mu.Lock()
377+
defer aSpace.mu.Unlock()
378+
379+
if ipr == (netip.Prefix{}) {
380+
ipr = nw
381+
}
382+
p, ok := aSpace.subnets[nw]
383+
if !ok {
384+
return network.SubnetStatus{}, types.NotFoundErrorf("cannot find address pool for %v", nw)
385+
}
386+
387+
iprcap := uint128.From(0, 1).Lsh(uint(ipr.Addr().BitLen() - ipr.Bits()))
388+
ipralloc := uint128.From(p.addrs.AddrsInPrefix(ipr))
389+
390+
return network.SubnetStatus{
391+
IPsInUse: uint128.From(p.addrs.Len()).Uint64Sat(),
392+
DynamicIPsAvailable: iprcap.Sub(ipralloc).Uint64Sat(),
393+
}, nil
394+
}

daemon/libnetwork/ipams/defaultipam/allocator.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/netip"
99

1010
"github.com/containerd/log"
11+
"github.com/moby/moby/api/types/network"
1112
"github.com/moby/moby/v2/daemon/libnetwork/internal/addrset"
1213
"github.com/moby/moby/v2/daemon/libnetwork/internal/netiputil"
1314
"github.com/moby/moby/v2/daemon/libnetwork/ipamapi"
@@ -23,6 +24,8 @@ const (
2324
globalAddressSpace = "GlobalDefault"
2425
)
2526

27+
var _ ipamapi.PoolStatuser = &Allocator{}
28+
2629
// Register registers the default ipam driver with libnetwork. It takes
2730
// two optional address pools respectively containing the list of user-defined
2831
// address pools for 'local' and 'global' address spaces.
@@ -312,6 +315,21 @@ func getAddress(base netip.Prefix, addrSet *addrset.AddrSet, prefAddress netip.A
312315
return addr, nil
313316
}
314317

318+
// PoolStatus returns the operational status of the specified IPAM pool.
319+
func (a *Allocator) PoolStatus(poolID string) (network.SubnetStatus, error) {
320+
k, err := PoolIDFromString(poolID)
321+
if err != nil {
322+
return network.SubnetStatus{}, types.InvalidParameterErrorf("invalid pool id: %s", poolID)
323+
}
324+
325+
aSpace, err := a.getAddrSpace(k.AddressSpace, k.Is6())
326+
if err != nil {
327+
return network.SubnetStatus{}, err
328+
}
329+
330+
return aSpace.allocationStatus(k.Subnet, k.ChildSubnet)
331+
}
332+
315333
// IsBuiltIn returns true for builtin drivers
316334
func (a *Allocator) IsBuiltIn() bool {
317335
return true

0 commit comments

Comments
 (0)