diff --git a/cmd/metal-api/internal/datastore/migrations/06_childprefixlength.go b/cmd/metal-api/internal/datastore/migrations/06_childprefixlength.go index f7d56961..1f8b3185 100644 --- a/cmd/metal-api/internal/datastore/migrations/06_childprefixlength.go +++ b/cmd/metal-api/internal/datastore/migrations/06_childprefixlength.go @@ -1,6 +1,8 @@ package migrations import ( + "net/netip" + r "gopkg.in/rethinkdb/rethinkdb-go.v6" "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore" @@ -9,6 +11,7 @@ import ( func init() { type tmpPartition struct { + // In theory this might be set in a partition, but in reality its not set anywhere PrivateNetworkPrefixLength uint8 `rethinkdb:"privatenetworkprefixlength"` } datastore.MustRegisterMigration(datastore.Migration{ @@ -31,24 +34,40 @@ func init() { return err } - // TODO: does not work somehow new := old - af, err := metal.GetAddressFamily(new.Prefixes) + var ( + af metal.AddressFamily + defaultChildPrefixLength = metal.ChildPrefixLength{} + ) + parsed, err := netip.ParsePrefix(new.Prefixes[0].String()) if err != nil { return err } - if af != nil { - if new.AddressFamilies == nil { - new.AddressFamilies = make(map[metal.AddressFamily]bool) - } - new.AddressFamilies[*af] = true + if parsed.Addr().Is4() { + af = metal.IPv4AddressFamily + defaultChildPrefixLength[af] = 22 + } + if parsed.Addr().Is6() { + af = metal.IPv6AddressFamily + defaultChildPrefixLength[af] = 64 + } + + if new.AddressFamilies == nil { + new.AddressFamilies = make(map[metal.AddressFamily]bool) } + new.AddressFamilies[af] = true + if new.PrivateSuper { if new.DefaultChildPrefixLength == nil { new.DefaultChildPrefixLength = make(map[metal.AddressFamily]uint8) } - new.DefaultChildPrefixLength[*af] = partition.PrivateNetworkPrefixLength + if partition.PrivateNetworkPrefixLength > 0 { + new.DefaultChildPrefixLength[af] = partition.PrivateNetworkPrefixLength + } else { + new.DefaultChildPrefixLength = defaultChildPrefixLength + } + } err = rs.UpdateNetwork(&old, &new) if err != nil { diff --git a/cmd/metal-api/internal/datastore/migrations_integration/migrate_integration_test.go b/cmd/metal-api/internal/datastore/migrations_integration/migrate_integration_test.go index 7114268e..fdb0e3b4 100644 --- a/cmd/metal-api/internal/datastore/migrations_integration/migrate_integration_test.go +++ b/cmd/metal-api/internal/datastore/migrations_integration/migrate_integration_test.go @@ -163,6 +163,9 @@ func Test_MigrationChildPrefixLength(t *testing.T) { ID: "p2", PrivateNetworkPrefixLength: 24, } + p3 = &tmpPartition{ + ID: "p3", + } n1 = &metal.Network{ Base: metal.Base{ ID: "n1", @@ -193,11 +196,23 @@ func Test_MigrationChildPrefixLength(t *testing.T) { PartitionID: "p2", PrivateSuper: false, } + n4 = &metal.Network{ + Base: metal.Base{ + ID: "n4", + }, + Prefixes: metal.Prefixes{ + {IP: "100.1.0.0", Length: "22"}, + }, + PartitionID: "p3", + PrivateSuper: true, + } ) _, err = r.DB("metal").Table("partition").Insert(p1).RunWrite(rs.Session()) require.NoError(t, err) _, err = r.DB("metal").Table("partition").Insert(p2).RunWrite(rs.Session()) require.NoError(t, err) + _, err = r.DB("metal").Table("partition").Insert(p3).RunWrite(rs.Session()) + require.NoError(t, err) err = rs.CreateNetwork(n1) require.NoError(t, err) @@ -205,6 +220,8 @@ func Test_MigrationChildPrefixLength(t *testing.T) { require.NoError(t, err) err = rs.CreateNetwork(n3) require.NoError(t, err) + err = rs.CreateNetwork(n4) + require.NoError(t, err) err = rs.Migrate(nil, false) require.NoError(t, err) @@ -233,4 +250,11 @@ func Test_MigrationChildPrefixLength(t *testing.T) { require.NotNil(t, n3fetched) require.Nil(t, n3fetched.DefaultChildPrefixLength) require.True(t, n3fetched.AddressFamilies[metal.IPv4AddressFamily]) + + n4fetched, err := rs.FindNetworkByID(n4.ID) + require.NoError(t, err) + require.NotNil(t, n4fetched) + require.NotNil(t, n4fetched.DefaultChildPrefixLength) + require.True(t, n4fetched.AddressFamilies[metal.IPv4AddressFamily]) + require.Equal(t, uint8(22), n4fetched.DefaultChildPrefixLength[metal.IPv4AddressFamily]) } diff --git a/cmd/metal-api/internal/ipam/ipam.go b/cmd/metal-api/internal/ipam/ipam.go index f820187b..326dfc92 100644 --- a/cmd/metal-api/internal/ipam/ipam.go +++ b/cmd/metal-api/internal/ipam/ipam.go @@ -52,7 +52,7 @@ func (i *ipam) AllocateChildPrefix(ctx context.Context, parentPrefix metal.Prefi Length: uint32(childLength), })) if err != nil { - return nil, fmt.Errorf("error creating new prefix in ipam: %w", err) + return nil, fmt.Errorf("error creating new prefix from:%s in ipam: %w", parentPrefix.String(), err) } prefix, _, err := metal.NewPrefixFromCIDR(ipamPrefix.Msg.Prefix.Cidr) diff --git a/cmd/metal-api/internal/metal/network.go b/cmd/metal-api/internal/metal/network.go index f1b2e53b..eaff83bc 100644 --- a/cmd/metal-api/internal/metal/network.go +++ b/cmd/metal-api/internal/metal/network.go @@ -1,12 +1,10 @@ package metal import ( - "fmt" "net" "net/netip" "strconv" - "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/samber/lo" ) @@ -351,21 +349,3 @@ func (nics Nics) ByIdentifier() map[string]*Nic { return res } - -func GetAddressFamily(prefixes Prefixes) (*AddressFamily, error) { - if len(prefixes) == 0 { - return nil, nil - } - - parsed, err := netip.ParsePrefix(prefixes[0].String()) - if err != nil { - return nil, err - } - if parsed.Addr().Is4() { - return pointer.Pointer(IPv4AddressFamily), nil - } - if parsed.Addr().Is6() { - return pointer.Pointer(IPv6AddressFamily), nil - } - return nil, fmt.Errorf("unable to detect addressfamily from prefixes:%v", prefixes) -} diff --git a/cmd/metal-api/internal/metal/network_test.go b/cmd/metal-api/internal/metal/network_test.go index a62ad927..a7dcfddf 100644 --- a/cmd/metal-api/internal/metal/network_test.go +++ b/cmd/metal-api/internal/metal/network_test.go @@ -4,8 +4,6 @@ import ( "fmt" "reflect" "testing" - - "github.com/metal-stack/metal-lib/pkg/pointer" ) func TestNics_ByIdentifier(t *testing.T) { @@ -337,47 +335,3 @@ func TestNicState_SetState(t *testing.T) { }) } } - -func Test_getAddressFamily(t *testing.T) { - tests := []struct { - name string - prefixes Prefixes - want *AddressFamily - wantErr bool - }{ - { - name: "ipv4", - prefixes: Prefixes{{IP: "10.0.0.0", Length: "8"}}, - want: pointer.Pointer(IPv4AddressFamily), - }, - { - name: "ipv6", - prefixes: Prefixes{{IP: "2001::", Length: "64"}}, - want: pointer.Pointer(IPv6AddressFamily), - }, - { - name: "empty prefixes", - prefixes: Prefixes{}, - want: nil, - wantErr: false, - }, - { - name: "malformed ipv4", - prefixes: Prefixes{{IP: "10.0.0.0.0", Length: "6"}}, - want: nil, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := GetAddressFamily(tt.prefixes) - if (err != nil) != tt.wantErr { - t.Errorf("getAddressFamily() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("getAddressFamily() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/cmd/metal-api/internal/service/async-actor.go b/cmd/metal-api/internal/service/async-actor.go index b4284f1b..507f509e 100644 --- a/cmd/metal-api/internal/service/async-actor.go +++ b/cmd/metal-api/internal/service/async-actor.go @@ -6,9 +6,9 @@ import ( "fmt" "log/slog" + "connectrpc.com/connect" "github.com/metal-stack/metal-api/cmd/metal-api/internal/headscale" - ipamer "github.com/metal-stack/go-ipam" "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore" "github.com/metal-stack/metal-api/cmd/metal-api/internal/ipam" "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" @@ -216,12 +216,15 @@ func (a *asyncActor) releaseIP(ip metal.IP) error { // now the IP should not exist any more in our datastore // so cleanup the ipam - + ctx := context.Background() err = a.ReleaseIP(ctx, ip) if err != nil { - if errors.Is(err, ipamer.ErrNotFound) { - return nil + var connectErr *connect.Error + if errors.As(err, &connectErr) { + if connectErr.Code() == connect.CodeNotFound { + return nil + } } return fmt.Errorf("cannot release IP %q: %w", ip, err) } diff --git a/cmd/metal-api/internal/service/ip-service.go b/cmd/metal-api/internal/service/ip-service.go index a0891385..ad8f0f12 100644 --- a/cmd/metal-api/internal/service/ip-service.go +++ b/cmd/metal-api/internal/service/ip-service.go @@ -23,8 +23,6 @@ import ( v1 "github.com/metal-stack/metal-api/cmd/metal-api/internal/service/v1" - goipam "github.com/metal-stack/go-ipam" - restfulspec "github.com/emicklei/go-restful-openapi/v2" restful "github.com/emicklei/go-restful/v3" "github.com/metal-stack/metal-lib/httperrors" @@ -289,7 +287,7 @@ func (r *ipResource) allocateIP(request *restful.Request, response *restful.Resp ok := nw.AddressFamilies[metal.ToAddressFamily(string(*requestPayload.AddressFamily))] if !ok { r.sendError(request, response, httperrors.BadRequest( - fmt.Errorf("there is no prefix for the given addressfamily:%s present in this network:%s", string(*requestPayload.AddressFamily), requestPayload.NetworkID)), + fmt.Errorf("there is no prefix for the given addressfamily:%s present in network:%s", string(*requestPayload.AddressFamily), requestPayload.NetworkID)), ) return } @@ -471,10 +469,13 @@ func allocateRandomIP(ctx context.Context, parent *metal.Network, ipamer ipam.IP } ipAddress, err = ipamer.AllocateIP(ctx, prefix) - if err != nil && errors.Is(err, goipam.ErrNoIPAvailable) { - continue - } if err != nil { + var connectErr *connect.Error + if errors.As(err, &connectErr) { + if connectErr.Code() == connect.CodeNotFound { + continue + } + } return "", "", err } diff --git a/cmd/metal-api/internal/service/ip-service_test.go b/cmd/metal-api/internal/service/ip-service_test.go index a8af1a91..b4e3834e 100644 --- a/cmd/metal-api/internal/service/ip-service_test.go +++ b/cmd/metal-api/internal/service/ip-service_test.go @@ -313,7 +313,7 @@ func TestAllocateIP(t *testing.T) { AddressFamily: pointer.Pointer(metal.IPv6AddressFamily), }, wantedStatus: http.StatusBadRequest, - wantErr: errors.New("there is no prefix for the given addressfamily:IPv6 present in this network:4"), + wantErr: errors.New("there is no prefix for the given addressfamily:IPv6 present in network:4"), }, } for i := range tests { diff --git a/cmd/metal-api/internal/service/machine-service.go b/cmd/metal-api/internal/service/machine-service.go index d432a216..f4034ffa 100644 --- a/cmd/metal-api/internal/service/machine-service.go +++ b/cmd/metal-api/internal/service/machine-service.go @@ -1396,6 +1396,14 @@ func findWaitingMachine(ctx context.Context, ds *datastore.RethinkStore, allocat // is enabled to clean up networks that were already created. func makeNetworks(ctx context.Context, ds *datastore.RethinkStore, ipamer ipam.IPAMer, allocationSpec *machineAllocationSpec, networks allocationNetworkMap, alloc *metal.MachineAllocation) error { for _, n := range networks { + if n == nil || n.network == nil { + continue + } + if len(n.network.AddressFamilies) == 0 { + n.network.AddressFamilies = metal.AddressFamilies{ + metal.IPv4AddressFamily: true, + } + } machineNetwork, err := makeMachineNetwork(ctx, ds, ipamer, allocationSpec, n) if err != nil { return err @@ -1472,10 +1480,12 @@ func gatherNetworksFromSpec(ds *datastore.RethinkStore, allocationSpec *machineA // - user specifies administrative networks, i.e. underlay or privatesuper networks // - user's private network is specified with noauto but no specific IPs are given: this would yield a machine with no ip address - specNetworks := make(map[string]*allocationNetwork) - var primaryPrivateNetwork *allocationNetwork - var privateNetworks []*allocationNetwork - var privateSharedNetworks []*allocationNetwork + var ( + specNetworks = make(map[string]*allocationNetwork) + primaryPrivateNetwork *allocationNetwork + privateNetworks []*allocationNetwork + privateSharedNetworks []*allocationNetwork + ) for _, networkSpec := range allocationSpec.Networks { auto := true diff --git a/cmd/metal-api/internal/service/network-service.go b/cmd/metal-api/internal/service/network-service.go index 49e95bd4..7004d5a0 100644 --- a/cmd/metal-api/internal/service/network-service.go +++ b/cmd/metal-api/internal/service/network-service.go @@ -8,6 +8,7 @@ import ( "net/http" "net/netip" + "connectrpc.com/connect" mdmv1 "github.com/metal-stack/masterdata-api/api/v1" mdm "github.com/metal-stack/masterdata-api/pkg/client" @@ -605,7 +606,7 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re // Allow configurable prefix length per AF length := superNetwork.DefaultChildPrefixLength - if requestPayload.Length != nil { + if len(requestPayload.Length) > 0 { for af, l := range requestPayload.Length { length[metal.ToAddressFamily(string(af))] = l } @@ -621,12 +622,13 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re length = metal.ChildPrefixLength{ af: bits, } + nwSpec.AddressFamilies = metal.AddressFamilies{af: true} } r.log.Info("network allocate", "supernetwork", superNetwork.ID, "defaultchildprefixlength", superNetwork.DefaultChildPrefixLength, "length", length) ctx := request.Request.Context() - nw, err := createChildNetwork(ctx, r.ds, r.ipamer, nwSpec, &superNetwork, length) + nw, err := r.createChildNetwork(ctx, nwSpec, &superNetwork, length) if err != nil { r.sendError(request, response, defaultError(err)) return @@ -641,20 +643,20 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re r.send(request, response, http.StatusCreated, v1.NewNetworkResponse(nw, usage)) } -func createChildNetwork(ctx context.Context, ds *datastore.RethinkStore, ipamer ipam.IPAMer, nwSpec *metal.Network, parent *metal.Network, childLengths metal.ChildPrefixLength) (*metal.Network, error) { - vrf, err := acquireRandomVRF(ds) +func (r *networkResource) createChildNetwork(ctx context.Context, nwSpec *metal.Network, parent *metal.Network, childLengths metal.ChildPrefixLength) (*metal.Network, error) { + vrf, err := acquireRandomVRF(r.ds) if err != nil { return nil, fmt.Errorf("could not acquire a vrf: %w", err) } var childPrefixes = metal.Prefixes{} for af, childLength := range childLengths { - childPrefix, err := createChildPrefix(ctx, parent.Prefixes, childLength, ipamer) + childPrefix, err := r.createChildPrefix(ctx, parent.Prefixes, af, childLength) if err != nil { return nil, err } if childPrefix == nil { - return nil, fmt.Errorf("could not allocate child prefix in parent network: %s for addressfamily: %s", parent.ID, af) + return nil, fmt.Errorf("could not allocate child prefix in parent network: %s for addressfamily: %s length:%d", parent.ID, af, childLength) } childPrefixes = append(childPrefixes, *childPrefix) } @@ -678,7 +680,7 @@ func createChildNetwork(ctx context.Context, ds *datastore.RethinkStore, ipamer AddressFamilies: nwSpec.AddressFamilies, } - err = ds.CreateNetwork(nw) + err = r.ds.CreateNetwork(nw) if err != nil { return nil, err } @@ -925,14 +927,31 @@ func getNetworkUsage(ctx context.Context, nw *metal.Network, ipamer ipam.IPAMer) return usage, nil } -func createChildPrefix(ctx context.Context, parentPrefixes metal.Prefixes, childLength uint8, ipamer ipam.IPAMer) (*metal.Prefix, error) { - var errors []error - var err error - var childPrefix *metal.Prefix +func (r *networkResource) createChildPrefix(ctx context.Context, parentPrefixes metal.Prefixes, af metal.AddressFamily, childLength uint8) (*metal.Prefix, error) { + var ( + errs []error + childPrefix *metal.Prefix + ) for _, parentPrefix := range parentPrefixes { - childPrefix, err = ipamer.AllocateChildPrefix(ctx, parentPrefix, childLength) + pfx, err := netip.ParsePrefix(parentPrefix.String()) if err != nil { - errors = append(errors, err) + return nil, fmt.Errorf("unable to parse prefix: %w", err) + } + if pfx.Addr().Is4() && af == metal.IPv6AddressFamily { + continue + } + if pfx.Addr().Is6() && af == metal.IPv4AddressFamily { + continue + } + childPrefix, err = r.ipamer.AllocateChildPrefix(ctx, parentPrefix, childLength) + if err != nil { + var connectErr *connect.Error + if errors.As(err, &connectErr) { + if connectErr.Code() == connect.CodeNotFound { + continue + } + } + errs = append(errs, err) continue } if childPrefix != nil { @@ -940,10 +959,10 @@ func createChildPrefix(ctx context.Context, parentPrefixes metal.Prefixes, child } } if childPrefix == nil { - if len(errors) > 0 { - return nil, fmt.Errorf("cannot allocate free child prefix in ipam: %v", errors) + if len(errs) > 0 { + return nil, fmt.Errorf("cannot allocate free child prefix in ipam: %w", errors.Join(errs...)) } - return nil, fmt.Errorf("cannot allocate free child prefix in one of the given parent prefixes in ipam: %v", parentPrefixes) + return nil, fmt.Errorf("cannot allocate free child prefix in one of the given parent prefixes in ipam: %s", parentPrefixes.String()) } return childPrefix, nil diff --git a/cmd/metal-api/internal/service/v1/network.go b/cmd/metal-api/internal/service/v1/network.go index fd854531..d5eec11a 100644 --- a/cmd/metal-api/internal/service/v1/network.go +++ b/cmd/metal-api/internal/service/v1/network.go @@ -30,10 +30,10 @@ type NetworkImmutable struct { // NetworkUsage reports core metrics about available and used IPs or Prefixes in a Network. type NetworkUsage struct { - AvailableIPs map[metal.AddressFamily]uint64 `json:"available_ips" description:"the total available IPs" readonly:"true"` - UsedIPs map[metal.AddressFamily]uint64 `json:"used_ips" description:"the total used IPs" readonly:"true"` - AvailablePrefixes map[metal.AddressFamily]uint64 `json:"available_prefixes" description:"the total available 2 bit Prefixes" readonly:"true"` - UsedPrefixes map[metal.AddressFamily]uint64 `json:"used_prefixes" description:"the total used Prefixes" readonly:"true"` + AvailableIPs uint64 `json:"available_ips" description:"the total available IPs" readonly:"true"` + UsedIPs uint64 `json:"used_ips" description:"the total used IPs" readonly:"true"` + AvailablePrefixes uint64 `json:"available_prefixes" description:"the total available 2 bit Prefixes" readonly:"true"` + UsedPrefixes uint64 `json:"used_prefixes" description:"the total used Prefixes" readonly:"true"` } // NetworkCreateRequest is used to create a new Network. @@ -74,7 +74,8 @@ type NetworkResponse struct { Common NetworkBase NetworkImmutable - Usage NetworkUsage `json:"usage" description:"usage of ips and prefixes in this network" readonly:"true"` + Usage NetworkUsage `json:"usage" description:"usage of IPv4 ips and prefixes in this network" readonly:"true"` + UsageV6 NetworkUsage `json:"usagev6" description:"usage of IPv6 ips and prefixes in this network" readonly:"true"` Timestamps } @@ -86,6 +87,8 @@ func NewNetworkResponse(network *metal.Network, usage *metal.NetworkUsage) *Netw var ( parentNetworkID *string + usagev4 NetworkUsage + usagev6 NetworkUsage ) if network.ParentNetworkID != "" { @@ -96,6 +99,32 @@ func NewNetworkResponse(network *metal.Network, usage *metal.NetworkUsage) *Netw labels = make(map[string]string) } + // Existing tenant networks where not migrated and get AF created here + if len(network.AddressFamilies) == 0 { + network.AddressFamilies = metal.AddressFamilies{ + metal.IPv4AddressFamily: true, + } + } + + for af := range network.AddressFamilies { + if af == metal.IPv4AddressFamily { + usagev4 = NetworkUsage{ + AvailableIPs: usage.AvailableIPs[af], + UsedIPs: usage.UsedIPs[af], + AvailablePrefixes: usage.AvailablePrefixes[af], + UsedPrefixes: usage.UsedPrefixes[af], + } + } + if af == metal.IPv6AddressFamily { + usagev6 = NetworkUsage{ + AvailableIPs: usage.AvailableIPs[af], + UsedIPs: usage.UsedIPs[af], + AvailablePrefixes: usage.AvailablePrefixes[af], + UsedPrefixes: usage.UsedPrefixes[af], + } + } + } + return &NetworkResponse{ Common: Common{ Identifiable: Identifiable{ @@ -123,12 +152,8 @@ func NewNetworkResponse(network *metal.Network, usage *metal.NetworkUsage) *Netw ParentNetworkID: parentNetworkID, AddressFamilies: network.AddressFamilies, }, - Usage: NetworkUsage{ - AvailableIPs: usage.AvailableIPs, - UsedIPs: usage.UsedIPs, - AvailablePrefixes: usage.AvailablePrefixes, - UsedPrefixes: usage.UsedPrefixes, - }, + Usage: usagev4, + UsageV6: usagev6, Timestamps: Timestamps{ Created: network.Created, Changed: network.Changed, diff --git a/go.mod b/go.mod index d8e197ca..d20af46c 100644 --- a/go.mod +++ b/go.mod @@ -38,11 +38,6 @@ require ( gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.2 ) -require ( - github.com/containerd/platforms v0.2.1 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect -) - replace ( // Newer versions do not export base entities which are used to composite other entities. // This breaks metalctl and friends @@ -70,6 +65,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/containerd v1.7.19 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/coreos/go-oidc/v3 v3.10.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect @@ -151,6 +147,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect diff --git a/spec/metal-api.json b/spec/metal-api.json index 81141fd9..ce13495f 100644 --- a/spec/metal-api.json +++ b/spec/metal-api.json @@ -3968,7 +3968,11 @@ }, "usage": { "$ref": "#/definitions/v1.NetworkUsage", - "description": "usage of ips and prefixes in this network" + "description": "usage of IPv4 ips and prefixes in this network" + }, + "usagev6": { + "$ref": "#/definitions/v1.NetworkUsage", + "description": "usage of IPv6 ips and prefixes in this network" }, "vrf": { "description": "the vrf this network is associated with", @@ -3988,7 +3992,8 @@ "prefixes", "privatesuper", "underlay", - "usage" + "usage", + "usagev6" ] }, "v1.NetworkUpdateRequest": { @@ -4038,32 +4043,24 @@ "v1.NetworkUsage": { "properties": { "available_ips": { - "additionalProperties": { - "type": "integer" - }, "description": "the total available IPs", - "type": "object" + "format": "integer", + "type": "integer" }, "available_prefixes": { - "additionalProperties": { - "type": "integer" - }, "description": "the total available 2 bit Prefixes", - "type": "object" + "format": "integer", + "type": "integer" }, "used_ips": { - "additionalProperties": { - "type": "integer" - }, "description": "the total used IPs", - "type": "object" + "format": "integer", + "type": "integer" }, "used_prefixes": { - "additionalProperties": { - "type": "integer" - }, "description": "the total used Prefixes", - "type": "object" + "format": "integer", + "type": "integer" } }, "required": [