Skip to content
This repository was archived by the owner on Jun 2, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions api/pkg/api/handler/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ func (cih CreateInstanceHandler) Handle(c echo.Context) error {
VpcPrefixID: &vpcPrefixID,
VpcPrefix: vpcPrefix, // We attach this here so it can be used when we convert to the API model.
RequestedIpAddress: ifc.IPAddress,
RoutingProfile: ifc.RoutingProfile.ToDBModel(),
Device: ifc.Device,
DeviceInstance: ifc.DeviceInstance,
VirtualFunctionID: ifc.VirtualFunctionID,
Expand Down Expand Up @@ -1335,6 +1336,7 @@ func (cih CreateInstanceHandler) Handle(c echo.Context) error {
DeviceInstance: dbifc.DeviceInstance,
VirtualFunctionID: dbifc.VirtualFunctionID,
RequestedIpAddress: dbifc.RequestedIpAddress,
RoutingProfile: dbifc.RoutingProfile,
IsPhysical: dbifc.IsPhysical,
Status: dbifc.Status,
CreatedBy: dbUser.ID,
Expand Down Expand Up @@ -1393,6 +1395,9 @@ func (cih CreateInstanceHandler) Handle(c echo.Context) error {
if dbifc.RequestedIpAddress != nil {
interfaceConfig.IpAddress = dbifc.RequestedIpAddress
}
if dbifc.RoutingProfile != nil {
interfaceConfig.RoutingProfile = dbifc.RoutingProfile.ToProto()
Comment thread
bcavnvidia marked this conversation as resolved.
}

interfaceConfigs = append(interfaceConfigs, interfaceConfig)
}
Expand Down Expand Up @@ -2435,6 +2440,7 @@ func (uih UpdateInstanceHandler) Handle(c echo.Context) error {
VpcPrefixID: &vpcPrefixID,
VpcPrefix: vpcPrefix, // We attach this here so it can be used when we convert to the API model.
RequestedIpAddress: ifc.IPAddress,
RoutingProfile: ifc.RoutingProfile.ToDBModel(),
Device: ifc.Device,
DeviceInstance: ifc.DeviceInstance,
VirtualFunctionID: ifc.VirtualFunctionID,
Expand Down Expand Up @@ -3013,6 +3019,7 @@ func (uih UpdateInstanceHandler) Handle(c echo.Context) error {
DeviceInstance: dbifc.DeviceInstance,
VirtualFunctionID: dbifc.VirtualFunctionID,
RequestedIpAddress: dbifc.RequestedIpAddress,
RoutingProfile: dbifc.RoutingProfile,
IsPhysical: dbifc.IsPhysical,
Status: dbifc.Status,
CreatedBy: dbUser.ID,
Expand Down Expand Up @@ -3434,6 +3441,9 @@ func (uih UpdateInstanceHandler) Handle(c echo.Context) error {
if ifc.RequestedIpAddress != nil {
interfaceConfig.IpAddress = ifc.RequestedIpAddress
}
if ifc.RoutingProfile != nil {
interfaceConfig.RoutingProfile = ifc.RoutingProfile.ToProto()
}

interfaceConfigs[i] = interfaceConfig
}
Expand Down
53 changes: 53 additions & 0 deletions api/pkg/api/handler/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,16 @@ func testUpdateOSIsActive(t *testing.T, dbSession *cdb.Session, ins *cdbm.Operat
return ins
}

// assertInterfaceRoutingProfilePrefixes verifies proto routing profile prefix order.
func assertInterfaceRoutingProfilePrefixes(t *testing.T, actual *cwssaws.InstanceInterfaceRoutingProfile, expected []string) {
t.Helper()
require.NotNil(t, actual)
require.Len(t, actual.AllowedAnycastPrefixes, len(expected))
for i, prefix := range expected {
assert.Equal(t, prefix, actual.AllowedAnycastPrefixes[i].Prefix)
}
}

func TestCreateInstanceHandler_Handle(t *testing.T) {

ctx := context.Background()
Expand Down Expand Up @@ -1646,6 +1656,9 @@ func TestCreateInstanceHandler_Handle(t *testing.T) {
IsPhysical: true,
Device: cdb.GetStrPtr("MT42822 BlueField-2 integrated ConnectX-6 Dx network controller"),
DeviceInstance: cdb.GetIntPtr(0),
RoutingProfile: &model.APIInterfaceRoutingProfile{
AllowedAnycastPrefixes: []string{"192.0.2.0/24", "2001:db8::/64"},
},
},
{
VpcPrefixID: cdb.GetStrPtr(vpcPrefix5.ID.String()),
Expand Down Expand Up @@ -3464,6 +3477,7 @@ func TestCreateInstanceHandler_Handle(t *testing.T) {
assert.Equal(t, len(rst.StatusHistory), 1)
if len(tt.args.reqData.Interfaces) > 0 {
require.Len(t, rst.Interfaces, len(tt.args.reqData.Interfaces))
hasRoutingProfile := false
for i := range tt.args.reqData.Interfaces {
if tt.args.reqData.Interfaces[i].SubnetID != nil {
assert.Equal(t, tt.args.reqData.Interfaces[i].SubnetID, rst.Interfaces[i].SubnetID)
Expand All @@ -3480,13 +3494,34 @@ func TestCreateInstanceHandler_Handle(t *testing.T) {
assert.Equal(t, tt.args.reqData.Interfaces[i].Device, rst.Interfaces[i].Device)
assert.Equal(t, tt.args.reqData.Interfaces[i].DeviceInstance, rst.Interfaces[i].DeviceInstance)
assert.Equal(t, tt.args.reqData.Interfaces[i].VirtualFunctionID, rst.Interfaces[i].VirtualFunctionID)
if tt.args.reqData.Interfaces[i].RoutingProfile != nil {
hasRoutingProfile = true
require.NotNil(t, rst.Interfaces[i].RoutingProfile)
assert.Equal(t, tt.args.reqData.Interfaces[i].RoutingProfile.AllowedAnycastPrefixes, rst.Interfaces[i].RoutingProfile.AllowedAnycastPrefixes)
}

// Handle the fact that single-interface instance get normalized to have
// PF for the first interface.
if len(tt.args.reqData.Interfaces) > 1 {
assert.Equal(t, tt.args.reqData.Interfaces[i].IsPhysical, rst.Interfaces[i].IsPhysical)
}
}

if hasRoutingProfile {
ifcDAO := cdbm.NewInterfaceDAO(dbSession)
dbIfcs, _, ierr := ifcDAO.GetAll(ec.Request().Context(), nil,
cdbm.InterfaceFilterInput{InstanceIDs: []uuid.UUID{uuid.MustParse(rst.ID)}},
cdbp.PageInput{OrderBy: &cdbp.OrderBy{Field: cdbm.InterfaceOrderByCreated, Order: cdbp.OrderAscending}},
nil)
require.NoError(t, ierr)
require.Len(t, dbIfcs, len(tt.args.reqData.Interfaces))
for i := range tt.args.reqData.Interfaces {
if tt.args.reqData.Interfaces[i].RoutingProfile != nil {
require.NotNil(t, dbIfcs[i].RoutingProfile)
assert.Equal(t, tt.args.reqData.Interfaces[i].RoutingProfile.AllowedAnycastPrefixes, dbIfcs[i].RoutingProfile.AllowedAnycastPrefixes)
}
}
}
}

if len(tsc.Calls) > 0 && len(tsc.Calls[len(tsc.Calls)-1].Arguments) > 3 {
Expand All @@ -3512,6 +3547,12 @@ func TestCreateInstanceHandler_Handle(t *testing.T) {
} else {
assert.False(t, req.AllowUnhealthyMachine, fmt.Sprintf("%v", req))
}

for i, reqIfc := range tt.args.reqData.Interfaces {
if reqIfc.RoutingProfile != nil {
assertInterfaceRoutingProfilePrefixes(t, req.Config.Network.Interfaces[i].RoutingProfile, reqIfc.RoutingProfile.AllowedAnycastPrefixes)
}
}
}

if len(tt.args.reqData.InfiniBandInterfaces) > 0 {
Expand Down Expand Up @@ -5613,6 +5654,9 @@ func TestUpdateInstanceHandler_Handle(t *testing.T) {
IsPhysical: true,
Device: cdb.GetStrPtr("MT42822 BlueField-2 integrated ConnectX-6 Dx network controller"),
DeviceInstance: cdb.GetIntPtr(0),
RoutingProfile: &model.APIInterfaceRoutingProfile{
AllowedAnycastPrefixes: []string{"192.0.2.0/24", "2001:db8::/64"},
},
},
},
},
Expand Down Expand Up @@ -6618,6 +6662,11 @@ func TestUpdateInstanceHandler_Handle(t *testing.T) {
assert.Equal(t, tt.args.reqData.Interfaces[i].IPAddress, ifcs[i].RequestedIpAddress)
}

if tt.args.reqData.Interfaces[i].RoutingProfile != nil {
require.NotNil(t, ifcs[i].RoutingProfile)
assert.Equal(t, tt.args.reqData.Interfaces[i].RoutingProfile.AllowedAnycastPrefixes, ifcs[i].RoutingProfile.AllowedAnycastPrefixes)
}

assert.Equal(t, cdbm.InterfaceStatusPending, ifcs[i].Status)
}
}
Expand Down Expand Up @@ -6790,6 +6839,10 @@ func TestUpdateInstanceHandler_Handle(t *testing.T) {
if reqInsIfcs[i].RequestedIpAddress != nil {
assert.Equal(t, siteIfc.IpAddress, reqInsIfcs[i].RequestedIpAddress)
}

if tt.args.reqData.Interfaces != nil && i < len(tt.args.reqData.Interfaces) && tt.args.reqData.Interfaces[i].RoutingProfile != nil {
assertInterfaceRoutingProfilePrefixes(t, siteIfc.RoutingProfile, tt.args.reqData.Interfaces[i].RoutingProfile.AllowedAnycastPrefixes)
}
}

// Verify the InfiniBand Interfaces are in the Site Controller request
Expand Down
5 changes: 5 additions & 0 deletions api/pkg/api/handler/instancebatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ func (bcih BatchCreateInstanceHandler) Handle(c echo.Context) error {
VpcPrefixID: &vpcPrefixUUID,
VpcPrefix: vpcPrefix,
RequestedIpAddress: nil, // Explicit IPs are not supported for batch create.
RoutingProfile: ifc.RoutingProfile.ToDBModel(),
Device: ifc.Device,
DeviceInstance: ifc.DeviceInstance,
VirtualFunctionID: ifc.VirtualFunctionID,
Expand Down Expand Up @@ -1336,6 +1337,7 @@ func (bcih BatchCreateInstanceHandler) Handle(c echo.Context) error {
DeviceInstance: dbifc.DeviceInstance,
VirtualFunctionID: dbifc.VirtualFunctionID,
RequestedIpAddress: dbifc.RequestedIpAddress,
RoutingProfile: dbifc.RoutingProfile,
IsPhysical: dbifc.IsPhysical,
Status: cdbm.InterfaceStatusPending,
CreatedBy: dbUser.ID,
Expand Down Expand Up @@ -1535,6 +1537,9 @@ func (bcih BatchCreateInstanceHandler) Handle(c echo.Context) error {
vfID := uint32(*ifc.VirtualFunctionID)
interfaceConfig.VirtualFunctionId = &vfID
}
if ifc.RoutingProfile != nil {
interfaceConfig.RoutingProfile = ifc.RoutingProfile.ToProto()
}
createdInstancesData[idx].interfaceConfigs = append(createdInstancesData[idx].interfaceConfigs, interfaceConfig)
}

Expand Down
58 changes: 58 additions & 0 deletions api/pkg/api/handler/instancebatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import (
authz "github.com/NVIDIA/infra-controller-rest/auth/pkg/authorization"
cdb "github.com/NVIDIA/infra-controller-rest/db/pkg/db"
cdbm "github.com/NVIDIA/infra-controller-rest/db/pkg/db/model"
cdbp "github.com/NVIDIA/infra-controller-rest/db/pkg/db/paginator"
cdbu "github.com/NVIDIA/infra-controller-rest/db/pkg/util"
cwssaws "github.com/NVIDIA/infra-controller-rest/workflow-schema/schema/site-agent/workflows/v1"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/uptrace/bun/extra/bundebug"
tmocks "go.temporal.io/sdk/mocks"
)
Expand Down Expand Up @@ -447,6 +449,9 @@ func TestBatchCreateInstanceHandler_Handle(t *testing.T) {
IsPhysical: true,
Device: cdb.GetStrPtr("MT42822 BlueField-2 integrated ConnectX-6 Dx network controller"),
DeviceInstance: cdb.GetIntPtr(0),
RoutingProfile: &model.APIInterfaceRoutingProfile{
AllowedAnycastPrefixes: []string{"192.0.2.0/24", "2001:db8::/64"},
},
},
{
VpcPrefixID: cdb.GetStrPtr(vpcPrefixSecondary.ID.String()),
Expand Down Expand Up @@ -1302,6 +1307,14 @@ func TestBatchCreateInstanceHandler_Handle(t *testing.T) {
assert.Nil(t, jsonErr)
assert.Equal(t, tt.args.reqData.Count, len(response), "Expected %d instances, got %d", tt.args.reqData.Count, len(response))

hasRoutingProfile := false
for _, reqIfc := range tt.args.reqData.Interfaces {
if reqIfc.RoutingProfile != nil {
hasRoutingProfile = true
break
}
}

// Verify instance names follow the pattern: namePrefix-randomSuffix-index
for i, inst := range response {
expectedPrefix := tt.args.reqData.NamePrefix + "-"
Expand All @@ -1313,6 +1326,51 @@ func TestBatchCreateInstanceHandler_Handle(t *testing.T) {
} else {
assert.Empty(t, inst.SecondaryVpcIDs)
}

if hasRoutingProfile {
require.Len(t, inst.Interfaces, len(tt.args.reqData.Interfaces))
for j, reqIfc := range tt.args.reqData.Interfaces {
if reqIfc.RoutingProfile != nil {
require.NotNil(t, inst.Interfaces[j].RoutingProfile)
assert.Equal(t, reqIfc.RoutingProfile.AllowedAnycastPrefixes, inst.Interfaces[j].RoutingProfile.AllowedAnycastPrefixes)
}
}

ifcDAO := cdbm.NewInterfaceDAO(dbSession)
dbIfcs, _, ierr := ifcDAO.GetAll(ec.Request().Context(), nil,
cdbm.InterfaceFilterInput{InstanceIDs: []uuid.UUID{uuid.MustParse(inst.ID)}},
cdbp.PageInput{OrderBy: &cdbp.OrderBy{Field: cdbm.InterfaceOrderByCreated, Order: cdbp.OrderAscending}},
nil)
require.NoError(t, ierr)
require.Len(t, dbIfcs, len(tt.args.reqData.Interfaces))
for j, reqIfc := range tt.args.reqData.Interfaces {
if reqIfc.RoutingProfile != nil {
require.NotNil(t, dbIfcs[j].RoutingProfile)
assert.Equal(t, reqIfc.RoutingProfile.AllowedAnycastPrefixes, dbIfcs[j].RoutingProfile.AllowedAnycastPrefixes)
}
}
}
}

if hasRoutingProfile {
var batchReq *cwssaws.BatchInstanceAllocationRequest
for i := len(tsc.Calls) - 1; i >= 0; i-- {
Comment thread
bcavnvidia marked this conversation as resolved.
call := tsc.Calls[i]
if call.Method == "ExecuteWorkflow" && len(call.Arguments) > 3 && call.Arguments[2] == "CreateInstances" {
batchReq = call.Arguments[3].(*cwssaws.BatchInstanceAllocationRequest)
Comment thread
bcavnvidia marked this conversation as resolved.
break
}
}
require.NotNil(t, batchReq)
require.Len(t, batchReq.InstanceRequests, len(response))
for _, instReq := range batchReq.InstanceRequests {
require.Len(t, instReq.Config.Network.Interfaces, len(tt.args.reqData.Interfaces))
for j, reqIfc := range tt.args.reqData.Interfaces {
if reqIfc.RoutingProfile != nil {
assertInterfaceRoutingProfilePrefixes(t, instReq.Config.Network.Interfaces[j].RoutingProfile, reqIfc.RoutingProfile.AllowedAnycastPrefixes)
}
}
}
}
}
}
Expand Down
Loading
Loading