Skip to content

vlab: generate wiring for externals #529

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
53 changes: 39 additions & 14 deletions cmd/hhfab/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func Run(ctx context.Context) error {
var wgMCLAGServers, wgESLAGServers, wgUnbundledServers, wgBundledServers uint
var wgNoSwitches bool
var wgGatewayUplinks uint
var wgExternals, wgExtMCLAGConns, wgExtESLAGConns, wgExtOrphanConns uint
vlabWiringGenFlags := []cli.Flag{
&cli.UintFlag{
Name: "spines-count",
Expand Down Expand Up @@ -246,6 +247,26 @@ func Run(ctx context.Context) error {
Destination: &wgGatewayUplinks,
Value: 2,
},
&cli.UintFlag{
Name: "externals",
Usage: "number of externals to generate",
Destination: &wgExternals,
},
&cli.UintFlag{
Name: "external-mclag-connections",
Usage: "number of external connections from MCLAG switches. NOTE: only 1 external connection in total is supported if using virtual switches",
Destination: &wgExtMCLAGConns,
},
&cli.UintFlag{
Name: "external-eslag-connections",
Usage: "number of external connections from ESLAG switches. NOTE: only 1 external connection in total is supported if using virtual switches",
Destination: &wgExtESLAGConns,
},
&cli.UintFlag{
Name: "external-orphan-connections",
Usage: "number of external connections from orphan switches. NOTE: only 1 external connection in total is supported if using virtual switches",
Destination: &wgExtOrphanConns,
},
}

var accessName string
Expand Down Expand Up @@ -679,20 +700,24 @@ func Run(ctx context.Context) error {
Before: before(false),
Action: func(_ *cli.Context) error {
builder := hhfab.VLABBuilder{
SpinesCount: uint8(wgSpinesCount), //nolint:gosec
FabricLinksCount: uint8(wgFabricLinksCount), //nolint:gosec
MCLAGLeafsCount: uint8(wgMCLAGLeafsCount), //nolint:gosec
ESLAGLeafGroups: wgESLAGLeafGroups,
OrphanLeafsCount: uint8(wgOrphanLeafsCount), //nolint:gosec
MCLAGSessionLinks: uint8(wgMCLAGSessionLinks), //nolint:gosec
MCLAGPeerLinks: uint8(wgMCLAGPeerLinks), //nolint:gosec
VPCLoopbacks: uint8(wgVPCLoopbacks), //nolint:gosec
MCLAGServers: uint8(wgMCLAGServers), //nolint:gosec
ESLAGServers: uint8(wgESLAGServers), //nolint:gosec
UnbundledServers: uint8(wgUnbundledServers), //nolint:gosec
BundledServers: uint8(wgBundledServers), //nolint:gosec
NoSwitches: wgNoSwitches,
GatewayUplinks: uint8(wgGatewayUplinks), //nolint:gosec
SpinesCount: uint8(wgSpinesCount), //nolint:gosec
FabricLinksCount: uint8(wgFabricLinksCount), //nolint:gosec
MCLAGLeafsCount: uint8(wgMCLAGLeafsCount), //nolint:gosec
ESLAGLeafGroups: wgESLAGLeafGroups,
OrphanLeafsCount: uint8(wgOrphanLeafsCount), //nolint:gosec
MCLAGSessionLinks: uint8(wgMCLAGSessionLinks), //nolint:gosec
MCLAGPeerLinks: uint8(wgMCLAGPeerLinks), //nolint:gosec
VPCLoopbacks: uint8(wgVPCLoopbacks), //nolint:gosec
MCLAGServers: uint8(wgMCLAGServers), //nolint:gosec
ESLAGServers: uint8(wgESLAGServers), //nolint:gosec
UnbundledServers: uint8(wgUnbundledServers), //nolint:gosec
BundledServers: uint8(wgBundledServers), //nolint:gosec
NoSwitches: wgNoSwitches,
GatewayUplinks: uint8(wgGatewayUplinks), //nolint:gosec
ExtCount: uint8(wgExternals), //nolint:gosec
ExtMCLAGConnCount: uint8(wgExtMCLAGConns), //nolint:gosec
ExtESLAGConnCount: uint8(wgExtESLAGConns), //nolint:gosec
ExtOrphanConnCount: uint8(wgExtOrphanConns), //nolint:gosec
}

if err := hhfab.VLABGenerate(ctx, workDir, cacheDir, builder, hhfab.DefaultVLABGeneratedFile); err != nil {
Expand Down
72 changes: 48 additions & 24 deletions pkg/hhfab/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -1495,7 +1495,6 @@ type JUnitTestSuite struct {

type SkipFlags struct {
VirtualSwitch bool `xml:"-"` // skip if there's any virtual switch in the vlab
NamedExternal bool `xml:"-"` // skip if the named external is not present
NoExternals bool `xml:"-"` // skip if there are no externals
ExtendedOnly bool `xml:"-"` // skip if extended tests are not enabled
SubInterfaces bool `xml:"-"` // skip if subinterfaces are not supported by some of the switches
Expand Down Expand Up @@ -1714,14 +1713,6 @@ func selectAndRunSuite(ctx context.Context, testCtx *VPCPeeringTestCtx, suite *J

continue
}
if test.SkipFlags.NamedExternal && skipFlags.NamedExternal {
suite.TestCases[i].Skipped = &Skipped{
Message: fmt.Sprintf("The named external (%s) is not present", testCtx.extName),
}
suite.Skipped++

continue
}
if test.SkipFlags.NoExternals && skipFlags.NoExternals {
suite.TestCases[i].Skipped = &Skipped{
Message: "There are no externals",
Expand Down Expand Up @@ -1862,7 +1853,7 @@ func makeVpcPeeringsBasicSuiteRun(testCtx *VPCPeeringTestCtx) *JUnitTestSuite {
Name: "Starter Test",
F: testCtx.vpcPeeringsStarterTest,
SkipFlags: SkipFlags{
NamedExternal: true,
NoExternals: true,
SubInterfaces: true,
},
},
Expand Down Expand Up @@ -1892,7 +1883,7 @@ func makeVpcPeeringsBasicSuiteRun(testCtx *VPCPeeringTestCtx) *JUnitTestSuite {
Name: "Sergei's Special Test",
F: testCtx.vpcPeeringsSergeisSpecialTest,
SkipFlags: SkipFlags{
NamedExternal: true,
NoExternals: true,
SubInterfaces: true,
},
},
Expand All @@ -1904,8 +1895,6 @@ func makeVpcPeeringsBasicSuiteRun(testCtx *VPCPeeringTestCtx) *JUnitTestSuite {

func RunReleaseTestSuites(ctx context.Context, workDir, cacheDir string, rtOtps ReleaseTestOpts) error {
testStart := time.Now()
// TODO: make this configurable
extName := "default"

cacheCancel, kube, err := GetKubeClientWithCache(ctx, workDir)
if err != nil {
Expand Down Expand Up @@ -1962,7 +1951,7 @@ func RunReleaseTestSuites(ctx context.Context, workDir, cacheDir string, rtOtps
// check for virtual switches
if !skipFlags.VirtualSwitch {
if sw.Spec.Profile == meta.SwitchProfileVS {
slog.Info("Virtual switch found", "switch", sw.Name)
slog.Warn("Virtual switch found, some tests will be skipped", "switch", sw.Name)
skipFlags.VirtualSwitch = true
}
}
Expand All @@ -1980,25 +1969,60 @@ func RunReleaseTestSuites(ctx context.Context, workDir, cacheDir string, rtOtps
return fmt.Errorf("getting switch profile %s: %w", sw.Spec.Profile, err)
}
if !profile.Spec.Features.Subinterfaces {
slog.Info("Subinterfaces not supported on leaf switch", "switch-profile", sw.Spec.Profile, "switch", sw.Name)
slog.Warn("Subinterfaces not supported on leaf switch, some tests will be skipped", "switch-profile", sw.Spec.Profile, "switch", sw.Name)
skipFlags.SubInterfaces = true
}
profiles = append(profiles, sw.Spec.Profile)
}
}
extList := &vpcapi.ExternalList{}
var extName string
if err := kube.List(ctx, extList); err != nil {
return fmt.Errorf("listing externals: %w", err)
}
if len(extList.Items) == 0 {
slog.Info("No externals found")
skipFlags.NoExternals = true
skipFlags.NamedExternal = true
} else {
ext := &vpcapi.External{}
if err := kube.Get(ctx, kclient.ObjectKey{Namespace: "default", Name: extName}, ext); err != nil {
slog.Info("Named External not found", "external", extName)
skipFlags.NamedExternal = true
for _, ext := range extList.Items {
if IsHardware(&ext) {
extName = ext.Name
slog.Debug("Using hardware external as the \"default\"", "external", extName)

break
}
slog.Debug("Skipping non-hardware external", "external", ext.Name)
}
if extName == "" {
slog.Debug("No hardware externals found, checking for virtual externals attached to hw switches")
for _, ext := range extList.Items {
extAttach := &vpcapi.ExternalAttachmentList{}
if err := kube.List(ctx, extAttach, kclient.MatchingLabels{wiringapi.LabelName("external"): ext.Name}); err != nil {
return fmt.Errorf("listing external attachments for %s: %w", ext.Name, err)
}
if len(extAttach.Items) == 0 {
continue
}
// check if all of the attachments are via hardware connections
someNotHW := false
for _, attach := range extAttach.Items {
conn := &wiringapi.Connection{}
if err := kube.Get(ctx, kclient.ObjectKey{Namespace: "default", Name: attach.Spec.Connection}, conn); err != nil {
return fmt.Errorf("getting connection %s: %w", attach.Spec.Connection, err)
}
if !IsHardware(conn) {
slog.Debug("Skipping external due to non-hardware attachment", "external", ext.Name, "connection", conn.Name)
someNotHW = true

break
}
}
if !someNotHW {
extName = ext.Name
slog.Debug("Using virtual external as the \"default\"", "external", extName)

break
}
}
if extName == "" {
slog.Warn("No viable external found, some tests will be skipped")
skipFlags.NoExternals = true
}
}

Expand Down
9 changes: 3 additions & 6 deletions pkg/hhfab/vlab_external_butane.tmpl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ storage:
!{{end}}{{range $vrfkey, $vrf := .ExternalVRFs}}
router bgp {{$vrf.ASN}} vrf {{$vrfkey}}
bgp log-neighbor-changes
bgp bestpath as-path multipath-relax
no bgp ebgp-requires-policy
no bgp network import-check {{range $connNicKey, $connNic := $.ExternalNICs}}{{range $attach := $connNic.Attachments}}{{if eq $attach.VRF $vrfkey}}
neighbor {{$attach.NeighborIP}} remote-as {{$attach.NeighborASN}}
Expand All @@ -154,23 +155,19 @@ storage:
address-family ipv4 unicast {{range $attach := $connNic.Attachments}}{{if eq $attach.VRF $vrfkey}}
neighbor {{$attach.NeighborIP}} activate
no neighbor {{$attach.NeighborIP}} send-community large
neighbor {{$attach.NeighborIP}} soft-reconfiguration inbound
neighbor {{$attach.NeighborIP}} route-map {{$vrfkey}}In in
neighbor {{$attach.NeighborIP}} route-map {{$vrfkey}}Out out {{end}}{{end}}{{if and ($connNic.Untagged) (eq $connNic.UntaggedCfg.VRF $vrfkey)}}
neighbor {{$connNic.UntaggedCfg.NeighborIP}} activate
no neighbor {{$connNic.UntaggedCfg.NeighborIP}} send-community large
neighbor {{$connNic.UntaggedCfg.NeighborIP}} route-map {{$vrfkey}}In in
neighbor {{$connNic.UntaggedCfg.NeighborIP}} route-map {{$vrfkey}}Out out{{end}}
maximum-paths 1
maximum-paths ibgp 1
redistribute static
import vrf default
!
address-family ipv6 unicast
maximum-paths 1
maximum-paths ibgp 1
!
!{{end}}{{end}}
router bgp 1
bgp bestpath as-path multipath-relax
address-family ipv4 unicast
redistribute static{{range $vrfkey, $vrf := .ExternalVRFs}}
import vrf {{$vrfkey}}{{end}}
Expand Down
Loading
Loading