Skip to content

Commit

Permalink
workaround: custom netbox client
Browse files Browse the repository at this point in the history
NetBox's openapi spec is wrong, and it affects the NetBox go client, so until that is fixed we'll use our own.
  • Loading branch information
Patrick Falk Nielsen committed Jul 31, 2024
1 parent 8c68aac commit cd12cd1
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 67 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.21.1
require (
fyne.io/systray v1.10.0
github.com/expr-lang/expr v1.16.9
github.com/netbox-community/go-netbox/v4 v4.0.3-0
github.com/sqweek/dialog v0.0.0-20240226140203-065105509627
golang.org/x/sync v0.7.0
gopkg.in/yaml.v3 v3.0.1
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/netbox-community/go-netbox/v4 v4.0.3-0 h1:Q6NucmGe9ZmU3mjziuqQlKqHZmzC3FYbZn9r+Y507hs=
github.com/netbox-community/go-netbox/v4 v4.0.3-0/go.mod h1:eux5UXpY7T1BADzdFI1V7o+iIGxSfPLUuZvcjVWca/w=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 h1:2JL2wmHXWIAxDofCK+AdkFi1KEg3dgkefCsm7isADzQ=
Expand Down
53 changes: 26 additions & 27 deletions internal/inventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/jysk-network/netbox-securecrt-inventory/internal/netbox"
"github.com/jysk-network/netbox-securecrt-inventory/pkg/evaluator"
"github.com/jysk-network/netbox-securecrt-inventory/pkg/securecrt"
nbm "github.com/netbox-community/go-netbox/v4"
)

