mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
api,daemon: report IPAM status for network
On API v1.52 and newer, the GET /networks/{id} endpoint returns
statistics about the IPAM state for the subnets assigned to the network.
Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
@@ -2574,6 +2574,21 @@ definitions:
|
||||
type: ServiceInfo
|
||||
hints:
|
||||
nullable: false
|
||||
Status:
|
||||
description: >
|
||||
provides runtime information about the network
|
||||
such as the number of allocated IPs.
|
||||
$ref: "#/definitions/NetworkStatus"
|
||||
|
||||
NetworkStatus:
|
||||
description: >
|
||||
provides runtime information about the network
|
||||
such as the number of allocated IPs.
|
||||
type: "object"
|
||||
x-go-name: Status
|
||||
properties:
|
||||
IPAM:
|
||||
$ref: "#/definitions/IPAMStatus"
|
||||
|
||||
ServiceInfo:
|
||||
x-nullable: false
|
||||
@@ -2685,6 +2700,46 @@ definitions:
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
IPAMStatus:
|
||||
type: "object"
|
||||
x-nullable: false
|
||||
x-omitempty: false
|
||||
properties:
|
||||
Subnets:
|
||||
type: "object"
|
||||
additionalProperties:
|
||||
$ref: "#/definitions/SubnetStatus"
|
||||
example:
|
||||
"172.16.0.0/16":
|
||||
IPsInUse: 3
|
||||
DynamicIPsAvailable: 65533
|
||||
"2001:db8:abcd:0012::0/96":
|
||||
IPsInUse: 5
|
||||
DynamicIPsAvailable: 4294967291
|
||||
x-go-type:
|
||||
type: SubnetStatuses
|
||||
kind: map
|
||||
|
||||
SubnetStatus:
|
||||
type: "object"
|
||||
x-nullable: false
|
||||
x-omitempty: false
|
||||
properties:
|
||||
IPsInUse:
|
||||
description: >
|
||||
Number of IP addresses in the subnet that are in use or reserved and
|
||||
are therefore unavailable for allocation, saturating at 2<sup>64</sup> - 1.
|
||||
type: integer
|
||||
format: uint64
|
||||
x-omitempty: false
|
||||
DynamicIPsAvailable:
|
||||
description: >
|
||||
Number of IP addresses within the network's IPRange for the subnet
|
||||
that are available for allocation, saturating at 2<sup>64</sup> - 1.
|
||||
type: integer
|
||||
format: uint64
|
||||
x-omitempty: false
|
||||
|
||||
EndpointResource:
|
||||
type: "object"
|
||||
description: >
|
||||
|
||||
@@ -20,4 +20,8 @@ type Inspect struct {
|
||||
// swarm scope networks, and omitted for local scope networks.
|
||||
//
|
||||
Services map[string]ServiceInfo `json:"Services,omitempty"`
|
||||
|
||||
// provides runtime information about the network such as the number of allocated IPs.
|
||||
//
|
||||
Status *Status `json:"Status,omitempty"`
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ type IPAMConfig struct {
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
type SubnetStatuses = map[netip.Prefix]SubnetStatus
|
||||
|
||||
type ipFamily string
|
||||
|
||||
const (
|
||||
|
||||
16
api/types/network/ipam_status.go
Normal file
16
api/types/network/ipam_status.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// IPAMStatus IPAM status
|
||||
//
|
||||
// swagger:model IPAMStatus
|
||||
type IPAMStatus struct {
|
||||
|
||||
// subnets
|
||||
// Example: {"172.16.0.0/16":{"DynamicIPsAvailable":65533,"IPsInUse":3},"2001:db8:abcd:0012::0/96":{"DynamicIPsAvailable":4294967291,"IPsInUse":5}}
|
||||
Subnets SubnetStatuses `json:"Subnets,omitempty"`
|
||||
}
|
||||
15
api/types/network/status.go
Normal file
15
api/types/network/status.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// Status provides runtime information about the network such as the number of allocated IPs.
|
||||
//
|
||||
// swagger:model Status
|
||||
type Status struct {
|
||||
|
||||
// IPAM
|
||||
IPAM IPAMStatus `json:"IPAM"`
|
||||
}
|
||||
20
api/types/network/subnet_status.go
Normal file
20
api/types/network/subnet_status.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// SubnetStatus subnet status
|
||||
//
|
||||
// swagger:model SubnetStatus
|
||||
type SubnetStatus struct {
|
||||
|
||||
// Number of IP addresses in the subnet that are in use or reserved and are therefore unavailable for allocation, saturating at 2<sup>64</sup> - 1.
|
||||
//
|
||||
IPsInUse uint64 `json:"IPsInUse"`
|
||||
|
||||
// Number of IP addresses within the network's IPRange for the subnet that are available for allocation, saturating at 2<sup>64</sup> - 1.
|
||||
//
|
||||
DynamicIPsAvailable uint64 `json:"DynamicIPsAvailable"`
|
||||
}
|
||||
@@ -66,3 +66,10 @@ func (x Uint128) Fill16(a *[16]byte) {
|
||||
func (x Uint128) Uint64() uint64 {
|
||||
return x.lo
|
||||
}
|
||||
|
||||
func (x Uint128) Uint64Sat() uint64 {
|
||||
if x.hi != 0 {
|
||||
return ^uint64(0)
|
||||
}
|
||||
return x.lo
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/types"
|
||||
)
|
||||
|
||||
@@ -57,6 +58,12 @@ type Ipam interface {
|
||||
IsBuiltIn() bool
|
||||
}
|
||||
|
||||
type PoolStatuser interface {
|
||||
Ipam
|
||||
// Status returns the operational status of the specified IPAM pool.
|
||||
PoolStatus(poolID string) (network.SubnetStatus, error)
|
||||
}
|
||||
|
||||
type PoolRequest struct {
|
||||
// AddressSpace is a mandatory field which denotes which block of pools
|
||||
// should be used to make the allocation. This value is opaque, and only
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/internal/netiputil"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/internal/uint128"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/ipamapi"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/ipamutils"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/ipbits"
|
||||
@@ -369,3 +371,24 @@ func (aSpace *addrSpace) releaseAddress(nw, sub netip.Prefix, address netip.Addr
|
||||
|
||||
return p.addrs.Remove(address)
|
||||
}
|
||||
|
||||
func (aSpace *addrSpace) allocationStatus(nw, ipr netip.Prefix) (network.SubnetStatus, error) {
|
||||
aSpace.mu.Lock()
|
||||
defer aSpace.mu.Unlock()
|
||||
|
||||
if ipr == (netip.Prefix{}) {
|
||||
ipr = nw
|
||||
}
|
||||
p, ok := aSpace.subnets[nw]
|
||||
if !ok {
|
||||
return network.SubnetStatus{}, types.NotFoundErrorf("cannot find address pool for %v", nw)
|
||||
}
|
||||
|
||||
iprcap := uint128.From(0, 1).Lsh(uint(ipr.Addr().BitLen() - ipr.Bits()))
|
||||
ipralloc := uint128.From(p.addrs.AddrsInPrefix(ipr))
|
||||
|
||||
return network.SubnetStatus{
|
||||
IPsInUse: uint128.From(p.addrs.Len()).Uint64Sat(),
|
||||
DynamicIPsAvailable: iprcap.Sub(ipralloc).Uint64Sat(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/internal/addrset"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/internal/netiputil"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/ipamapi"
|
||||
@@ -23,6 +24,8 @@ const (
|
||||
globalAddressSpace = "GlobalDefault"
|
||||
)
|
||||
|
||||
var _ ipamapi.PoolStatuser = &Allocator{}
|
||||
|
||||
// Register registers the default ipam driver with libnetwork. It takes
|
||||
// two optional address pools respectively containing the list of user-defined
|
||||
// address pools for 'local' and 'global' address spaces.
|
||||
@@ -312,6 +315,21 @@ func getAddress(base netip.Prefix, addrSet *addrset.AddrSet, prefAddress netip.A
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// PoolStatus returns the operational status of the specified IPAM pool.
|
||||
func (a *Allocator) PoolStatus(poolID string) (network.SubnetStatus, error) {
|
||||
k, err := PoolIDFromString(poolID)
|
||||
if err != nil {
|
||||
return network.SubnetStatus{}, types.InvalidParameterErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace, k.Is6())
|
||||
if err != nil {
|
||||
return network.SubnetStatus{}, err
|
||||
}
|
||||
|
||||
return aSpace.allocationStatus(k.Subnet, k.ChildSubnet)
|
||||
}
|
||||
|
||||
// IsBuiltIn returns true for builtin drivers
|
||||
func (a *Allocator) IsBuiltIn() bool {
|
||||
return true
|
||||
|
||||
@@ -9,12 +9,14 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/v2/daemon/internal/sliceutil"
|
||||
"github.com/moby/moby/v2/daemon/internal/stringid"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/datastore"
|
||||
@@ -30,6 +32,7 @@ import (
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/scope"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/types"
|
||||
"github.com/moby/moby/v2/errdefs"
|
||||
"github.com/moby/moby/v2/internal/iterutil"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -2171,3 +2174,37 @@ func (n *Network) deleteLoadBalancerSandbox() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Network) IPAMStatus(ctx context.Context) (network.IPAMStatus, error) {
|
||||
status := network.IPAMStatus{
|
||||
Subnets: make(map[netip.Prefix]network.SubnetStatus),
|
||||
}
|
||||
|
||||
if n.hasSpecialDriver() {
|
||||
// Special drivers do not assign addresses from IPAM
|
||||
return status, nil
|
||||
}
|
||||
|
||||
ipamdriver, _, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
ipam, ok := ipamdriver.(ipamapi.PoolStatuser)
|
||||
if !ok {
|
||||
return status, nil
|
||||
}
|
||||
|
||||
var errs []error
|
||||
info4, info6 := n.IpamInfo()
|
||||
for info := range iterutil.Chain(slices.Values(info4), slices.Values(info6)) {
|
||||
pstat, err := ipam.PoolStatus(info.PoolID)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("failed to retrieve pool %s status: %w", info.PoolID, err))
|
||||
continue
|
||||
}
|
||||
prefix, _ := netiputil.ToPrefix(info.Pool)
|
||||
status.Subnets[prefix] = pstat
|
||||
}
|
||||
|
||||
return status, errors.Join(errs...)
|
||||
}
|
||||
|
||||
@@ -602,6 +602,19 @@ func (daemon *Daemon) GetNetworks(filter network.Filter, config backend.NetworkL
|
||||
if config.WithServices {
|
||||
nr.Services = buildServiceAttachments(n)
|
||||
}
|
||||
if config.WithStatus {
|
||||
ipam, err := n.IPAMStatus(context.TODO())
|
||||
if err != nil {
|
||||
log.G(context.TODO()).WithFields(log.Fields{
|
||||
"network": n.Name(),
|
||||
"id": n.ID(),
|
||||
"error": err,
|
||||
}).Warning("Error encountered while gathering IPAM status for network")
|
||||
}
|
||||
nr.Status = &networktypes.Status{
|
||||
IPAM: ipam,
|
||||
}
|
||||
}
|
||||
networks = append(networks, nr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,4 +215,5 @@ type PluginDisableConfig struct {
|
||||
// NetworkListConfig stores the options available for listing networks
|
||||
type NetworkListConfig struct {
|
||||
WithServices bool
|
||||
WithStatus bool
|
||||
}
|
||||
|
||||
@@ -146,7 +146,10 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
|
||||
}
|
||||
filter.IDAlsoMatchesName = true
|
||||
|
||||
networks, _ := n.backend.GetNetworks(filter, backend.NetworkListConfig{WithServices: verbose})
|
||||
networks, _ := n.backend.GetNetworks(filter, backend.NetworkListConfig{
|
||||
WithServices: verbose,
|
||||
WithStatus: versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.52"),
|
||||
})
|
||||
for _, nw := range networks {
|
||||
if nw.ID == term {
|
||||
return httputils.WriteJSON(w, http.StatusOK, nw)
|
||||
|
||||
@@ -60,15 +60,18 @@ EOT
|
||||
# TODO: Restore when go-swagger is updated
|
||||
# See https://github.com/moby/moby/pull/47526#discussion_r1551800022
|
||||
|
||||
generate_model types/network --keep-spec-order <<- 'EOT'
|
||||
generate_model types/network --keep-spec-order --additional-initialism=IPAM <<- 'EOT'
|
||||
ConfigReference
|
||||
EndpointResource
|
||||
IPAMStatus
|
||||
Network
|
||||
NetworkCreateResponse
|
||||
NetworkInspect
|
||||
NetworkStatus
|
||||
NetworkSummary
|
||||
NetworkTaskInfo
|
||||
PeerInfo
|
||||
SubnetStatus
|
||||
EOT
|
||||
|
||||
generate_model types/plugin <<- 'EOT'
|
||||
|
||||
@@ -137,16 +137,20 @@ func WithIPAM(subnet, gateway string) func(*client.NetworkCreateOptions) {
|
||||
|
||||
// WithIPAMRange adds an IPAM with the specified Subnet, IPRange and Gateway to the network
|
||||
func WithIPAMRange(subnet, iprange, gateway string) func(*client.NetworkCreateOptions) {
|
||||
return WithIPAMConfig(network.IPAMConfig{
|
||||
Subnet: subnet,
|
||||
IPRange: iprange,
|
||||
Gateway: gateway,
|
||||
AuxAddress: map[string]string{},
|
||||
})
|
||||
}
|
||||
|
||||
// WithIPAMConfig adds the provided IPAM configurations to the network
|
||||
func WithIPAMConfig(configs ...network.IPAMConfig) func(*client.NetworkCreateOptions) {
|
||||
return func(n *client.NetworkCreateOptions) {
|
||||
if n.IPAM == nil {
|
||||
n.IPAM = &network.IPAM{}
|
||||
}
|
||||
|
||||
n.IPAM.Config = append(n.IPAM.Config, network.IPAMConfig{
|
||||
Subnet: subnet,
|
||||
IPRange: iprange,
|
||||
Gateway: gateway,
|
||||
AuxAddress: map[string]string{},
|
||||
})
|
||||
n.IPAM.Config = append(n.IPAM.Config, configs...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package bridge
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
@@ -1064,3 +1065,154 @@ func TestPortBindingBackfillingForOlderContainers(t *testing.T) {
|
||||
}}
|
||||
assert.DeepEqual(t, expMappings, inspect.HostConfig.PortBindings)
|
||||
}
|
||||
|
||||
func TestBridgeIPAMStatus(t *testing.T) {
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t, client.WithVersion("1.52"))
|
||||
|
||||
checkSubnets := func(
|
||||
netName string, want networktypes.SubnetStatuses) bool {
|
||||
t.Helper()
|
||||
nw, err := c.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
|
||||
if assert.Check(t, err) && assert.Check(t, nw.Status != nil) {
|
||||
return assert.Check(t, is.DeepEqual(want, nw.Status.IPAM.Subnets))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
t.Run("DualStack", func(t *testing.T) {
|
||||
const (
|
||||
netName = "testipambridge"
|
||||
|
||||
ipv4gw = "192.168.0.1"
|
||||
ipv4Range = "192.168.0.64/31"
|
||||
prefIPv4OutOfRange = "192.168.0.129"
|
||||
auxIPv4FromRange = "192.168.0.65"
|
||||
auxIPv4OutOfRange = "192.168.0.128"
|
||||
|
||||
ipv6gw = "2001:db8:abcd::1"
|
||||
ipv6Range = "2001:db8:abcd::/120"
|
||||
prefIPv6OutOfRange = "2001:db8:abcd::9000"
|
||||
auxIPv6FromRange = "2001:db8:abcd::2a"
|
||||
auxIPv6OutOfRange = "2001:db8:abcd::8000"
|
||||
)
|
||||
var (
|
||||
cidrv4 = netip.MustParsePrefix("192.168.0.0/24")
|
||||
cidrv6 = netip.MustParsePrefix("2001:db8:abcd::/64")
|
||||
)
|
||||
|
||||
network.CreateNoError(ctx, t, c, netName,
|
||||
network.WithIPv4(true),
|
||||
network.WithIPAMConfig(networktypes.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: ipv4Range,
|
||||
Gateway: ipv4gw,
|
||||
AuxAddress: map[string]string{
|
||||
"reserved": auxIPv4FromRange,
|
||||
"reserved_1": auxIPv4OutOfRange,
|
||||
}}),
|
||||
network.WithIPv6(),
|
||||
network.WithIPAMConfig(networktypes.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: ipv6Range,
|
||||
Gateway: ipv6gw,
|
||||
AuxAddress: map[string]string{
|
||||
"reserved1": auxIPv6FromRange,
|
||||
"reserved2": auxIPv6OutOfRange,
|
||||
},
|
||||
}),
|
||||
)
|
||||
defer c.NetworkRemove(ctx, netName)
|
||||
|
||||
checkSubnets(netName, map[netip.Prefix]networktypes.SubnetStatus{
|
||||
cidrv4: {
|
||||
// 1 subnet + 1 gateway + 1 broadcast + 2 aux addresses
|
||||
IPsInUse: 5,
|
||||
// IPv4 /31 IPRange (2 addresses) - aux in-range
|
||||
DynamicIPsAvailable: 1,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 4, // 1 gateway + 1 anycast + 2 aux addresses
|
||||
DynamicIPsAvailable: 253, // IPv6 /120 IPRange (256 addresses) - 1 router-anycast - 1 gateway - 1 aux in-range
|
||||
},
|
||||
})
|
||||
|
||||
func() {
|
||||
// From IPRange pool: both counters should be changed by 1
|
||||
id := ctr.Run(ctx, t, c, ctr.WithNetworkMode(netName))
|
||||
defer c.ContainerRemove(ctx, id, client.ContainerRemoveOptions{Force: true})
|
||||
|
||||
checkSubnets(netName, map[netip.Prefix]networktypes.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 6,
|
||||
DynamicIPsAvailable: 0,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 252,
|
||||
},
|
||||
})
|
||||
|
||||
// Out of IPRange pools: subnet counter should be changed by 1
|
||||
id = ctr.Run(ctx, t, c,
|
||||
ctr.WithNetworkMode(netName),
|
||||
ctr.WithIPv4(netName, prefIPv4OutOfRange),
|
||||
ctr.WithIPv6(netName, prefIPv6OutOfRange),
|
||||
)
|
||||
defer c.ContainerRemove(ctx, id, client.ContainerRemoveOptions{Force: true})
|
||||
|
||||
checkSubnets(netName, map[netip.Prefix]networktypes.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 7,
|
||||
DynamicIPsAvailable: 0, // unchanged
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 6,
|
||||
DynamicIPsAvailable: 252, // unchanged
|
||||
},
|
||||
})
|
||||
}()
|
||||
|
||||
// Counters should decrease after container removal
|
||||
checkSubnets(netName, map[netip.Prefix]networktypes.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 1,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 4,
|
||||
DynamicIPsAvailable: 253,
|
||||
},
|
||||
})
|
||||
|
||||
oldc := d.NewClientT(t, client.WithVersion("1.51"))
|
||||
nw, err := oldc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
|
||||
if assert.Check(t, err) {
|
||||
assert.Check(t, nw.Status == nil, "expected nil Status with API version 1.51")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("IPv6", func(t *testing.T) {
|
||||
const netName = "testipambridgev6"
|
||||
cidr := netip.MustParsePrefix("2001:db8:abcd::/56")
|
||||
network.CreateNoError(ctx, t, c, netName,
|
||||
network.WithIPv4(false),
|
||||
network.WithIPv6(),
|
||||
network.WithIPAMConfig(networktypes.IPAMConfig{
|
||||
Subnet: cidr.String(),
|
||||
}),
|
||||
)
|
||||
defer c.NetworkRemove(ctx, netName)
|
||||
|
||||
checkSubnets(netName, map[netip.Prefix]networktypes.SubnetStatus{
|
||||
cidr: {
|
||||
IPsInUse: 2,
|
||||
DynamicIPsAvailable: math.MaxUint64,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ package ipvlan
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
dclient "github.com/moby/moby/client"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/netlabel"
|
||||
@@ -484,6 +486,11 @@ func TestIpvlanIPAM(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
subnetv4 = netip.MustParsePrefix("10.42.42.0/24")
|
||||
subnetv6 = netip.MustParsePrefix("2001:db8:abcd::/64")
|
||||
)
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := testutil.StartSpan(ctx, t)
|
||||
@@ -492,6 +499,15 @@ func TestIpvlanIPAM(t *testing.T) {
|
||||
netOpts := []func(*dclient.NetworkCreateOptions){
|
||||
net.WithIPvlan("", "l3"),
|
||||
net.WithIPv4(tc.enableIPv4),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv4.String(),
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv6.String(),
|
||||
IPRange: "2001:db8:abcd::100/120",
|
||||
},
|
||||
),
|
||||
}
|
||||
if tc.enableIPv6 {
|
||||
netOpts = append(netOpts, net.WithIPv6())
|
||||
@@ -509,10 +525,15 @@ func TestIpvlanIPAM(t *testing.T) {
|
||||
assert.Check(t, is.Contains(loRes.Combined(), " inet "))
|
||||
assert.Check(t, is.Contains(loRes.Combined(), " inet6 "))
|
||||
|
||||
wantSubnetStatus := make(map[netip.Prefix]network.SubnetStatus)
|
||||
eth0Res := container.ExecT(ctx, t, c, id, []string{"ip", "a", "show", "dev", "eth0"})
|
||||
if tc.enableIPv4 || tc.expIPv4 {
|
||||
assert.Check(t, is.Contains(eth0Res.Combined(), " inet "),
|
||||
"Expected IPv4 in: %s", eth0Res.Combined())
|
||||
wantSubnetStatus[subnetv4] = network.SubnetStatus{
|
||||
IPsInUse: 3, // network, broadcast, container
|
||||
DynamicIPsAvailable: 253,
|
||||
}
|
||||
} else {
|
||||
assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet "),
|
||||
"Expected no IPv4 in: %s", eth0Res.Combined())
|
||||
@@ -520,6 +541,10 @@ func TestIpvlanIPAM(t *testing.T) {
|
||||
if tc.enableIPv6 {
|
||||
assert.Check(t, is.Contains(eth0Res.Combined(), " inet6 "),
|
||||
"Expected IPv6 in: %s", eth0Res.Combined())
|
||||
wantSubnetStatus[subnetv6] = network.SubnetStatus{
|
||||
IPsInUse: 2, // subnet-router anycast, container
|
||||
DynamicIPsAvailable: 255,
|
||||
}
|
||||
} else {
|
||||
assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet6 "),
|
||||
"Expected no IPv6 in: %s", eth0Res.Combined())
|
||||
@@ -531,10 +556,188 @@ func TestIpvlanIPAM(t *testing.T) {
|
||||
expDisableIPv6 = "0"
|
||||
}
|
||||
assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), expDisableIPv6))
|
||||
|
||||
cc := d.NewClientT(t, dclient.WithVersion("1.52"))
|
||||
inspect, err := cc.NetworkInspect(ctx, netName, dclient.NetworkInspectOptions{})
|
||||
if assert.Check(t, err) && assert.Check(t, inspect.Status != nil) {
|
||||
assert.Check(t, is.DeepEqual(wantSubnetStatus, inspect.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
|
||||
}
|
||||
cc.Close()
|
||||
cc = d.NewClientT(t, dclient.WithVersion("1.51"))
|
||||
inspect, err = cc.NetworkInspect(ctx, netName, dclient.NetworkInspectOptions{})
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, inspect.Status == nil)
|
||||
cc.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// IPVLAN networks are allowed to be assigned IPAM subnets that overlap with
|
||||
// other IPVLAN networks' IPAM subnets. But no two IPVLAN endpoints may be
|
||||
// assigned the same address, even when the endpoints are attached to different
|
||||
// networks. The assignment of an address to an endpoint on one network may
|
||||
// therefore reduce the number of available addresses to assign to other
|
||||
// networks' endpoints.
|
||||
func TestIpvlanIPAMOverlap(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon)
|
||||
skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
|
||||
checkNetworkIPAMState := func(networkID string, want map[netip.Prefix]network.SubnetStatus) bool {
|
||||
t.Helper()
|
||||
nw, err := c.NetworkInspect(ctx, networkID, dclient.NetworkInspectOptions{})
|
||||
if assert.Check(t, err) && assert.Check(t, nw.Status != nil) {
|
||||
return assert.Check(t, is.DeepEqual(want, nw.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Create three networks with joined and overlapped IPAM ranges
|
||||
// and verify that the IPAM state is correct
|
||||
|
||||
const (
|
||||
netName1 = "ipvlannet1"
|
||||
netName2 = "ipvlannet2"
|
||||
netName3 = "ipvlannet3"
|
||||
)
|
||||
cidrv4 := netip.MustParsePrefix("192.168.0.0/24")
|
||||
cidrv6 := netip.MustParsePrefix("2001:db8:abcd::/64")
|
||||
|
||||
net.CreateNoError(ctx, t, c, netName1,
|
||||
net.WithIPvlan("", "l3"),
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/25",
|
||||
Gateway: "192.168.0.1",
|
||||
AuxAddress: map[string]string{
|
||||
"reserved": "192.168.0.100",
|
||||
},
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/124",
|
||||
},
|
||||
),
|
||||
)
|
||||
defer c.NetworkRemove(ctx, netName1)
|
||||
assert.Check(t, n.IsNetworkAvailable(ctx, c, netName1))
|
||||
|
||||
checkNetworkIPAMState(netName1, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 4,
|
||||
DynamicIPsAvailable: 125,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 1,
|
||||
DynamicIPsAvailable: 15,
|
||||
},
|
||||
})
|
||||
|
||||
net.CreateNoError(ctx, t, c, netName2,
|
||||
net.WithIPvlan("", "l3"),
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/24",
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/120",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
defer c.NetworkRemove(ctx, netName2)
|
||||
assert.Check(t, n.IsNetworkAvailable(ctx, c, netName2))
|
||||
|
||||
checkNetworkIPAMState(netName2, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 4,
|
||||
DynamicIPsAvailable: 252,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 1,
|
||||
DynamicIPsAvailable: 255,
|
||||
},
|
||||
})
|
||||
|
||||
net.CreateNoError(ctx, t, c, netName3,
|
||||
net.WithIPvlan("", "l3"),
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.128/25",
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::80/124",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
defer c.NetworkRemove(ctx, netName3)
|
||||
assert.Check(t, n.IsNetworkAvailable(ctx, c, netName3))
|
||||
|
||||
checkNetworkIPAMState(netName3, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 4,
|
||||
DynamicIPsAvailable: 127,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 1,
|
||||
DynamicIPsAvailable: 16,
|
||||
},
|
||||
})
|
||||
|
||||
// Create a container on one of the networks
|
||||
id := container.Run(ctx, t, c, container.WithNetworkMode(netName1))
|
||||
defer c.ContainerRemove(ctx, id, dclient.ContainerRemoveOptions{Force: true})
|
||||
|
||||
// Verify that the IPAM status of all three networks are affected.
|
||||
checkNetworkIPAMState(netName1, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 124,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 2,
|
||||
DynamicIPsAvailable: 14,
|
||||
},
|
||||
})
|
||||
|
||||
checkNetworkIPAMState(netName2, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 251,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 2,
|
||||
DynamicIPsAvailable: 254,
|
||||
},
|
||||
})
|
||||
|
||||
checkNetworkIPAMState(netName3, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 127,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 2,
|
||||
DynamicIPsAvailable: 16,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestIPVlanDNS checks whether DNS is forwarded, for combinations of l2/l3 mode,
|
||||
// with/without a parent interface, and with '--internal'. Note that, there's no
|
||||
// attempt here to give the ipvlan network external connectivity - when this test
|
||||
|
||||
@@ -4,10 +4,12 @@ package macvlan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/moby/moby/api/types/network"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/moby/moby/v2/daemon/libnetwork/netlabel"
|
||||
@@ -480,6 +482,10 @@ func TestMacvlanIPAM(t *testing.T) {
|
||||
expIPv4: true,
|
||||
},
|
||||
}
|
||||
var (
|
||||
subnetv4 = netip.MustParsePrefix("10.66.77.0/24")
|
||||
subnetv6 = netip.MustParsePrefix("2001:db8:abcd::/64")
|
||||
)
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -490,6 +496,21 @@ func TestMacvlanIPAM(t *testing.T) {
|
||||
net.WithMacvlan(""),
|
||||
net.WithOption("macvlan_mode", "bridge"),
|
||||
net.WithIPv4(tc.enableIPv4),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv4.String(),
|
||||
IPRange: "10.66.77.64/30",
|
||||
Gateway: "10.66.77.1",
|
||||
AuxAddress: map[string]string{
|
||||
"inrange": "10.66.77.65",
|
||||
"outofrange": "10.66.77.128",
|
||||
},
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: subnetv6.String(),
|
||||
IPRange: "2001:db8:abcd::/120",
|
||||
},
|
||||
),
|
||||
}
|
||||
if tc.enableIPv6 {
|
||||
netOpts = append(netOpts, net.WithIPv6())
|
||||
@@ -507,10 +528,15 @@ func TestMacvlanIPAM(t *testing.T) {
|
||||
assert.Check(t, is.Contains(loRes.Combined(), " inet "))
|
||||
assert.Check(t, is.Contains(loRes.Combined(), " inet6 "))
|
||||
|
||||
wantSubnetStatus := make(map[netip.Prefix]network.SubnetStatus)
|
||||
eth0Res := container.ExecT(ctx, t, c, id, []string{"ip", "a", "show", "dev", "eth0"})
|
||||
if tc.enableIPv4 || tc.expIPv4 {
|
||||
assert.Check(t, is.Contains(eth0Res.Combined(), " inet "),
|
||||
"Expected IPv4 in: %s", eth0Res.Combined())
|
||||
wantSubnetStatus[subnetv4] = network.SubnetStatus{
|
||||
IPsInUse: 6, // network, gateway, 2x aux, broadcast, container
|
||||
DynamicIPsAvailable: 2, // container, aux "inrange"
|
||||
}
|
||||
} else {
|
||||
assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet "),
|
||||
"Expected no IPv4 in: %s", eth0Res.Combined())
|
||||
@@ -518,6 +544,10 @@ func TestMacvlanIPAM(t *testing.T) {
|
||||
if tc.enableIPv6 {
|
||||
assert.Check(t, is.Contains(eth0Res.Combined(), " inet6 "),
|
||||
"Expected IPv6 in: %s", eth0Res.Combined())
|
||||
wantSubnetStatus[subnetv6] = network.SubnetStatus{
|
||||
IPsInUse: 2, // subnet-router anycast, container
|
||||
DynamicIPsAvailable: 254,
|
||||
}
|
||||
} else {
|
||||
assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet6 "),
|
||||
"Expected no IPv6 in: %s", eth0Res.Combined())
|
||||
@@ -529,10 +559,188 @@ func TestMacvlanIPAM(t *testing.T) {
|
||||
expDisableIPv6 = "0"
|
||||
}
|
||||
assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), expDisableIPv6))
|
||||
|
||||
cc := d.NewClientT(t, client.WithVersion("1.52"))
|
||||
inspect, err := cc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
|
||||
if assert.Check(t, err) && assert.Check(t, inspect.Status != nil) {
|
||||
assert.Check(t, is.DeepEqual(wantSubnetStatus, inspect.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
|
||||
}
|
||||
cc.Close()
|
||||
cc = d.NewClientT(t, client.WithVersion("1.51"))
|
||||
inspect, err = cc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
|
||||
assert.Check(t, err)
|
||||
assert.Check(t, inspect.Status == nil)
|
||||
cc.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MACVLAN networks are allowed to be assigned IPAM subnets that overlap with
|
||||
// other MACVLAN networks' IPAM subnets. But no two MACVLAN endpoints may be
|
||||
// assigned the same address, even when the endpoints are attached to different
|
||||
// networks. The assignment of an address to an endpoint on one network may
|
||||
// therefore reduce the number of available addresses to assign to other
|
||||
// networks' endpoints.
|
||||
func TestMacvlanIPAMOverlap(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon)
|
||||
skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
d := daemon.New(t)
|
||||
d.StartWithBusybox(ctx, t)
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
|
||||
checkNetworkIPAMState := func(networkID string, want map[netip.Prefix]network.SubnetStatus) bool {
|
||||
t.Helper()
|
||||
nw, err := c.NetworkInspect(ctx, networkID, client.NetworkInspectOptions{})
|
||||
if assert.Check(t, err) && assert.Check(t, nw.Status != nil) {
|
||||
return assert.Check(t, is.DeepEqual(want, nw.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Create three networks with joined and overlapped IPAM ranges
|
||||
// and verify that the IPAM state is correct
|
||||
|
||||
const (
|
||||
netName1 = "macvlannet1"
|
||||
netName2 = "macvlannet2"
|
||||
netName3 = "macvlannet3"
|
||||
)
|
||||
cidrv4 := netip.MustParsePrefix("192.168.0.0/24")
|
||||
cidrv6 := netip.MustParsePrefix("2001:db8:abcd::/64")
|
||||
|
||||
net.CreateNoError(ctx, t, c, netName1,
|
||||
net.WithMacvlan(""),
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/25",
|
||||
Gateway: "192.168.0.1",
|
||||
AuxAddress: map[string]string{
|
||||
"reserved": "192.168.0.100",
|
||||
},
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/124",
|
||||
},
|
||||
),
|
||||
)
|
||||
defer c.NetworkRemove(ctx, netName1)
|
||||
assert.Check(t, n.IsNetworkAvailable(ctx, c, netName1))
|
||||
|
||||
checkNetworkIPAMState(netName1, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 4,
|
||||
DynamicIPsAvailable: 125,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 1,
|
||||
DynamicIPsAvailable: 15,
|
||||
},
|
||||
})
|
||||
|
||||
net.CreateNoError(ctx, t, c, netName2,
|
||||
net.WithMacvlan(""),
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.0/24",
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::/120",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
defer c.NetworkRemove(ctx, netName2)
|
||||
assert.Check(t, n.IsNetworkAvailable(ctx, c, netName2))
|
||||
|
||||
checkNetworkIPAMState(netName2, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 4,
|
||||
DynamicIPsAvailable: 252,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 1,
|
||||
DynamicIPsAvailable: 255,
|
||||
},
|
||||
})
|
||||
|
||||
net.CreateNoError(ctx, t, c, netName3,
|
||||
net.WithMacvlan(""),
|
||||
net.WithIPv6(),
|
||||
net.WithIPAMConfig(
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv4.String(),
|
||||
IPRange: "192.168.0.128/25",
|
||||
},
|
||||
network.IPAMConfig{
|
||||
Subnet: cidrv6.String(),
|
||||
IPRange: "2001:db8:abcd::80/124",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
defer c.NetworkRemove(ctx, netName3)
|
||||
assert.Check(t, n.IsNetworkAvailable(ctx, c, netName3))
|
||||
|
||||
checkNetworkIPAMState(netName3, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 4,
|
||||
DynamicIPsAvailable: 127,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 1,
|
||||
DynamicIPsAvailable: 16,
|
||||
},
|
||||
})
|
||||
|
||||
// Create a container on one of the networks
|
||||
id := container.Run(ctx, t, c, container.WithNetworkMode(netName1))
|
||||
defer c.ContainerRemove(ctx, id, client.ContainerRemoveOptions{Force: true})
|
||||
|
||||
// Verify that the IPAM status of all three networks are affected.
|
||||
checkNetworkIPAMState(netName1, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 124,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 2,
|
||||
DynamicIPsAvailable: 14,
|
||||
},
|
||||
})
|
||||
|
||||
checkNetworkIPAMState(netName2, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 251,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 2,
|
||||
DynamicIPsAvailable: 254,
|
||||
},
|
||||
})
|
||||
|
||||
checkNetworkIPAMState(netName3, map[netip.Prefix]network.SubnetStatus{
|
||||
cidrv4: {
|
||||
IPsInUse: 5,
|
||||
DynamicIPsAvailable: 127,
|
||||
},
|
||||
cidrv6: {
|
||||
IPsInUse: 2,
|
||||
DynamicIPsAvailable: 16,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestMACVlanDNS checks whether DNS is forwarded, with/without a parent
|
||||
// interface, and with '--internal'. Note that there's no attempt here to give
|
||||
// the macvlan network external connectivity - when this test supplies a parent
|
||||
|
||||
4
vendor/github.com/moby/moby/api/types/network/inspect.go
generated
vendored
4
vendor/github.com/moby/moby/api/types/network/inspect.go
generated
vendored
@@ -20,4 +20,8 @@ type Inspect struct {
|
||||
// swarm scope networks, and omitted for local scope networks.
|
||||
//
|
||||
Services map[string]ServiceInfo `json:"Services,omitempty"`
|
||||
|
||||
// provides runtime information about the network such as the number of allocated IPs.
|
||||
//
|
||||
Status *Status `json:"Status,omitempty"`
|
||||
}
|
||||
|
||||
2
vendor/github.com/moby/moby/api/types/network/ipam.go
generated
vendored
2
vendor/github.com/moby/moby/api/types/network/ipam.go
generated
vendored
@@ -22,6 +22,8 @@ type IPAMConfig struct {
|
||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
type SubnetStatuses = map[netip.Prefix]SubnetStatus
|
||||
|
||||
type ipFamily string
|
||||
|
||||
const (
|
||||
|
||||
16
vendor/github.com/moby/moby/api/types/network/ipam_status.go
generated
vendored
Normal file
16
vendor/github.com/moby/moby/api/types/network/ipam_status.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// IPAMStatus IPAM status
|
||||
//
|
||||
// swagger:model IPAMStatus
|
||||
type IPAMStatus struct {
|
||||
|
||||
// subnets
|
||||
// Example: {"172.16.0.0/16":{"DynamicIPsAvailable":65533,"IPsInUse":3},"2001:db8:abcd:0012::0/96":{"DynamicIPsAvailable":4294967291,"IPsInUse":5}}
|
||||
Subnets SubnetStatuses `json:"Subnets,omitempty"`
|
||||
}
|
||||
15
vendor/github.com/moby/moby/api/types/network/status.go
generated
vendored
Normal file
15
vendor/github.com/moby/moby/api/types/network/status.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// Status provides runtime information about the network such as the number of allocated IPs.
|
||||
//
|
||||
// swagger:model Status
|
||||
type Status struct {
|
||||
|
||||
// IPAM
|
||||
IPAM IPAMStatus `json:"IPAM"`
|
||||
}
|
||||
20
vendor/github.com/moby/moby/api/types/network/subnet_status.go
generated
vendored
Normal file
20
vendor/github.com/moby/moby/api/types/network/subnet_status.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package network
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// SubnetStatus subnet status
|
||||
//
|
||||
// swagger:model SubnetStatus
|
||||
type SubnetStatus struct {
|
||||
|
||||
// Number of IP addresses in the subnet that are in use or reserved and are therefore unavailable for allocation, saturating at 2<sup>64</sup> - 1.
|
||||
//
|
||||
IPsInUse uint64 `json:"IPsInUse"`
|
||||
|
||||
// Number of IP addresses within the network's IPRange for the subnet that are available for allocation, saturating at 2<sup>64</sup> - 1.
|
||||
//
|
||||
DynamicIPsAvailable uint64 `json:"DynamicIPsAvailable"`
|
||||
}
|
||||
Reference in New Issue
Block a user