Skip to content

Commit

Permalink
feat(isoxe): add more hw info to devices
Browse files Browse the repository at this point in the history
  • Loading branch information
bl4ko committed Feb 18, 2025
1 parent e3f06b7 commit 7d4a34d
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 123 deletions.
16 changes: 0 additions & 16 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
github.com/PaloAltoNetworks/pango v0.10.2 h1:Tjn6vIzzAq6Dd7N0mDuiP8w8pz8k5W9zz/TTSUQCsQY=
github.com/PaloAltoNetworks/pango v0.10.2/go.mod h1:GztcRnVLur7G+VFG7Z5ZKNFgScLtsycwPMp1qVebE5g=
github.com/bl4ko/go-devicetype-library v0.1.45 h1:UsxWYNHBrpNAcp8OA6PQzzCk2OqtWuarh6OEb0JHirQ=
github.com/bl4ko/go-devicetype-library v0.1.45/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.46 h1:d7LPEQUAklMWqmdgoyZMCmg04E4KQ/bgVns+WfSn9fk=
github.com/bl4ko/go-devicetype-library v0.1.46/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.47 h1:U3+n7VNRj4SfUsUvtXHbyQdu7CQbL1Fpn1QK9djnMOE=
github.com/bl4ko/go-devicetype-library v0.1.47/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.48 h1:SIC4fG+Ge8cALbkFgRxhI3Hw8aGBWn1KWNUrFNPG0VM=
github.com/bl4ko/go-devicetype-library v0.1.48/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.49 h1:jaPE8UpDjSG3IrSV4GZEcO0NpE2zf4FfwkDQcbYXAXA=
github.com/bl4ko/go-devicetype-library v0.1.49/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.52 h1:0ziJ9jwlnTGU0PDOBNjrKADFhLXykzoMo0I23aagH8s=
github.com/bl4ko/go-devicetype-library v0.1.52/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.53 h1:/czx4YwdTyJtt7pbom/Srat9Uc1k0vMp6cAGDfm8BJg=
github.com/bl4ko/go-devicetype-library v0.1.53/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.54 h1:tf/yps5QnTGaVWmIAKOp0QfQndPAwrgi6s4eI5Mk2yk=
github.com/bl4ko/go-devicetype-library v0.1.54/go.mod h1:Pzm1BlRyR4uECezsRINDA6ZieFPumdFL+6yySpXM6t8=
github.com/bl4ko/go-devicetype-library v0.1.55 h1:LiWK/qMUbNXubzuyNIZD+kMal3sHhVUmgm74x5OHmPI=
Expand Down Expand Up @@ -75,8 +61,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vmware/govmomi v0.48.0 h1:CP5bCvkDNGkmn29UlcJKTWMLwDg3iusP8anrZnedWrg=
github.com/vmware/govmomi v0.48.0/go.mod h1:bYwUHpGpisE4AOlDl5eph90T+cjJMIcKx/kaa5v5rQM=
github.com/vmware/govmomi v0.48.1 h1:aAjmoFzSShYA9ED66JaOJzSBvukvrQLYZljZL+pgfKQ=
github.com/vmware/govmomi v0.48.1/go.mod h1:UFM2aCkggPToQf8TqY3xfd9bOX58vbVa+UAK1JdDTNM=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
Expand Down
93 changes: 5 additions & 88 deletions internal/source/ios-xe/iosxe.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package iosxe

import (
"encoding/xml"
"fmt"
"time"

Expand All @@ -18,99 +17,16 @@ type IOSXESource struct {
common.Config

// IOSXE fetched data. Initialized in init functions.
SystemInfo systemReply
Interfaces map[string]iface
ArpEntries []arpEntry
HardwareInfo hardwareReply
SystemInfo systemReply
Interfaces map[string]iface
ArpEntries []arpEntry

// IOSXE synced data. Created in sync functions.
NBDevice *objects.Device
NBInterfaces map[string]*objects.Interface // interfaceName -> netboxInterface
}

const systemFilter = `<system xmlns="http://openconfig.net/yang/system">
<config>
</config>
<state>
<hostname/>
<domain-name/>
</state>
</system>
`

type systemReply struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 rpc-reply"`
MessageID string `xml:"message-id,attr"`
Hostname string `xml:"data>system>state>hostname"`
DomainName string `xml:"data>system>state>domain-name"`
}

const interfaceFilter = `<interfaces xmlns="http://openconfig.net/yang/interfaces">
<interface>
<name/>
<state>
<name/>
<type/>
<enabled/>
</state>
<ethernet xmlns="http://openconfig.net/yang/interfaces/ethernet">
<state>
<mac-address/>
<auto-negotiate/>
<port-speed/>
</state>
</ethernet>
</interface>
</interfaces>`

