diff --git a/api/wiring/v1beta1/switchprofile_types.go b/api/wiring/v1beta1/switchprofile_types.go index 7a4d14ae7..2c0d30b19 100644 --- a/api/wiring/v1beta1/switchprofile_types.go +++ b/api/wiring/v1beta1/switchprofile_types.go @@ -37,6 +37,7 @@ const ( ManagementPortNOSNamePrefix = "Management" DataPortPrefix = "E" DataPortNOSNamePrefix = "Ethernet" + DataPortCmlsNamePrefix = "swp" BreakoutNOSNamePrefix = "1/" ONIEPortNamePrefix = "eth" @@ -286,61 +287,67 @@ func (sp *SwitchProfile) Validate(_ context.Context, _ kclient.Reader, _ *meta.F } nosPortNames[port.NOSName] = true - if port.BaseNOSName != "" { - if _, ok := baseNOSPortNames[port.BaseNOSName]; ok { - return nil, errors.Errorf("port %q base NOS name %q is duplicated", name, port.BaseNOSName) + if sp.Spec.NOSType != meta.NOSTypeCumulusMlx && sp.Spec.NOSType != meta.NOSTypeCumulusVX { + if port.BaseNOSName != "" { + if _, ok := baseNOSPortNames[port.BaseNOSName]; ok { + return nil, errors.Errorf("port %q base NOS name %q is duplicated", name, port.BaseNOSName) + } + baseNOSPortNames[port.BaseNOSName] = true } - baseNOSPortNames[port.BaseNOSName] = true } if port.Management { - if !strings.HasPrefix(name, ManagementPortPrefix) { - return nil, errors.Errorf("management port %q name must start with M", name) - } + if sp.Spec.NOSType == meta.NOSTypeCumulusMlx || sp.Spec.NOSType == meta.NOSTypeCumulusVX { + if port.NOSName != "eth0" { + return nil, errors.Errorf("management port %q name must be eth0 in Cumulus", name) + } + } else { + if !strings.HasPrefix(name, ManagementPortPrefix) { + return nil, errors.Errorf("management port %q name must start with M", name) + } - if port, err := strconv.Atoi(name[1:]); err != nil || port <= 0 { - return nil, errors.Errorf("management port %q name must end with a positive integer", name) - } + if port, err := strconv.Atoi(name[1:]); err != nil || port <= 0 { + return nil, errors.Errorf("management port %q name must end with a positive integer", name) + } - if !strings.HasPrefix(port.NOSName, ManagementPortNOSNamePrefix) { - return nil, errors.Errorf("management port %q NOS name must start with Management", name) - } + if !strings.HasPrefix(port.NOSName, ManagementPortNOSNamePrefix) { + return nil, errors.Errorf("management port %q NOS name must start with Management", name) + } - if port, err := strconv.Atoi(port.NOSName[len(ManagementPortNOSNamePrefix):]); err != nil || port < 0 { - return nil, errors.Errorf("management port %q NOS name must end with a positive integer", name) - } + if port, err := strconv.Atoi(port.NOSName[len(ManagementPortNOSNamePrefix):]); err != nil || port < 0 { + return nil, errors.Errorf("management port %q NOS name must end with a positive integer", name) + } - if port.OniePortName == "" { - return nil, errors.Errorf("management port %q must have an ONIE port name", name) - } + if port.OniePortName == "" { + return nil, errors.Errorf("management port %q must have an ONIE port name", name) + } - if !strings.HasPrefix(port.OniePortName, ONIEPortNamePrefix) { - return nil, errors.Errorf("management port %q ONIE port name must start with eth", name) - } + if !strings.HasPrefix(port.OniePortName, ONIEPortNamePrefix) { + return nil, errors.Errorf("management port %q ONIE port name must start with eth", name) + } - if port, err := strconv.Atoi(port.OniePortName[len(ONIEPortNamePrefix):]); err != nil || port < 0 { - return nil, errors.Errorf("management port %q ONIE port name must end with a zero or positive integer", name) - } + if port, err := strconv.Atoi(port.OniePortName[len(ONIEPortNamePrefix):]); err != nil || port < 0 { + return nil, errors.Errorf("management port %q ONIE port name must end with a zero or positive integer", name) + } - if port.Group != "" { - return nil, errors.Errorf("management port %q must not have a group", name) - } + if port.Group != "" { + return nil, errors.Errorf("management port %q must not have a group", name) + } - if port.Profile != "" { - return nil, errors.Errorf("management port %q must not have a profile", name) - } + if port.Profile != "" { + return nil, errors.Errorf("management port %q must not have a profile", name) + } - if port.BaseNOSName != "" { - return nil, errors.Errorf("management port %q must not have a base NOS name", name) + if port.BaseNOSName != "" { + return nil, errors.Errorf("management port %q must not have a base NOS name", name) + } } continue } - if !strings.HasPrefix(name, DataPortPrefix) { return nil, errors.Errorf("data port %q must start with E", name) } - portParts := strings.Split(name[len(DataPortPrefix):], "/") if len(portParts) != 2 { return nil, errors.Errorf("data port %q name must have two segments separated by a slash (e.g. asic/port)", name) @@ -385,24 +392,26 @@ func (sp *SwitchProfile) Validate(_ context.Context, _ kclient.Reader, _ *meta.F return nil, errors.Errorf("breakout port %q must have a NOS name", name) } - if !strings.HasPrefix(port.NOSName, BreakoutNOSNamePrefix) { - return nil, errors.Errorf("breakout port %q NOS name must start with %s", name, BreakoutNOSNamePrefix) - } - - if _, err := strconv.Atoi(port.NOSName[len(BreakoutNOSNamePrefix):]); err != nil { - return nil, errors.Errorf("breakout port %q NOS name must end with a positive integer", name) + if sp.Spec.NOSType != meta.NOSTypeCumulusMlx && sp.Spec.NOSType != meta.NOSTypeCumulusVX { + if !strings.HasPrefix(port.NOSName, BreakoutNOSNamePrefix) { + return nil, errors.Errorf("breakout port %q NOS name must start with %s", name, BreakoutNOSNamePrefix) + } + if _, err := strconv.Atoi(port.NOSName[len(BreakoutNOSNamePrefix):]); err != nil { + return nil, errors.Errorf("breakout port %q NOS name must end with a positive integer", name) + } } if port.BaseNOSName == "" { return nil, errors.Errorf("breakout port %q must have a base NOS name", name) } + if sp.Spec.NOSType != meta.NOSTypeCumulusMlx && sp.Spec.NOSType != meta.NOSTypeCumulusVX { + if !strings.HasPrefix(port.BaseNOSName, DataPortNOSNamePrefix) { + return nil, errors.Errorf("breakout port %q base NOS name must start with %s", name, DataPortNOSNamePrefix) + } - if !strings.HasPrefix(port.BaseNOSName, DataPortNOSNamePrefix) { - return nil, errors.Errorf("breakout port %q base NOS name must start with %s", name, DataPortNOSNamePrefix) - } - - if port, err := strconv.Atoi(port.BaseNOSName[len(DataPortNOSNamePrefix):]); err != nil || port < 0 { - return nil, errors.Errorf("breakout port %q base NOS name must end with a zero or positive integer", name) + if port, err := strconv.Atoi(port.BaseNOSName[len(DataPortNOSNamePrefix):]); err != nil || port < 0 { + return nil, errors.Errorf("breakout port %q base NOS name must end with a zero or positive integer", name) + } } } } @@ -419,12 +428,14 @@ func (sp *SwitchProfile) Validate(_ context.Context, _ kclient.Reader, _ *meta.F } if !isBreakout { - if !strings.HasPrefix(port.NOSName, DataPortNOSNamePrefix) { - return nil, errors.Errorf("data port %q NOS name must start with %s", name, DataPortNOSNamePrefix) - } + if sp.Spec.NOSType != meta.NOSTypeCumulusMlx && sp.Spec.NOSType != meta.NOSTypeCumulusVX { + if !strings.HasPrefix(port.NOSName, DataPortNOSNamePrefix) { + return nil, errors.Errorf("data port %q NOS name must start with %s", name, DataPortNOSNamePrefix) + } - if port, err := strconv.Atoi(port.NOSName[len(DataPortNOSNamePrefix):]); err != nil || port < 0 { - return nil, errors.Errorf("data port %q NOS name must end with a zero or positive integer", name) + if port, err := strconv.Atoi(port.NOSName[len(DataPortNOSNamePrefix):]); err != nil || port < 0 { + return nil, errors.Errorf("data port %q NOS name must end with a zero or positive integer", name) + } } } @@ -626,7 +637,6 @@ func (sp *SwitchProfileSpec) GetAPI2NOSPortsFor(sw *SwitchSpec) (map[string]stri continue } - if port.Profile != "" && sp.PortProfiles[port.Profile].Breakout != nil { breakoutProfile := sp.PortProfiles[port.Profile].Breakout @@ -634,30 +644,45 @@ func (sp *SwitchProfileSpec) GetAPI2NOSPortsFor(sw *SwitchSpec) (map[string]stri if !ok { swBreakout = breakoutProfile.Default } - if breakoutMode, ok := breakoutProfile.Supported[swBreakout]; ok { - nosNameBaseStr, cut := strings.CutPrefix(port.BaseNOSName, DataPortNOSNamePrefix) - if !cut { - return nil, errors.Errorf("port %q base NOS name %q is invalid (no expected prefix)", portName, port.NOSName) - } - nosNameBase, err := strconv.Atoi(nosNameBaseStr) - if err != nil { - return nil, errors.Errorf("port %q base NOS name %q is invalid (suffix isn't a number)", portName, port.NOSName) - } - - for breakoutIdx, offsetStr := range breakoutMode.Offsets { - offset, err := strconv.Atoi(offsetStr) + if sp.NOSType == meta.NOSTypeCumulusMlx || sp.NOSType == meta.NOSTypeCumulusVX { + for breakoutIdx, offsetStr := range breakoutMode.Offsets { + nosName := port.BaseNOSName + port.NOSName + switch { + case offsetStr == "": + ports[portName] = nosName + ports[fmt.Sprintf("%s/%d", portName, breakoutIdx+1)] = nosName + case offsetStr == "0": + ports[portName] = nosName + "s" + offsetStr + ports[fmt.Sprintf("%s/%d", portName, breakoutIdx+1)] = nosName + "s" + offsetStr + default: + ports[fmt.Sprintf("%s/%d", portName, breakoutIdx+1)] = nosName + "s" + offsetStr + } + } + } else { + nosNameBaseStr, cut := strings.CutPrefix(port.BaseNOSName, DataPortNOSNamePrefix) + if !cut { + return nil, errors.Errorf("port %q base NOS name %q is invalid (no expected prefix)", portName, port.NOSName) + } + nosNameBase, err := strconv.Atoi(nosNameBaseStr) if err != nil { - return nil, errors.Errorf("port %q NOS name %q breakout mode %q offset %q is invalid (not a number)", portName, port.NOSName, swBreakout, offsetStr) + return nil, errors.Errorf("port %q base NOS name %q is invalid (suffix isn't a number)", portName, port.NOSName) } - nosName := fmt.Sprintf("%s%d", DataPortNOSNamePrefix, nosNameBase+offset) + for breakoutIdx, offsetStr := range breakoutMode.Offsets { + offset, err := strconv.Atoi(offsetStr) + if err != nil { + return nil, errors.Errorf("port %q NOS name %q breakout mode %q offset %q is invalid (not a number)", portName, port.NOSName, swBreakout, offsetStr) + } - if breakoutIdx == 0 { - ports[portName] = nosName - } + nosName := fmt.Sprintf("%s%d", DataPortNOSNamePrefix, nosNameBase+offset) + + if breakoutIdx == 0 { + ports[portName] = nosName + } - ports[fmt.Sprintf("%s/%d", portName, breakoutIdx+1)] = nosName + ports[fmt.Sprintf("%s/%d", portName, breakoutIdx+1)] = nosName + } } } else { return nil, errors.Errorf("port %q has a breakout %q not supported by profile %q", portName, swBreakout, port.Profile) @@ -677,27 +702,50 @@ func (sp *SwitchProfileSpec) GetNOS2APIPortsFor(sw *SwitchSpec) (map[string]stri if sw == nil { return nil, errors.Errorf("switch spec is nil") } - - api2NOS, err := sp.GetAPI2NOSPortsFor(sw) - if err != nil { - return nil, errors.Wrap(err, "failed to get API to NOS ports") - } - ports := map[string]string{} - for apiPort, nosPort := range api2NOS { - if prevAPIPort, exists := ports[nosPort]; exists { - if prevAPIPort+"/1" == apiPort { - ports[nosPort] = apiPort - - continue - } else if prevAPIPort == apiPort+"/1" { - continue - } - return nil, errors.Errorf("NOS port %q is duplicated", nosPort) + if sp.NOSType == meta.NOSTypeCumulusMlx || sp.NOSType == meta.NOSTypeCumulusVX { + for portName, port := range sp.Ports { + if port.Management { + ports[port.NOSName] = "M1" + } + if port.Profile != "" && sp.PortProfiles[port.Profile].Breakout != nil { + breakoutProfile := sp.PortProfiles[port.Profile].Breakout + swBreakout, ok := sw.PortBreakouts[portName] + if !ok { + swBreakout = breakoutProfile.Default + } + if breakoutMode, ok := breakoutProfile.Supported[swBreakout]; ok { + for breakoutIdx, offsetStr := range breakoutMode.Offsets { + switch { + case offsetStr == "": + ports[port.BaseNOSName+port.NOSName] = "E1/" + port.NOSName + "/1" + default: + ports[port.BaseNOSName+port.NOSName+"s"+offsetStr] = "E1/" + port.NOSName + "/" + strconv.Itoa(breakoutIdx+1) + } + } + } + } + } + } else { + api2NOS, err := sp.GetAPI2NOSPortsFor(sw) + if err != nil { + return nil, errors.Wrap(err, "failed to get API to NOS ports") } + for apiPort, nosPort := range api2NOS { + if prevAPIPort, exists := ports[nosPort]; exists { + if prevAPIPort+"/1" == apiPort { + ports[nosPort] = apiPort + + continue + } else if prevAPIPort == apiPort+"/1" { + continue + } - ports[nosPort] = apiPort + return nil, errors.Errorf("NOS port %q is duplicated", nosPort) + } + ports[nosPort] = apiPort + } } return ports, nil @@ -753,24 +801,36 @@ func (sp *SwitchProfileSpec) GetAllBreakoutNOSNames() (map[string]bool, error) { continue } - nosNameBaseStr, cut := strings.CutPrefix(port.BaseNOSName, DataPortNOSNamePrefix) - if !cut { - return nil, errors.Errorf("port %q base NOS name %q is invalid (no expected prefix)", portName, port.NOSName) - } - nosNameBase, err := strconv.Atoi(nosNameBaseStr) - if err != nil { - return nil, errors.Errorf("port %q base NOS name %q is invalid (suffix isn't a number)", portName, port.NOSName) - } - - for _, breakoutMode := range profile.Breakout.Supported { - for _, offsetStr := range breakoutMode.Offsets { - offset, err := strconv.Atoi(offsetStr) - if err != nil { - return nil, errors.Errorf("port %q NOS name %q breakout mode %q offset %q is invalid (not a number)", portName, port.NOSName, breakoutMode, offsetStr) + if sp.NOSType == meta.NOSTypeCumulusMlx || sp.NOSType == meta.NOSTypeCumulusVX { + for _, breakoutMode := range profile.Breakout.Supported { + for _, offsetStr := range breakoutMode.Offsets { + if offsetStr == "" { + continue + } + nosName := port.BaseNOSName + port.NOSName + "s" + offsetStr + ports[nosName] = true } + } + } else { + nosNameBaseStr, cut := strings.CutPrefix(port.BaseNOSName, DataPortNOSNamePrefix) + if !cut { + return nil, errors.Errorf("port %q base NOS name %q is invalid (no expected prefix)", portName, port.NOSName) + } + nosNameBase, err := strconv.Atoi(nosNameBaseStr) + if err != nil { + return nil, errors.Errorf("port %q base NOS name %q is invalid (suffix isn't a number)", portName, port.NOSName) + } + + for _, breakoutMode := range profile.Breakout.Supported { + for _, offsetStr := range breakoutMode.Offsets { + offset, err := strconv.Atoi(offsetStr) + if err != nil { + return nil, errors.Errorf("port %q NOS name %q breakout mode %q offset %q is invalid (not a number)", portName, port.NOSName, breakoutMode, offsetStr) + } - nosName := fmt.Sprintf("%s%d", DataPortNOSNamePrefix, nosNameBase+offset) - ports[nosName] = true + nosName := fmt.Sprintf("%s%d", DataPortNOSNamePrefix, nosNameBase+offset) + ports[nosName] = true + } } } } diff --git a/api/wiring/v1beta1/switchprofile_types_test.go b/api/wiring/v1beta1/switchprofile_types_test.go index 0b013802e..5ffe6ef87 100644 --- a/api/wiring/v1beta1/switchprofile_types_test.go +++ b/api/wiring/v1beta1/switchprofile_types_test.go @@ -36,6 +36,8 @@ func TestGetAPI2NOSPortsFor(t *testing.T) { sp: &wiringapi.SwitchProfileSpec{ DisplayName: "Test", SwitchSilicon: "test", + NOSType: meta.NOSTypeSONiCBCMVS, + Platform: "vs", Ports: map[string]wiringapi.SwitchProfilePort{ "M1": {NOSName: "Management0", Management: true, OniePortName: "eth0"}, "E1/1": {NOSName: "Ethernet0", Label: "1", Group: "1"}, @@ -108,12 +110,80 @@ func TestGetAPI2NOSPortsFor(t *testing.T) { "M1": "Management0", }, }, + { + name: "cumulus", + sp: &wiringapi.SwitchProfileSpec{ + DisplayName: "nvidia", + SwitchSilicon: "spectrum", + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn4600", + Ports: map[string]wiringapi.SwitchProfilePort{ + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", Label: "1", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/2": {NOSName: "2", Label: "2", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/3": {NOSName: "3", Label: "3", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/4": {NOSName: "4", Label: "4", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/5": {NOSName: "5", Label: "5", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/6": {NOSName: "6", Label: "6", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/7": {NOSName: "7", Label: "7", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/8": {NOSName: "8", Label: "8", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/9": {NOSName: "9", Label: "9", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/10": {NOSName: "10", Label: "10", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "QSFP28-100G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x100G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x100G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "1x50G": {Offsets: []string{""}}, + "4x25G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + "1x25G": {Offsets: []string{""}}, + "1x10G": {Offsets: []string{""}}, + }, + }, + }, + }, + }, + sw: &wiringapi.SwitchSpec{ + PortBreakouts: map[string]string{ + "E1/8": "4x25G", + "E1/9": "2x50G", + }, + }, + want: map[string]string{ + "E1/1": "swp1", + "E1/1/1": "swp1", + "E1/2": "swp2", + "E1/2/1": "swp2", + "E1/3": "swp3", + "E1/3/1": "swp3", + "E1/4": "swp4", + "E1/4/1": "swp4", + "E1/5": "swp5", + "E1/5/1": "swp5", + "E1/6": "swp6", + "E1/6/1": "swp6", + "E1/7": "swp7", + "E1/7/1": "swp7", + "E1/8": "swp8s0", + "E1/8/1": "swp8s0", + "E1/8/2": "swp8s1", + "E1/8/3": "swp8s2", + "E1/8/4": "swp8s3", + "E1/9": "swp9s0", + "E1/9/1": "swp9s0", + "E1/9/2": "swp9s1", + "E1/10": "swp10", + "E1/10/1": "swp10", + "M1": "eth0", + }, + }, } { t.Run(tt.name, func(t *testing.T) { - if tt.sp != nil { - tt.sp.NOSType = meta.NOSTypeSONiCBCMVS - tt.sp.Platform = "vs" - } _, err := (&wiringapi.SwitchProfile{ ObjectMeta: kmetav1.ObjectMeta{ Name: "test", @@ -150,6 +220,8 @@ func TestGetNOS2APIPortsFor(t *testing.T) { sp: &wiringapi.SwitchProfileSpec{ DisplayName: "Test", SwitchSilicon: "test", + NOSType: meta.NOSTypeSONiCBCMVS, + Platform: "vs", Ports: map[string]wiringapi.SwitchProfilePort{ "M1": {NOSName: "Management0", Management: true, OniePortName: "eth0"}, "E1/1": {NOSName: "Ethernet0", Label: "1", Group: "1"}, @@ -219,12 +291,70 @@ func TestGetNOS2APIPortsFor(t *testing.T) { "Management0": "M1", }, }, + { + name: "cumulus", + sp: &wiringapi.SwitchProfileSpec{ + DisplayName: "nvidia", + SwitchSilicon: "spectrum", + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn4600", + Ports: map[string]wiringapi.SwitchProfilePort{ + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", Label: "1", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/2": {NOSName: "2", Label: "2", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/3": {NOSName: "3", Label: "3", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/4": {NOSName: "4", Label: "4", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/5": {NOSName: "5", Label: "5", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/6": {NOSName: "6", Label: "6", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/7": {NOSName: "7", Label: "7", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/8": {NOSName: "8", Label: "8", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/9": {NOSName: "9", Label: "9", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/10": {NOSName: "10", Label: "10", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "QSFP28-100G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x100G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x100G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "1x50G": {Offsets: []string{""}}, + "4x25G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + "1x25G": {Offsets: []string{""}}, + "1x10G": {Offsets: []string{""}}, + }, + }, + }, + }, + }, + sw: &wiringapi.SwitchSpec{ + PortBreakouts: map[string]string{ + "E1/8": "4x25G", + "E1/9": "2x50G", + }, + }, + want: map[string]string{ + "swp1": "E1/1/1", + "swp2": "E1/2/1", + "swp3": "E1/3/1", + "swp4": "E1/4/1", + "swp5": "E1/5/1", + "swp6": "E1/6/1", + "swp7": "E1/7/1", + "swp8s0": "E1/8/1", + "swp8s1": "E1/8/2", + "swp8s2": "E1/8/3", + "swp8s3": "E1/8/4", + "swp9s0": "E1/9/1", + "swp9s1": "E1/9/2", + "swp10": "E1/10/1", + "eth0": "M1", + }, + }, } { t.Run(tt.name, func(t *testing.T) { - if tt.sp != nil { - tt.sp.NOSType = meta.NOSTypeSONiCBCMVS - tt.sp.Platform = "vs" - } _, err := (&wiringapi.SwitchProfile{ ObjectMeta: kmetav1.ObjectMeta{ Name: "test", @@ -260,6 +390,8 @@ func TestGetAllBreakoutNOSNames(t *testing.T) { sp: &wiringapi.SwitchProfileSpec{ DisplayName: "Test", SwitchSilicon: "test", + NOSType: meta.NOSTypeSONiCBCMVS, + Platform: "vs", Ports: map[string]wiringapi.SwitchProfilePort{ "E1/7": {NOSName: "1/7", Label: "7", Profile: "QSFP28-100G", BaseNOSName: "Ethernet20"}, "E1/8": {NOSName: "1/8", Label: "8", Profile: "QSFP28-100G", BaseNOSName: "Ethernet24"}, @@ -298,6 +430,8 @@ func TestGetAllBreakoutNOSNames(t *testing.T) { sp: &wiringapi.SwitchProfileSpec{ DisplayName: "Test", SwitchSilicon: "test", + NOSType: meta.NOSTypeSONiCBCMVS, + Platform: "vs", Ports: map[string]wiringapi.SwitchProfilePort{ "E1/7": {NOSName: "1/7", Label: "7", Profile: "QSFP28-100G", BaseNOSName: "Ethernet20"}, "E1/8": {NOSName: "1/8", Label: "8", Profile: "QSFP28-100G", BaseNOSName: "Ethernet24"}, @@ -325,12 +459,48 @@ func TestGetAllBreakoutNOSNames(t *testing.T) { "Ethernet26": true, }, }, + { + name: "cumulus", + sp: &wiringapi.SwitchProfileSpec{ + DisplayName: "nvidia", + SwitchSilicon: "spectrum", + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn4600", + Ports: map[string]wiringapi.SwitchProfilePort{ + "E1/7": {NOSName: "7", Label: "7", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/8": {NOSName: "8", Label: "8", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "QSFP28-100G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x100G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x100G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "1x50G": {Offsets: []string{""}}, + "4x25G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + "1x25G": {Offsets: []string{""}}, + "1x10G": {Offsets: []string{""}}, + }, + }, + }, + }, + }, + want: map[string]bool{ + "swp7s0": true, + "swp7s1": true, + "swp7s2": true, + "swp7s3": true, + "swp8s0": true, + "swp8s1": true, + "swp8s2": true, + "swp8s3": true, + }, + }, } { t.Run(tt.name, func(t *testing.T) { - if tt.sp != nil { - tt.sp.NOSType = meta.NOSTypeSONiCBCMVS - tt.sp.Platform = "vs" - } _, err := (&wiringapi.SwitchProfile{ ObjectMeta: kmetav1.ObjectMeta{ Name: "test", @@ -365,6 +535,8 @@ func TestSwitchProfileDefault(t *testing.T) { sp: &wiringapi.SwitchProfileSpec{ DisplayName: "Test", SwitchSilicon: "test", + NOSType: meta.NOSTypeSONiCBCMVS, + Platform: "vs", Ports: map[string]wiringapi.SwitchProfilePort{ "M1": {NOSName: "Management0", Management: true, OniePortName: "eth0"}, "E1/1": {NOSName: "Ethernet0", Label: "1", Group: "1"}, @@ -422,12 +594,50 @@ func TestSwitchProfileDefault(t *testing.T) { wiringapi.AnnotationPorts: "6xSFP28-25G, 4xQSFP28-100G", }, }, + { + name: "cumulus", + sp: &wiringapi.SwitchProfileSpec{ + DisplayName: "nvidia", + SwitchSilicon: "spectrum", + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn4600", + Ports: map[string]wiringapi.SwitchProfilePort{ + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", Label: "1", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/2": {NOSName: "2", Label: "2", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/3": {NOSName: "3", Label: "3", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/4": {NOSName: "4", Label: "4", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/5": {NOSName: "5", Label: "5", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/6": {NOSName: "6", Label: "6", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/7": {NOSName: "7", Label: "7", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/8": {NOSName: "8", Label: "8", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/9": {NOSName: "9", Label: "9", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/10": {NOSName: "10", Label: "10", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "QSFP28-100G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x100G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x100G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "1x50G": {Offsets: []string{""}}, + "4x25G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + "1x25G": {Offsets: []string{""}}, + "1x10G": {Offsets: []string{""}}, + }, + }, + }, + }, + }, + want: map[string]string{ + wiringapi.AnnotationPorts: "10xQSFP28-100G", + }, + }, } { t.Run(tt.name, func(t *testing.T) { - if tt.sp != nil { - tt.sp.NOSType = meta.NOSTypeSONiCBCMVS - tt.sp.Platform = "vs" - } _, err := (&wiringapi.SwitchProfile{ ObjectMeta: kmetav1.ObjectMeta{ Name: "test", @@ -525,6 +735,8 @@ func TestGetAvailableAPIPorts(t *testing.T) { sp: &wiringapi.SwitchProfileSpec{ DisplayName: "Test", SwitchSilicon: "test", + NOSType: meta.NOSTypeSONiCBCMVS, + Platform: "vs", Ports: map[string]wiringapi.SwitchProfilePort{ "M1": {NOSName: "Management0", Management: true, OniePortName: "eth0"}, "E1/1": {NOSName: "Ethernet0", Label: "1", Group: "1"}, @@ -598,6 +810,8 @@ func TestGetAvailableAPIPorts(t *testing.T) { sp: &wiringapi.SwitchProfileSpec{ DisplayName: "Test", SwitchSilicon: "test", + NOSType: meta.NOSTypeSONiCBCMVS, + Platform: "vs", Ports: map[string]wiringapi.SwitchProfilePort{ "M1": {NOSName: "Management0", Management: true, OniePortName: "eth0"}, "E1/1": {NOSName: "Ethernet0", Label: "1", Group: "1"}, @@ -657,13 +871,69 @@ func TestGetAvailableAPIPorts(t *testing.T) { "E1/9/1": true, }, }, + { + name: "cumulus", + sp: &wiringapi.SwitchProfileSpec{ + DisplayName: "nvidia", + SwitchSilicon: "spectrum", + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn4600", + Ports: map[string]wiringapi.SwitchProfilePort{ + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", Label: "1", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/2": {NOSName: "2", Label: "2", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/3": {NOSName: "3", Label: "3", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/4": {NOSName: "4", Label: "4", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/5": {NOSName: "5", Label: "5", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/6": {NOSName: "6", Label: "6", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/7": {NOSName: "7", Label: "7", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/8": {NOSName: "8", Label: "8", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/9": {NOSName: "9", Label: "9", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + "E1/10": {NOSName: "10", Label: "10", Profile: "QSFP28-100G", BaseNOSName: "swp"}, + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "QSFP28-100G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x100G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x100G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "1x50G": {Offsets: []string{""}}, + "4x25G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + "1x25G": {Offsets: []string{""}}, + "1x10G": {Offsets: []string{""}}, + }, + }, + }, + }, + }, + sw: &wiringapi.SwitchSpec{ + PortBreakouts: map[string]string{ + "E1/8": "4x25G", + "E1/9": "2x50G", + }, + }, + want: map[string]bool{ + "E1/1/1": true, + "E1/2/1": true, + "E1/3/1": true, + "E1/4/1": true, + "E1/5/1": true, + "E1/6/1": true, + "E1/7/1": true, + "E1/8/1": true, + "E1/8/2": true, + "E1/8/3": true, + "E1/8/4": true, + "E1/9/1": true, + "E1/9/2": true, + "E1/10/1": true, + }, + }, } { t.Run(tt.name, func(t *testing.T) { - if tt.sp != nil { - tt.sp.NOSType = meta.NOSTypeSONiCBCMVS - tt.sp.Platform = "vs" - } - _, err := (&wiringapi.SwitchProfile{ ObjectMeta: kmetav1.ObjectMeta{ Name: "test", diff --git a/pkg/agent/cmls/config.tmpl.yaml b/pkg/agent/cmls/config.tmpl.yaml index 59cab4721..570de5732 100644 --- a/pkg/agent/cmls/config.tmpl.yaml +++ b/pkg/agent/cmls/config.tmpl.yaml @@ -14,7 +14,7 @@ {{ $.ProtocolIP }}: {} {{ if $.VTEPIP }}{{ $.VTEPIP }}: {}{{ end }} type: loopback - swp1-64: + swp1-{{ $.FrontPorts }}: ipv6: state: disabled lldp: diff --git a/pkg/agent/cmls/cumulus.go b/pkg/agent/cmls/cumulus.go index 21e75da15..116f742ab 100644 --- a/pkg/agent/cmls/cumulus.go +++ b/pkg/agent/cmls/cumulus.go @@ -47,6 +47,7 @@ type ConfigIn struct { ProtocolIP string VTEPIP string ASN uint32 + FrontPorts uint16 HostSubnet string RouterID string VXLANSource string @@ -313,6 +314,7 @@ func buildConfigFor(tmpl string, agent *agentapi.Agent) (*bytes.Buffer, error) { ProtocolIP: agent.Spec.Switch.ProtocolIP, VTEPIP: agent.Spec.Switch.VTEPIP, ASN: agent.Spec.Switch.ASN, + FrontPorts: uint16(len(agent.Spec.SwitchProfile.Ports) - 1), //nolint:gosec // disable G115 RouterID: protocolIP.Addr().String(), VXLANSource: vtepIP.Addr().String(), VPCs: vpcs, diff --git a/pkg/agent/cmls/ztp_config.tmpl.yaml b/pkg/agent/cmls/ztp_config.tmpl.yaml index 22e83f29e..c0f7a6b8a 100644 --- a/pkg/agent/cmls/ztp_config.tmpl.yaml +++ b/pkg/agent/cmls/ztp_config.tmpl.yaml @@ -6,7 +6,7 @@ {{ $.ManagementIP }}: {} type: eth vrf: mgmt - swp1-64: + swp1-{{- $.FrontPorts }}: ipv6: state: disabled link: diff --git a/pkg/agent/cmls/ztp_script.tmpl.sh b/pkg/agent/cmls/ztp_script.tmpl.sh index caa177da4..582f7a559 100644 --- a/pkg/agent/cmls/ztp_script.tmpl.sh +++ b/pkg/agent/cmls/ztp_script.tmpl.sh @@ -47,7 +47,7 @@ EOF echo "$(date) INFO: Configuration applied successfully" # TODO check if it's needed or affected by agent - mkdir /var/log/ntpsec/ + mkdir -p /var/log/ntpsec/ chown -R ntpsec /var/log/ntpsec/ systemctl restart ntp@mgmt.service diff --git a/pkg/ctrl/switchprofile/README.md b/pkg/ctrl/switchprofile/README.md index 0f700e355..1f3d254d3 100644 --- a/pkg/ctrl/switchprofile/README.md +++ b/pkg/ctrl/switchprofile/README.md @@ -179,3 +179,40 @@ ping and iperf3 testing to ensure that traffic is flowing as expected. A leaf will need similar testing to a spine. Create a VPC attached to leaf and have the traffic transit the leaf in and out of the VPC. Additionally test that the leaf allows for Remote VPC peering. + +# Cumulus Switch Profiles + +## Get Platform info + +This command and output will give you the cpu, port type and count, system type, asic, etc. + +``` +cumulus@nv-sn3700c:mgmt:~$ nv show platform + operational +------------- ------------------------------------------------- +system-mac 9c:05:91:28:4b:fd +manufacturer Mellanox +cpu x86_64 Intel(R) Pentium(R) CPU D1508 @ 2.20GHz x4 +memory 7.28 GB +disk-size 28GB +port-layout 32 x 100G-QSFP28 +part-number MSN3700-CS2FC +serial-number MT2249J39547 +asic-model Spectrum-2 +system-uuid 6fb8ea6e-7335-11ed-8000-9c0591284b00 +system-type MSN3700C +``` + +## Breakouts + +When creating a profile, for the single breakout ports use the empty string "" to indicate a 1 port breakout. When for examples port swp1 is broken out 4 ways, any configuration applied to swp1 will apply to all the breakout ports. There are test cases for Cumulus switches that follow this convention as well. + +The port breakouts and limitations are best copied from the nvidia documentation, and then verified at the cli. +The [nvidia switch documentation](https://docs.nvidia.com/networking/switches/index.html#nvidiatab-ethernet-switches) for switches does get modified by nvidia so it won't exist forever, sadly. + +If a picture of the available breakouts isn't available, the manual process of configuring breakouts via the cli is the best option. + +0. Start with adjacent ports for example swp5 and swp6. Attempt the largest breakouts first. If there is a limitation on adjacent ports that will cut down on the options that you need to test. + + **note** In the past breakout limitations have been on physically adjacent ports, if there are 4 ports in a column on the front panel of a switch include all of those ports in the breakout testing from the previous step +1. once the breakout is settled, set the speed of the ports via the cli. diff --git a/pkg/ctrl/switchprofile/catalog.go b/pkg/ctrl/switchprofile/catalog.go index 3084cdf90..ca3a82969 100644 --- a/pkg/ctrl/switchprofile/catalog.go +++ b/pkg/ctrl/switchprofile/catalog.go @@ -57,6 +57,9 @@ var sonicCLSPlusSwitchProfiles = []wiringapi.SwitchProfile{ } var cumulusSwitchProfiles = []wiringapi.SwitchProfile{ + CmlsNvidiaSN2201, + CmlsNvidiaSN3700c, + CmlsNvidiaSN4600, CumulusVX, } diff --git a/pkg/ctrl/switchprofile/p_cmls_nvidia_sn2201.go b/pkg/ctrl/switchprofile/p_cmls_nvidia_sn2201.go new file mode 100644 index 000000000..d213c9a3c --- /dev/null +++ b/pkg/ctrl/switchprofile/p_cmls_nvidia_sn2201.go @@ -0,0 +1,122 @@ +// Copyright 2025 Hedgehog +// SPDX-License-Identifier: Apache-2.0 + +package switchprofile + +import ( + "go.githedgehog.com/fabric/api/meta" + wiringapi "go.githedgehog.com/fabric/api/wiring/v1beta1" + kmetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var CmlsNvidiaSN2201 = wiringapi.SwitchProfile{ + ObjectMeta: kmetav1.ObjectMeta{ + Name: "nvidia-sn2201", + }, + Spec: wiringapi.SwitchProfileSpec{ + DisplayName: "NVIDIA SN2201", + SwitchSilicon: SiliconSpectrum, + Features: wiringapi.SwitchProfileFeatures{ + Subinterfaces: true, + ACLs: true, + L2VNI: true, + L3VNI: true, + RoCE: false, + MCLAG: false, + ESLAG: true, + ECMPRoCEQPN: false, + }, + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn2201", + Config: wiringapi.SwitchProfileConfig{}, + MaxPorts: 64, + Ports: map[string]wiringapi.SwitchProfilePort{ + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", BaseNOSName: "swp", Label: "1", Profile: "RJ45-1G"}, + "E1/2": {NOSName: "2", BaseNOSName: "swp", Label: "2", Profile: "RJ45-1G"}, + "E1/3": {NOSName: "3", BaseNOSName: "swp", Label: "3", Profile: "RJ45-1G"}, + "E1/4": {NOSName: "4", BaseNOSName: "swp", Label: "4", Profile: "RJ45-1G"}, + "E1/5": {NOSName: "5", BaseNOSName: "swp", Label: "5", Profile: "RJ45-1G"}, + "E1/6": {NOSName: "6", BaseNOSName: "swp", Label: "6", Profile: "RJ45-1G"}, + "E1/7": {NOSName: "7", BaseNOSName: "swp", Label: "7", Profile: "RJ45-1G"}, + "E1/8": {NOSName: "8", BaseNOSName: "swp", Label: "8", Profile: "RJ45-1G"}, + "E1/9": {NOSName: "9", BaseNOSName: "swp", Label: "9", Profile: "RJ45-1G"}, + "E1/10": {NOSName: "10", BaseNOSName: "swp", Label: "10", Profile: "RJ45-1G"}, + "E1/11": {NOSName: "11", BaseNOSName: "swp", Label: "11", Profile: "RJ45-1G"}, + "E1/12": {NOSName: "12", BaseNOSName: "swp", Label: "12", Profile: "RJ45-1G"}, + "E1/13": {NOSName: "13", BaseNOSName: "swp", Label: "13", Profile: "RJ45-1G"}, + "E1/14": {NOSName: "14", BaseNOSName: "swp", Label: "14", Profile: "RJ45-1G"}, + "E1/15": {NOSName: "15", BaseNOSName: "swp", Label: "15", Profile: "RJ45-1G"}, + "E1/16": {NOSName: "16", BaseNOSName: "swp", Label: "16", Profile: "RJ45-1G"}, + "E1/17": {NOSName: "17", BaseNOSName: "swp", Label: "17", Profile: "RJ45-1G"}, + "E1/18": {NOSName: "18", BaseNOSName: "swp", Label: "18", Profile: "RJ45-1G"}, + "E1/19": {NOSName: "19", BaseNOSName: "swp", Label: "19", Profile: "RJ45-1G"}, + "E1/20": {NOSName: "20", BaseNOSName: "swp", Label: "20", Profile: "RJ45-1G"}, + "E1/21": {NOSName: "21", BaseNOSName: "swp", Label: "21", Profile: "RJ45-1G"}, + "E1/22": {NOSName: "22", BaseNOSName: "swp", Label: "22", Profile: "RJ45-1G"}, + "E1/23": {NOSName: "23", BaseNOSName: "swp", Label: "23", Profile: "RJ45-1G"}, + "E1/24": {NOSName: "24", BaseNOSName: "swp", Label: "24", Profile: "RJ45-1G"}, + "E1/25": {NOSName: "25", BaseNOSName: "swp", Label: "25", Profile: "RJ45-1G"}, + "E1/26": {NOSName: "26", BaseNOSName: "swp", Label: "26", Profile: "RJ45-1G"}, + "E1/27": {NOSName: "27", BaseNOSName: "swp", Label: "27", Profile: "RJ45-1G"}, + "E1/28": {NOSName: "28", BaseNOSName: "swp", Label: "28", Profile: "RJ45-1G"}, + "E1/29": {NOSName: "29", BaseNOSName: "swp", Label: "29", Profile: "RJ45-1G"}, + "E1/30": {NOSName: "30", BaseNOSName: "swp", Label: "30", Profile: "RJ45-1G"}, + "E1/31": {NOSName: "31", BaseNOSName: "swp", Label: "31", Profile: "RJ45-1G"}, + "E1/32": {NOSName: "32", BaseNOSName: "swp", Label: "32", Profile: "RJ45-1G"}, + "E1/33": {NOSName: "33", BaseNOSName: "swp", Label: "33", Profile: "RJ45-1G"}, + "E1/34": {NOSName: "34", BaseNOSName: "swp", Label: "34", Profile: "RJ45-1G"}, + "E1/35": {NOSName: "35", BaseNOSName: "swp", Label: "35", Profile: "RJ45-1G"}, + "E1/36": {NOSName: "36", BaseNOSName: "swp", Label: "36", Profile: "RJ45-1G"}, + "E1/37": {NOSName: "37", BaseNOSName: "swp", Label: "37", Profile: "RJ45-1G"}, + "E1/38": {NOSName: "38", BaseNOSName: "swp", Label: "38", Profile: "RJ45-1G"}, + "E1/39": {NOSName: "39", BaseNOSName: "swp", Label: "39", Profile: "RJ45-1G"}, + "E1/40": {NOSName: "40", BaseNOSName: "swp", Label: "40", Profile: "RJ45-1G"}, + "E1/41": {NOSName: "41", BaseNOSName: "swp", Label: "41", Profile: "RJ45-1G"}, + "E1/42": {NOSName: "42", BaseNOSName: "swp", Label: "42", Profile: "RJ45-1G"}, + "E1/43": {NOSName: "43", BaseNOSName: "swp", Label: "43", Profile: "RJ45-1G"}, + "E1/44": {NOSName: "44", BaseNOSName: "swp", Label: "44", Profile: "RJ45-1G"}, + "E1/45": {NOSName: "45", BaseNOSName: "swp", Label: "45", Profile: "RJ45-1G"}, + "E1/46": {NOSName: "46", BaseNOSName: "swp", Label: "46", Profile: "RJ45-1G"}, + "E1/47": {NOSName: "47", BaseNOSName: "swp", Label: "47", Profile: "RJ45-1G"}, + "E1/48": {NOSName: "48", BaseNOSName: "swp", Label: "48", Profile: "RJ45-1G"}, + "E1/49": {NOSName: "49", BaseNOSName: "swp", Label: "49", Profile: "QSFP28-100G", Pipeline: "1"}, + "E1/50": {NOSName: "50", BaseNOSName: "swp", Label: "50", Profile: "QSFP28-100G", Pipeline: "2"}, + "E1/51": {NOSName: "51", BaseNOSName: "swp", Label: "51", Profile: "QSFP28-100G", Pipeline: "3"}, + "E1/52": {NOSName: "52", BaseNOSName: "swp", Label: "52", Profile: "QSFP28-100G", Pipeline: "4"}, + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "RJ45-1G": { + Speed: &wiringapi.SwitchProfilePortProfileSpeed{ + Default: "1G", + Supported: []string{"1G"}, + }, + AutoNegAllowed: true, + AutoNegDefault: true, + }, + "QSFP28-100G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x100G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x25G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "1x50G": {Offsets: []string{""}}, + "1x100G": {Offsets: []string{""}}, + "2x25G": {Offsets: []string{"0", "1"}}, + "2x40G": {Offsets: []string{"0", "1"}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + }, + }, + AutoNegAllowed: true, + AutoNegDefault: true, + }, + }, + Pipelines: map[string]wiringapi.SwitchProfilePipeline{ + "1": {MaxPorts: 4}, + "2": {MaxPorts: 4}, + "3": {MaxPorts: 4}, + "4": {MaxPorts: 4}, + }, + }, +} diff --git a/pkg/ctrl/switchprofile/p_cmls_nvidia_sn3700c.go b/pkg/ctrl/switchprofile/p_cmls_nvidia_sn3700c.go new file mode 100644 index 000000000..9dc2b363b --- /dev/null +++ b/pkg/ctrl/switchprofile/p_cmls_nvidia_sn3700c.go @@ -0,0 +1,126 @@ +// Copyright 2025 Hedgehog +// SPDX-License-Identifier: Apache-2.0 + +package switchprofile + +import ( + "go.githedgehog.com/fabric/api/meta" + wiringapi "go.githedgehog.com/fabric/api/wiring/v1beta1" + kmetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var CmlsNvidiaSN3700c = wiringapi.SwitchProfile{ + ObjectMeta: kmetav1.ObjectMeta{ + Name: "nvidia-sn3700c", + }, + Spec: wiringapi.SwitchProfileSpec{ + DisplayName: "NVIDIA SN3700C", + SwitchSilicon: SiliconSpectrum2, + Features: wiringapi.SwitchProfileFeatures{ + Subinterfaces: true, + ACLs: true, + L2VNI: true, + L3VNI: true, + RoCE: true, + MCLAG: false, + ESLAG: true, + ECMPRoCEQPN: false, + }, + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn3700c", + Config: wiringapi.SwitchProfileConfig{}, + MaxPorts: 128, + Ports: map[string]wiringapi.SwitchProfilePort{ + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", BaseNOSName: "swp", Label: "1", Profile: "QSFP28-100G", Pipeline: "1"}, + "E1/2": {NOSName: "2", BaseNOSName: "swp", Label: "2", Profile: "QSFP28-100G", Pipeline: "2"}, + "E1/3": {NOSName: "3", BaseNOSName: "swp", Label: "3", Profile: "QSFP28-100G", Pipeline: "3"}, + "E1/4": {NOSName: "4", BaseNOSName: "swp", Label: "4", Profile: "QSFP28-100G", Pipeline: "4"}, + "E1/5": {NOSName: "5", BaseNOSName: "swp", Label: "5", Profile: "QSFP28-100G", Pipeline: "5"}, + "E1/6": {NOSName: "6", BaseNOSName: "swp", Label: "6", Profile: "QSFP28-100G", Pipeline: "6"}, + "E1/7": {NOSName: "7", BaseNOSName: "swp", Label: "7", Profile: "QSFP28-100G", Pipeline: "7"}, + "E1/8": {NOSName: "8", BaseNOSName: "swp", Label: "8", Profile: "QSFP28-100G", Pipeline: "8"}, + "E1/9": {NOSName: "9", BaseNOSName: "swp", Label: "9", Profile: "QSFP28-100G", Pipeline: "9"}, + "E1/10": {NOSName: "10", BaseNOSName: "swp", Label: "10", Profile: "QSFP28-100G", Pipeline: "10"}, + "E1/11": {NOSName: "11", BaseNOSName: "swp", Label: "11", Profile: "QSFP28-100G", Pipeline: "11"}, + "E1/12": {NOSName: "12", BaseNOSName: "swp", Label: "12", Profile: "QSFP28-100G", Pipeline: "12"}, + "E1/13": {NOSName: "13", BaseNOSName: "swp", Label: "13", Profile: "QSFP28-100G", Pipeline: "13"}, + "E1/14": {NOSName: "14", BaseNOSName: "swp", Label: "14", Profile: "QSFP28-100G", Pipeline: "14"}, + "E1/15": {NOSName: "15", BaseNOSName: "swp", Label: "15", Profile: "QSFP28-100G", Pipeline: "15"}, + "E1/16": {NOSName: "16", BaseNOSName: "swp", Label: "16", Profile: "QSFP28-100G", Pipeline: "16"}, + "E1/17": {NOSName: "17", BaseNOSName: "swp", Label: "17", Profile: "QSFP28-100G", Pipeline: "17"}, + "E1/18": {NOSName: "18", BaseNOSName: "swp", Label: "18", Profile: "QSFP28-100G", Pipeline: "18"}, + "E1/19": {NOSName: "19", BaseNOSName: "swp", Label: "19", Profile: "QSFP28-100G", Pipeline: "19"}, + "E1/20": {NOSName: "20", BaseNOSName: "swp", Label: "20", Profile: "QSFP28-100G", Pipeline: "20"}, + "E1/21": {NOSName: "21", BaseNOSName: "swp", Label: "21", Profile: "QSFP28-100G", Pipeline: "21"}, + "E1/22": {NOSName: "22", BaseNOSName: "swp", Label: "22", Profile: "QSFP28-100G", Pipeline: "22"}, + "E1/23": {NOSName: "23", BaseNOSName: "swp", Label: "23", Profile: "QSFP28-100G", Pipeline: "23"}, + "E1/24": {NOSName: "24", BaseNOSName: "swp", Label: "24", Profile: "QSFP28-100G", Pipeline: "24"}, + "E1/25": {NOSName: "25", BaseNOSName: "swp", Label: "25", Profile: "QSFP28-100G", Pipeline: "25"}, + "E1/26": {NOSName: "26", BaseNOSName: "swp", Label: "26", Profile: "QSFP28-100G", Pipeline: "26"}, + "E1/27": {NOSName: "27", BaseNOSName: "swp", Label: "27", Profile: "QSFP28-100G", Pipeline: "27"}, + "E1/28": {NOSName: "28", BaseNOSName: "swp", Label: "28", Profile: "QSFP28-100G", Pipeline: "28"}, + "E1/29": {NOSName: "29", BaseNOSName: "swp", Label: "29", Profile: "QSFP28-100G", Pipeline: "29"}, + "E1/30": {NOSName: "30", BaseNOSName: "swp", Label: "30", Profile: "QSFP28-100G", Pipeline: "30"}, + "E1/31": {NOSName: "31", BaseNOSName: "swp", Label: "31", Profile: "QSFP28-100G", Pipeline: "31"}, + "E1/32": {NOSName: "32", BaseNOSName: "swp", Label: "32", Profile: "QSFP28-100G", Pipeline: "32"}, + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "QSFP28-100G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x100G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x25G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "1x50G": {Offsets: []string{""}}, + "1x100G": {Offsets: []string{""}}, + "1x200G": {Offsets: []string{""}}, + "2x25G": {Offsets: []string{"0", "1"}}, + "2x40G": {Offsets: []string{"0", "1"}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "2x100G": {Offsets: []string{"0", "1"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x40G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x50G": {Offsets: []string{"0", "1", "2", "3"}}, + }, + }, + AutoNegAllowed: true, + AutoNegDefault: true, + }, + }, + Pipelines: map[string]wiringapi.SwitchProfilePipeline{ + "1": {MaxPorts: 4}, + "2": {MaxPorts: 4}, + "3": {MaxPorts: 4}, + "4": {MaxPorts: 4}, + "5": {MaxPorts: 4}, + "6": {MaxPorts: 4}, + "7": {MaxPorts: 4}, + "8": {MaxPorts: 4}, + "9": {MaxPorts: 4}, + "10": {MaxPorts: 4}, + "11": {MaxPorts: 4}, + "12": {MaxPorts: 4}, + "13": {MaxPorts: 4}, + "14": {MaxPorts: 4}, + "15": {MaxPorts: 4}, + "16": {MaxPorts: 4}, + "17": {MaxPorts: 4}, + "18": {MaxPorts: 4}, + "19": {MaxPorts: 4}, + "20": {MaxPorts: 4}, + "21": {MaxPorts: 4}, + "22": {MaxPorts: 4}, + "23": {MaxPorts: 4}, + "24": {MaxPorts: 4}, + "25": {MaxPorts: 4}, + "26": {MaxPorts: 4}, + "27": {MaxPorts: 4}, + "28": {MaxPorts: 4}, + "29": {MaxPorts: 4}, + "30": {MaxPorts: 4}, + "31": {MaxPorts: 4}, + "32": {MaxPorts: 4}, + }, + }, +} diff --git a/pkg/ctrl/switchprofile/p_cmls_nvidia_sn4600.go b/pkg/ctrl/switchprofile/p_cmls_nvidia_sn4600.go new file mode 100644 index 000000000..90d4afeea --- /dev/null +++ b/pkg/ctrl/switchprofile/p_cmls_nvidia_sn4600.go @@ -0,0 +1,190 @@ +// Copyright 2025 Hedgehog +// SPDX-License-Identifier: Apache-2.0 + +package switchprofile + +import ( + "go.githedgehog.com/fabric/api/meta" + wiringapi "go.githedgehog.com/fabric/api/wiring/v1beta1" + kmetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var CmlsNvidiaSN4600 = wiringapi.SwitchProfile{ + ObjectMeta: kmetav1.ObjectMeta{ + Name: "nvidia-sn4600", + }, + Spec: wiringapi.SwitchProfileSpec{ + DisplayName: "NVIDIA SN4600", + SwitchSilicon: SiliconSpectrum3, + Features: wiringapi.SwitchProfileFeatures{ + Subinterfaces: true, + ACLs: true, + L2VNI: true, + L3VNI: true, + RoCE: true, + MCLAG: false, + ESLAG: true, + ECMPRoCEQPN: false, + }, + NOSType: meta.NOSTypeCumulusMlx, + Platform: "x86-64-nv-sn4600", + Config: wiringapi.SwitchProfileConfig{}, + MaxPorts: 128, + Ports: map[string]wiringapi.SwitchProfilePort{ + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", BaseNOSName: "swp", Label: "1", Profile: "QSFP56-200G", Pipeline: "1"}, + "E1/2": {NOSName: "2", BaseNOSName: "swp", Label: "2", Profile: "QSFP56-200G", Pipeline: "2"}, + "E1/3": {NOSName: "3", BaseNOSName: "swp", Label: "3", Profile: "QSFP56-200G", Pipeline: "3"}, + "E1/4": {NOSName: "4", BaseNOSName: "swp", Label: "4", Profile: "QSFP56-200G", Pipeline: "4"}, + "E1/5": {NOSName: "5", BaseNOSName: "swp", Label: "5", Profile: "QSFP56-200G", Pipeline: "5"}, + "E1/6": {NOSName: "6", BaseNOSName: "swp", Label: "6", Profile: "QSFP56-200G", Pipeline: "6"}, + "E1/7": {NOSName: "7", BaseNOSName: "swp", Label: "7", Profile: "QSFP56-200G", Pipeline: "7"}, + "E1/8": {NOSName: "8", BaseNOSName: "swp", Label: "8", Profile: "QSFP56-200G", Pipeline: "8"}, + "E1/9": {NOSName: "9", BaseNOSName: "swp", Label: "9", Profile: "QSFP56-200G", Pipeline: "9"}, + "E1/10": {NOSName: "10", BaseNOSName: "swp", Label: "10", Profile: "QSFP56-200G", Pipeline: "10"}, + "E1/11": {NOSName: "11", BaseNOSName: "swp", Label: "11", Profile: "QSFP56-200G", Pipeline: "11"}, + "E1/12": {NOSName: "12", BaseNOSName: "swp", Label: "12", Profile: "QSFP56-200G", Pipeline: "12"}, + "E1/13": {NOSName: "13", BaseNOSName: "swp", Label: "13", Profile: "QSFP56-200G", Pipeline: "13"}, + "E1/14": {NOSName: "14", BaseNOSName: "swp", Label: "14", Profile: "QSFP56-200G", Pipeline: "14"}, + "E1/15": {NOSName: "15", BaseNOSName: "swp", Label: "15", Profile: "QSFP56-200G", Pipeline: "15"}, + "E1/16": {NOSName: "16", BaseNOSName: "swp", Label: "16", Profile: "QSFP56-200G", Pipeline: "16"}, + "E1/17": {NOSName: "17", BaseNOSName: "swp", Label: "17", Profile: "QSFP56-200G", Pipeline: "17"}, + "E1/18": {NOSName: "18", BaseNOSName: "swp", Label: "18", Profile: "QSFP56-200G", Pipeline: "18"}, + "E1/19": {NOSName: "19", BaseNOSName: "swp", Label: "19", Profile: "QSFP56-200G", Pipeline: "19"}, + "E1/20": {NOSName: "20", BaseNOSName: "swp", Label: "20", Profile: "QSFP56-200G", Pipeline: "20"}, + "E1/21": {NOSName: "21", BaseNOSName: "swp", Label: "21", Profile: "QSFP56-200G", Pipeline: "21"}, + "E1/22": {NOSName: "22", BaseNOSName: "swp", Label: "22", Profile: "QSFP56-200G", Pipeline: "22"}, + "E1/23": {NOSName: "23", BaseNOSName: "swp", Label: "23", Profile: "QSFP56-200G", Pipeline: "23"}, + "E1/24": {NOSName: "24", BaseNOSName: "swp", Label: "24", Profile: "QSFP56-200G", Pipeline: "24"}, + "E1/25": {NOSName: "25", BaseNOSName: "swp", Label: "25", Profile: "QSFP56-200G", Pipeline: "25"}, + "E1/26": {NOSName: "26", BaseNOSName: "swp", Label: "26", Profile: "QSFP56-200G", Pipeline: "26"}, + "E1/27": {NOSName: "27", BaseNOSName: "swp", Label: "27", Profile: "QSFP56-200G", Pipeline: "27"}, + "E1/28": {NOSName: "28", BaseNOSName: "swp", Label: "28", Profile: "QSFP56-200G", Pipeline: "28"}, + "E1/29": {NOSName: "29", BaseNOSName: "swp", Label: "29", Profile: "QSFP56-200G", Pipeline: "29"}, + "E1/30": {NOSName: "30", BaseNOSName: "swp", Label: "30", Profile: "QSFP56-200G", Pipeline: "30"}, + "E1/31": {NOSName: "31", BaseNOSName: "swp", Label: "31", Profile: "QSFP56-200G", Pipeline: "31"}, + "E1/32": {NOSName: "32", BaseNOSName: "swp", Label: "32", Profile: "QSFP56-200G", Pipeline: "32"}, + "E1/33": {NOSName: "33", BaseNOSName: "swp", Label: "33", Profile: "QSFP56-200G", Pipeline: "33"}, + "E1/34": {NOSName: "34", BaseNOSName: "swp", Label: "34", Profile: "QSFP56-200G", Pipeline: "34"}, + "E1/35": {NOSName: "35", BaseNOSName: "swp", Label: "35", Profile: "QSFP56-200G", Pipeline: "35"}, + "E1/36": {NOSName: "36", BaseNOSName: "swp", Label: "36", Profile: "QSFP56-200G", Pipeline: "36"}, + "E1/37": {NOSName: "37", BaseNOSName: "swp", Label: "37", Profile: "QSFP56-200G", Pipeline: "37"}, + "E1/38": {NOSName: "38", BaseNOSName: "swp", Label: "38", Profile: "QSFP56-200G", Pipeline: "38"}, + "E1/39": {NOSName: "39", BaseNOSName: "swp", Label: "39", Profile: "QSFP56-200G", Pipeline: "39"}, + "E1/40": {NOSName: "40", BaseNOSName: "swp", Label: "40", Profile: "QSFP56-200G", Pipeline: "40"}, + "E1/41": {NOSName: "41", BaseNOSName: "swp", Label: "41", Profile: "QSFP56-200G", Pipeline: "41"}, + "E1/42": {NOSName: "42", BaseNOSName: "swp", Label: "42", Profile: "QSFP56-200G", Pipeline: "42"}, + "E1/43": {NOSName: "43", BaseNOSName: "swp", Label: "43", Profile: "QSFP56-200G", Pipeline: "43"}, + "E1/44": {NOSName: "44", BaseNOSName: "swp", Label: "44", Profile: "QSFP56-200G", Pipeline: "44"}, + "E1/45": {NOSName: "45", BaseNOSName: "swp", Label: "45", Profile: "QSFP56-200G", Pipeline: "45"}, + "E1/46": {NOSName: "46", BaseNOSName: "swp", Label: "46", Profile: "QSFP56-200G", Pipeline: "46"}, + "E1/47": {NOSName: "47", BaseNOSName: "swp", Label: "47", Profile: "QSFP56-200G", Pipeline: "47"}, + "E1/48": {NOSName: "48", BaseNOSName: "swp", Label: "48", Profile: "QSFP56-200G", Pipeline: "48"}, + "E1/49": {NOSName: "49", BaseNOSName: "swp", Label: "49", Profile: "QSFP56-200G", Pipeline: "49"}, + "E1/50": {NOSName: "50", BaseNOSName: "swp", Label: "50", Profile: "QSFP56-200G", Pipeline: "50"}, + "E1/51": {NOSName: "51", BaseNOSName: "swp", Label: "51", Profile: "QSFP56-200G", Pipeline: "51"}, + "E1/52": {NOSName: "52", BaseNOSName: "swp", Label: "52", Profile: "QSFP56-200G", Pipeline: "52"}, + "E1/53": {NOSName: "53", BaseNOSName: "swp", Label: "53", Profile: "QSFP56-200G", Pipeline: "53"}, + "E1/54": {NOSName: "54", BaseNOSName: "swp", Label: "54", Profile: "QSFP56-200G", Pipeline: "54"}, + "E1/55": {NOSName: "55", BaseNOSName: "swp", Label: "55", Profile: "QSFP56-200G", Pipeline: "55"}, + "E1/56": {NOSName: "56", BaseNOSName: "swp", Label: "56", Profile: "QSFP56-200G", Pipeline: "56"}, + "E1/57": {NOSName: "57", BaseNOSName: "swp", Label: "57", Profile: "QSFP56-200G", Pipeline: "57"}, + "E1/58": {NOSName: "58", BaseNOSName: "swp", Label: "58", Profile: "QSFP56-200G", Pipeline: "58"}, + "E1/59": {NOSName: "59", BaseNOSName: "swp", Label: "59", Profile: "QSFP56-200G", Pipeline: "59"}, + "E1/60": {NOSName: "60", BaseNOSName: "swp", Label: "60", Profile: "QSFP56-200G", Pipeline: "60"}, + "E1/61": {NOSName: "61", BaseNOSName: "swp", Label: "61", Profile: "QSFP56-200G", Pipeline: "61"}, + "E1/62": {NOSName: "62", BaseNOSName: "swp", Label: "62", Profile: "QSFP56-200G", Pipeline: "62"}, + "E1/63": {NOSName: "63", BaseNOSName: "swp", Label: "63", Profile: "QSFP56-200G", Pipeline: "63"}, + "E1/64": {NOSName: "64", BaseNOSName: "swp", Label: "64", Profile: "QSFP56-200G", Pipeline: "64"}, // 64x QSFP56-200G + }, + PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ + "QSFP56-200G": { + Breakout: &wiringapi.SwitchProfilePortProfileBreakout{ + Default: "1x200G", + Supported: map[string]wiringapi.SwitchProfilePortProfileBreakoutMode{ + "1x25G": {Offsets: []string{""}}, + "1x40G": {Offsets: []string{""}}, + "1x50G": {Offsets: []string{""}}, + "1x100G": {Offsets: []string{""}}, + "1x200G": {Offsets: []string{""}}, + "2x25G": {Offsets: []string{"0", "1"}}, + "2x40G": {Offsets: []string{"0", "1"}}, + "2x50G": {Offsets: []string{"0", "1"}}, + "2x100G": {Offsets: []string{"0", "1"}}, + "4x10G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x40G": {Offsets: []string{"0", "1", "2", "3"}}, + "4x50G": {Offsets: []string{"0", "1", "2", "3"}}, + }, + }, + AutoNegAllowed: true, + AutoNegDefault: true, + }, + }, + Pipelines: map[string]wiringapi.SwitchProfilePipeline{ + "1": {MaxPorts: 4}, + "2": {MaxPorts: 2}, + "3": {MaxPorts: 4}, + "4": {MaxPorts: 2}, + "5": {MaxPorts: 4}, + "6": {MaxPorts: 2}, + "7": {MaxPorts: 4}, + "8": {MaxPorts: 2}, + "9": {MaxPorts: 4}, + "10": {MaxPorts: 2}, + "11": {MaxPorts: 4}, + "12": {MaxPorts: 2}, + "13": {MaxPorts: 4}, + "14": {MaxPorts: 2}, + "15": {MaxPorts: 4}, + "16": {MaxPorts: 2}, + "17": {MaxPorts: 4}, + "18": {MaxPorts: 2}, + "19": {MaxPorts: 4}, + "20": {MaxPorts: 2}, + "21": {MaxPorts: 4}, + "22": {MaxPorts: 2}, + "23": {MaxPorts: 4}, + "24": {MaxPorts: 2}, + "25": {MaxPorts: 4}, + "26": {MaxPorts: 2}, + "27": {MaxPorts: 4}, + "28": {MaxPorts: 2}, + "29": {MaxPorts: 4}, + "30": {MaxPorts: 2}, + "31": {MaxPorts: 4}, + "32": {MaxPorts: 2}, + "33": {MaxPorts: 4}, + "34": {MaxPorts: 2}, + "35": {MaxPorts: 4}, + "36": {MaxPorts: 2}, + "37": {MaxPorts: 4}, + "38": {MaxPorts: 2}, + "39": {MaxPorts: 4}, + "40": {MaxPorts: 2}, + "41": {MaxPorts: 4}, + "42": {MaxPorts: 2}, + "43": {MaxPorts: 4}, + "44": {MaxPorts: 2}, + "45": {MaxPorts: 4}, + "46": {MaxPorts: 2}, + "47": {MaxPorts: 4}, + "48": {MaxPorts: 2}, + "49": {MaxPorts: 4}, + "50": {MaxPorts: 2}, + "51": {MaxPorts: 4}, + "52": {MaxPorts: 2}, + "53": {MaxPorts: 4}, + "54": {MaxPorts: 2}, + "55": {MaxPorts: 4}, + "56": {MaxPorts: 2}, + "57": {MaxPorts: 4}, + "58": {MaxPorts: 2}, + "59": {MaxPorts: 4}, + "60": {MaxPorts: 2}, + "61": {MaxPorts: 4}, + "62": {MaxPorts: 2}, + "63": {MaxPorts: 4}, + "64": {MaxPorts: 2}, + }, + }, +} diff --git a/pkg/ctrl/switchprofile/p_cmls_vx.go b/pkg/ctrl/switchprofile/p_cmls_vx.go index c68193686..41b75e0b1 100644 --- a/pkg/ctrl/switchprofile/p_cmls_vx.go +++ b/pkg/ctrl/switchprofile/p_cmls_vx.go @@ -22,7 +22,7 @@ var CumulusVX = wiringapi.SwitchProfile{ L2VNI: true, L3VNI: true, RoCE: true, - MCLAG: true, + MCLAG: false, ESLAG: true, ECMPRoCEQPN: false, }, @@ -34,40 +34,71 @@ var CumulusVX = wiringapi.SwitchProfile{ }, // TODO update with actual values, it's obviously incorrect right now but it doesn't matter for the initial research Ports: map[string]wiringapi.SwitchProfilePort{ - "M1": {NOSName: "Management0", Management: true, OniePortName: "eth0"}, - "E1/1": {NOSName: "Ethernet0", Label: "1", Profile: "QSFP28-100G"}, - "E1/2": {NOSName: "Ethernet1", Label: "2", Profile: "QSFP28-100G"}, - "E1/3": {NOSName: "Ethernet2", Label: "3", Profile: "QSFP28-100G"}, - "E1/4": {NOSName: "Ethernet3", Label: "4", Profile: "QSFP28-100G"}, - "E1/5": {NOSName: "Ethernet4", Label: "5", Profile: "QSFP28-100G"}, - "E1/6": {NOSName: "Ethernet5", Label: "6", Profile: "QSFP28-100G"}, - "E1/7": {NOSName: "Ethernet6", Label: "7", Profile: "QSFP28-100G"}, - "E1/8": {NOSName: "Ethernet7", Label: "8", Profile: "QSFP28-100G"}, - "E1/9": {NOSName: "Ethernet8", Label: "9", Profile: "QSFP28-100G"}, - "E1/10": {NOSName: "Ethernet9", Label: "10", Profile: "QSFP28-100G"}, - "E1/11": {NOSName: "Ethernet10", Label: "11", Profile: "QSFP28-100G"}, - "E1/12": {NOSName: "Ethernet11", Label: "12", Profile: "QSFP28-100G"}, - "E1/13": {NOSName: "Ethernet12", Label: "13", Profile: "QSFP28-100G"}, - "E1/14": {NOSName: "Ethernet13", Label: "14", Profile: "QSFP28-100G"}, - "E1/15": {NOSName: "Ethernet14", Label: "15", Profile: "QSFP28-100G"}, - "E1/16": {NOSName: "Ethernet15", Label: "16", Profile: "QSFP28-100G"}, - "E1/17": {NOSName: "Ethernet16", Label: "17", Profile: "QSFP28-100G"}, - "E1/18": {NOSName: "Ethernet17", Label: "18", Profile: "QSFP28-100G"}, - "E1/19": {NOSName: "Ethernet18", Label: "19", Profile: "QSFP28-100G"}, - "E1/20": {NOSName: "Ethernet19", Label: "20", Profile: "QSFP28-100G"}, - "E1/21": {NOSName: "Ethernet20", Label: "21", Profile: "QSFP28-100G"}, - "E1/22": {NOSName: "Ethernet21", Label: "22", Profile: "QSFP28-100G"}, - "E1/23": {NOSName: "Ethernet22", Label: "23", Profile: "QSFP28-100G"}, - "E1/24": {NOSName: "Ethernet23", Label: "24", Profile: "QSFP28-100G"}, - "E1/25": {NOSName: "Ethernet24", Label: "25", Profile: "QSFP28-100G"}, - "E1/26": {NOSName: "Ethernet25", Label: "26", Profile: "QSFP28-100G"}, - "E1/27": {NOSName: "Ethernet26", Label: "27", Profile: "QSFP28-100G"}, - "E1/28": {NOSName: "Ethernet27", Label: "28", Profile: "QSFP28-100G"}, - "E1/29": {NOSName: "Ethernet28", Label: "29", Profile: "QSFP28-100G"}, - "E1/30": {NOSName: "Ethernet29", Label: "30", Profile: "QSFP28-100G"}, - "E1/31": {NOSName: "Ethernet30", Label: "31", Profile: "QSFP28-100G"}, - "E1/32": {NOSName: "Ethernet31", Label: "32", Profile: "QSFP28-100G"}, // 32x QSFP28-100G - "E1/33": {NOSName: "Ethernet32", Label: "33", Profile: "SFP28-10G"}, // 1x SFP28-10G + "M1": {NOSName: "eth0", Management: true, OniePortName: "eth0"}, + "E1/1": {NOSName: "1", BaseNOSName: "swp", Label: "1", Profile: "SFP28-10G"}, + "E1/2": {NOSName: "2", BaseNOSName: "swp", Label: "2", Profile: "SFP28-10G"}, + "E1/3": {NOSName: "3", BaseNOSName: "swp", Label: "3", Profile: "SFP28-10G"}, + "E1/4": {NOSName: "4", BaseNOSName: "swp", Label: "4", Profile: "SFP28-10G"}, + "E1/5": {NOSName: "5", BaseNOSName: "swp", Label: "5", Profile: "SFP28-10G"}, + "E1/6": {NOSName: "6", BaseNOSName: "swp", Label: "6", Profile: "SFP28-10G"}, + "E1/7": {NOSName: "7", BaseNOSName: "swp", Label: "7", Profile: "SFP28-10G"}, + "E1/8": {NOSName: "8", BaseNOSName: "swp", Label: "8", Profile: "SFP28-10G"}, + "E1/9": {NOSName: "9", BaseNOSName: "swp", Label: "9", Profile: "SFP28-10G"}, + "E1/10": {NOSName: "10", BaseNOSName: "swp", Label: "10", Profile: "SFP28-10G"}, + "E1/11": {NOSName: "11", BaseNOSName: "swp", Label: "11", Profile: "SFP28-10G"}, + "E1/12": {NOSName: "12", BaseNOSName: "swp", Label: "12", Profile: "SFP28-10G"}, + "E1/13": {NOSName: "13", BaseNOSName: "swp", Label: "13", Profile: "SFP28-10G"}, + "E1/14": {NOSName: "14", BaseNOSName: "swp", Label: "14", Profile: "SFP28-10G"}, + "E1/15": {NOSName: "15", BaseNOSName: "swp", Label: "15", Profile: "SFP28-10G"}, + "E1/16": {NOSName: "16", BaseNOSName: "swp", Label: "16", Profile: "SFP28-10G"}, + "E1/17": {NOSName: "17", BaseNOSName: "swp", Label: "17", Profile: "SFP28-10G"}, + "E1/18": {NOSName: "18", BaseNOSName: "swp", Label: "18", Profile: "SFP28-10G"}, + "E1/19": {NOSName: "19", BaseNOSName: "swp", Label: "19", Profile: "SFP28-10G"}, + "E1/20": {NOSName: "20", BaseNOSName: "swp", Label: "20", Profile: "SFP28-10G"}, + "E1/21": {NOSName: "21", BaseNOSName: "swp", Label: "21", Profile: "SFP28-10G"}, + "E1/22": {NOSName: "22", BaseNOSName: "swp", Label: "22", Profile: "SFP28-10G"}, + "E1/23": {NOSName: "23", BaseNOSName: "swp", Label: "23", Profile: "SFP28-10G"}, + "E1/24": {NOSName: "24", BaseNOSName: "swp", Label: "24", Profile: "SFP28-10G"}, + "E1/25": {NOSName: "25", BaseNOSName: "swp", Label: "25", Profile: "SFP28-10G"}, + "E1/26": {NOSName: "26", BaseNOSName: "swp", Label: "26", Profile: "SFP28-10G"}, + "E1/27": {NOSName: "27", BaseNOSName: "swp", Label: "27", Profile: "SFP28-10G"}, + "E1/28": {NOSName: "28", BaseNOSName: "swp", Label: "28", Profile: "SFP28-10G"}, + "E1/29": {NOSName: "29", BaseNOSName: "swp", Label: "29", Profile: "SFP28-10G"}, + "E1/30": {NOSName: "30", BaseNOSName: "swp", Label: "30", Profile: "SFP28-10G"}, + "E1/31": {NOSName: "31", BaseNOSName: "swp", Label: "31", Profile: "SFP28-10G"}, + "E1/32": {NOSName: "32", BaseNOSName: "swp", Label: "32", Profile: "SFP28-10G"}, + "E1/33": {NOSName: "33", BaseNOSName: "swp", Label: "33", Profile: "SFP28-10G"}, + "E1/34": {NOSName: "34", BaseNOSName: "swp", Label: "34", Profile: "SFP28-10G"}, + "E1/35": {NOSName: "35", BaseNOSName: "swp", Label: "35", Profile: "SFP28-10G"}, + "E1/36": {NOSName: "36", BaseNOSName: "swp", Label: "36", Profile: "SFP28-10G"}, + "E1/37": {NOSName: "37", BaseNOSName: "swp", Label: "37", Profile: "SFP28-10G"}, + "E1/38": {NOSName: "38", BaseNOSName: "swp", Label: "38", Profile: "SFP28-10G"}, + "E1/39": {NOSName: "39", BaseNOSName: "swp", Label: "39", Profile: "SFP28-10G"}, + "E1/40": {NOSName: "40", BaseNOSName: "swp", Label: "40", Profile: "SFP28-10G"}, + "E1/41": {NOSName: "41", BaseNOSName: "swp", Label: "41", Profile: "SFP28-10G"}, + "E1/42": {NOSName: "42", BaseNOSName: "swp", Label: "42", Profile: "SFP28-10G"}, + "E1/43": {NOSName: "43", BaseNOSName: "swp", Label: "43", Profile: "SFP28-10G"}, + "E1/44": {NOSName: "44", BaseNOSName: "swp", Label: "44", Profile: "SFP28-10G"}, + "E1/45": {NOSName: "45", BaseNOSName: "swp", Label: "45", Profile: "SFP28-10G"}, + "E1/46": {NOSName: "46", BaseNOSName: "swp", Label: "46", Profile: "SFP28-10G"}, + "E1/47": {NOSName: "47", BaseNOSName: "swp", Label: "47", Profile: "SFP28-10G"}, + "E1/48": {NOSName: "48", BaseNOSName: "swp", Label: "48", Profile: "SFP28-10G"}, + "E1/49": {NOSName: "49", BaseNOSName: "swp", Label: "49", Profile: "SFP28-10G"}, + "E1/50": {NOSName: "50", BaseNOSName: "swp", Label: "50", Profile: "SFP28-10G"}, + "E1/51": {NOSName: "51", BaseNOSName: "swp", Label: "51", Profile: "SFP28-10G"}, + "E1/52": {NOSName: "52", BaseNOSName: "swp", Label: "52", Profile: "SFP28-10G"}, + "E1/53": {NOSName: "53", BaseNOSName: "swp", Label: "53", Profile: "SFP28-10G"}, + "E1/54": {NOSName: "54", BaseNOSName: "swp", Label: "54", Profile: "SFP28-10G"}, + "E1/55": {NOSName: "55", BaseNOSName: "swp", Label: "55", Profile: "SFP28-10G"}, + "E1/56": {NOSName: "56", BaseNOSName: "swp", Label: "56", Profile: "SFP28-10G"}, + "E1/57": {NOSName: "57", BaseNOSName: "swp", Label: "57", Profile: "SFP28-10G"}, + "E1/58": {NOSName: "58", BaseNOSName: "swp", Label: "58", Profile: "SFP28-10G"}, + "E1/59": {NOSName: "59", BaseNOSName: "swp", Label: "59", Profile: "SFP28-10G"}, + "E1/60": {NOSName: "60", BaseNOSName: "swp", Label: "60", Profile: "SFP28-10G"}, + "E1/61": {NOSName: "61", BaseNOSName: "swp", Label: "61", Profile: "SFP28-10G"}, + "E1/62": {NOSName: "62", BaseNOSName: "swp", Label: "62", Profile: "SFP28-10G"}, + "E1/63": {NOSName: "63", BaseNOSName: "swp", Label: "63", Profile: "SFP28-10G"}, + "E1/64": {NOSName: "64", BaseNOSName: "swp", Label: "64", Profile: "SFP28-10G"}, }, PortProfiles: map[string]wiringapi.SwitchProfilePortProfile{ "SFP28-10G": { @@ -76,12 +107,6 @@ var CumulusVX = wiringapi.SwitchProfile{ Supported: []string{"1G", "10G"}, }, }, - "QSFP28-100G": { - Speed: &wiringapi.SwitchProfilePortProfileSpeed{ - Default: "100G", - Supported: []string{"10G", "25G", "50G", "100G"}, - }, - }, }, }, } diff --git a/pkg/ctrl/switchprofile/silicons.go b/pkg/ctrl/switchprofile/silicons.go index 983d61aa6..94137255e 100644 --- a/pkg/ctrl/switchprofile/silicons.go +++ b/pkg/ctrl/switchprofile/silicons.go @@ -14,4 +14,7 @@ const ( SiliconBroadcomTH3 = "Broadcom TH3" SiliconBroadcomTH4G = "Broadcom TH4G" SiliconBroadcomTH5 = "Broadcom TH5" + SiliconSpectrum = "Spectrum" + SiliconSpectrum2 = "Spectrum-2" + SiliconSpectrum3 = "Spectrum-3" )