Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dualstack Network Support #549

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5ea7ea6
IPv6 Support
majst01 Jul 4, 2024
22d916e
More tests
majst01 Jul 4, 2024
e8571c9
Validate Prefixes and DestinationPrefixes on create and update network
majst01 Jul 7, 2024
59c7511
Constify defaultChildPrefixlength
majst01 Jul 7, 2024
9f52dfd
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Jul 8, 2024
89a0857
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Jul 9, 2024
aae9274
Support DualStack Networks
majst01 Jul 17, 2024
1b09d5d
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Jul 23, 2024
87a2a98
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Jul 24, 2024
23e4cbd
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Aug 5, 2024
a175469
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Aug 6, 2024
c519d3b
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Aug 7, 2024
a1575ef
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Aug 8, 2024
f553164
Merge master
majst01 Aug 13, 2024
fa47c76
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Aug 14, 2024
6430ea1
Make additional announcable cidrs configurable per tenant super network
majst01 Aug 14, 2024
5f3287e
Merge additionalannouncablecidrs in
majst01 Aug 16, 2024
1822069
Merge master
majst01 Aug 26, 2024
72ac513
satisfy linter
majst01 Aug 26, 2024
82cc73e
Merge master
majst01 Sep 5, 2024
d2cb195
sanitize go.mod
majst01 Sep 5, 2024
d408762
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Sep 10, 2024
8f06d84
Merge branch 'master' of https://github.com/metal-stack/metal-api int…
majst01 Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package migrations

import (
"net/netip"

r "gopkg.in/rethinkdb/rethinkdb-go.v6"

"github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore"
"github.com/metal-stack/metal-api/cmd/metal-api/internal/metal"
)

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{
Name: "migrate partition.childprefixlength to tenant super network",
Version: 7,
Up: func(db *r.Term, session r.QueryExecutor, rs *datastore.RethinkStore) error {
nws, err := rs.ListNetworks()
if err != nil {
return err
}

for _, old := range nws {
cursor, err := db.Table("partition").Get(old.PartitionID).Run(session)
if err != nil {
return err
}
if cursor.IsNil() {
_ = cursor.Close()
continue
}
var partition tmpPartition
err = cursor.One(&partition)
if err != nil {
_ = cursor.Close()
return err
}
err = cursor.Close()
if err != nil {
return err
}
new := old

var (
af metal.AddressFamily
defaultChildPrefixLength = metal.ChildPrefixLength{}
)
// FIXME check all prefixes
parsed, err := netip.ParsePrefix(new.Prefixes[0].String())
if err != nil {
return err
}
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)
}
if partition.PrivateNetworkPrefixLength > 0 {
new.DefaultChildPrefixLength[af] = partition.PrivateNetworkPrefixLength
} else {
new.DefaultChildPrefixLength = defaultChildPrefixLength
}

}
err = rs.UpdateNetwork(&old, &new)
if err != nil {
return err
}
}

_, err = db.Table("partition").Replace(r.Row.Without("privatenetworkprefixlength")).RunWrite(session)
return err
},
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
_ "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore/migrations"
"github.com/metal-stack/metal-api/cmd/metal-api/internal/metal"
"github.com/metal-stack/metal-api/test"
r "gopkg.in/rethinkdb/rethinkdb-go.v6"

"testing"

Expand All @@ -29,7 +30,7 @@ func Test_Migration(t *testing.T) {
_ = container.Terminate(context.Background())
}()

log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))

rs := datastore.New(log, c.IP+":"+c.Port, c.DB, c.User, c.Password)
rs.VRFPoolRangeMin = 10000
Expand Down Expand Up @@ -141,3 +142,134 @@ func Test_Migration(t *testing.T) {
assert.Equal(t, ec.Events[0].Time.Unix(), lastEventTime.Unix())
assert.Equal(t, ec.Events[1].Time.Unix(), now.Unix())
}

func Test_MigrationChildPrefixLength(t *testing.T) {
type tmpPartition struct {
ID string `rethinkdb:"id"`
PrivateNetworkPrefixLength uint8 `rethinkdb:"privatenetworkprefixlength"`
}

container, c, err := test.StartRethink(t)
require.NoError(t, err)
defer func() {
_ = container.Terminate(context.Background())
}()

log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))

rs := datastore.New(log, c.IP+":"+c.Port, c.DB, c.User, c.Password)
// limit poolsize to speed up initialization
rs.VRFPoolRangeMin = 10000
rs.VRFPoolRangeMax = 10010
rs.ASNPoolRangeMin = 10000
rs.ASNPoolRangeMax = 10010

err = rs.Connect()
require.NoError(t, err)
err = rs.Initialize()
require.NoError(t, err)

var (
p1 = &tmpPartition{
ID: "p1",
PrivateNetworkPrefixLength: 22,
}
p2 = &tmpPartition{
ID: "p2",
PrivateNetworkPrefixLength: 24,
}
p3 = &tmpPartition{
ID: "p3",
}
n1 = &metal.Network{
Base: metal.Base{
ID: "n1",
},
PartitionID: "p1",
Prefixes: metal.Prefixes{
{IP: "10.0.0.0", Length: "8"},
},
PrivateSuper: true,
}
n2 = &metal.Network{
Base: metal.Base{
ID: "n2",
},
Prefixes: metal.Prefixes{
{IP: "2001::", Length: "64"},
},
PartitionID: "p2",
PrivateSuper: true,
}
n3 = &metal.Network{
Base: metal.Base{
ID: "n3",
},
Prefixes: metal.Prefixes{
{IP: "100.1.0.0", Length: "22"},
},
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)
err = rs.CreateNetwork(n2)
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)