// InterfacesReply holds the entire response structure with the message ID and a slice of interfaces.
type interfaceReply struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 rpc-reply"`
MessageID string `xml:"message-id,attr"`
Interfaces []iface `xml:"data>interfaces>interface"`
}

type iface struct {
Name string `xml:"name"`
State interfaceState `xml:"state"`
Ethernet ethernetState `xml:"ethernet>state"`
}

// InterfaceState captures the state of the interface, including its operational status.
type interfaceState struct {
Name string `xml:"name"`
Type string `xml:"type,attr"`
Enabled bool `xml:"enabled"`
}

// EthernetState provides details about the Ethernet settings.
type ethernetState struct {
MACAddress string `xml:"mac-address"`
AutoNegotiate bool `xml:"auto-negotiate"`
PortSpeed string `xml:"port-speed"`
}

const arpFilter = `<arp-data xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-arp-oper"/>`

type arpReply struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 rpc-reply"`
MessageID string `xml:"message-id,attr"`
ArpVrf []arpVrf `xml:"data>arp-data>arp-vrf"`
}

type arpVrf struct {
Vrf string `xml:"vrf"`
ArpOper []arpEntry `xml:"arp-oper"`
}

type arpEntry struct {
Address string `xml:"address"`
Interface string `xml:"interface"`
Type string `xml:"type"`
Mode string `xml:"mode"`
HWType string `xml:"hwtype"`
MAC string `xml:"hardware"`
}

func (is *IOSXESource) Init() error {
d, err := netconf.NewDriver(
is.SourceConfig.Hostname,
Expand All @@ -131,6 +47,7 @@ func (is *IOSXESource) Init() error {
// Initialize items from vsphere API to local storage
initFunctions := []func(*netconf.Driver) error{
is.initDeviceInfo,
is.initDeviceHardwareInfo,
is.initInterfaces,
is.initArpData,
}
Expand Down
38 changes: 38 additions & 0 deletions internal/source/ios-xe/iosxe_filters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package iosxe

const hwFilter = `<device-hardware-data xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-device-hardware-oper">
<device-hardware>
<device-inventory/>
</device-hardware>
</device-hardware-data>`

const systemFilter = `<system xmlns="http://openconfig.net/yang/system">
<config>
</config>
<state>
<hostname/>
<domain-name/>
</state>
</system>
`

const interfaceFilter = `<interfaces xmlns="http://openconfig.net/yang/interfaces">
<interface>
<name/>
<state>
<description/>
<name/>
<type/>
<enabled/>
</state>
<ethernet xmlns="http://openconfig.net/yang/interfaces/ethernet">
<state>
<mac-address/>
<auto-negotiate/>
<port-speed/>
</state>
</ethernet>
</interface>
</interfaces>`

const arpFilter = `<arp-data xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-arp-oper"/>`
12 changes: 12 additions & 0 deletions internal/source/ios-xe/iosxe_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ func (is *IOSXESource) initDeviceInfo(d *netconf.Driver) error {
return nil
}

func (is *IOSXESource) initDeviceHardwareInfo(d *netconf.Driver) error {
r, err := d.Get(hwFilter)
if err != nil {
return fmt.Errorf("error with hardware filter: %s", err)
}
err = xml.Unmarshal(r.RawResult, &is.HardwareInfo)
if err != nil {
return fmt.Errorf("error with unmarshaling hardware info: %s", err)
}
return nil
}

func (is *IOSXESource) initInterfaces(d *netconf.Driver) error {
var ifaceReply interfaceReply
r, err := d.Get(interfaceFilter)
Expand Down
77 changes: 77 additions & 0 deletions internal/source/ios-xe/iosxe_schemas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package iosxe

import "encoding/xml"

// hardwareReply is the top-level structure for the hardware response.
type hardwareReply struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 rpc-reply"`
MessageID string `xml:"message-id,attr"`
Inventory []HWInventory `xml:"data>device-hardware-data>device-hardware>device-inventory"`
}

// HWInventory holds the part and serial numbers from each inventory entry.
type HWInventory struct {
Type string `xml:"hw-type"`
DevIndex string `xml:"hw-dev-index"`
Version string `xml:"version"`
PartNumber string `xml:"part-number"`
SerialNumber string `xml:"serial-number"`
Description string `xml:"hw-description"`
DevName string `xml:"dev-name"`
Class string `xml:"hw-class"`
}

type systemReply struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 rpc-reply"`
MessageID string `xml:"message-id,attr"`
Hostname string `xml:"data>system>state>hostname"`
DomainName string `xml:"data>system>state>domain-name"`
}

// InterfacesReply holds the entire response structure with the message ID and a slice of interfaces.
type interfaceReply struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 rpc-reply"`
MessageID string `xml:"message-id,attr"`
Interfaces []iface `xml:"data>interfaces>interface"`
}

type iface struct {
Name string `xml:"name"`
State interfaceState `xml:"state"`
Ethernet ethernetState `xml:"ethernet>state"`
}

// InterfaceState captures the state of the interface, including its operational status.
type interfaceState struct {
Name string `xml:"name"`
Type string `xml:"type,attr"`
Enabled bool `xml:"enabled"`
Description string `xml:"description"`
}

// EthernetState provides details about the Ethernet settings.
type ethernetState struct {
MACAddress string `xml:"mac-address"`
AutoNegotiate bool `xml:"auto-negotiate"`
PortSpeed string `xml:"port-speed"`
}

type arpReply struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:netconf:base:1.0 rpc-reply"`
MessageID string `xml:"message-id,attr"`
ArpVrf []arpVrf `xml:"data>arp-data>arp-vrf"`
}

