Skip to content

Commit

Permalink
Add ip-utilization info into network inspect
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Epifanov <[email protected]>
  • Loading branch information
aepifanov committed Jan 29, 2025
1 parent 04f9d3e commit bd020f4
Show file tree
Hide file tree
Showing 28 changed files with 916 additions and 505 deletions.
6 changes: 6 additions & 0 deletions api/server/router/network/network_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
// network with the same ID (from local scope previously)
if _, ok := listByFullName[nw.ID]; !ok {
listByFullName[nw.ID] = nw
} else {
// If the network is already in the listByFullName, update the available IPs from the swarm scope
n := listByFullName[nw.ID]
n.AvailableIps = nw.AvailableIps
n.AvailableAllocationIps = nw.AvailableAllocationIps
listByFullName[nw.ID] = n
}
}
if strings.HasPrefix(nw.ID, term) {
Expand Down
10 changes: 6 additions & 4 deletions api/types/network/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ type IPAM struct {

// IPAMConfig represents IPAM configurations
type IPAMConfig struct {
Subnet string `json:",omitempty"`
IPRange string `json:",omitempty"`
Gateway string `json:",omitempty"`
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
Subnet string `json:",omitempty"`
IPRange string `json:",omitempty"`
Gateway string `json:",omitempty"`
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
AvailableIps uint64 `json:",omitempty"`
AvailableAllocationIps uint64 `json:",omitempty"`
}

type ipFamily string
Expand Down
38 changes: 20 additions & 18 deletions api/types/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,24 +72,26 @@ type DisconnectOptions struct {

// Inspect is the body of the "get network" http response message.
type Inspect struct {
Name string // Name is the name of the network
ID string `json:"Id"` // ID uniquely identifies a network on a single machine
Created time.Time // Created is the time the network created
Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level)
Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`)
EnableIPv4 bool // EnableIPv4 represents whether IPv4 is enabled
EnableIPv6 bool // EnableIPv6 represents whether IPv6 is enabled
IPAM IPAM // IPAM is the network's IP Address Management
Internal bool // Internal represents if the network is used internal only
Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster.
ConfigFrom ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network.
ConfigOnly bool // ConfigOnly networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services.
Containers map[string]EndpointResource // Containers contains endpoints belonging to the network
Options map[string]string // Options holds the network specific options to use for when creating the network
Labels map[string]string // Labels holds metadata specific to the network being created
Peers []PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network
Services map[string]ServiceInfo `json:",omitempty"`
Name string // Name is the name of the network
ID string `json:"Id"` // ID uniquely identifies a network on a single machine
Created time.Time // Created is the time the network created
Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level)
Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`)
EnableIPv4 bool // EnableIPv4 represents whether IPv4 is enabled
EnableIPv6 bool // EnableIPv6 represents whether IPv6 is enabled
IPAM IPAM // IPAM is the network's IP Address Management
AvailableIps uint64 // AvailableIps represents the number of available IPs in the network
AvailableAllocationIps uint64 // AvailableAllocationIps represents the number of available dynamic IPs in the network
Internal bool // Internal represents if the network is used internal only
Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster.
ConfigFrom ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network.
ConfigOnly bool // ConfigOnly networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services.
Containers map[string]EndpointResource // Containers contains endpoints belonging to the network
Options map[string]string // Options holds the network specific options to use for when creating the network
Labels map[string]string // Labels holds metadata specific to the network being created
Peers []PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network
Services map[string]ServiceInfo `json:",omitempty"`
}

// Summary is used as response when listing networks. It currently is an alias
Expand Down
8 changes: 5 additions & 3 deletions api/types/swarm/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ type IPAMOptions struct {

// IPAMConfig represents ipam configuration.
type IPAMConfig struct {
Subnet string `json:",omitempty"`
Range string `json:",omitempty"`
Gateway string `json:",omitempty"`
Subnet string `json:",omitempty"`
Range string `json:",omitempty"`
Gateway string `json:",omitempty"`
AvailableIps uint64 `json:",omitempty"`
AvailableAllocationIps uint64 `json:",omitempty"`
}
26 changes: 17 additions & 9 deletions daemon/cluster/convert/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ func swarmPortConfigToAPIPortConfig(portConfig *swarmapi.PortConfig) types.PortC
func BasicNetworkFromGRPC(n swarmapi.Network) network.Inspect {
spec := n.Spec
var ipam network.IPAM
var availableIps, availableAllocationIps uint64
if n.IPAM != nil {
if n.IPAM.Driver != nil {
ipam.Driver = n.IPAM.Driver.Name
Expand All @@ -154,18 +155,25 @@ func BasicNetworkFromGRPC(n swarmapi.Network) network.Inspect {
AuxAddress: ic.Reserved,
})
}

for _, ipamCfg := range n.IPAM.Configs {
availableIps += ipamCfg.AvailableIps
availableAllocationIps += ipamCfg.AvailableAllocationIps
}
}

nr := network.Inspect{
ID: n.ID,
Name: n.Spec.Annotations.Name,
Scope: scope.Swarm,
EnableIPv6: spec.Ipv6Enabled,
IPAM: ipam,
Internal: spec.Internal,
Attachable: spec.Attachable,
Ingress: IsIngressNetwork(&n),
Labels: n.Spec.Annotations.Labels,
ID: n.ID,
Name: n.Spec.Annotations.Name,
Scope: scope.Swarm,
EnableIPv6: spec.Ipv6Enabled,
IPAM: ipam,
Internal: spec.Internal,
AvailableIps: availableIps,
AvailableAllocationIps: availableAllocationIps,
Attachable: spec.Attachable,
Ingress: IsIngressNetwork(&n),
Labels: n.Spec.Annotations.Labels,
}
nr.Created, _ = gogotypes.TimestampFromProto(n.Meta.CreatedAt)

Expand Down
41 changes: 23 additions & 18 deletions daemon/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,25 +617,30 @@ func buildNetworkResource(nw *libnetwork.Network) networktypes.Inspect {
if nw == nil {
return networktypes.Inspect{}
}

availableIps, availableAllocationIps, err := nw.AvailableIPs()
if err != nil {
availableIps = 0
}
return networktypes.Inspect{
Name: nw.Name(),
ID: nw.ID(),
Created: nw.Created(),
Scope: nw.Scope(),
Driver: nw.Type(),
EnableIPv4: nw.IPv4Enabled(),
EnableIPv6: nw.IPv6Enabled(),
IPAM: buildIPAMResources(nw),
Internal: nw.Internal(),
Attachable: nw.Attachable(),
Ingress: nw.Ingress(),
ConfigFrom: networktypes.ConfigReference{Network: nw.ConfigFrom()},
ConfigOnly: nw.ConfigOnly(),
Containers: map[string]networktypes.EndpointResource{},
Options: nw.DriverOptions(),
Labels: nw.Labels(),
Peers: buildPeerInfoResources(nw.Peers()),
Name: nw.Name(),
ID: nw.ID(),
Created: nw.Created(),
Scope: nw.Scope(),
Driver: nw.Type(),
EnableIPv4: nw.IPv4Enabled(),
EnableIPv6: nw.IPv6Enabled(),
AvailableIps: availableIps,
AvailableAllocationIps: availableAllocationIps,
IPAM: buildIPAMResources(nw),
Internal: nw.Internal(),
Attachable: nw.Attachable(),
Ingress: nw.Ingress(),
ConfigFrom: networktypes.ConfigReference{Network: nw.ConfigFrom()},
ConfigOnly: nw.ConfigOnly(),
Containers: map[string]networktypes.EndpointResource{},
Options: nw.DriverOptions(),
Labels: nw.Labels(),
Peers: buildPeerInfoResources(nw.Peers()),
}
}

Expand Down
46 changes: 41 additions & 5 deletions libnetwork/cnmallocator/networkallocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/docker/docker/libnetwork/driverapi"
"github.com/docker/docker/libnetwork/drivers/remote"
"github.com/docker/docker/libnetwork/drvregistry"
"github.com/docker/docker/libnetwork/internal/netiputil"
"github.com/docker/docker/libnetwork/ipamapi"
"github.com/docker/docker/libnetwork/ipams/defaultipam"
remoteipam "github.com/docker/docker/libnetwork/ipams/remote"
Expand Down Expand Up @@ -114,6 +115,23 @@ func (p *Provider) NewAllocator(netConfig *networkallocator.Config) (networkallo
return na, nil
}

func (na *cnmNetworkAllocator) GetNetwork(id string) (*api.Network, error) {
n, ok := na.networks[id]
if !ok {
return nil, fmt.Errorf("network %s not found", id)
}
return n.nw, nil
}
func (na *cnmNetworkAllocator) UpdateAvailableIps(networkId, cidr string, availableIps, availableAllocationIps uint64) error {
n, ok := na.networks[networkId]
if !ok {
return fmt.Errorf("network %s not found", networkId)
}
n.nw.UpdateAvailableIps(cidr, availableIps, availableAllocationIps)
na.networks[networkId] = n
return nil
}

// Allocate allocates all the necessary resources both general
// and driver-specific which may be specified in the NetworkSpec
func (na *cnmNetworkAllocator) Allocate(n *api.Network) error {
Expand Down Expand Up @@ -533,7 +551,7 @@ func (na *cnmNetworkAllocator) releaseEndpoints(networks []*api.NetworkAttachmen
poolID := localNet.endpoints[addr]
delete(localNet.endpoints, addr)

ip, _, err := net.ParseCIDR(addr)
ip, ipNet, err := net.ParseCIDR(addr)
if err != nil {
log.G(context.TODO()).Errorf("Could not parse IP address %s while releasing", addr)
continue
Expand All @@ -542,6 +560,11 @@ func (na *cnmNetworkAllocator) releaseEndpoints(networks []*api.NetworkAttachmen
if err := ipam.ReleaseAddress(poolID, ip); err != nil {
log.G(context.TODO()).WithError(err).Errorf("IPAM failure while releasing IP address %s", addr)
}

availableIps, availableAllocationIps, _ := ipam.GetAvailableIPs(poolID)
if err := na.UpdateAvailableIps(localNet.nw.ID, ipNet.String(), availableIps, availableAllocationIps); err != nil {
return errors.Wrap(err, "could not find network")
}
}

// Clear out the address list when we are done with
Expand Down Expand Up @@ -589,7 +612,7 @@ func (na *cnmNetworkAllocator) allocateVIP(vip *api.Endpoint_VirtualIP) error {
opts = setIPAMSerialAlloc(localNet.nw.IPAM.Driver.Options)
}

for _, poolID := range localNet.pools {
for cidr, poolID := range localNet.pools {
ip, _, err := ipam.RequestAddress(poolID, addr, opts)
if err != nil && err != ipamapi.ErrNoAvailableIPs && err != ipamapi.ErrIPOutOfRange {
return errors.Wrap(err, "could not allocate VIP from IPAM")
Expand All @@ -600,6 +623,11 @@ func (na *cnmNetworkAllocator) allocateVIP(vip *api.Endpoint_VirtualIP) error {
ipStr := ip.String()
localNet.endpoints[ipStr] = poolID
vip.Addr = ipStr

availableIps, availableAllocationIps, _ := ipam.GetAvailableIPs(poolID)
if err := na.UpdateAvailableIps(localNet.nw.ID, cidr, availableIps, availableAllocationIps); err != nil {
return errors.Wrap(err, "could not find network")
}
return nil
}
}
Expand All @@ -625,7 +653,7 @@ func (na *cnmNetworkAllocator) deallocateVIP(vip *api.Endpoint_VirtualIP) error
poolID := localNet.endpoints[vip.Addr]
delete(localNet.endpoints, vip.Addr)

ip, _, err := net.ParseCIDR(vip.Addr)
ip, ipNet, err := net.ParseCIDR(vip.Addr)
if err != nil {
log.G(context.TODO()).Errorf("Could not parse VIP address %s while releasing", vip.Addr)
return err
Expand All @@ -636,7 +664,9 @@ func (na *cnmNetworkAllocator) deallocateVIP(vip *api.Endpoint_VirtualIP) error
return err
}

return nil
availableIps, availableAllocationIps, _ := ipam.GetAvailableIPs(poolID)
cidr, _ := netiputil.ToPrefix(ipNet)
return na.UpdateAvailableIps(vip.NetworkID, cidr.String(), availableIps, availableAllocationIps)
}

// allocate the IP addresses for a single network attachment of the task.
Expand Down Expand Up @@ -678,7 +708,7 @@ func (na *cnmNetworkAllocator) allocateNetworkIPs(nAttach *api.NetworkAttachment
opts = setIPAMSerialAlloc(localNet.nw.IPAM.Driver.Options)
}

for _, poolID := range localNet.pools {
for cidr, poolID := range localNet.pools {
var err error

ip, _, err = ipam.RequestAddress(poolID, addr, opts)
Expand All @@ -692,6 +722,10 @@ func (na *cnmNetworkAllocator) allocateNetworkIPs(nAttach *api.NetworkAttachment
localNet.endpoints[ipStr] = poolID
addresses[i] = ipStr
nAttach.Addresses = addresses
availableIps, availableAllocationIps, _ := ipam.GetAvailableIPs(poolID)
if err := na.UpdateAvailableIps(nAttach.Network.ID, cidr, availableIps, availableAllocationIps); err != nil {
return errors.Wrap(err, "could not find network")
}
return nil
}
}
Expand Down Expand Up @@ -937,6 +971,8 @@ func (na *cnmNetworkAllocator) allocatePools(n *api.Network) (map[string]string,
ic.Gateway = gwIP.IP.String()
}

availableIPs, availableAllocationIPs, _ := ipam.GetAvailableIPs(alloc.PoolID)
n.UpdateAvailableIps(alloc.Pool.String(), availableIPs, availableAllocationIPs)
}

return pools, nil
Expand Down
4 changes: 4 additions & 0 deletions libnetwork/cnmallocator/networkallocator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,10 @@ func (a *mockIpam) IsBuiltIn() bool {
return true
}

func (a *mockIpam) GetAvailableIPs(poolID string) (uint64, uint64, error) {
return 0, 0, nil
}

func TestCorrectlyPassIPAMOptions(t *testing.T) {
var err error
expectedIpamOptions := map[string]string{"network-name": "freddie"}
Expand Down
12 changes: 12 additions & 0 deletions libnetwork/internal/addrset/addrset.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,18 @@ func (as *AddrSet) getBitmap(addr netip.Addr) (*bitmap.Bitmap, netip.Prefix, err
return bm, bmKey, nil
}

func (as *AddrSet) Unselected() uint64 {
var unselected uint64
maxUint64 := ^uint64(0)
for _, bm := range as.bitmaps {
if maxUint64-unselected < bm.Unselected() {
return maxUint64
}
unselected += bm.Unselected()
}
return unselected
}

func (as *AddrSet) addrsPerBitmap() uint64 {
bits := as.pool.Addr().BitLen() - as.pool.Bits()
if bits > maxBitsPerBitmap {
Expand Down
3 changes: 3 additions & 0 deletions libnetwork/ipamapi/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ type Ipam interface {

// IsBuiltIn returns true if it is a built-in driver.
IsBuiltIn() bool

// GetAvailableIPs returns the number of free IPs in the pool for IPv4
GetAvailableIPs(poolID string) (uint64, uint64, error)
}

type PoolRequest struct {
Expand Down
Loading

0 comments on commit bd020f4

Please sign in to comment.