p, err := rs.FindPartition(p1.ID)
require.NoError(t, err)
require.NotNil(t, p)
p, err = rs.FindPartition(p2.ID)
require.NoError(t, err)
require.NotNil(t, p)

n1fetched, err := rs.FindNetworkByID(n1.ID)
require.NoError(t, err)
require.NotNil(t, n1fetched)
require.Equal(t, p1.PrivateNetworkPrefixLength, n1fetched.DefaultChildPrefixLength[metal.IPv4AddressFamily], "childprefixlength:%v", n1fetched.DefaultChildPrefixLength)
require.True(t, n1fetched.AddressFamilies[metal.IPv4AddressFamily])

n2fetched, err := rs.FindNetworkByID(n2.ID)
require.NoError(t, err)
require.NotNil(t, n2fetched)
require.Equal(t, p2.PrivateNetworkPrefixLength, n2fetched.DefaultChildPrefixLength[metal.IPv6AddressFamily], "childprefixlength:%v", n2fetched.DefaultChildPrefixLength)
require.True(t, n2fetched.AddressFamilies[metal.IPv6AddressFamily])

n3fetched, err := rs.FindNetworkByID(n3.ID)
require.NoError(t, err)
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])
}
7 changes: 6 additions & 1 deletion cmd/metal-api/internal/datastore/rethinkdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ func New(log *slog.Logger, dbhost string, dbname string, dbuser string, dbpass s
}
}

// Session exported for migration unit test
func (rs *RethinkStore) Session() r.QueryExecutor {
return rs.session
}

func multi(session r.QueryExecutor, tt ...r.Term) error {
for _, t := range tt {
if err := t.Exec(session); err != nil {
Expand Down Expand Up @@ -373,7 +378,7 @@ func (rs *RethinkStore) findEntity(query *r.Term, entity interface{}) error {
}
defer res.Close()
if res.IsNil() {
return metal.NotFound("no %v with found", getEntityName(entity))
return metal.NotFound("no %v found", getEntityName(entity))
}

hasResult := res.Next(entity)
Expand Down
34 changes: 27 additions & 7 deletions cmd/metal-api/internal/ipam/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/netip"

"github.com/metal-stack/metal-api/cmd/metal-api/internal/metal"
"github.com/metal-stack/metal-lib/pkg/healthstatus"
Expand Down Expand Up @@ -51,10 +52,10 @@ 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)
prefix, _, err := metal.NewPrefixFromCIDR(ipamPrefix.Msg.Prefix.Cidr)
if err != nil {
return nil, fmt.Errorf("error creating prefix from ipam prefix: %w", err)
}
Expand Down Expand Up @@ -154,14 +155,33 @@ func (i *ipam) PrefixUsage(ctx context.Context, cidr string) (*metal.NetworkUsag
if err != nil {
return nil, fmt.Errorf("prefix usage for cidr:%s not found %w", cidr, err)
}

pfx, err := netip.ParsePrefix(cidr)
if err != nil {
return nil, err
}
af := metal.IPv4AddressFamily
if pfx.Addr().Is6() {
af = metal.IPv6AddressFamily
}
availableIPs := map[metal.AddressFamily]uint64{
af: usage.Msg.AvailableIps,
}
usedIPs := map[metal.AddressFamily]uint64{
af: usage.Msg.AcquiredIps,
}
availablePrefixes := map[metal.AddressFamily]uint64{
af: usage.Msg.AvailableSmallestPrefixes,
}
usedPrefixes := map[metal.AddressFamily]uint64{
af: usage.Msg.AcquiredPrefixes,
}
return &metal.NetworkUsage{
AvailableIPs: usage.Msg.AvailableIps,
UsedIPs: usage.Msg.AcquiredIps,
AvailableIPs: availableIPs,
UsedIPs: usedIPs,
// FIXME add usage.AvailablePrefixList as already done here
// https://github.com/metal-stack/metal-api/pull/152/files#diff-fe05f7f1480be933b5c482b74af28c8b9ca7ef2591f8341eb6e6663cbaeda7baR828
AvailablePrefixes: usage.Msg.AvailableSmallestPrefixes,
UsedPrefixes: usage.Msg.AcquiredPrefixes,
AvailablePrefixes: availablePrefixes,
UsedPrefixes: usedPrefixes,
}, nil
}

Expand Down
5 changes: 1 addition & 4 deletions cmd/metal-api/internal/metal/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,6 @@ func (r IngressRule) Validate() error {
if err := validateCIDRs(r.From); err != nil {
return err
}
if err := validateCIDRs(slices.Concat(r.From, r.To)); err != nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this was ok to delete

return err
}

return nil
}
Expand Down Expand Up @@ -279,7 +276,7 @@ func validateCIDRs(cidrs []string) error {
var newaf string
if p.Addr().Is4() {
newaf = "ipv4"
} else {
} else if p.Addr().Is6() {
newaf = "ipv6"
}
if af != "" && af != newaf {
Expand Down
Loading