type arpVrf struct {
Vrf string `xml:"vrf"`
ArpOper []arpEntry `xml:"arp-oper"`
}

type arpEntry struct {
Address string `xml:"address"`
Interface string `xml:"interface"`
Type string `xml:"type"`
Mode string `xml:"mode"`
HWType string `xml:"hwtype"`
MAC string `xml:"hardware"`
}
68 changes: 49 additions & 19 deletions internal/source/ios-xe/iosxe_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"time"

devices "github.com/bl4ko/go-devicetype-library/pkg"
"github.com/bl4ko/netbox-ssot/internal/constants"
"github.com/bl4ko/netbox-ssot/internal/netbox/inventory"
"github.com/bl4ko/netbox-ssot/internal/netbox/objects"
Expand All @@ -13,26 +14,53 @@ import (

// Syncs dnac sites to netbox inventory.
func (is *IOSXESource) syncDevice(nbi *inventory.NetboxInventory) error {
var err error
deviceName := is.SystemInfo.Hostname
if deviceName == "" {
return fmt.Errorf("hostname for device is empty")
}
deviceModel := constants.DefaultModel

var deviceModel, serialNumber, description string
if len(is.HardwareInfo.Inventory) > 0 {
for _, inv := range is.HardwareInfo.Inventory {
if inv.Type == "hw-type-chassis" {
deviceModel = inv.PartNumber
serialNumber = inv.SerialNumber
description = inv.Description
}
}
}
if deviceModel == "" {
deviceModel = constants.DefaultModel
}
deviceManufacturer, err := nbi.AddManufacturer(is.Ctx, &objects.Manufacturer{
Name: "Cisco",
Slug: utils.Slugify("Cisco"),
})
if err != nil {
return fmt.Errorf("failed adding manufacturer: %s", err)
}
deviceType, err := nbi.AddDeviceType(is.Ctx, &objects.DeviceType{
Manufacturer: deviceManufacturer,
Model: deviceModel,
Slug: utils.Slugify(deviceManufacturer.Name + deviceModel),
})
if err != nil {
return fmt.Errorf("add device type: %s", err)
var deviceType *objects.DeviceType
if deviceData, ok := devices.DeviceTypesMap[deviceManufacturer.Name][deviceModel]; ok {
deviceType, err = nbi.AddDeviceType(is.Ctx, &objects.DeviceType{
Manufacturer: deviceManufacturer,
Model: deviceModel,
Slug: deviceData.Slug,
})
if err != nil {
return fmt.Errorf("add device type: %s", err)
}
} else {
if err != nil {
return fmt.Errorf("failed adding manufacturer: %s", err)
}
deviceType, err = nbi.AddDeviceType(is.Ctx, &objects.DeviceType{
Manufacturer: deviceManufacturer,
Model: deviceModel,
Slug: utils.GenerateDeviceTypeSlug(deviceManufacturer.Name, deviceModel),
})
if err != nil {
return fmt.Errorf("add device type: %s", err)
}
}

deviceTenant, err := common.MatchHostToTenant(
is.Ctx,
nbi,
Expand Down Expand Up @@ -85,15 +113,17 @@ func (is *IOSXESource) syncDevice(nbi *inventory.NetboxInventory) error {
}
NBDevice, err := nbi.AddDevice(is.Ctx, &objects.Device{
NetboxObject: objects.NetboxObject{
Tags: is.GetSourceTags(),
Tags: is.GetSourceTags(),
Description: description,
},
Name: deviceName,
Site: deviceSite,
DeviceRole: deviceRole,
Status: &objects.DeviceStatusActive,
DeviceType: deviceType,
Tenant: deviceTenant,
Platform: devicePlatform,
Name: deviceName,
SerialNumber: serialNumber,
Site: deviceSite,
DeviceRole: deviceRole,
Status: &objects.DeviceStatusActive,
DeviceType: deviceType,
Tenant: deviceTenant,
Platform: devicePlatform,
})
if err != nil {
return fmt.Errorf("add device: %s", err)
Expand Down

0 comments on commit 7d4a34d

Please sign in to comment.