Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
BirknerAlex committed Oct 18, 2023
1 parent ad9f107 commit dab1349
Show file tree
Hide file tree
Showing 19 changed files with 3,593 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
testdata/fsos_s5/*.txt text eol=crlf
testdata/fsos_n5/*.txt text eol=crlf
2 changes: 2 additions & 0 deletions pkg/vendors/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const (
VendorFSOSS3 Vendor = "fsos_s3"
// VendorFSOSS5 FSComS3 (FiberStore), series S5XX
VendorFSOSS5 Vendor = "fsos_s5"
// VendorFSOSN5 FSComS3 (FiberStore), series N5XX
VendorFSOSN5 Vendor = "fsos_n5"
// VendorJuniper Juniper, up to version 15 (legacy)
VendorJuniper Vendor = "juniper"
// VendorJuniperELS Juniper, version 15.1 and higher with advanced
Expand Down
69 changes: 69 additions & 0 deletions pkg/vendors/fsos_n5/arp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package fsos_n5

import (
"github.com/g-portal/switchmgr-go/pkg/models"
"github.com/g-portal/switchmgr-go/pkg/vendors/fsos_s3/utils"
"golang.org/x/exp/slices"
"regexp"
"strings"
)

// Example
// 1 649d.99d2.502d MLAG AggregatePort 10 32d 21:31:31
var arpLineRgx = regexp.MustCompile(`^([0-9]+)\s+([0-9a-f]{4}\.[0-9a-f]{4}\.[0-9a-f]{4})\s+([A-Z]+)\s+([a-zA-Z]+\s[0-9]+)\s+([0-9a-z]+)\s([0-9:]+)$`)

func (fs *FSComN5) ListArpTable() ([]models.ArpEntry, error) {
output, err := fs.SendCommands("show mac-address-table")
if err != nil {
return nil, err
}

return ParseArpTable(output)
}

func ParseArpTable(output string) ([]models.ArpEntry, error) {
var table []models.ArpEntry

portsWithMac := map[string][]models.MacAddress{}

for _, line := range strings.Split(output, "\n") {
line = strings.TrimSpace(line)
if !arpLineRgx.MatchString(line) {
continue
}

matches := arpLineRgx.FindStringSubmatch(line)
if len(matches) != 7 {
continue
}

matches[4] = utils.ConvertInterface(matches[4])
if _, ok := portsWithMac[matches[4]]; !ok {
portsWithMac[matches[4]] = []models.MacAddress{}
}

mac := models.MacAddress(strings.TrimSpace(matches[2]))
if !mac.Valid() {
continue
}

// reformat mac address
mac = models.MacAddress(mac.String())

// avoid duplicate entries
if slices.Contains(portsWithMac[matches[4]], mac) {
continue
}

portsWithMac[matches[4]] = append(portsWithMac[matches[4]], mac)
}

for portName, macs := range portsWithMac {
table = append(table, models.ArpEntry{
SwitchPort: portName,
MacAddresses: macs,
})
}

return table, nil
}
53 changes: 53 additions & 0 deletions pkg/vendors/fsos_n5/arp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package fsos_n5_test

import (
"github.com/g-portal/switchmgr-go/pkg/models"
"github.com/g-portal/switchmgr-go/pkg/vendors/fsos_n5"
"github.com/g-portal/switchmgr-go/pkg/vendors/fsos_n5/utils"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"testing"
)

var expectedEntries = map[string][]models.MacAddress{
"AggregatePort 1": {
"00:c0:1d:c0:ff:ee",
"98:5d:82:47:d0:93",
"98:5d:82:47:d6:d9",
},
"AggregatePort 7": {
"48:df:37:79:be:20",
},
"AggregatePort 8": {
"48:df:37:79:be:90",
},
}

func TestParseArpTable(t *testing.T) {
entries, err := fsos_n5.ParseArpTable(utils.ReadTestData("show mac address table", nil))
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}

if len(entries) != 3 {
t.Fatalf("expected 3 entries, got %d", len(entries))
}

for _, entry := range entries {
expectedMacs, ok := expectedEntries[entry.SwitchPort]
if !ok {
t.Fatalf("unexpected port %s", entry.SwitchPort)
}

if len(entry.MacAddresses) != len(expectedMacs) {
t.Fatalf("invalid amount of macs for port %q, got %v, expected %v", entry.SwitchPort,
len(entry.MacAddresses), len(expectedMacs))
}

if diff := cmp.Diff(entry.MacAddresses, expectedMacs, cmpopts.SortSlices(func(a, b string) bool { return a < b })); diff != "" {
t.Fatalf("mac addresses for port %q don't match. diff: %s", entry.SwitchPort, diff)
}

}

}
111 changes: 111 additions & 0 deletions pkg/vendors/fsos_n5/configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package fsos_n5

import (
"fmt"
"github.com/g-portal/switchmgr-go/pkg/iosconfig"
"github.com/g-portal/switchmgr-go/pkg/models"
"github.com/g-portal/switchmgr-go/pkg/utils"
)

type Configuration iosconfig.Config

func (cfg Configuration) ListInterfaces() ([]*models.Interface, error) {
iosConfig := iosconfig.Config(cfg)

vlanIDs := make([]int32, 0)
for id := range iosConfig.Vlans() {
vlanIDs = append(vlanIDs, id)
}

interfaces := make([]*models.Interface, 0)
for nic, config := range iosConfig.Interfaces() {
mgmt := false

mode := InterfaceMode(config.GetStringValue("switchport mode", string(InterfaceModeAccess)))

var untaggedVLAN *int32
var taggedVLANs []int32

enable := !config.Exists("shutdown", true)

interfaceMode := models.InterfaceModeAccess

switch mode {
case InterfaceModeAccess:
accessVlanID := config.GetInt32Value("switchport access vlan", 1)
untaggedVLAN = &accessVlanID
case InterfaceModeTrunk:
interfaceMode = models.InterfaceModeTrunk
accessVlanID := int32(1)
if config.Exists("switchport trunk vlan-untagged", false) {
accessVlanID = config.GetInt32Value("switchport trunk vlan-untagged", 1)
} else if config.Exists("switchport trunk native vlan", false) {
accessVlanID = config.GetInt32Value("switchport trunk native vlan", 1)
}

untaggedVLAN = &accessVlanID
taggedVLANs = config.GetInt32Values("switchport trunk allowed vlan add", vlanIDs)

if config.Exists("switchport trunk allowed vlan add", false) {
taggedVLANs = utils.ConvertVlans(config.GetStringValue("switchport trunk allowed vlan add", ""), ",")
}

// remove untagged vlan from tagged VLANs
taggedVLANs = utils.DeleteVlanFromIDs(taggedVLANs, *untaggedVLAN)
case InterfaceModeHybrid:
interfaceMode = models.InterfaceModeTrunk
//TODO implement hybrid mode
default:
return nil, fmt.Errorf("unknown interface mode %q", mode)
}

if config.Exists("ip address", false) {
mgmt = true
}

interfaces = append(interfaces, &models.Interface{
Name: nic,
Description: config.GetStringValue("description", ""),
Enabled: enable,
Mode: interfaceMode,
UntaggedVLAN: untaggedVLAN,
TaggedVLANs: taggedVLANs,
Management: mgmt,
})
}

return interfaces, nil
}

// GetConfiguration returns the configuration of a FSComS3 switch.
func (fs *FSComN5) GetConfiguration() (*Configuration, error) {
output, err := fs.SendCommands("show running-config")
if err != nil {
return nil, err
}

cfg := ParseConfiguration(output)
config := Configuration(cfg)

return &config, nil
}

func (cfg Configuration) GetInterface(name string) (*models.Interface, error) {
nics, err := cfg.ListInterfaces()
if err != nil {
return nil, err
}

for _, nic := range nics {
if nic.Name == name {
return nic, nil
}
}

return nil, fmt.Errorf("interface %q not found", name)
}

// ParseConfiguration parses the configuration of a FSComS3 switch.
func ParseConfiguration(cfg string) iosconfig.Config {
return iosconfig.Parse(cfg)
}
9 changes: 9 additions & 0 deletions pkg/vendors/fsos_n5/fsos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package fsos_n5

import (
"github.com/g-portal/switchmgr-go/pkg/vendors/fsos_s3"
)

type FSComN5 struct {
fsos_s3.FSComS3
}
57 changes: 57 additions & 0 deletions pkg/vendors/fsos_n5/hardware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package fsos_n5

import (
"errors"
"github.com/g-portal/switchmgr-go/pkg/models"
"regexp"
)

var serialRegex = regexp.MustCompile(`System\sserial\snumber\s+:\s([0-9A-Z]+)\r\n`)
var modelRegex = regexp.MustCompile(`System\sdescription\s+:\s.+\(([0-9A-Z-]+)\).+\r\n`)
var versionRegex = regexp.MustCompile(`System\ssoftware\sversion\s+:\s(.+)\r\n`)
var hostnameRegex = regexp.MustCompile(`hostname\s(.+)\r\n`)

func (fs *FSComN5) GetHardwareInfo() (*models.HardwareInfo, error) {
output, err := fs.SendCommands("show version", "show running-config | include hostname")
if err != nil {
return nil, err
}

return ParseHardwareInfo(output)
}

func ParseHardwareInfo(output string) (*models.HardwareInfo, error) {
hwInfo := &models.HardwareInfo{
Vendor: "Fiberstore",
}

// serial
matches := serialRegex.FindStringSubmatch(output)
if len(matches) != 2 {
return nil, errors.New("could not parse serial")
}
hwInfo.Serial = matches[1]

// model
matches = modelRegex.FindStringSubmatch(output)
if len(matches) != 2 {
return nil, errors.New("could not parse model")
}
hwInfo.Model = matches[1]

// firmware version
matches = versionRegex.FindStringSubmatch(output)
if len(matches) != 2 {
return nil, errors.New("could not parse firmware version")
}
hwInfo.FirmwareVersion = matches[1]

// hostname
matches = hostnameRegex.FindStringSubmatch(output)
if len(matches) != 2 {
return nil, errors.New("could not parse hostname")
}
hwInfo.Hostname = matches[1]

return hwInfo, nil
}
39 changes: 39 additions & 0 deletions pkg/vendors/fsos_n5/hardware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package fsos_n5_test

import (
"github.com/g-portal/switchmgr-go/pkg/vendors/fsos_n5"
"github.com/g-portal/switchmgr-go/pkg/vendors/fsos_n5/utils"
"testing"
)

func TestParseHardwareInfo(t *testing.T) {
hwInfo, err := fsos_n5.ParseHardwareInfo(utils.ReadTestData("show version", nil))
if err != nil {
t.Fatalf("Error parsing hardware info: %s", err.Error())
}

if hwInfo == nil {
t.Fatalf("Hardware info is nil")
}

if hwInfo.Serial != "G1RL71S00XXXX" {
t.Fatalf("Serial number is wrong: %s", hwInfo.Serial)
}

if hwInfo.Model != "N5860-48SC" {
t.Fatalf("Model is wrong: %s", hwInfo.Model)
}

if hwInfo.Vendor != "Fiberstore" {
t.Fatalf("Vendor is wrong: %s", hwInfo.Vendor)
}

if hwInfo.FirmwareVersion != "N5860_FSOS 11.0(5)B9P66S2" {
t.Fatalf("Firmware version is wrong: %s", hwInfo.FirmwareVersion)
}

if hwInfo.Hostname != "openshift-core2" {
t.Fatalf("Hostname is wrong: %s", hwInfo.Hostname)
}

}
Loading

0 comments on commit dab1349

Please sign in to comment.