const (
Expand Down Expand Up @@ -41,7 +40,7 @@ func New(cfg *config.Config, nb *netbox.NetBox, scrt *securecrt.SecureCRT, state
return &inv
}

func (i *InventorySync) getSite(sites []nbm.Site, siteID int32) (*nbm.Site, error) {
func (i *InventorySync) getSite(sites []netbox.Site, siteID int32) (*netbox.Site, error) {
for x := 0; x < len(sites); x++ {
if sites[x].Id == siteID {
return &sites[x], nil
Expand All @@ -51,30 +50,30 @@ func (i *InventorySync) getSite(sites []nbm.Site, siteID int32) (*nbm.Site, erro
return nil, ErrorFailedToFindSite
}

func (i *InventorySync) getRegionName(site *nbm.Site) string {
if site.AdditionalProperties != nil && site.Region.Name != nil {
return *site.Region.Name
func (i *InventorySync) getRegionName(site *netbox.Site) string {
if site.Region != nil {
return site.Region.Name
}
return "No Region"
}

func (i *InventorySync) getTenant(device interface{}) string {
nd, ok := device.(nbm.DeviceWithConfigContext)
if ok && nd.Tenant.IsSet() {
return nd.Tenant.Get().Name
nd, ok := device.(netbox.DeviceWithConfigContext)
if ok && nd.Tenant != nil {
return nd.Tenant.Name
}

vm, ok := device.(nbm.VirtualMachineWithConfigContext)
if ok && vm.Tenant.IsSet() {
return vm.Tenant.Get().Name
vm, ok := device.(netbox.VirtualMachineWithConfigContext)
if ok && vm.Tenant != nil {
return vm.Tenant.Name
}

return "No Tenant"
}

func (i *InventorySync) getPrimaryIP(primaryIP nbm.NullableIPAddress) *string {
if primaryIP.IsSet() {
address := primaryIP.Get().GetAddress()
func (i *InventorySync) getPrimaryIP(primaryIP *netbox.IPAddress) *string {
if primaryIP != nil {
address := primaryIP.Address
address = strings.Split(address, "/")[0]
return &address
}
Expand Down Expand Up @@ -102,7 +101,7 @@ func (i *InventorySync) getCommonEnvironment(sync_type string) *evaluator.Enviro
}
}

func (i *InventorySync) getDeviceSessions(devices []nbm.DeviceWithConfigContext, sites []nbm.Site) ([]*securecrt.SecureCRTSession, error) {
func (i *InventorySync) getDeviceSessions(devices []netbox.DeviceWithConfigContext, sites []netbox.Site) ([]*securecrt.SecureCRTSession, error) {
var sessions []*securecrt.SecureCRTSession
for _, device := range devices {
site, err := i.getSite(sites, device.Site.Id)
Expand All @@ -111,8 +110,8 @@ func (i *InventorySync) getDeviceSessions(devices []nbm.DeviceWithConfigContext,
}

ipAddress := i.getPrimaryIP(device.PrimaryIp4)
if ipAddress != nil {
return nil, fmt.Errorf("primary ip is not set on %s", device.GetName())
if ipAddress == nil {
return nil, fmt.Errorf("primary ip is not set on %s", device.Name)
}

tenant := i.getTenant(device)
Expand All @@ -121,7 +120,7 @@ func (i *InventorySync) getDeviceSessions(devices []nbm.DeviceWithConfigContext,
deviceType := device.DeviceType.Display
siteGroup := ""
if site.Group != nil {
siteGroup = *site.Group.Slug
siteGroup = site.Group.Slug
}

env := i.getCommonEnvironment("device")
Expand Down Expand Up @@ -154,34 +153,34 @@ func (i *InventorySync) getDeviceSessions(devices []nbm.DeviceWithConfigContext,
return sessions, nil
}

func (i *InventorySync) getVirtualMachineSessions(devices []nbm.VirtualMachineWithConfigContext, sites []nbm.Site) ([]*securecrt.SecureCRTSession, error) {
func (i *InventorySync) getVirtualMachineSessions(devices []netbox.VirtualMachineWithConfigContext, sites []netbox.Site) ([]*securecrt.SecureCRTSession, error) {
var sessions []*securecrt.SecureCRTSession
for _, device := range devices {
if !device.Site.IsSet() {
return nil, fmt.Errorf("site is not set on vm: %s", device.GetName())
if device.Site == nil {
return nil, fmt.Errorf("site is not set on vm: %s", device.Name)
}

site, err := i.getSite(sites, device.Site.Get().Id)
site, err := i.getSite(sites, device.Site.Id)
if err != nil {
return nil, err
}

ipAddress := i.getPrimaryIP(device.PrimaryIp4)
if ipAddress != nil {
return nil, fmt.Errorf("primary ip is not set on %s", device.GetName())
if ipAddress == nil {
return nil, fmt.Errorf("primary ip is not set on %s", device.Name)
}

tenant := i.getTenant(device)
regionName := i.getRegionName(site)
siteAddress := strings.ReplaceAll(site.PhysicalAddress, "\r\n", ", ")
deviceType := ""
if device.Platform.IsSet() {
deviceType = device.Platform.Get().Display
if device.Platform != nil {
deviceType = device.Platform.Display
}

siteGroup := ""
if site.Group != nil {
siteGroup = *site.Group.Slug
siteGroup = site.Group.Slug
}

env := i.getCommonEnvironment("virtual_machine")
Expand Down
196 changes: 196 additions & 0 deletions internal/netbox/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package netbox

type Region struct {
Id int32 `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
}

type SiteGroup struct {
Id int32 `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
}

type Site struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
// Full name of the site
Name string `json:"name"`
Slug string `json:"slug"`
PhysicalAddress string `json:"physical_address"`
Description *string `json:"description,omitempty"`
Region *Region `json:"region,omitempty"`
Group *SiteGroup `json:"group,omitempty"`
}

type Manufacturer struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description,omitempty"`
}

type DeviceType struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Manufacturer Manufacturer `json:"manufacturer"`
Model string `json:"model"`
Slug string `json:"slug"`
Description *string `json:"description,omitempty"`
}

type DeviceRole struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description,omitempty"`
VirtualmachineCount int64 `json:"virtualmachine_count"`
}

type VirtualChassis struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
}

type Tenant struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description,omitempty"`
}

type Platform struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description,omitempty"`
VirtualmachineCount int64 `json:"virtualmachine_count"`
}

type Location struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Description *string `json:"description,omitempty"`
Depth int32 `json:"_depth"`
}

type Rack struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
}

type DeviceStatus struct {
Value *string `json:"value,omitempty"`
Label *string `json:"label,omitempty"`
}

type IPAddress struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Address string `json:"address"`
Description *string `json:"description,omitempty"`
}

type NestedTag struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Slug string `json:"slug"`
Color *string `json:"color,omitempty"`
}

type DeviceWithConfigContext struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name,omitempty"`
DeviceType DeviceType `json:"device_type"`
Role DeviceRole `json:"role"`
Tenant *Tenant `json:"tenant,omitempty"`
Platform *Platform `json:"platform,omitempty"`
Serial *string `json:"serial,omitempty"`
AssetTag *string `json:"asset_tag,omitempty"`
Site Site `json:"site"`
Location *Location `json:"location,omitempty"`
Rack *Rack `json:"rack,omitempty"`
Position *float64 `json:"position,omitempty"`
// GPS coordinate in decimal format (xx.yyyyyy)
Latitude *float64 `json:"latitude,omitempty"`
// GPS coordinate in decimal format (xx.yyyyyy)
Longitude *float64 `json:"longitude,omitempty"`
Status *DeviceStatus `json:"status,omitempty"`
PrimaryIp *IPAddress `json:"primary_ip"`
PrimaryIp4 *IPAddress `json:"primary_ip4,omitempty"`
PrimaryIp6 *IPAddress `json:"primary_ip6,omitempty"`
OobIp *IPAddress `json:"oob_ip,omitempty"`
Cluster *IPAddress `json:"cluster,omitempty"`
VirtualChassis *VirtualChassis `json:"virtual_chassis,omitempty"`
VcPosition *int32 `json:"vc_position,omitempty"`
// Virtual chassis master election priority
VcPriority *int32 `json:"vc_priority,omitempty"`
Description *string `json:"description,omitempty"`
Comments *string `json:"comments,omitempty"`
ConfigContext interface{} `json:"config_context"`
// Local config context data takes precedence over source contexts in the final rendered config context
LocalContextData interface{} `json:"local_context_data,omitempty"`
Tags []NestedTag `json:"tags,omitempty"`
CustomFields map[string]interface{} `json:"custom_fields,omitempty"`
AdditionalProperties map[string]interface{}
}

type Cluster struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
}

type VirtualMachineWithConfigContext struct {
Id int32 `json:"id"`
Url string `json:"url"`
Display string `json:"display"`
Name string `json:"name"`
Status *DeviceStatus `json:"status,omitempty"`
Site *Site `json:"site,omitempty"`
Cluster *Cluster `json:"cluster,omitempty"`
Role *DeviceRole `json:"role,omitempty"`
Tenant *Tenant `json:"tenant,omitempty"`
Platform *Platform `json:"platform,omitempty"`
PrimaryIp *IPAddress `json:"primary_ip"`
PrimaryIp4 *IPAddress `json:"primary_ip4,omitempty"`
PrimaryIp6 *IPAddress `json:"primary_ip6,omitempty"`
Vcpus *float64 `json:"vcpus,omitempty"`
Memory *int32 `json:"memory,omitempty"`
Disk *int32 `json:"disk,omitempty"`
Description *string `json:"description,omitempty"`
Comments *string `json:"comments,omitempty"`
// Local config context data takes precedence over source contexts in the final rendered config context
LocalContextData interface{} `json:"local_context_data,omitempty"`
Tags []NestedTag `json:"tags,omitempty"`
CustomFields map[string]interface{} `json:"custom_fields,omitempty"`
ConfigContext interface{} `json:"config_context"`
AdditionalProperties map[string]interface{}
}
Loading

0 comments on commit cd12cd1

Please sign in to comment.