From a69c706838711a741f1c3a05d01dfb37a67b9abb Mon Sep 17 00:00:00 2001 From: bl4ko Date: Sat, 2 Mar 2024 02:21:51 +0100 Subject: [PATCH] fix: if one source fails other should continue still --- cmd/netbox-ssot/main.go | 56 +- internal/source/dnac/dnac.go | 100 +- internal/source/dnac/dnac_sync.go | 526 ++++---- internal/source/ovirt/ovirt.go | 132 +- internal/source/ovirt/ovirt_sync.go | 1413 ++++++++++--------- internal/source/vmware/vmware.go | 226 ++-- internal/source/vmware/vmware_sync.go | 1788 ++++++++++++------------- 7 files changed, 2035 insertions(+), 2206 deletions(-) diff --git a/cmd/netbox-ssot/main.go b/cmd/netbox-ssot/main.go index 8b2f56c6..f04587cd 100644 --- a/cmd/netbox-ssot/main.go +++ b/cmd/netbox-ssot/main.go @@ -28,7 +28,6 @@ func main() { // Create our main context mainCtx := context.Background() mainCtx = context.WithValue(mainCtx, constants.CtxSourceKey, "main") - mainCtx, cancel := context.WithCancel(mainCtx) // Initialize Logger ssotLogger, err := logger.New(config.Logger.Dest, config.Logger.Level) @@ -58,6 +57,8 @@ func main() { // Variable to store if the run was successful. If it wasn't we don't remove orphans. successfullRun := true + // Variable to store failed sources + encounteredErrors := map[string]bool{} // Go through all sources and sync data var wg sync.WaitGroup @@ -76,31 +77,32 @@ func main() { // Run each source in parallel go func(sourceCtx context.Context, source common.Source) { defer wg.Done() - select { - case <-sourceCtx.Done(): - ssotLogger.Infof(sourceCtx, "Signal received closing source") - default: - // Source initialization - ssotLogger.Info(sourceCtx, "Initializing source") - err = source.Init() - if err != nil { - ssotLogger.Error(sourceCtx, err) - successfullRun = false - cancel() - return - } - ssotLogger.Infof(sourceCtx, "Successfully initialized source %s", constants.CheckMark) + sourceName, ok := sourceCtx.Value(constants.CtxSourceKey).(string) + if !ok { + ssotLogger.Errorf(sourceCtx, "source ctx value is not set") + return + } + // Source initialization + ssotLogger.Info(sourceCtx, "Initializing source") + err = source.Init() + if err != nil { + ssotLogger.Error(sourceCtx, err) + successfullRun = false + encounteredErrors[sourceName] = true + return + } + ssotLogger.Infof(sourceCtx, "Successfully initialized source %s", constants.CheckMark) - // Source synchronization - ssotLogger.Info(sourceCtx, "Syncing source...") - err = source.Sync(netboxInventory) - if err != nil { - ssotLogger.Error(sourceCtx, err) - cancel() - return - } - ssotLogger.Infof(sourceCtx, "Source synced successfully %s", constants.CheckMark) + // Source synchronization + ssotLogger.Info(sourceCtx, "Syncing source...") + err = source.Sync(netboxInventory) + if err != nil { + successfullRun = false + ssotLogger.Error(sourceCtx, err) + encounteredErrors[sourceName] = true + return } + ssotLogger.Infof(sourceCtx, "Source synced successfully %s", constants.CheckMark) }(sourceCtx, source) } wg.Wait() @@ -118,14 +120,14 @@ func main() { ssotLogger.Info(mainCtx, "Skipping removing orphaned objects...") } - // End the context if it hasn't been yet - cancel() duration := time.Since(startTime) minutes := int(duration.Minutes()) seconds := int((duration - time.Duration(minutes)*time.Minute).Seconds()) if successfullRun { ssotLogger.Infof(mainCtx, "%s Syncing took %d min %d sec in total", constants.Rocket, minutes, seconds) } else { - ssotLogger.Fatalf("%s syncing was unsuccessful", constants.WarningSign) + for source := range encounteredErrors { + ssotLogger.Infof(mainCtx, "%s syncing of source %s failed", constants.WarningSign, source) + } } } diff --git a/internal/source/dnac/dnac.go b/internal/source/dnac/dnac.go index 59b87e1e..c3258ca9 100644 --- a/internal/source/dnac/dnac.go +++ b/internal/source/dnac/dnac.go @@ -39,71 +39,61 @@ type DnacSource struct { } func (ds *DnacSource) Init() error { - select { - case <-ds.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - dnacURL := fmt.Sprintf("%s://%s:%d", ds.Config.SourceConfig.HTTPScheme, ds.Config.SourceConfig.Hostname, ds.Config.SourceConfig.Port) - Client, err := dnac.NewClientWithOptions(dnacURL, ds.SourceConfig.Username, ds.SourceConfig.Password, "false", strconv.FormatBool(ds.SourceConfig.ValidateCert), nil) - if err != nil { - return fmt.Errorf("creating dnac client: %s", err) - } + dnacURL := fmt.Sprintf("%s://%s:%d", ds.Config.SourceConfig.HTTPScheme, ds.Config.SourceConfig.Hostname, ds.Config.SourceConfig.Port) + Client, err := dnac.NewClientWithOptions(dnacURL, ds.SourceConfig.Username, ds.SourceConfig.Password, "false", strconv.FormatBool(ds.SourceConfig.ValidateCert), nil) + if err != nil { + return fmt.Errorf("creating dnac client: %s", err) + } - // Initialize regex relations for this source - ds.VlanGroupRelations = utils.ConvertStringsToRegexPairs(ds.SourceConfig.VlanGroupRelations) - ds.Logger.Debugf(ds.Ctx, "VlanGroupRelations: %s", ds.VlanGroupRelations) - ds.VlanTenantRelations = utils.ConvertStringsToRegexPairs(ds.SourceConfig.VlanTenantRelations) - ds.Logger.Debugf(ds.Ctx, "VlanTenantRelations: %s", ds.VlanTenantRelations) - ds.HostTenantRelations = utils.ConvertStringsToRegexPairs(ds.SourceConfig.HostTenantRelations) - ds.Logger.Debugf(ds.Ctx, "HostTenantRelations: %s", ds.HostTenantRelations) + // Initialize regex relations for this source + ds.VlanGroupRelations = utils.ConvertStringsToRegexPairs(ds.SourceConfig.VlanGroupRelations) + ds.Logger.Debugf(ds.Ctx, "VlanGroupRelations: %s", ds.VlanGroupRelations) + ds.VlanTenantRelations = utils.ConvertStringsToRegexPairs(ds.SourceConfig.VlanTenantRelations) + ds.Logger.Debugf(ds.Ctx, "VlanTenantRelations: %s", ds.VlanTenantRelations) + ds.HostTenantRelations = utils.ConvertStringsToRegexPairs(ds.SourceConfig.HostTenantRelations) + ds.Logger.Debugf(ds.Ctx, "HostTenantRelations: %s", ds.HostTenantRelations) - // Initialize items from vsphere API to local storage - initFunctions := []func(*dnac.Client) error{ - ds.InitSites, - ds.InitMemberships, - ds.InitDevices, - ds.InitInterfaces, - } + // Initialize items from vsphere API to local storage + initFunctions := []func(*dnac.Client) error{ + ds.InitSites, + ds.InitMemberships, + ds.InitDevices, + ds.InitInterfaces, + } - for _, initFunc := range initFunctions { - startTime := time.Now() - if err := initFunc(Client); err != nil { - return fmt.Errorf("dnac initialization failure: %v", err) - } - duration := time.Since(startTime) - ds.Logger.Infof(ds.Ctx, "Successfully initialized %s in %f seconds", utils.ExtractFunctionName(initFunc), duration.Seconds()) + for _, initFunc := range initFunctions { + startTime := time.Now() + if err := initFunc(Client); err != nil { + return fmt.Errorf("dnac initialization failure: %v", err) } - return nil + duration := time.Since(startTime) + ds.Logger.Infof(ds.Ctx, "Successfully initialized %s in %f seconds", utils.ExtractFunctionName(initFunc), duration.Seconds()) } + return nil } func (ds *DnacSource) Sync(nbi *inventory.NetboxInventory) error { - select { - case <-ds.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - // initialize variables, that are shared between sync functions - ds.VID2nbVlan = make(map[int]*objects.Vlan) - ds.SiteID2nbSite = make(map[string]*objects.Site) - ds.DeviceID2nbDevice = make(map[string]*objects.Device) - ds.InterfaceID2nbInterface = make(map[string]*objects.Interface) + // initialize variables, that are shared between sync functions + ds.VID2nbVlan = make(map[int]*objects.Vlan) + ds.SiteID2nbSite = make(map[string]*objects.Site) + ds.DeviceID2nbDevice = make(map[string]*objects.Device) + ds.InterfaceID2nbInterface = make(map[string]*objects.Interface) - syncFunctions := []func(*inventory.NetboxInventory) error{ - ds.SyncSites, - ds.SyncVlans, - ds.SyncDevices, - ds.SyncDeviceInterfaces, - } + syncFunctions := []func(*inventory.NetboxInventory) error{ + ds.SyncSites, + ds.SyncVlans, + ds.SyncDevices, + ds.SyncDeviceInterfaces, + } - for _, syncFunc := range syncFunctions { - startTime := time.Now() - err := syncFunc(nbi) - if err != nil { - return err - } - duration := time.Since(startTime) - ds.Logger.Infof(ds.Ctx, "Successfully synced %s in %f seconds", utils.ExtractFunctionName(syncFunc), duration.Seconds()) + for _, syncFunc := range syncFunctions { + startTime := time.Now() + err := syncFunc(nbi) + if err != nil { + return err } - return nil + duration := time.Since(startTime) + ds.Logger.Infof(ds.Ctx, "Successfully synced %s in %f seconds", utils.ExtractFunctionName(syncFunc), duration.Seconds()) } + return nil } diff --git a/internal/source/dnac/dnac_sync.go b/internal/source/dnac/dnac_sync.go index 32cfa020..b35dc060 100644 --- a/internal/source/dnac/dnac_sync.go +++ b/internal/source/dnac/dnac_sync.go @@ -15,344 +15,324 @@ import ( // Syncs dnac sites to netbox inventory. func (ds *DnacSource) SyncSites(nbi *inventory.NetboxInventory) error { for _, site := range ds.Sites { - select { - case <-ds.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - dnacSite := &objects.Site{ - NetboxObject: objects.NetboxObject{ - Tags: ds.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: ds.SourceConfig.Name, - }, + dnacSite := &objects.Site{ + NetboxObject: objects.NetboxObject{ + Tags: ds.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: ds.SourceConfig.Name, }, - Name: site.Name, - Slug: utils.Slugify(site.Name), - } - for _, additionalInfo := range site.AdditionalInfo { - if additionalInfo.Namespace == "Location" { - dnacSite.PhysicalAddress = additionalInfo.Attributes.Address - longitude, err := strconv.ParseFloat(additionalInfo.Attributes.Longitude, 64) - if err != nil { - dnacSite.Longitude = longitude - } - latitude, err := strconv.ParseFloat(additionalInfo.Attributes.Latitude, 64) - if err != nil { - dnacSite.Latitude = latitude - } + }, + Name: site.Name, + Slug: utils.Slugify(site.Name), + } + for _, additionalInfo := range site.AdditionalInfo { + if additionalInfo.Namespace == "Location" { + dnacSite.PhysicalAddress = additionalInfo.Attributes.Address + longitude, err := strconv.ParseFloat(additionalInfo.Attributes.Longitude, 64) + if err != nil { + dnacSite.Longitude = longitude + } + latitude, err := strconv.ParseFloat(additionalInfo.Attributes.Latitude, 64) + if err != nil { + dnacSite.Latitude = latitude } } - nbSite, err := nbi.AddSite(ds.Ctx, dnacSite) - if err != nil { - return fmt.Errorf("adding site: %s", err) - } - ds.SiteID2nbSite[site.ID] = nbSite } + nbSite, err := nbi.AddSite(ds.Ctx, dnacSite) + if err != nil { + return fmt.Errorf("adding site: %s", err) + } + ds.SiteID2nbSite[site.ID] = nbSite } return nil } func (ds *DnacSource) SyncVlans(nbi *inventory.NetboxInventory) error { for vid, vlan := range ds.Vlans { - select { - case <-ds.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - vlanGroup, err := common.MatchVlanToGroup(nbi, vlan.InterfaceName, ds.VlanGroupRelations) - if err != nil { - return fmt.Errorf("vlanGroup: %s", err) - } - vlanTenant, err := common.MatchVlanToTenant(nbi, vlan.InterfaceName, ds.VlanTenantRelations) - if err != nil { - return fmt.Errorf("vlanTenant: %s", err) - } - newVlan, err := nbi.AddVlan(ds.Ctx, &objects.Vlan{ + vlanGroup, err := common.MatchVlanToGroup(nbi, vlan.InterfaceName, ds.VlanGroupRelations) + if err != nil { + return fmt.Errorf("vlanGroup: %s", err) + } + vlanTenant, err := common.MatchVlanToTenant(nbi, vlan.InterfaceName, ds.VlanTenantRelations) + if err != nil { + return fmt.Errorf("vlanTenant: %s", err) + } + newVlan, err := nbi.AddVlan(ds.Ctx, &objects.Vlan{ + NetboxObject: objects.NetboxObject{ + Tags: ds.Config.SourceTags, + Description: vlan.VLANType, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: ds.SourceConfig.Name, + }, + }, + Name: vlan.InterfaceName, + Group: vlanGroup, + Vid: vid, + Tenant: vlanTenant, + }) + if err != nil { + return fmt.Errorf("adding vlan: %s", err) + } + + if vlan.Prefix != "" && vlan.NetworkAddress != "" { + // Create prefix for this vlan + prefix := fmt.Sprintf("%s/%s", vlan.NetworkAddress, vlan.Prefix) + _, err = nbi.AddPrefix(ds.Ctx, &objects.Prefix{ NetboxObject: objects.NetboxObject{ - Tags: ds.Config.SourceTags, - Description: vlan.VLANType, + Tags: ds.Config.SourceTags, CustomFields: map[string]string{ constants.CustomFieldSourceName: ds.SourceConfig.Name, }, }, - Name: vlan.InterfaceName, - Group: vlanGroup, - Vid: vid, + Prefix: prefix, Tenant: vlanTenant, + Vlan: newVlan, }) if err != nil { - return fmt.Errorf("adding vlan: %s", err) - } - - if vlan.Prefix != "" && vlan.NetworkAddress != "" { - // Create prefix for this vlan - prefix := fmt.Sprintf("%s/%s", vlan.NetworkAddress, vlan.Prefix) - _, err = nbi.AddPrefix(ds.Ctx, &objects.Prefix{ - NetboxObject: objects.NetboxObject{ - Tags: ds.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: ds.SourceConfig.Name, - }, - }, - Prefix: prefix, - Tenant: vlanTenant, - Vlan: newVlan, - }) - if err != nil { - return fmt.Errorf("adding prefix: %s", err) - } + return fmt.Errorf("adding prefix: %s", err) } - - ds.VID2nbVlan[vid] = newVlan } + + ds.VID2nbVlan[vid] = newVlan } return nil } func (ds *DnacSource) SyncDevices(nbi *inventory.NetboxInventory) error { for _, device := range ds.Devices { - select { - case <-ds.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - var description, comments string - if device.Description != "" { - description = device.Description - } - if len(description) > objects.MaxDescriptionLength { - comments = description - description = "See comments" - } + var description, comments string + if device.Description != "" { + description = device.Description + } + if len(description) > objects.MaxDescriptionLength { + comments = description + description = "See comments" + } - ciscoManufacturer, err := nbi.AddManufacturer(ds.Ctx, &objects.Manufacturer{ - Name: "Cisco", - Slug: utils.Slugify("Cisco"), - }) - if err != nil { - return fmt.Errorf("failed creating device: %s", err) - } + ciscoManufacturer, err := nbi.AddManufacturer(ds.Ctx, &objects.Manufacturer{ + Name: "Cisco", + Slug: utils.Slugify("Cisco"), + }) + if err != nil { + return fmt.Errorf("failed creating device: %s", err) + } - deviceRole, err := nbi.AddDeviceRole(ds.Ctx, &objects.DeviceRole{ - Name: device.Family, - Slug: utils.Slugify(device.Family), - Color: objects.ColorAqua, - VMRole: false, - }) - if err != nil { - return fmt.Errorf("adding dnac device role: %s", err) - } + deviceRole, err := nbi.AddDeviceRole(ds.Ctx, &objects.DeviceRole{ + Name: device.Family, + Slug: utils.Slugify(device.Family), + Color: objects.ColorAqua, + VMRole: false, + }) + if err != nil { + return fmt.Errorf("adding dnac device role: %s", err) + } - platformName := device.SoftwareType - if platformName == "" { - platformName = device.PlatformID // Fallback name - } + platformName := device.SoftwareType + if platformName == "" { + platformName = device.PlatformID // Fallback name + } - platform, err := nbi.AddPlatform(ds.Ctx, &objects.Platform{ - Name: platformName, - Slug: utils.Slugify(platformName), - Manufacturer: ciscoManufacturer, - }) - if err != nil { - return fmt.Errorf("dnac platform: %s", err) - } + platform, err := nbi.AddPlatform(ds.Ctx, &objects.Platform{ + Name: platformName, + Slug: utils.Slugify(platformName), + Manufacturer: ciscoManufacturer, + }) + if err != nil { + return fmt.Errorf("dnac platform: %s", err) + } - var deviceSite *objects.Site - var ok bool - if deviceSite, ok = ds.SiteID2nbSite[ds.Device2Site[device.ID]]; !ok { - ds.Logger.Errorf(ds.Ctx, "DeviceSite is not existing for device %s, this should not happen. This device will be skipped", device.ID) - continue - } + var deviceSite *objects.Site + var ok bool + if deviceSite, ok = ds.SiteID2nbSite[ds.Device2Site[device.ID]]; !ok { + ds.Logger.Errorf(ds.Ctx, "DeviceSite is not existing for device %s, this should not happen. This device will be skipped", device.ID) + continue + } - if device.Type == "" { - ds.Logger.Errorf(ds.Ctx, "Device type for device %s is empty, this should not happen. This device will be skipped", device.ID) - } + if device.Type == "" { + ds.Logger.Errorf(ds.Ctx, "Device type for device %s is empty, this should not happen. This device will be skipped", device.ID) + } - deviceType, err := nbi.AddDeviceType(ds.Ctx, &objects.DeviceType{ - Manufacturer: ciscoManufacturer, - Model: device.Type, - Slug: utils.Slugify(device.Type), - }) - if err != nil { - return fmt.Errorf("add device type: %s", err) - } + deviceType, err := nbi.AddDeviceType(ds.Ctx, &objects.DeviceType{ + Manufacturer: ciscoManufacturer, + Model: device.Type, + Slug: utils.Slugify(device.Type), + }) + if err != nil { + return fmt.Errorf("add device type: %s", err) + } - deviceTenant, err := common.MatchHostToTenant(nbi, device.Hostname, ds.HostTenantRelations) - if err != nil { - return fmt.Errorf("hostTenant: %s", err) - } + deviceTenant, err := common.MatchHostToTenant(nbi, device.Hostname, ds.HostTenantRelations) + if err != nil { + return fmt.Errorf("hostTenant: %s", err) + } - nbDevice, err := nbi.AddDevice(ds.Ctx, &objects.Device{ - NetboxObject: objects.NetboxObject{ - Tags: ds.Config.SourceTags, - Description: description, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: ds.SourceConfig.Name, - }, + nbDevice, err := nbi.AddDevice(ds.Ctx, &objects.Device{ + NetboxObject: objects.NetboxObject{ + Tags: ds.Config.SourceTags, + Description: description, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: ds.SourceConfig.Name, }, - Name: device.Hostname, - Tenant: deviceTenant, - DeviceRole: deviceRole, - SerialNumber: device.SerialNumber, - Platform: platform, - Comments: comments, - Site: deviceSite, - DeviceType: deviceType, - }) + }, + Name: device.Hostname, + Tenant: deviceTenant, + DeviceRole: deviceRole, + SerialNumber: device.SerialNumber, + Platform: platform, + Comments: comments, + Site: deviceSite, + DeviceType: deviceType, + }) - if err != nil { - return fmt.Errorf("adding dnac device: %s", err) - } - - ds.DeviceID2nbDevice[device.ID] = nbDevice + if err != nil { + return fmt.Errorf("adding dnac device: %s", err) } + + ds.DeviceID2nbDevice[device.ID] = nbDevice } return nil } func (ds *DnacSource) SyncDeviceInterfaces(nbi *inventory.NetboxInventory) error { for ifaceID, iface := range ds.Interfaces { - select { - case <-ds.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") + ifaceDescription := iface.Description + ifaceDevice := ds.DeviceID2nbDevice[iface.DeviceID] + var ifaceDuplex *objects.InterfaceDuplex + switch iface.Duplex { + case "FullDuplex": + ifaceDuplex = &objects.DuplexFull + case "AutoNegotiate": + ifaceDuplex = &objects.DuplexAuto + case "HalfDuplex": + ifaceDuplex = &objects.DuplexHalf + case "": default: - ifaceDescription := iface.Description - ifaceDevice := ds.DeviceID2nbDevice[iface.DeviceID] - var ifaceDuplex *objects.InterfaceDuplex - switch iface.Duplex { - case "FullDuplex": - ifaceDuplex = &objects.DuplexFull - case "AutoNegotiate": - ifaceDuplex = &objects.DuplexAuto - case "HalfDuplex": - ifaceDuplex = &objects.DuplexHalf - case "": - default: - ds.Logger.Warningf(ds.Ctx, "Unknown duplex value: %s", iface.Duplex) - } + ds.Logger.Warningf(ds.Ctx, "Unknown duplex value: %s", iface.Duplex) + } - var ifaceStatus bool - switch iface.Status { - case "down": - ifaceStatus = false - case "up": - ifaceStatus = true - default: - ds.Logger.Errorf(ds.Ctx, "wrong interface status: %s", iface.Status) - } + var ifaceStatus bool + switch iface.Status { + case "down": + ifaceStatus = false + case "up": + ifaceStatus = true + default: + ds.Logger.Errorf(ds.Ctx, "wrong interface status: %s", iface.Status) + } - ifaceSpeed, err := strconv.Atoi(iface.Speed) - if err != nil { - ds.Logger.Errorf(ds.Ctx, "wrong speed for iface %s", iface.Speed) - } + ifaceSpeed, err := strconv.Atoi(iface.Speed) + if err != nil { + ds.Logger.Errorf(ds.Ctx, "wrong speed for iface %s", iface.Speed) + } - var ifaceType *objects.InterfaceType - switch iface.InterfaceType { - case "Physical": - ifaceType = objects.IfaceSpeed2IfaceType[objects.InterfaceSpeed(ifaceSpeed)] - if ifaceType == nil { - ifaceType = &objects.OtherInterfaceType - } - case "Virtual": - ifaceType = &objects.VirtualInterfaceType - default: - ds.Logger.Errorf(ds.Ctx, "Unknown interface type: %s. Skipping this device...", iface.InterfaceType) - continue + var ifaceType *objects.InterfaceType + switch iface.InterfaceType { + case "Physical": + ifaceType = objects.IfaceSpeed2IfaceType[objects.InterfaceSpeed(ifaceSpeed)] + if ifaceType == nil { + ifaceType = &objects.OtherInterfaceType } + case "Virtual": + ifaceType = &objects.VirtualInterfaceType + default: + ds.Logger.Errorf(ds.Ctx, "Unknown interface type: %s. Skipping this device...", iface.InterfaceType) + continue + } - ifaceName := iface.PortName - if ifaceName == "" { - ds.Logger.Errorf(ds.Ctx, "Unknown interface name for iface: %s", ifaceID) - continue - } + ifaceName := iface.PortName + if ifaceName == "" { + ds.Logger.Errorf(ds.Ctx, "Unknown interface name for iface: %s", ifaceID) + continue + } - var ifaceMode *objects.InterfaceMode - var ifaceAccessVlan *objects.Vlan - var ifaceTrunkVlans []*objects.Vlan - vid, err := strconv.Atoi(iface.VLANID) - if err != nil { - ds.Logger.Errorf(ds.Ctx, "Can't parse vid for iface %s", iface.VLANID) - continue - } - switch iface.PortMode { - case "access": - ifaceMode = &objects.InterfaceModeAccess - ifaceAccessVlan = ds.VID2nbVlan[vid] - case "trunk": - ifaceMode = &objects.InterfaceModeTagged - // TODO: ifaceTrunkVlans = append(ifaceTrunkVlans, ds.Vid2nbVlan[vid]) - case "dynamic_auto": - // TODO: how to handle this mode in netbox - ds.Logger.Debugf(ds.Ctx, "vlan mode 'dynamic_auto' is not implemented yet") - case "routed": - ds.Logger.Debugf(ds.Ctx, "vlan mode 'routed' is not implemented yet") - default: - ds.Logger.Errorf(ds.Ctx, "Unknown interface mode: '%s'", iface.PortMode) - } + var ifaceMode *objects.InterfaceMode + var ifaceAccessVlan *objects.Vlan + var ifaceTrunkVlans []*objects.Vlan + vid, err := strconv.Atoi(iface.VLANID) + if err != nil { + ds.Logger.Errorf(ds.Ctx, "Can't parse vid for iface %s", iface.VLANID) + continue + } + switch iface.PortMode { + case "access": + ifaceMode = &objects.InterfaceModeAccess + ifaceAccessVlan = ds.VID2nbVlan[vid] + case "trunk": + ifaceMode = &objects.InterfaceModeTagged + // TODO: ifaceTrunkVlans = append(ifaceTrunkVlans, ds.Vid2nbVlan[vid]) + case "dynamic_auto": + // TODO: how to handle this mode in netbox + ds.Logger.Debugf(ds.Ctx, "vlan mode 'dynamic_auto' is not implemented yet") + case "routed": + ds.Logger.Debugf(ds.Ctx, "vlan mode 'routed' is not implemented yet") + default: + ds.Logger.Errorf(ds.Ctx, "Unknown interface mode: '%s'", iface.PortMode) + } - nbIface, err := nbi.AddInterface(ds.Ctx, &objects.Interface{ + nbIface, err := nbi.AddInterface(ds.Ctx, &objects.Interface{ + NetboxObject: objects.NetboxObject{ + Description: ifaceDescription, + Tags: ds.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: ds.SourceConfig.Name, + }, + }, + Name: iface.PortName, + MAC: strings.ToUpper(iface.MacAddress), + Speed: objects.InterfaceSpeed(ifaceSpeed), + Status: ifaceStatus, + Duplex: ifaceDuplex, + Device: ifaceDevice, + Type: ifaceType, + Mode: ifaceMode, + UntaggedVlan: ifaceAccessVlan, + TaggedVlans: ifaceTrunkVlans, + }) + if err != nil { + return fmt.Errorf("add device interface: %s", err) + } + + // Add IP address to the interface + if iface.IPv4Address != "" { + defaultMask := 32 + if iface.IPv4Mask != "" { + maskBits, err := utils.MaskToBits(iface.IPv4Mask) + if err != nil { + return fmt.Errorf("wrong mask: %s", err) + } + defaultMask = maskBits + } + nbIPAddress, err := nbi.AddIPAddress(ds.Ctx, &objects.IPAddress{ NetboxObject: objects.NetboxObject{ - Description: ifaceDescription, - Tags: ds.Config.SourceTags, + Tags: ds.Config.SourceTags, CustomFields: map[string]string{ constants.CustomFieldSourceName: ds.SourceConfig.Name, }, }, - Name: iface.PortName, - MAC: strings.ToUpper(iface.MacAddress), - Speed: objects.InterfaceSpeed(ifaceSpeed), - Status: ifaceStatus, - Duplex: ifaceDuplex, - Device: ifaceDevice, - Type: ifaceType, - Mode: ifaceMode, - UntaggedVlan: ifaceAccessVlan, - TaggedVlans: ifaceTrunkVlans, + Address: fmt.Sprintf("%s/%d", iface.IPv4Address, defaultMask), + Status: &objects.IPAddressStatusActive, + DNSName: utils.ReverseLookup(iface.IPv4Address), + AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, + AssignedObjectID: nbIface.ID, }) if err != nil { - return fmt.Errorf("add device interface: %s", err) + return fmt.Errorf("adding ip address: %s", err) } - // Add IP address to the interface - if iface.IPv4Address != "" { - defaultMask := 32 - if iface.IPv4Mask != "" { - maskBits, err := utils.MaskToBits(iface.IPv4Mask) - if err != nil { - return fmt.Errorf("wrong mask: %s", err) - } - defaultMask = maskBits - } - nbIPAddress, err := nbi.AddIPAddress(ds.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: ds.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: ds.SourceConfig.Name, - }, - }, - Address: fmt.Sprintf("%s/%d", iface.IPv4Address, defaultMask), - Status: &objects.IPAddressStatusActive, - DNSName: utils.ReverseLookup(iface.IPv4Address), - AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, - AssignedObjectID: nbIface.ID, - }) + // To determine if this interface, has the same IP address as the device's management IP + // we need to check if management IP is in the same subnet as this interface + deviceManagementIP := ds.Devices[iface.DeviceID].ManagementIPAddress + if deviceManagementIP == iface.IPv4Address { + deviceCopy := *ifaceDevice + deviceCopy.PrimaryIPv4 = nbIPAddress + _, err = nbi.AddDevice(ds.Ctx, &deviceCopy) if err != nil { - return fmt.Errorf("adding ip address: %s", err) - } - - // To determine if this interface, has the same IP address as the device's management IP - // we need to check if management IP is in the same subnet as this interface - deviceManagementIP := ds.Devices[iface.DeviceID].ManagementIPAddress - if deviceManagementIP == iface.IPv4Address { - deviceCopy := *ifaceDevice - deviceCopy.PrimaryIPv4 = nbIPAddress - _, err = nbi.AddDevice(ds.Ctx, &deviceCopy) - if err != nil { - return fmt.Errorf("adding primary ipv4 address: %s", err) - } + return fmt.Errorf("adding primary ipv4 address: %s", err) } } - - ds.InterfaceID2nbInterface[ifaceID] = nbIface } + + ds.InterfaceID2nbInterface[ifaceID] = nbIface } return nil } diff --git a/internal/source/ovirt/ovirt.go b/internal/source/ovirt/ovirt.go index e2b261d4..c24ebf4c 100644 --- a/internal/source/ovirt/ovirt.go +++ b/internal/source/ovirt/ovirt.go @@ -40,85 +40,75 @@ type NetworkData struct { // Function that initializes state from ovirt api to local storage. func (o *OVirtSource) Init() error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("source stopped by context") - default: - // Initialize regex relations - o.Logger.Debug(o.Ctx, o.Ctx, "Initializing regex relations for oVirt source ", o.SourceConfig.Name) - o.HostSiteRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.HostSiteRelations) - o.Logger.Debug(o.Ctx, o.Ctx, "HostSiteRelations: ", o.HostSiteRelations) - o.ClusterSiteRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.ClusterSiteRelations) - o.Logger.Debug(o.Ctx, "ClusterSiteRelations: ", o.ClusterSiteRelations) - o.ClusterTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.ClusterTenantRelations) - o.Logger.Debug(o.Ctx, "ClusterTenantRelations: ", o.ClusterTenantRelations) - o.HostTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.HostTenantRelations) - o.Logger.Debug(o.Ctx, "HostTenantRelations: ", o.HostTenantRelations) - o.VMTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.VMTenantRelations) - o.Logger.Debug(o.Ctx, "VmTenantRelations: ", o.VMTenantRelations) - o.VlanGroupRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.VlanGroupRelations) - o.Logger.Debug(o.Ctx, "VlanGroupRelations: ", o.VlanGroupRelations) - o.VlanTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.VlanTenantRelations) - o.Logger.Debug(o.Ctx, "VlanTenantRelations: ", o.VlanTenantRelations) - // Initialize the connection - o.Logger.Debug(o.Ctx, "Initializing oVirt source ", o.SourceConfig.Name) - conn, err := ovirtsdk4.NewConnectionBuilder(). - URL(fmt.Sprintf("%s://%s:%d/ovirt-engine/api", o.SourceConfig.HTTPScheme, o.SourceConfig.Hostname, o.SourceConfig.Port)). - Username(o.SourceConfig.Username). - Password(o.SourceConfig.Password). - Insecure(!o.SourceConfig.ValidateCert). - Compress(true). - Timeout(time.Second * constants.DefaultAPITimeout). - Build() - if err != nil { - return fmt.Errorf("failed to create oVirt connection: %v", err) - } - defer conn.Close() + // Initialize regex relations + o.Logger.Debug(o.Ctx, o.Ctx, "Initializing regex relations for oVirt source ", o.SourceConfig.Name) + o.HostSiteRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.HostSiteRelations) + o.Logger.Debug(o.Ctx, o.Ctx, "HostSiteRelations: ", o.HostSiteRelations) + o.ClusterSiteRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.ClusterSiteRelations) + o.Logger.Debug(o.Ctx, "ClusterSiteRelations: ", o.ClusterSiteRelations) + o.ClusterTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.ClusterTenantRelations) + o.Logger.Debug(o.Ctx, "ClusterTenantRelations: ", o.ClusterTenantRelations) + o.HostTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.HostTenantRelations) + o.Logger.Debug(o.Ctx, "HostTenantRelations: ", o.HostTenantRelations) + o.VMTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.VMTenantRelations) + o.Logger.Debug(o.Ctx, "VmTenantRelations: ", o.VMTenantRelations) + o.VlanGroupRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.VlanGroupRelations) + o.Logger.Debug(o.Ctx, "VlanGroupRelations: ", o.VlanGroupRelations) + o.VlanTenantRelations = utils.ConvertStringsToRegexPairs(o.SourceConfig.VlanTenantRelations) + o.Logger.Debug(o.Ctx, "VlanTenantRelations: ", o.VlanTenantRelations) + // Initialize the connection + o.Logger.Debug(o.Ctx, "Initializing oVirt source ", o.SourceConfig.Name) + conn, err := ovirtsdk4.NewConnectionBuilder(). + URL(fmt.Sprintf("%s://%s:%d/ovirt-engine/api", o.SourceConfig.HTTPScheme, o.SourceConfig.Hostname, o.SourceConfig.Port)). + Username(o.SourceConfig.Username). + Password(o.SourceConfig.Password). + Insecure(!o.SourceConfig.ValidateCert). + Compress(true). + Timeout(time.Second * constants.DefaultAPITimeout). + Build() + if err != nil { + return fmt.Errorf("failed to create oVirt connection: %v", err) + } + defer conn.Close() - // Initialize items to local storage - initFunctions := []func(*ovirtsdk4.Connection) error{ - o.InitNetworks, - o.InitDisks, - o.InitDataCenters, - o.InitClusters, - o.InitHosts, - o.InitVms, - } + // Initialize items to local storage + initFunctions := []func(*ovirtsdk4.Connection) error{ + o.InitNetworks, + o.InitDisks, + o.InitDataCenters, + o.InitClusters, + o.InitHosts, + o.InitVms, + } - for _, initFunc := range initFunctions { - startTime := time.Now() - if err := initFunc(conn); err != nil { - return fmt.Errorf("failed to initialize oVirt %s: %v", strings.TrimPrefix(fmt.Sprintf("%T", initFunc), "*source.OVirtSource.Init"), err) - } - duration := time.Since(startTime) - o.Logger.Infof(o.Ctx, "Successfully initialized %s in %f seconds", utils.ExtractFunctionName(initFunc), duration.Seconds()) + for _, initFunc := range initFunctions { + startTime := time.Now() + if err := initFunc(conn); err != nil { + return fmt.Errorf("failed to initialize oVirt %s: %v", strings.TrimPrefix(fmt.Sprintf("%T", initFunc), "*source.OVirtSource.Init"), err) } - return nil + duration := time.Since(startTime) + o.Logger.Infof(o.Ctx, "Successfully initialized %s in %f seconds", utils.ExtractFunctionName(initFunc), duration.Seconds()) } + return nil } // Function that syncs all data from oVirt to Netbox. func (o *OVirtSource) Sync(nbi *inventory.NetboxInventory) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("source stopped by context") - default: - syncFunctions := []func(*inventory.NetboxInventory) error{ - o.syncNetworks, - o.syncDatacenters, - o.syncClusters, - o.syncHosts, - o.syncVms, - } - for _, syncFunc := range syncFunctions { - startTime := time.Now() - err := syncFunc(nbi) - if err != nil { - return err - } - duration := time.Since(startTime) - o.Logger.Infof(o.Ctx, "Successfully synced %s in %f seconds", utils.ExtractFunctionName(syncFunc), duration.Seconds()) + syncFunctions := []func(*inventory.NetboxInventory) error{ + o.syncNetworks, + o.syncDatacenters, + o.syncClusters, + o.syncHosts, + o.syncVms, + } + for _, syncFunc := range syncFunctions { + startTime := time.Now() + err := syncFunc(nbi) + if err != nil { + return err } - return nil + duration := time.Since(startTime) + o.Logger.Infof(o.Ctx, "Successfully synced %s in %f seconds", utils.ExtractFunctionName(syncFunc), duration.Seconds()) } + return nil } diff --git a/internal/source/ovirt/ovirt_sync.go b/internal/source/ovirt/ovirt_sync.go index e27746a1..7698ad02 100644 --- a/internal/source/ovirt/ovirt_sync.go +++ b/internal/source/ovirt/ovirt_sync.go @@ -14,892 +14,847 @@ import ( // Syncs networks received from oVirt API to the netbox. func (o *OVirtSource) syncNetworks(nbi *inventory.NetboxInventory) error { - select { - case <-o.Ctx.Done(): - return nil - default: - for _, network := range o.Networks.OVirtNetworks { - name, exists := network.Name() - if !exists { - return fmt.Errorf("network %v has no name", network) + for _, network := range o.Networks.OVirtNetworks { + name, exists := network.Name() + if !exists { + return fmt.Errorf("network %v has no name", network) + } + description, _ := network.Description() + // TODO: handle other networks + if networkVlan, exists := network.Vlan(); exists { + // Get vlanGroup from relation + vlanGroup, err := common.MatchVlanToGroup(nbi, name, o.VlanGroupRelations) + if err != nil { + return err } - description, _ := network.Description() - // TODO: handle other networks - if networkVlan, exists := network.Vlan(); exists { - // Get vlanGroup from relation - vlanGroup, err := common.MatchVlanToGroup(nbi, name, o.VlanGroupRelations) - if err != nil { - return err - } - // Get tenant from relation - vlanTenant, err := common.MatchVlanToTenant(nbi, name, o.VlanTenantRelations) - if err != nil { - return err - } - if networkVlanID, exists := networkVlan.Id(); exists { - _, err := nbi.AddVlan(o.Ctx, &objects.Vlan{ - NetboxObject: objects.NetboxObject{ - Description: description, - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, + // Get tenant from relation + vlanTenant, err := common.MatchVlanToTenant(nbi, name, o.VlanTenantRelations) + if err != nil { + return err + } + if networkVlanID, exists := networkVlan.Id(); exists { + _, err := nbi.AddVlan(o.Ctx, &objects.Vlan{ + NetboxObject: objects.NetboxObject{ + Description: description, + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, }, - Name: name, - Group: vlanGroup, - Vid: int(networkVlanID), - Status: &objects.VlanStatusActive, - Tenant: vlanTenant, - Comments: network.MustComment(), - }) - if err != nil { - return fmt.Errorf("adding vlan: %v", err) - } + }, + Name: name, + Group: vlanGroup, + Vid: int(networkVlanID), + Status: &objects.VlanStatusActive, + Tenant: vlanTenant, + Comments: network.MustComment(), + }) + if err != nil { + return fmt.Errorf("adding vlan: %v", err) } } } - return nil } + return nil } func (o *OVirtSource) syncDatacenters(nbi *inventory.NetboxInventory) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - for _, datacenter := range o.DataCenters { - name, exists := datacenter.Name() - if !exists { - return fmt.Errorf("failed to get name for oVirt datacenter %s", name) - } - description, _ := datacenter.Description() - - nbClusterGroup := &objects.ClusterGroup{ - NetboxObject: objects.NetboxObject{ - Description: description, - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, - }, - Name: name, - Slug: utils.Slugify(name), - } - _, err := nbi.AddClusterGroup(o.Ctx, nbClusterGroup) - if err != nil { - return fmt.Errorf("failed to add oVirt data center %s as Netbox cluster group: %v", name, err) - } + for _, datacenter := range o.DataCenters { + name, exists := datacenter.Name() + if !exists { + return fmt.Errorf("failed to get name for oVirt datacenter %s", name) } - return nil - } -} + description, _ := datacenter.Description() -func (o *OVirtSource) syncClusters(nbi *inventory.NetboxInventory) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - clusterType := &objects.ClusterType{ + nbClusterGroup := &objects.ClusterGroup{ NetboxObject: objects.NetboxObject{ - Tags: o.Config.SourceTags, + Description: description, + Tags: o.Config.SourceTags, CustomFields: map[string]string{ constants.CustomFieldSourceName: o.SourceConfig.Name, }, }, - Name: "oVirt", - Slug: "ovirt", + Name: name, + Slug: utils.Slugify(name), } - clusterType, err := nbi.AddClusterType(o.Ctx, clusterType) + _, err := nbi.AddClusterGroup(o.Ctx, nbClusterGroup) if err != nil { - return fmt.Errorf("failed to add oVirt cluster type: %v", err) + return fmt.Errorf("failed to add oVirt data center %s as Netbox cluster group: %v", name, err) } - // Then sync oVirt Clusters as NetBoxClusters - for _, cluster := range o.Clusters { - clusterName, exists := cluster.Name() - if !exists { - return fmt.Errorf("failed to get name for oVirt cluster %s", clusterName) - } - description, exists := cluster.Description() - if !exists { - o.Logger.Warning(o.Ctx, "description for oVirt cluster ", clusterName, " is empty.") - } - var clusterGroup *objects.ClusterGroup - var clusterGroupName string - if _, ok := o.DataCenters[cluster.MustDataCenter().MustId()]; ok { - clusterGroupName = o.DataCenters[cluster.MustDataCenter().MustId()].MustName() - } else { - o.Logger.Warning(o.Ctx, "failed to get datacenter for oVirt cluster ", clusterName) - } - if clusterGroupName != "" { - clusterGroup = nbi.ClusterGroupsIndexByName[clusterGroupName] + } + return nil +} + +func (o *OVirtSource) syncClusters(nbi *inventory.NetboxInventory) error { + clusterType := &objects.ClusterType{ + NetboxObject: objects.NetboxObject{ + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, + }, + }, + Name: "oVirt", + Slug: "ovirt", + } + clusterType, err := nbi.AddClusterType(o.Ctx, clusterType) + if err != nil { + return fmt.Errorf("failed to add oVirt cluster type: %v", err) + } + // Then sync oVirt Clusters as NetBoxClusters + for _, cluster := range o.Clusters { + clusterName, exists := cluster.Name() + if !exists { + return fmt.Errorf("failed to get name for oVirt cluster %s", clusterName) + } + description, exists := cluster.Description() + if !exists { + o.Logger.Warning(o.Ctx, "description for oVirt cluster ", clusterName, " is empty.") + } + var clusterGroup *objects.ClusterGroup + var clusterGroupName string + if _, ok := o.DataCenters[cluster.MustDataCenter().MustId()]; ok { + clusterGroupName = o.DataCenters[cluster.MustDataCenter().MustId()].MustName() + } else { + o.Logger.Warning(o.Ctx, "failed to get datacenter for oVirt cluster ", clusterName) + } + if clusterGroupName != "" { + clusterGroup = nbi.ClusterGroupsIndexByName[clusterGroupName] + } + var clusterSite *objects.Site + if o.ClusterSiteRelations != nil { + match, err := utils.MatchStringToValue(clusterName, o.ClusterSiteRelations) + if err != nil { + return fmt.Errorf("failed to match oVirt cluster %s to a Netbox site: %v", clusterName, err) } - var clusterSite *objects.Site - if o.ClusterSiteRelations != nil { - match, err := utils.MatchStringToValue(clusterName, o.ClusterSiteRelations) - if err != nil { - return fmt.Errorf("failed to match oVirt cluster %s to a Netbox site: %v", clusterName, err) - } - if match != "" { - if _, ok := nbi.SitesIndexByName[match]; !ok { - return fmt.Errorf("failed to match oVirt cluster %s to a Netbox site: %v. Site with this name doesn't exist", clusterName, match) - } - clusterSite = nbi.SitesIndexByName[match] + if match != "" { + if _, ok := nbi.SitesIndexByName[match]; !ok { + return fmt.Errorf("failed to match oVirt cluster %s to a Netbox site: %v. Site with this name doesn't exist", clusterName, match) } + clusterSite = nbi.SitesIndexByName[match] } - var clusterTenant *objects.Tenant - if o.ClusterTenantRelations != nil { - match, err := utils.MatchStringToValue(clusterName, o.ClusterTenantRelations) - if err != nil { - return fmt.Errorf("error occurred when matching oVirt cluster %s to a Netbox tenant: %v", clusterName, err) - } - if match != "" { - if _, ok := nbi.TenantsIndexByName[match]; !ok { - return fmt.Errorf("failed to match oVirt cluster %s to a Netbox tenant: %v. Tenant with this name doesn't exist", clusterName, match) - } - clusterTenant = nbi.TenantsIndexByName[match] + } + var clusterTenant *objects.Tenant + if o.ClusterTenantRelations != nil { + match, err := utils.MatchStringToValue(clusterName, o.ClusterTenantRelations) + if err != nil { + return fmt.Errorf("error occurred when matching oVirt cluster %s to a Netbox tenant: %v", clusterName, err) + } + if match != "" { + if _, ok := nbi.TenantsIndexByName[match]; !ok { + return fmt.Errorf("failed to match oVirt cluster %s to a Netbox tenant: %v. Tenant with this name doesn't exist", clusterName, match) } + clusterTenant = nbi.TenantsIndexByName[match] } + } - nbCluster := &objects.Cluster{ - NetboxObject: objects.NetboxObject{ - Description: description, - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, + nbCluster := &objects.Cluster{ + NetboxObject: objects.NetboxObject{ + Description: description, + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, }, - Name: clusterName, - Type: clusterType, - Status: objects.ClusterStatusActive, - Group: clusterGroup, - Site: clusterSite, - Tenant: clusterTenant, - } - err := nbi.AddCluster(o.Ctx, nbCluster) - if err != nil { - return fmt.Errorf("failed to add oVirt cluster %s as Netbox cluster: %v", clusterName, err) - } + }, + Name: clusterName, + Type: clusterType, + Status: objects.ClusterStatusActive, + Group: clusterGroup, + Site: clusterSite, + Tenant: clusterTenant, + } + err := nbi.AddCluster(o.Ctx, nbCluster) + if err != nil { + return fmt.Errorf("failed to add oVirt cluster %s as Netbox cluster: %v", clusterName, err) } - return nil } + return nil } // Host in oVirt is a represented as device in netbox with a // custom role Server. func (o *OVirtSource) syncHosts(nbi *inventory.NetboxInventory) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - for hostID, host := range o.Hosts { - hostName, exists := host.Name() - if !exists { - o.Logger.Warningf(o.Ctx, "name of host with id=%s is empty", hostID) - } - hostCluster := nbi.ClustersIndexByName[o.Clusters[host.MustCluster().MustId()].MustName()] - - hostSite, err := common.MatchHostToSite(nbi, hostName, o.HostSiteRelations) - if err != nil { - return fmt.Errorf("hostSite: %s", err) - } - hostTenant, err := common.MatchHostToTenant(nbi, hostName, o.HostTenantRelations) - if err != nil { - return fmt.Errorf("hostTenant: %s", err) - } + for hostID, host := range o.Hosts { + hostName, exists := host.Name() + if !exists { + o.Logger.Warningf(o.Ctx, "name of host with id=%s is empty", hostID) + } + hostCluster := nbi.ClustersIndexByName[o.Clusters[host.MustCluster().MustId()].MustName()] - var hostSerialNumber, manufacturerName, hostAssetTag, hostModel string - hwInfo, exists := host.HardwareInformation() - if exists { - hostAssetTag, exists = hwInfo.Uuid() - if !exists { - o.Logger.Warning(o.Ctx, "Uuid (asset tag) for oVirt host ", hostName, " is empty. Can't identify it, so it will be skipped...") - continue - } - hostSerialNumber, exists = hwInfo.SerialNumber() - if !exists { - o.Logger.Warning(o.Ctx, "Serial number for oVirt host ", hostName, " is empty.") - } - manufacturerName, _ = hwInfo.Manufacturer() - manufacturerName, err = utils.MatchStringToValue(manufacturerName, objects.ManufacturerMap) - if err != nil { - return fmt.Errorf("error occurred when matching oVirt host %s to a Netbox manufacturer: %v", hostName, err) - } + hostSite, err := common.MatchHostToSite(nbi, hostName, o.HostSiteRelations) + if err != nil { + return fmt.Errorf("hostSite: %s", err) + } + hostTenant, err := common.MatchHostToTenant(nbi, hostName, o.HostTenantRelations) + if err != nil { + return fmt.Errorf("hostTenant: %s", err) + } - hostModel, exists = hwInfo.ProductName() - if !exists { - hostModel = constants.DefaultModel // Model is also required for adding device type into netbox - } - } else { - o.Logger.Warning(o.Ctx, "Hardware information for oVirt host ", hostName, " is empty, it can't be identified so it will be skipped.") + var hostSerialNumber, manufacturerName, hostAssetTag, hostModel string + hwInfo, exists := host.HardwareInformation() + if exists { + hostAssetTag, exists = hwInfo.Uuid() + if !exists { + o.Logger.Warning(o.Ctx, "Uuid (asset tag) for oVirt host ", hostName, " is empty. Can't identify it, so it will be skipped...") continue } - - var hostManufacturer *objects.Manufacturer - if manufacturerName == "" { - manufacturerName = constants.DefaultManufacturer + hostSerialNumber, exists = hwInfo.SerialNumber() + if !exists { + o.Logger.Warning(o.Ctx, "Serial number for oVirt host ", hostName, " is empty.") } - hostManufacturer, err = nbi.AddManufacturer(o.Ctx, &objects.Manufacturer{ - Name: manufacturerName, - Slug: utils.Slugify(manufacturerName), - }) + manufacturerName, _ = hwInfo.Manufacturer() + manufacturerName, err = utils.MatchStringToValue(manufacturerName, objects.ManufacturerMap) if err != nil { - return fmt.Errorf("failed adding oVirt Manufacturer %v with error: %s", hostManufacturer, err) + return fmt.Errorf("error occurred when matching oVirt host %s to a Netbox manufacturer: %v", hostName, err) } - var hostDeviceType *objects.DeviceType - hostDeviceType, err = nbi.AddDeviceType(o.Ctx, &objects.DeviceType{ - Manufacturer: hostManufacturer, - Model: hostModel, - Slug: utils.Slugify(hostModel), - }) - if err != nil { - return fmt.Errorf("failed adding oVirt DeviceType %v with error: %s", hostDeviceType, err) + hostModel, exists = hwInfo.ProductName() + if !exists { + hostModel = constants.DefaultModel // Model is also required for adding device type into netbox } + } else { + o.Logger.Warning(o.Ctx, "Hardware information for oVirt host ", hostName, " is empty, it can't be identified so it will be skipped.") + continue + } - var hostStatus *objects.DeviceStatus - ovirtStatus, exists := host.Status() - if exists { - switch ovirtStatus { - case ovirtsdk4.HOSTSTATUS_UP: - hostStatus = &objects.DeviceStatusActive - default: - hostStatus = &objects.DeviceStatusOffline - } + var hostManufacturer *objects.Manufacturer + if manufacturerName == "" { + manufacturerName = constants.DefaultManufacturer + } + hostManufacturer, err = nbi.AddManufacturer(o.Ctx, &objects.Manufacturer{ + Name: manufacturerName, + Slug: utils.Slugify(manufacturerName), + }) + if err != nil { + return fmt.Errorf("failed adding oVirt Manufacturer %v with error: %s", hostManufacturer, err) + } + + var hostDeviceType *objects.DeviceType + hostDeviceType, err = nbi.AddDeviceType(o.Ctx, &objects.DeviceType{ + Manufacturer: hostManufacturer, + Model: hostModel, + Slug: utils.Slugify(hostModel), + }) + if err != nil { + return fmt.Errorf("failed adding oVirt DeviceType %v with error: %s", hostDeviceType, err) + } + + var hostStatus *objects.DeviceStatus + ovirtStatus, exists := host.Status() + if exists { + switch ovirtStatus { + case ovirtsdk4.HOSTSTATUS_UP: + hostStatus = &objects.DeviceStatusActive + default: + hostStatus = &objects.DeviceStatusOffline } + } - var hostPlatform *objects.Platform - var osType, osVersion string - if os, exists := host.Os(); exists { - if ovirtOsType, exists := os.Type(); exists { - osType = ovirtOsType - } - if ovirtOsVersion, exists := os.Version(); exists { - if osFullVersion, exists := ovirtOsVersion.FullVersion(); exists { - osVersion = osFullVersion - } - } + var hostPlatform *objects.Platform + var osType, osVersion string + if os, exists := host.Os(); exists { + if ovirtOsType, exists := os.Type(); exists { + osType = ovirtOsType } - platformName := utils.GeneratePlatformName(osType, osVersion) - hostPlatform, err = nbi.AddPlatform(o.Ctx, &objects.Platform{ - Name: platformName, - Slug: utils.Slugify(platformName), - }) - if err != nil { - return fmt.Errorf("failed adding oVirt Platform %v with error: %s", hostPlatform, err) + if ovirtOsVersion, exists := os.Version(); exists { + if osFullVersion, exists := ovirtOsVersion.FullVersion(); exists { + osVersion = osFullVersion + } } + } + platformName := utils.GeneratePlatformName(osType, osVersion) + hostPlatform, err = nbi.AddPlatform(o.Ctx, &objects.Platform{ + Name: platformName, + Slug: utils.Slugify(platformName), + }) + if err != nil { + return fmt.Errorf("failed adding oVirt Platform %v with error: %s", hostPlatform, err) + } - var hostDescription string - if description, exists := host.Description(); exists { - hostDescription = description - } + var hostDescription string + if description, exists := host.Description(); exists { + hostDescription = description + } - var hostComment string - if comment, exists := host.Comment(); exists { - hostComment = comment - } + var hostComment string + if comment, exists := host.Comment(); exists { + hostComment = comment + } - var hostCPUCores string - if cpu, exists := host.Cpu(); exists { - hostCPUCores, exists = cpu.Name() - if !exists { - o.Logger.Warning(o.Ctx, "oVirt hostCpuCores of ", hostName, " is empty.") - } + var hostCPUCores string + if cpu, exists := host.Cpu(); exists { + hostCPUCores, exists = cpu.Name() + if !exists { + o.Logger.Warning(o.Ctx, "oVirt hostCpuCores of ", hostName, " is empty.") } + } - mem, _ := host.Memory() - mem /= (constants.KiB * constants.KiB * constants.KiB) // Value is in Bytes, we convert to GB + mem, _ := host.Memory() + mem /= (constants.KiB * constants.KiB * constants.KiB) // Value is in Bytes, we convert to GB - nbHost := &objects.Device{ - NetboxObject: objects.NetboxObject{ - Description: hostDescription, - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - constants.CustomFieldHostCPUCoresName: hostCPUCores, - constants.CustomFieldHostMemoryName: fmt.Sprintf("%d GB", mem), - }, + nbHost := &objects.Device{ + NetboxObject: objects.NetboxObject{ + Description: hostDescription, + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, + constants.CustomFieldHostCPUCoresName: hostCPUCores, + constants.CustomFieldHostMemoryName: fmt.Sprintf("%d GB", mem), }, - Name: hostName, - Status: hostStatus, - Platform: hostPlatform, - DeviceRole: nbi.DeviceRolesIndexByName["Server"], - Site: hostSite, - Tenant: hostTenant, - Cluster: hostCluster, - Comments: hostComment, - SerialNumber: hostSerialNumber, - AssetTag: hostAssetTag, - DeviceType: hostDeviceType, - } - nbHost, err = nbi.AddDevice(o.Ctx, nbHost) - if err != nil { - return fmt.Errorf("failed to add oVirt host %s with error: %v", host.MustName(), err) - } + }, + Name: hostName, + Status: hostStatus, + Platform: hostPlatform, + DeviceRole: nbi.DeviceRolesIndexByName["Server"], + Site: hostSite, + Tenant: hostTenant, + Cluster: hostCluster, + Comments: hostComment, + SerialNumber: hostSerialNumber, + AssetTag: hostAssetTag, + DeviceType: hostDeviceType, + } + nbHost, err = nbi.AddDevice(o.Ctx, nbHost) + if err != nil { + return fmt.Errorf("failed to add oVirt host %s with error: %v", host.MustName(), err) + } - // We also need to sync nics separately, because nic is a separate object in netbox - err = o.syncHostNics(nbi, host, nbHost) - if err != nil { - return fmt.Errorf("failed to sync oVirt host %s nics with error: %v", host.MustName(), err) - } + // We also need to sync nics separately, because nic is a separate object in netbox + err = o.syncHostNics(nbi, host, nbHost) + if err != nil { + return fmt.Errorf("failed to sync oVirt host %s nics with error: %v", host.MustName(), err) } - return nil } + return nil } func (o *OVirtSource) syncHostNics(nbi *inventory.NetboxInventory, ovirtHost *ovirtsdk4.Host, nbHost *objects.Device) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - if nics, exists := ovirtHost.Nics(); exists { - master2slave := make(map[string][]string) // masterId: [slaveId1, slaveId2, ...] - parent2child := make(map[string][]string) // parentId: [childId, ... ] - processedNicsIDs := make(map[string]bool) // set of all nic ids that have already been processed - - nicID2nic := map[string]*objects.Interface{} // nicId: nic - nicID2IPv4 := map[string]string{} // nicId: ipAv4address/mask - nicID2IPv6 := map[string]string{} // nicId: ipv6Address/mask - - var hostIP string - if hostAddress, exists := ovirtHost.Address(); exists { - hostIP = utils.Lookup(hostAddress) - } - - // First loop, we loop through all the nics and collect all the information - err := o.collectHostNicsData(nbHost, nbi, nics, parent2child, master2slave, nicID2nic, processedNicsIDs, nicID2IPv4, nicID2IPv6) - if err != nil { - return fmt.Errorf("collect host nics data: %s", err) - } + if nics, exists := ovirtHost.Nics(); exists { + master2slave := make(map[string][]string) // masterId: [slaveId1, slaveId2, ...] + parent2child := make(map[string][]string) // parentId: [childId, ... ] + processedNicsIDs := make(map[string]bool) // set of all nic ids that have already been processed + + nicID2nic := map[string]*objects.Interface{} // nicId: nic + nicID2IPv4 := map[string]string{} // nicId: ipAv4address/mask + nicID2IPv6 := map[string]string{} // nicId: ipv6Address/mask + + var hostIP string + if hostAddress, exists := ovirtHost.Address(); exists { + hostIP = utils.Lookup(hostAddress) + } - // Second loop to add relations between interfaces (e.g. [eno1, eno2] -> bond1) - for masterID, slavesIDs := range master2slave { - var err error - masterInterface := nicID2nic[masterID] - if _, ok := processedNicsIDs[masterID]; ok { - masterInterface, err = nbi.AddInterface(o.Ctx, masterInterface) - if err != nil { - return fmt.Errorf("failed to add oVirt master interface %s with error: %v", masterInterface.Name, err) - } - delete(processedNicsIDs, masterID) - nicID2nic[masterID] = masterInterface + // First loop, we loop through all the nics and collect all the information + err := o.collectHostNicsData(nbHost, nbi, nics, parent2child, master2slave, nicID2nic, processedNicsIDs, nicID2IPv4, nicID2IPv6) + if err != nil { + return fmt.Errorf("collect host nics data: %s", err) + } + + // Second loop to add relations between interfaces (e.g. [eno1, eno2] -> bond1) + for masterID, slavesIDs := range master2slave { + var err error + masterInterface := nicID2nic[masterID] + if _, ok := processedNicsIDs[masterID]; ok { + masterInterface, err = nbi.AddInterface(o.Ctx, masterInterface) + if err != nil { + return fmt.Errorf("failed to add oVirt master interface %s with error: %v", masterInterface.Name, err) } - for _, slaveID := range slavesIDs { - slaveInterface := nicID2nic[slaveID] - slaveInterface.LAG = masterInterface - slaveInterface, err := nbi.AddInterface(o.Ctx, slaveInterface) - if err != nil { - return fmt.Errorf("failed to add oVirt slave interface %s with error: %v", slaveInterface.Name, err) - } - delete(processedNicsIDs, slaveID) - nicID2nic[slaveID] = slaveInterface + delete(processedNicsIDs, masterID) + nicID2nic[masterID] = masterInterface + } + for _, slaveID := range slavesIDs { + slaveInterface := nicID2nic[slaveID] + slaveInterface.LAG = masterInterface + slaveInterface, err := nbi.AddInterface(o.Ctx, slaveInterface) + if err != nil { + return fmt.Errorf("failed to add oVirt slave interface %s with error: %v", slaveInterface.Name, err) } + delete(processedNicsIDs, slaveID) + nicID2nic[slaveID] = slaveInterface } + } - // Third loop we connect children with parents (e.g. [bond1.605, bond1.604, bond1.603] -> bond1) - for parent, children := range parent2child { - parentInterface := nicID2nic[parent] - if _, ok := processedNicsIDs[parent]; ok { - parentInterface, err := nbi.AddInterface(o.Ctx, parentInterface) - if err != nil { - return fmt.Errorf("failed to add oVirt parent interface %s with error: %v", parentInterface.Name, err) - } - nicID2nic[parent] = parentInterface - delete(processedNicsIDs, parent) - } - for _, child := range children { - childInterface := nicID2nic[child] - childInterface.ParentInterface = parentInterface - childInterface, err := nbi.AddInterface(o.Ctx, childInterface) - if err != nil { - return fmt.Errorf("failed to add oVirt child interface %s with error: %v", childInterface.Name, err) - } - nicID2nic[child] = childInterface - delete(processedNicsIDs, child) + // Third loop we connect children with parents (e.g. [bond1.605, bond1.604, bond1.603] -> bond1) + for parent, children := range parent2child { + parentInterface := nicID2nic[parent] + if _, ok := processedNicsIDs[parent]; ok { + parentInterface, err := nbi.AddInterface(o.Ctx, parentInterface) + if err != nil { + return fmt.Errorf("failed to add oVirt parent interface %s with error: %v", parentInterface.Name, err) } + nicID2nic[parent] = parentInterface + delete(processedNicsIDs, parent) } - - // Fourth loop we check if there are any nics that were not processed - for nicID := range processedNicsIDs { - nbNic, err := nbi.AddInterface(o.Ctx, nicID2nic[nicID]) + for _, child := range children { + childInterface := nicID2nic[child] + childInterface.ParentInterface = parentInterface + childInterface, err := nbi.AddInterface(o.Ctx, childInterface) if err != nil { - return fmt.Errorf("failed to add oVirt interface %s with error: %v", nicID2nic[nicID].Name, err) + return fmt.Errorf("failed to add oVirt child interface %s with error: %v", childInterface.Name, err) } - nicID2nic[nicID] = nbNic + nicID2nic[child] = childInterface + delete(processedNicsIDs, child) + } + } + + // Fourth loop we check if there are any nics that were not processed + for nicID := range processedNicsIDs { + nbNic, err := nbi.AddInterface(o.Ctx, nicID2nic[nicID]) + if err != nil { + return fmt.Errorf("failed to add oVirt interface %s with error: %v", nicID2nic[nicID].Name, err) } + nicID2nic[nicID] = nbNic + } - // Fifth loop we add ip addresses to interfaces - for nicID, ipv4 := range nicID2IPv4 { - nbNic := nicID2nic[nicID] - address := strings.Split(ipv4, "/")[0] - nbIPAddress, err := nbi.AddIPAddress(o.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, + // Fifth loop we add ip addresses to interfaces + for nicID, ipv4 := range nicID2IPv4 { + nbNic := nicID2nic[nicID] + address := strings.Split(ipv4, "/")[0] + nbIPAddress, err := nbi.AddIPAddress(o.Ctx, &objects.IPAddress{ + NetboxObject: objects.NetboxObject{ + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, }, - Address: ipv4, - Status: &objects.IPAddressStatusActive, // TODO - DNSName: utils.ReverseLookup(address), - AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, - AssignedObjectID: nbNic.ID, - }) + }, + Address: ipv4, + Status: &objects.IPAddressStatusActive, // TODO + DNSName: utils.ReverseLookup(address), + AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, + AssignedObjectID: nbNic.ID, + }) + if err != nil { + return fmt.Errorf("add ipv4 address: %s", err) + } + if address == hostIP { + hostCopy := *nbHost + hostCopy.PrimaryIPv4 = nbIPAddress + _, err := nbi.AddDevice(o.Ctx, &hostCopy) if err != nil { - return fmt.Errorf("add ipv4 address: %s", err) - } - if address == hostIP { - hostCopy := *nbHost - hostCopy.PrimaryIPv4 = nbIPAddress - _, err := nbi.AddDevice(o.Ctx, &hostCopy) - if err != nil { - return fmt.Errorf("adding primary ipv4 address: %s", err) - } + return fmt.Errorf("adding primary ipv4 address: %s", err) } } - for nicID, ipv6 := range nicID2IPv6 { - nbNic := nicID2nic[nicID] - address := strings.Split(ipv6, "/")[0] - _, err := nbi.AddIPAddress(o.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, + } + for nicID, ipv6 := range nicID2IPv6 { + nbNic := nicID2nic[nicID] + address := strings.Split(ipv6, "/")[0] + _, err := nbi.AddIPAddress(o.Ctx, &objects.IPAddress{ + NetboxObject: objects.NetboxObject{ + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, }, - Address: ipv6, - Status: &objects.IPAddressStatusActive, // TODO - DNSName: utils.ReverseLookup(address), - AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, - AssignedObjectID: nbNic.ID, - }) - if err != nil { - return fmt.Errorf("add ipv6 address: %s", err) - } + }, + Address: ipv6, + Status: &objects.IPAddressStatusActive, // TODO + DNSName: utils.ReverseLookup(address), + AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, + AssignedObjectID: nbNic.ID, + }) + if err != nil { + return fmt.Errorf("add ipv6 address: %s", err) } } - return nil } + return nil } func (o *OVirtSource) collectHostNicsData(nbHost *objects.Device, nbi *inventory.NetboxInventory, nics *ovirtsdk4.HostNicSlice, parent2child map[string][]string, master2slave map[string][]string, nicID2nic map[string]*objects.Interface, processedNicsIDs map[string]bool, nicID2IPv4 map[string]string, nicID2IPv6 map[string]string) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - for _, nic := range nics.Slice() { - nicID, exists := nic.Id() - if !exists { - o.Logger.Warning(o.Ctx, "id for oVirt nic with id ", nicID, " is empty. This should not happen! Skipping...") - continue - } - nicName, exists := nic.Name() - if !exists { - o.Logger.Warning(o.Ctx, "name for oVirt nic with id ", nicID, " is empty.") - } - // var nicType *objects.InterfaceType - nicSpeedBips, exists := nic.Speed() - if !exists { - o.Logger.Warning(o.Ctx, "speed for oVirt nic with id ", nicID, " is empty.") - } - nicSpeedKbps := nicSpeedBips / constants.KB + for _, nic := range nics.Slice() { + nicID, exists := nic.Id() + if !exists { + o.Logger.Warning(o.Ctx, "id for oVirt nic with id ", nicID, " is empty. This should not happen! Skipping...") + continue + } + nicName, exists := nic.Name() + if !exists { + o.Logger.Warning(o.Ctx, "name for oVirt nic with id ", nicID, " is empty.") + } + // var nicType *objects.InterfaceType + nicSpeedBips, exists := nic.Speed() + if !exists { + o.Logger.Warning(o.Ctx, "speed for oVirt nic with id ", nicID, " is empty.") + } + nicSpeedKbps := nicSpeedBips / constants.KB - nicMtu, exists := nic.Mtu() - if !exists { - o.Logger.Warning(o.Ctx, "mtu for oVirt nic with id ", nicID, " is empty.") - } + nicMtu, exists := nic.Mtu() + if !exists { + o.Logger.Warning(o.Ctx, "mtu for oVirt nic with id ", nicID, " is empty.") + } - nicComment, _ := nic.Comment() + nicComment, _ := nic.Comment() - var nicEnabled bool - ovirtNicStatus, exists := nic.Status() - if exists { - switch ovirtNicStatus { - case ovirtsdk4.NICSTATUS_UP: - nicEnabled = true - default: - nicEnabled = false - } + var nicEnabled bool + ovirtNicStatus, exists := nic.Status() + if exists { + switch ovirtNicStatus { + case ovirtsdk4.NICSTATUS_UP: + nicEnabled = true + default: + nicEnabled = false } + } - // bridged, exists := nic.Bridged() // TODO: bridged interface - // if exists { - // if bridged { - // // This interface is bridged - // fmt.Printf("nic[%s] is bridged\n", nicName) - // } - // } - - // Determine nic type (virtual, physical, bond...) - var nicType *objects.InterfaceType - nicBaseInterface, exists := nic.BaseInterface() - if exists { - // This interface is a vlan bond. We treat is as a virtual interface - nicType = &objects.VirtualInterfaceType - parent2child[nicBaseInterface] = append(parent2child[nicBaseInterface], nicID) - } + // bridged, exists := nic.Bridged() // TODO: bridged interface + // if exists { + // if bridged { + // // This interface is bridged + // fmt.Printf("nic[%s] is bridged\n", nicName) + // } + // } + + // Determine nic type (virtual, physical, bond...) + var nicType *objects.InterfaceType + nicBaseInterface, exists := nic.BaseInterface() + if exists { + // This interface is a vlan bond. We treat is as a virtual interface + nicType = &objects.VirtualInterfaceType + parent2child[nicBaseInterface] = append(parent2child[nicBaseInterface], nicID) + } - nicBonding, exists := nic.Bonding() + nicBonding, exists := nic.Bonding() + if exists { + // Bond interface, we give it a type of LAG + nicType = &objects.LAGInterfaceType + slaves, exists := nicBonding.Slaves() if exists { - // Bond interface, we give it a type of LAG - nicType = &objects.LAGInterfaceType - slaves, exists := nicBonding.Slaves() - if exists { - for _, slave := range slaves.Slice() { - master2slave[nicID] = append(master2slave[nicID], slave.MustId()) - } + for _, slave := range slaves.Slice() { + master2slave[nicID] = append(master2slave[nicID], slave.MustId()) } } + } + if nicType == nil { + // This is a physical interface. + nicType = objects.IfaceSpeed2IfaceType[objects.InterfaceSpeed(nicSpeedKbps)] if nicType == nil { - // This is a physical interface. - nicType = objects.IfaceSpeed2IfaceType[objects.InterfaceSpeed(nicSpeedKbps)] - if nicType == nil { - nicType = &objects.OtherInterfaceType - } + nicType = &objects.OtherInterfaceType } + } - var nicVlan *objects.Vlan - vlan, exists := nic.Vlan() + var nicVlan *objects.Vlan + vlan, exists := nic.Vlan() + if exists { + vlanID, exists := vlan.Id() if exists { - vlanID, exists := vlan.Id() - if exists { - vlanName := o.Networks.Vid2Name[int(vlanID)] - // Get vlanGroup from relation - vlanGroup, err := common.MatchVlanToGroup(nbi, vlanName, o.VlanGroupRelations) - if err != nil { - return err - } - // Get vlan from inventory - nicVlan = nbi.VlansIndexByVlanGroupIDAndVID[vlanGroup.ID][int(vlanID)] + vlanName := o.Networks.Vid2Name[int(vlanID)] + // Get vlanGroup from relation + vlanGroup, err := common.MatchVlanToGroup(nbi, vlanName, o.VlanGroupRelations) + if err != nil { + return err } + // Get vlan from inventory + nicVlan = nbi.VlansIndexByVlanGroupIDAndVID[vlanGroup.ID][int(vlanID)] } + } - var nicTaggedVlans []*objects.Vlan - if nicVlan != nil { - nicTaggedVlans = []*objects.Vlan{nicVlan} - } + var nicTaggedVlans []*objects.Vlan + if nicVlan != nil { + nicTaggedVlans = []*objects.Vlan{nicVlan} + } - newInterface := &objects.Interface{ - NetboxObject: objects.NetboxObject{ - Tags: o.Config.SourceTags, - Description: nicComment, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - constants.CustomFieldSourceIDName: nicID, - }, + newInterface := &objects.Interface{ + NetboxObject: objects.NetboxObject{ + Tags: o.Config.SourceTags, + Description: nicComment, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, + constants.CustomFieldSourceIDName: nicID, }, - Device: nbHost, - Name: nicName, - Speed: objects.InterfaceSpeed(nicSpeedKbps), - Status: nicEnabled, - MTU: int(nicMtu), - Type: nicType, - TaggedVlans: nicTaggedVlans, - } + }, + Device: nbHost, + Name: nicName, + Speed: objects.InterfaceSpeed(nicSpeedKbps), + Status: nicEnabled, + MTU: int(nicMtu), + Type: nicType, + TaggedVlans: nicTaggedVlans, + } - var err error - // Extract ip info - if nicIPv4, exists := nic.Ip(); exists { - if nicAddress, exists := nicIPv4.Address(); exists { - mask := 32 - if nicMask, exists := nicIPv4.Netmask(); exists { - mask, err = utils.MaskToBits(nicMask) - if err != nil { - return fmt.Errorf("mask to bits: %s", err) - } + var err error + // Extract ip info + if nicIPv4, exists := nic.Ip(); exists { + if nicAddress, exists := nicIPv4.Address(); exists { + mask := 32 + if nicMask, exists := nicIPv4.Netmask(); exists { + mask, err = utils.MaskToBits(nicMask) + if err != nil { + return fmt.Errorf("mask to bits: %s", err) } - ipv4Address := fmt.Sprintf("%s/%d", nicAddress, mask) - nicID2IPv4[nicID] = ipv4Address } + ipv4Address := fmt.Sprintf("%s/%d", nicAddress, mask) + nicID2IPv4[nicID] = ipv4Address } - if nicIPv6, exists := nic.Ipv6(); exists { - if nicAddress, exists := nicIPv6.Address(); exists { - mask := 128 - if nicMask, exists := nicIPv6.Netmask(); exists { - mask, err = utils.MaskToBits(nicMask) - if err != nil { - return fmt.Errorf("mask to bits: %s", err) - } + } + if nicIPv6, exists := nic.Ipv6(); exists { + if nicAddress, exists := nicIPv6.Address(); exists { + mask := 128 + if nicMask, exists := nicIPv6.Netmask(); exists { + mask, err = utils.MaskToBits(nicMask) + if err != nil { + return fmt.Errorf("mask to bits: %s", err) } - ipv6Address := fmt.Sprintf("%s/%d", nicAddress, mask) - nicID2IPv6[nicID] = ipv6Address } + ipv6Address := fmt.Sprintf("%s/%d", nicAddress, mask) + nicID2IPv6[nicID] = ipv6Address } - - processedNicsIDs[nicID] = true - nicID2nic[nicID] = newInterface } - return nil + + processedNicsIDs[nicID] = true + nicID2nic[nicID] = newInterface } + return nil } func (o *OVirtSource) syncVms(nbi *inventory.NetboxInventory) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - for vmID, ovirtVM := range o.Vms { - collectedVM, err := o.extractVMData(nbi, vmID, ovirtVM) - if err != nil { - return err - } + for vmID, ovirtVM := range o.Vms { + collectedVM, err := o.extractVMData(nbi, vmID, ovirtVM) + if err != nil { + return err + } - nbVM, err := nbi.AddVM(o.Ctx, collectedVM) - if err != nil { - return fmt.Errorf("failed to sync oVirt vm: %v", err) - } + nbVM, err := nbi.AddVM(o.Ctx, collectedVM) + if err != nil { + return fmt.Errorf("failed to sync oVirt vm: %v", err) + } - err = o.syncVMInterfaces(nbi, ovirtVM, nbVM) - if err != nil { - return fmt.Errorf("failed to sync oVirt vm's interfaces: %v", err) - } + err = o.syncVMInterfaces(nbi, ovirtVM, nbVM) + if err != nil { + return fmt.Errorf("failed to sync oVirt vm's interfaces: %v", err) } - return nil } + return nil } func (o *OVirtSource) extractVMData(nbi *inventory.NetboxInventory, vmID string, vm *ovirtsdk4.Vm) (*objects.VM, error) { - select { // VM name, which is used as unique identifier for VMs in Netbox - case <-o.Ctx.Done(): - return nil, fmt.Errorf("goroutine ended by context") - default: - vmName, exists := vm.Name() - if !exists { - o.Logger.Warning(o.Ctx, "name for oVirt vm with id ", vmID, " is empty. VM has to have unique name to be synced to netbox. Skipping...") - } + vmName, exists := vm.Name() + if !exists { + o.Logger.Warning(o.Ctx, "name for oVirt vm with id ", vmID, " is empty. VM has to have unique name to be synced to netbox. Skipping...") + } - // VM's Cluster - var vmCluster *objects.Cluster - cluster, exists := vm.Cluster() - if exists { - if _, ok := o.Clusters[cluster.MustId()]; ok { - vmCluster = nbi.ClustersIndexByName[o.Clusters[cluster.MustId()].MustName()] - } + // VM's Cluster + var vmCluster *objects.Cluster + cluster, exists := vm.Cluster() + if exists { + if _, ok := o.Clusters[cluster.MustId()]; ok { + vmCluster = nbi.ClustersIndexByName[o.Clusters[cluster.MustId()].MustName()] } + } - // Get VM's site,tenant and platform from cluster - var vmTenantGroup *objects.TenantGroup - var vmTenant *objects.Tenant - var vmSite *objects.Site - if vmCluster != nil { - vmTenantGroup = vmCluster.TenantGroup - vmTenant = vmCluster.Tenant - vmSite = vmCluster.Site - } + // Get VM's site,tenant and platform from cluster + var vmTenantGroup *objects.TenantGroup + var vmTenant *objects.Tenant + var vmSite *objects.Site + if vmCluster != nil { + vmTenantGroup = vmCluster.TenantGroup + vmTenant = vmCluster.Tenant + vmSite = vmCluster.Site + } - // VM's Status - var vmStatus *objects.VMStatus - status, exists := vm.Status() - if exists { - switch status { - case ovirtsdk4.VMSTATUS_UP: - vmStatus = &objects.VMStatusActive - default: - vmStatus = &objects.VMStatusOffline - } + // VM's Status + var vmStatus *objects.VMStatus + status, exists := vm.Status() + if exists { + switch status { + case ovirtsdk4.VMSTATUS_UP: + vmStatus = &objects.VMStatusActive + default: + vmStatus = &objects.VMStatusOffline } + } - // VM's Host Device (server) - var vmHostDevice *objects.Device - if host, exists := vm.Host(); exists { - if oHost, ok := o.Hosts[host.MustId()]; ok { - if oHostName, ok := oHost.Name(); ok { - vmHostDevice = nbi.DevicesIndexByNameAndSiteID[oHostName][vmSite.ID] - } + // VM's Host Device (server) + var vmHostDevice *objects.Device + if host, exists := vm.Host(); exists { + if oHost, ok := o.Hosts[host.MustId()]; ok { + if oHostName, ok := oHost.Name(); ok { + vmHostDevice = nbi.DevicesIndexByNameAndSiteID[oHostName][vmSite.ID] } } + } - // vmVCPUs - var vmVCPUs float32 - if cpuData, exists := vm.Cpu(); exists { - if cpuTopology, exists := cpuData.Topology(); exists { - if cores, exists := cpuTopology.Cores(); exists { - vmVCPUs = float32(cores) - } + // vmVCPUs + var vmVCPUs float32 + if cpuData, exists := vm.Cpu(); exists { + if cpuTopology, exists := cpuData.Topology(); exists { + if cores, exists := cpuTopology.Cores(); exists { + vmVCPUs = float32(cores) } } + } - // Memory - var vmMemorySizeBytes int64 - if memory, exists := vm.Memory(); exists { - vmMemorySizeBytes = memory - } + // Memory + var vmMemorySizeBytes int64 + if memory, exists := vm.Memory(); exists { + vmMemorySizeBytes = memory + } - // Disks - var vmDiskSizeBytes int64 - if diskAttachment, exists := vm.DiskAttachments(); exists { - for _, diskAttachment := range diskAttachment.Slice() { - if ovirtDisk, exists := diskAttachment.Disk(); exists { - disk := o.Disks[ovirtDisk.MustId()] - if provisionedDiskSize, exists := disk.ProvisionedSize(); exists { - vmDiskSizeBytes += provisionedDiskSize - } + // Disks + var vmDiskSizeBytes int64 + if diskAttachment, exists := vm.DiskAttachments(); exists { + for _, diskAttachment := range diskAttachment.Slice() { + if ovirtDisk, exists := diskAttachment.Disk(); exists { + disk := o.Disks[ovirtDisk.MustId()] + if provisionedDiskSize, exists := disk.ProvisionedSize(); exists { + vmDiskSizeBytes += provisionedDiskSize } } } + } - // VM's comments - var vmComments string - if comments, exists := vm.Comment(); exists { - vmComments = comments - } + // VM's comments + var vmComments string + if comments, exists := vm.Comment(); exists { + vmComments = comments + } - // VM's Platform - var vmPlatform *objects.Platform - vmOsType := constants.DefaultOSName - vmOsVersion := constants.DefaultOSVersion - if guestOs, exists := vm.GuestOperatingSystem(); exists { - if guestOsType, exists := guestOs.Distribution(); exists { - vmOsType = guestOsType - } - if guestOsKernel, exists := guestOs.Kernel(); exists { - if guestOsVersion, exists := guestOsKernel.Version(); exists { - if osFullVersion, exists := guestOsVersion.FullVersion(); exists { - vmOsVersion = osFullVersion - } + // VM's Platform + var vmPlatform *objects.Platform + vmOsType := constants.DefaultOSName + vmOsVersion := constants.DefaultOSVersion + if guestOs, exists := vm.GuestOperatingSystem(); exists { + if guestOsType, exists := guestOs.Distribution(); exists { + vmOsType = guestOsType + } + if guestOsKernel, exists := guestOs.Kernel(); exists { + if guestOsVersion, exists := guestOsKernel.Version(); exists { + if osFullVersion, exists := guestOsVersion.FullVersion(); exists { + vmOsVersion = osFullVersion } } - } else { - if os, exists := vm.Os(); exists { - if ovirtOsType, exists := os.Type(); exists { - vmOsType = ovirtOsType - } - if ovirtOsVersion, exists := os.Version(); exists { - if osFullVersion, exists := ovirtOsVersion.FullVersion(); exists { - vmOsVersion = osFullVersion - } + } + } else { + if os, exists := vm.Os(); exists { + if ovirtOsType, exists := os.Type(); exists { + vmOsType = ovirtOsType + } + if ovirtOsVersion, exists := os.Version(); exists { + if osFullVersion, exists := ovirtOsVersion.FullVersion(); exists { + vmOsVersion = osFullVersion } } } - platformName := utils.GeneratePlatformName(vmOsType, vmOsVersion) - vmPlatform, err := nbi.AddPlatform(o.Ctx, &objects.Platform{ - Name: platformName, - Slug: utils.Slugify(platformName), - }) - if err != nil { - return nil, fmt.Errorf("failed adding oVirt vm's Platform %v with error: %s", vmPlatform, err) - } + } + platformName := utils.GeneratePlatformName(vmOsType, vmOsVersion) + vmPlatform, err := nbi.AddPlatform(o.Ctx, &objects.Platform{ + Name: platformName, + Slug: utils.Slugify(platformName), + }) + if err != nil { + return nil, fmt.Errorf("failed adding oVirt vm's Platform %v with error: %s", vmPlatform, err) + } - return &objects.VM{ - NetboxObject: objects.NetboxObject{ - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, + return &objects.VM{ + NetboxObject: objects.NetboxObject{ + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, }, - Name: vmName, - Cluster: vmCluster, - Site: vmSite, - Tenant: vmTenant, - TenantGroup: vmTenantGroup, - Status: vmStatus, - Host: vmHostDevice, - Platform: vmPlatform, - Comments: vmComments, - VCPUs: vmVCPUs, - Memory: int(vmMemorySizeBytes / constants.KiB / constants.KiB), // MBs - Disk: int(vmDiskSizeBytes / constants.KiB / constants.KiB / constants.KiB), // GBs - }, nil - } + }, + Name: vmName, + Cluster: vmCluster, + Site: vmSite, + Tenant: vmTenant, + TenantGroup: vmTenantGroup, + Status: vmStatus, + Host: vmHostDevice, + Platform: vmPlatform, + Comments: vmComments, + VCPUs: vmVCPUs, + Memory: int(vmMemorySizeBytes / constants.KiB / constants.KiB), // MBs + Disk: int(vmDiskSizeBytes / constants.KiB / constants.KiB / constants.KiB), // GBs + }, nil } // Syncs VM's interfaces to Netbox. func (o *OVirtSource) syncVMInterfaces(nbi *inventory.NetboxInventory, ovirtVM *ovirtsdk4.Vm, netboxVM *objects.VM) error { - select { - case <-o.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - if reportedDevices, exist := ovirtVM.ReportedDevices(); exist { - for _, reportedDevice := range reportedDevices.Slice() { - if reportedDeviceType, exist := reportedDevice.Type(); exist { - if reportedDeviceType == "network" { - // We add interface to the list - var vmInterface *objects.VMInterface - var err error - if reportedDeviceName, exists := reportedDevice.Name(); exists { - vmInterface, err = nbi.AddVMInterface(o.Ctx, &objects.VMInterface{ - NetboxObject: objects.NetboxObject{ - Tags: o.Config.SourceTags, - Description: reportedDevice.MustDescription(), - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, + if reportedDevices, exist := ovirtVM.ReportedDevices(); exist { + for _, reportedDevice := range reportedDevices.Slice() { + if reportedDeviceType, exist := reportedDevice.Type(); exist { + if reportedDeviceType == "network" { + // We add interface to the list + var vmInterface *objects.VMInterface + var err error + if reportedDeviceName, exists := reportedDevice.Name(); exists { + vmInterface, err = nbi.AddVMInterface(o.Ctx, &objects.VMInterface{ + NetboxObject: objects.NetboxObject{ + Tags: o.Config.SourceTags, + Description: reportedDevice.MustDescription(), + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, }, - VM: netboxVM, - Name: reportedDeviceName, - MACAddress: strings.ToUpper(reportedDevice.MustMac().MustAddress()), - Enabled: true, // TODO - }) - if err != nil { - return fmt.Errorf("failed to sync oVirt vm's interface %s: %v", reportedDeviceName, err) - } - } else { - o.Logger.Warning(o.Ctx, "name for oVirt vm's reported device is empty. Skipping...") - continue + }, + VM: netboxVM, + Name: reportedDeviceName, + MACAddress: strings.ToUpper(reportedDevice.MustMac().MustAddress()), + Enabled: true, // TODO + }) + if err != nil { + return fmt.Errorf("failed to sync oVirt vm's interface %s: %v", reportedDeviceName, err) } + } else { + o.Logger.Warning(o.Ctx, "name for oVirt vm's reported device is empty. Skipping...") + continue + } - if reportedDeviceIps, exist := reportedDevice.Ips(); exist { - for _, ip := range reportedDeviceIps.Slice() { - if ipAddress, exists := ip.Address(); exists { - if ipVersion, exists := ip.Version(); exists { - // Filter IPs, we won't sync IPs from specific interfaces - // like docker, flannel, calico, etc. interfaces - valid, err := utils.IsVMInterfaceNameValid(vmInterface.Name) - if err != nil { - return fmt.Errorf("failed to match oVirt vm's interface %s to a Netbox interface filter: %v", vmInterface.Name, err) - } - if !valid { - continue - } + if reportedDeviceIps, exist := reportedDevice.Ips(); exist { + for _, ip := range reportedDeviceIps.Slice() { + if ipAddress, exists := ip.Address(); exists { + if ipVersion, exists := ip.Version(); exists { + // Filter IPs, we won't sync IPs from specific interfaces + // like docker, flannel, calico, etc. interfaces + valid, err := utils.IsVMInterfaceNameValid(vmInterface.Name) + if err != nil { + return fmt.Errorf("failed to match oVirt vm's interface %s to a Netbox interface filter: %v", vmInterface.Name, err) + } + if !valid { + continue + } - // Try to do reverse lookup of IP to get DNS name - hostname := utils.ReverseLookup(ipAddress) - - // Set default mask - var ipMask string - if netMask, exists := ip.Netmask(); exists { - ipMask = fmt.Sprintf("/%s", netMask) - } else { - switch ipVersion { - case "v4": - ipMask = "/32" - case "v6": - ipMask = "/128" - } + // Try to do reverse lookup of IP to get DNS name + hostname := utils.ReverseLookup(ipAddress) + + // Set default mask + var ipMask string + if netMask, exists := ip.Netmask(); exists { + ipMask = fmt.Sprintf("/%s", netMask) + } else { + switch ipVersion { + case "v4": + ipMask = "/32" + case "v6": + ipMask = "/128" } + } - newIPAddress, err := nbi.AddIPAddress(o.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: o.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: o.SourceConfig.Name, - }, + newIPAddress, err := nbi.AddIPAddress(o.Ctx, &objects.IPAddress{ + NetboxObject: objects.NetboxObject{ + Tags: o.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: o.SourceConfig.Name, }, - Address: ipAddress + ipMask, - Tenant: netboxVM.Tenant, - Status: &objects.IPAddressStatusActive, - DNSName: hostname, - AssignedObjectType: objects.AssignedObjectTypeVMInterface, - AssignedObjectID: vmInterface.ID, - }) - - if err != nil { - // TODO: return should be here, commented just for now - // return fmt.Errorf("failed to sync oVirt vm's interface %s ip %s: %v", vmInterface, ip.MustAddress(), err) - o.Logger.Error(o.Ctx, fmt.Sprintf("failed to sync oVirt vm's interface %s ip %s: %v", vmInterface, ip.MustAddress(), err)) - } + }, + Address: ipAddress + ipMask, + Tenant: netboxVM.Tenant, + Status: &objects.IPAddressStatusActive, + DNSName: hostname, + AssignedObjectType: objects.AssignedObjectTypeVMInterface, + AssignedObjectID: vmInterface.ID, + }) + + if err != nil { + // TODO: return should be here, commented just for now + // return fmt.Errorf("failed to sync oVirt vm's interface %s ip %s: %v", vmInterface, ip.MustAddress(), err) + o.Logger.Error(o.Ctx, fmt.Sprintf("failed to sync oVirt vm's interface %s ip %s: %v", vmInterface, ip.MustAddress(), err)) + } - // Check if ip is primary - if ipVersion == "v4" { - vmIP := utils.Lookup(netboxVM.Name) - if vmIP != "" && vmIP == ipAddress || netboxVM.PrimaryIPv4 == nil { - vmCopy := *netboxVM - vmCopy.PrimaryIPv4 = newIPAddress - _, err := nbi.AddVM(o.Ctx, &vmCopy) - if err != nil { - return fmt.Errorf("adding primary ipv4 address: %s", err) - } + // Check if ip is primary + if ipVersion == "v4" { + vmIP := utils.Lookup(netboxVM.Name) + if vmIP != "" && vmIP == ipAddress || netboxVM.PrimaryIPv4 == nil { + vmCopy := *netboxVM + vmCopy.PrimaryIPv4 = newIPAddress + _, err := nbi.AddVM(o.Ctx, &vmCopy) + if err != nil { + return fmt.Errorf("adding primary ipv4 address: %s", err) } } } @@ -910,6 +865,6 @@ func (o *OVirtSource) syncVMInterfaces(nbi *inventory.NetboxInventory, ovirtVM * } } } - return nil } + return nil } diff --git a/internal/source/vmware/vmware.go b/internal/source/vmware/vmware.go index 29cb1597..696a3f19 100644 --- a/internal/source/vmware/vmware.go +++ b/internal/source/vmware/vmware.go @@ -86,144 +86,134 @@ type HostPortgroupData struct { } func (vc *VmwareSource) Init() error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended by context") - default: - // Initialize regex relations - vc.Logger.Debug(vc.Ctx, "Initializing regex relations for oVirt source ", vc.SourceConfig.Name) - vc.HostSiteRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.HostSiteRelations) - vc.Logger.Debug(vc.Ctx, "HostSiteRelations: ", vc.HostSiteRelations) - vc.ClusterSiteRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.ClusterSiteRelations) - vc.Logger.Debug(vc.Ctx, "ClusterSiteRelations: ", vc.ClusterSiteRelations) - vc.ClusterTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.ClusterTenantRelations) - vc.Logger.Debug(vc.Ctx, "ClusterTenantRelations: ", vc.ClusterTenantRelations) - vc.HostTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.HostTenantRelations) - vc.Logger.Debug(vc.Ctx, "HostTenantRelations: ", vc.HostTenantRelations) - vc.VMTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.VMTenantRelations) - vc.Logger.Debug(vc.Ctx, "VmTenantRelations: ", vc.VMTenantRelations) - vc.VlanGroupRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.VlanGroupRelations) - vc.Logger.Debug(vc.Ctx, "VlanGroupRelations: ", vc.VlanGroupRelations) - vc.VlanTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.VlanTenantRelations) - vc.Logger.Debug(vc.Ctx, "VlanTenantRelations: ", vc.VlanTenantRelations) - vc.CustomFieldMappings = utils.ConvertStringsToPairs(vc.SourceConfig.CustomFieldMappings) - vc.Logger.Debug(vc.Ctx, "CustomFieldMappings: ", vc.CustomFieldMappings) - - // Initialize the connection - vc.Logger.Debug(vc.Ctx, "Initializing oVirt source ", vc.SourceConfig.Name) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // Correctly handle backslashes in username and password - escapedUsername := url.PathEscape(vc.SourceConfig.Username) - escapedPassword := url.PathEscape(vc.SourceConfig.Password) - - vcURL := fmt.Sprintf("%s://%s:%s@%s:%d/sdk", vc.SourceConfig.HTTPScheme, escapedUsername, escapedPassword, vc.SourceConfig.Hostname, vc.SourceConfig.Port) - - url, err := url.Parse(vcURL) - if err != nil { - return fmt.Errorf("failed parsing url for %s with error %s", vc.SourceConfig.Hostname, err) - } + // Initialize regex relations + vc.Logger.Debug(vc.Ctx, "Initializing regex relations for oVirt source ", vc.SourceConfig.Name) + vc.HostSiteRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.HostSiteRelations) + vc.Logger.Debug(vc.Ctx, "HostSiteRelations: ", vc.HostSiteRelations) + vc.ClusterSiteRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.ClusterSiteRelations) + vc.Logger.Debug(vc.Ctx, "ClusterSiteRelations: ", vc.ClusterSiteRelations) + vc.ClusterTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.ClusterTenantRelations) + vc.Logger.Debug(vc.Ctx, "ClusterTenantRelations: ", vc.ClusterTenantRelations) + vc.HostTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.HostTenantRelations) + vc.Logger.Debug(vc.Ctx, "HostTenantRelations: ", vc.HostTenantRelations) + vc.VMTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.VMTenantRelations) + vc.Logger.Debug(vc.Ctx, "VmTenantRelations: ", vc.VMTenantRelations) + vc.VlanGroupRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.VlanGroupRelations) + vc.Logger.Debug(vc.Ctx, "VlanGroupRelations: ", vc.VlanGroupRelations) + vc.VlanTenantRelations = utils.ConvertStringsToRegexPairs(vc.SourceConfig.VlanTenantRelations) + vc.Logger.Debug(vc.Ctx, "VlanTenantRelations: ", vc.VlanTenantRelations) + vc.CustomFieldMappings = utils.ConvertStringsToPairs(vc.SourceConfig.CustomFieldMappings) + vc.Logger.Debug(vc.Ctx, "CustomFieldMappings: ", vc.CustomFieldMappings) + + // Initialize the connection + vc.Logger.Debug(vc.Ctx, "Initializing oVirt source ", vc.SourceConfig.Name) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Correctly handle backslashes in username and password + escapedUsername := url.PathEscape(vc.SourceConfig.Username) + escapedPassword := url.PathEscape(vc.SourceConfig.Password) + + vcURL := fmt.Sprintf("%s://%s:%s@%s:%d/sdk", vc.SourceConfig.HTTPScheme, escapedUsername, escapedPassword, vc.SourceConfig.Hostname, vc.SourceConfig.Port) + + url, err := url.Parse(vcURL) + if err != nil { + return fmt.Errorf("failed parsing url for %s with error %s", vc.SourceConfig.Hostname, err) + } - conn, err := govmomi.NewClient(ctx, url, !vc.SourceConfig.ValidateCert) - if err != nil { - return fmt.Errorf("failed creating a govmomi client with an error: %s", err) - } + conn, err := govmomi.NewClient(ctx, url, !vc.SourceConfig.ValidateCert) + if err != nil { + return fmt.Errorf("failed creating a govmomi client with an error: %s", err) + } - // View manager is used to create and manage views. Views are a mechanism in vSphere - // to group and manage objects in the inventory. - viewManager := view.NewManager(conn.Client) + // View manager is used to create and manage views. Views are a mechanism in vSphere + // to group and manage objects in the inventory. + viewManager := view.NewManager(conn.Client) - // viewType specifies the types of objects to be included in our container view. - // Each string in this slice represents a different vSphere Managed Object type. - viewType := []string{ - "Datastore", "Datacenter", "ClusterComputeResource", "HostSystem", "VirtualMachine", "Network", - } + // viewType specifies the types of objects to be included in our container view. + // Each string in this slice represents a different vSphere Managed Object type. + viewType := []string{ + "Datastore", "Datacenter", "ClusterComputeResource", "HostSystem", "VirtualMachine", "Network", + } - // A container view is a subset of the vSphere inventory, focusing on the specified - // object types, making it easier to manage and retrieve data for these objects. - containerView, err := viewManager.CreateContainerView(ctx, conn.Client.ServiceContent.RootFolder, viewType, true) - if err != nil { - return fmt.Errorf("failed creating containerView: %s", err) - } + // A container view is a subset of the vSphere inventory, focusing on the specified + // object types, making it easier to manage and retrieve data for these objects. + containerView, err := viewManager.CreateContainerView(ctx, conn.Client.ServiceContent.RootFolder, viewType, true) + if err != nil { + return fmt.Errorf("failed creating containerView: %s", err) + } - vc.Logger.Debug(vc.Ctx, "Connection to vmware source ", vc.SourceConfig.Hostname, " established successfully") + vc.Logger.Debug(vc.Ctx, "Connection to vmware source ", vc.SourceConfig.Hostname, " established successfully") - // Create CustomFieldManager to map custom field ids to their names - // This is required to determine which custom field key is used for - // which custom field name (e.g.g 202 -> vm owner, 203 -> vm description...) - err = vc.CreateCustomFieldRelation(ctx, conn.Client) - if err != nil { - return fmt.Errorf("create custom field relation failed: %s", err) - } + // Create CustomFieldManager to map custom field ids to their names + // This is required to determine which custom field key is used for + // which custom field name (e.g.g 202 -> vm owner, 203 -> vm description...) + err = vc.CreateCustomFieldRelation(ctx, conn.Client) + if err != nil { + return fmt.Errorf("create custom field relation failed: %s", err) + } - // Find relation between data centers and clusters. Currently we have to manually traverse - // the tree to get this relation. - err = vc.CreateClusterDataCenterRelation(ctx, conn.Client) - if err != nil { - return fmt.Errorf("create cluster datacenter relation failed: %s", err) - } + // Find relation between data centers and clusters. Currently we have to manually traverse + // the tree to get this relation. + err = vc.CreateClusterDataCenterRelation(ctx, conn.Client) + if err != nil { + return fmt.Errorf("create cluster datacenter relation failed: %s", err) + } - // Initialize items from vsphere API to local storage - initFunctions := []func(context.Context, *view.ContainerView) error{ - vc.InitNetworks, - vc.InitDisks, - vc.InitDataCenters, - vc.InitClusters, - vc.InitHosts, - vc.InitVms, - } + // Initialize items from vsphere API to local storage + initFunctions := []func(context.Context, *view.ContainerView) error{ + vc.InitNetworks, + vc.InitDisks, + vc.InitDataCenters, + vc.InitClusters, + vc.InitHosts, + vc.InitVms, + } - for _, initFunc := range initFunctions { - startTime := time.Now() - if err := initFunc(ctx, containerView); err != nil { - return fmt.Errorf("vmware initialization failure: %v", err) - } - duration := time.Since(startTime) - vc.Logger.Infof(vc.Ctx, "Successfully initialized %s in %f seconds", utils.ExtractFunctionName(initFunc), duration.Seconds()) + for _, initFunc := range initFunctions { + startTime := time.Now() + if err := initFunc(ctx, containerView); err != nil { + return fmt.Errorf("vmware initialization failure: %v", err) } + duration := time.Since(startTime) + vc.Logger.Infof(vc.Ctx, "Successfully initialized %s in %f seconds", utils.ExtractFunctionName(initFunc), duration.Seconds()) + } - // Ensure the containerView is destroyed after we are done with it - err = containerView.Destroy(ctx) - if err != nil { - vc.Logger.Errorf(vc.Ctx, "failed destroying containerView: %s", err) - } + // Ensure the containerView is destroyed after we are done with it + err = containerView.Destroy(ctx) + if err != nil { + vc.Logger.Errorf(vc.Ctx, "failed destroying containerView: %s", err) + } - err = conn.Logout(ctx) - if err != nil { - return fmt.Errorf("error occurred when ending vmware connection to host %s: %s", vc.SourceConfig.Hostname, err) - } + err = conn.Logout(ctx) + if err != nil { + return fmt.Errorf("error occurred when ending vmware connection to host %s: %s", vc.SourceConfig.Hostname, err) + } - vc.Logger.Debug(vc.Ctx, "Successfully closed connection to vmware host: ", vc.SourceConfig.Hostname) + vc.Logger.Debug(vc.Ctx, "Successfully closed connection to vmware host: ", vc.SourceConfig.Hostname) - return nil - } + return nil } // Function that syncs all data from oVirt to Netbox. func (vc *VmwareSource) Sync(nbi *inventory.NetboxInventory) error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine stopped by context") - default: - syncFunctions := []func(*inventory.NetboxInventory) error{ - vc.syncNetworks, - vc.syncDatacenters, - vc.syncClusters, - vc.syncHosts, - vc.syncVms, - } - for _, syncFunc := range syncFunctions { - startTime := time.Now() - err := syncFunc(nbi) - if err != nil { - return err - } - duration := time.Since(startTime) - vc.Logger.Infof(vc.Ctx, "Successfully synced %s in %f seconds", utils.ExtractFunctionName(syncFunc), duration.Seconds()) + syncFunctions := []func(*inventory.NetboxInventory) error{ + vc.syncNetworks, + vc.syncDatacenters, + vc.syncClusters, + vc.syncHosts, + vc.syncVms, + } + for _, syncFunc := range syncFunctions { + startTime := time.Now() + err := syncFunc(nbi) + if err != nil { + return err } - return nil + duration := time.Since(startTime) + vc.Logger.Infof(vc.Ctx, "Successfully synced %s in %f seconds", utils.ExtractFunctionName(syncFunc), duration.Seconds()) } + return nil } // Currently we have to traverse the vsphere tree to get datacenter to cluster relation diff --git a/internal/source/vmware/vmware_sync.go b/internal/source/vmware/vmware_sync.go index 265effd4..fee33c22 100644 --- a/internal/source/vmware/vmware_sync.go +++ b/internal/source/vmware/vmware_sync.go @@ -17,522 +17,482 @@ import ( func (vc *VmwareSource) syncNetworks(nbi *inventory.NetboxInventory) error { for _, dvpg := range vc.Networks.DistributedVirtualPortgroups { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - // TODO: currently we are syncing only vlans - // Get vlanGroup from relations - vlanGroup, err := common.MatchVlanToGroup(nbi, dvpg.Name, vc.VlanGroupRelations) - if err != nil { - return fmt.Errorf("vlanGroup: %s", err) - } - // Get tenant from relations - vlanTenant, err := common.MatchVlanToTenant(nbi, dvpg.Name, vc.VlanTenantRelations) - if err != nil { - return fmt.Errorf("vlanTenant: %s", err) - } - if len(dvpg.VlanIDs) == 1 && len(dvpg.VlanIDRanges) == 0 { - _, err := nbi.AddVlan(vc.Ctx, &objects.Vlan{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, - }, - Name: dvpg.Name, - Group: vlanGroup, - Vid: dvpg.VlanIDs[0], - Status: &objects.VlanStatusActive, - Tenant: vlanTenant, - }) - if err != nil { - return err - } - } + // TODO: currently we are syncing only vlans + // Get vlanGroup from relations + vlanGroup, err := common.MatchVlanToGroup(nbi, dvpg.Name, vc.VlanGroupRelations) + if err != nil { + return fmt.Errorf("vlanGroup: %s", err) } - } - return nil -} - -func (vc *VmwareSource) syncDatacenters(nbi *inventory.NetboxInventory) error { - for _, dc := range vc.DataCenters { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - nbClusterGroup := &objects.ClusterGroup{ + // Get tenant from relations + vlanTenant, err := common.MatchVlanToTenant(nbi, dvpg.Name, vc.VlanTenantRelations) + if err != nil { + return fmt.Errorf("vlanTenant: %s", err) + } + if len(dvpg.VlanIDs) == 1 && len(dvpg.VlanIDRanges) == 0 { + _, err := nbi.AddVlan(vc.Ctx, &objects.Vlan{ NetboxObject: objects.NetboxObject{ - Description: fmt.Sprintf("Datacenter from source %s", vc.SourceConfig.Hostname), - Tags: vc.Config.SourceTags, + Tags: vc.Config.SourceTags, CustomFields: map[string]string{ constants.CustomFieldSourceName: vc.SourceConfig.Name, }, }, - Name: dc.Name, - Slug: utils.Slugify(dc.Name), - } - _, err := nbi.AddClusterGroup(vc.Ctx, nbClusterGroup) + Name: dvpg.Name, + Group: vlanGroup, + Vid: dvpg.VlanIDs[0], + Status: &objects.VlanStatusActive, + Tenant: vlanTenant, + }) if err != nil { - return fmt.Errorf("failed to add vmware datacenter %s as Netbox ClusterGroup: %v", dc.Name, err) + return err } } } return nil } -func (vc *VmwareSource) syncClusters(nbi *inventory.NetboxInventory) error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - clusterType := &objects.ClusterType{ +func (vc *VmwareSource) syncDatacenters(nbi *inventory.NetboxInventory) error { + for _, dc := range vc.DataCenters { + nbClusterGroup := &objects.ClusterGroup{ NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, + Description: fmt.Sprintf("Datacenter from source %s", vc.SourceConfig.Hostname), + Tags: vc.Config.SourceTags, CustomFields: map[string]string{ constants.CustomFieldSourceName: vc.SourceConfig.Name, }, }, - Name: "Vmware ESXi", - Slug: utils.Slugify("Vmware ESXi"), + Name: dc.Name, + Slug: utils.Slugify(dc.Name), } - clusterType, err := nbi.AddClusterType(vc.Ctx, clusterType) + _, err := nbi.AddClusterGroup(vc.Ctx, nbClusterGroup) if err != nil { - return fmt.Errorf("failed to add vmware ClusterType: %v", err) + return fmt.Errorf("failed to add vmware datacenter %s as Netbox ClusterGroup: %v", dc.Name, err) } - // Then sync vmware Clusters as NetBoxClusters - for clusterID, cluster := range vc.Clusters { - clusterName := cluster.Name + } + return nil +} + +func (vc *VmwareSource) syncClusters(nbi *inventory.NetboxInventory) error { + clusterType := &objects.ClusterType{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, + }, + }, + Name: "Vmware ESXi", + Slug: utils.Slugify("Vmware ESXi"), + } + clusterType, err := nbi.AddClusterType(vc.Ctx, clusterType) + if err != nil { + return fmt.Errorf("failed to add vmware ClusterType: %v", err) + } + // Then sync vmware Clusters as NetBoxClusters + for clusterID, cluster := range vc.Clusters { + clusterName := cluster.Name - var clusterGroup *objects.ClusterGroup - datacenterID := vc.Cluster2Datacenter[clusterID] - clusterGroup = nbi.ClusterGroupsIndexByName[vc.DataCenters[datacenterID].Name] + var clusterGroup *objects.ClusterGroup + datacenterID := vc.Cluster2Datacenter[clusterID] + clusterGroup = nbi.ClusterGroupsIndexByName[vc.DataCenters[datacenterID].Name] - var clusterSite *objects.Site - if vc.ClusterSiteRelations != nil { - match, err := utils.MatchStringToValue(clusterName, vc.ClusterSiteRelations) - if err != nil { - return fmt.Errorf("failed to match vmware cluster %s to a Netbox site: %v", clusterName, err) - } - if match != "" { - if _, ok := nbi.SitesIndexByName[match]; !ok { - return fmt.Errorf("failed to match vmware cluster %s to a Netbox site: %v. Site with this name doesn't exist", clusterName, match) - } - clusterSite = nbi.SitesIndexByName[match] + var clusterSite *objects.Site + if vc.ClusterSiteRelations != nil { + match, err := utils.MatchStringToValue(clusterName, vc.ClusterSiteRelations) + if err != nil { + return fmt.Errorf("failed to match vmware cluster %s to a Netbox site: %v", clusterName, err) + } + if match != "" { + if _, ok := nbi.SitesIndexByName[match]; !ok { + return fmt.Errorf("failed to match vmware cluster %s to a Netbox site: %v. Site with this name doesn't exist", clusterName, match) } + clusterSite = nbi.SitesIndexByName[match] } + } - var clusterTenant *objects.Tenant - if vc.ClusterTenantRelations != nil { - match, err := utils.MatchStringToValue(clusterName, vc.ClusterTenantRelations) - if err != nil { - return fmt.Errorf("error occurred when matching vmware cluster %s to a Netbox tenant: %v", clusterName, err) - } - if match != "" { - if _, ok := nbi.TenantsIndexByName[match]; !ok { - return fmt.Errorf("failed to match vmware cluster %s to a Netbox tenant: %v. Tenant with this name doesn't exist", clusterName, match) - } - clusterTenant = nbi.TenantsIndexByName[match] + var clusterTenant *objects.Tenant + if vc.ClusterTenantRelations != nil { + match, err := utils.MatchStringToValue(clusterName, vc.ClusterTenantRelations) + if err != nil { + return fmt.Errorf("error occurred when matching vmware cluster %s to a Netbox tenant: %v", clusterName, err) + } + if match != "" { + if _, ok := nbi.TenantsIndexByName[match]; !ok { + return fmt.Errorf("failed to match vmware cluster %s to a Netbox tenant: %v. Tenant with this name doesn't exist", clusterName, match) } + clusterTenant = nbi.TenantsIndexByName[match] } + } - nbCluster := &objects.Cluster{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, + nbCluster := &objects.Cluster{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, }, - Name: clusterName, - Type: clusterType, - Status: objects.ClusterStatusActive, - Group: clusterGroup, - Site: clusterSite, - Tenant: clusterTenant, - } - err := nbi.AddCluster(vc.Ctx, nbCluster) - if err != nil { - return fmt.Errorf("failed to add vmware cluster %s as Netbox cluster: %v", clusterName, err) - } + }, + Name: clusterName, + Type: clusterType, + Status: objects.ClusterStatusActive, + Group: clusterGroup, + Site: clusterSite, + Tenant: clusterTenant, + } + err := nbi.AddCluster(vc.Ctx, nbCluster) + if err != nil { + return fmt.Errorf("failed to add vmware cluster %s as Netbox cluster: %v", clusterName, err) } - return nil } + return nil } // Host in vmware is a represented as device in netbox with a // custom role Server. func (vc *VmwareSource) syncHosts(nbi *inventory.NetboxInventory) error { for hostID, host := range vc.Hosts { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - var err error - hostName := host.Name - hostCluster := nbi.ClustersIndexByName[vc.Clusters[vc.Host2Cluster[hostID]].Name] + var err error + hostName := host.Name + hostCluster := nbi.ClustersIndexByName[vc.Clusters[vc.Host2Cluster[hostID]].Name] - hostSite, err := common.MatchHostToSite(nbi, hostName, vc.HostSiteRelations) - if err != nil { - return fmt.Errorf("hostSite: %s", err) - } - hostTenant, err := common.MatchHostToTenant(nbi, hostName, vc.HostTenantRelations) - if err != nil { - return fmt.Errorf("hostTenant: %s", err) - } - hostAssetTag := host.Summary.Hardware.Uuid - hostModel := host.Summary.Hardware.Model - - var hostSerialNumber string - // find serial number from host summary.hardware.OtherIdentifyingInfo (vmware specific logic) - serialInfoTypes := map[string]bool{ - "EnclosureSerialNumberTag": true, - "ServiceTag": true, - "SerialNumberTag": true, - } - for _, info := range host.Summary.Hardware.OtherIdentifyingInfo { - infoType := info.IdentifierType.GetElementDescription().Key - if serialInfoTypes[infoType] { - if info.IdentifierValue != "" { - hostSerialNumber = info.IdentifierValue - break - } + hostSite, err := common.MatchHostToSite(nbi, hostName, vc.HostSiteRelations) + if err != nil { + return fmt.Errorf("hostSite: %s", err) + } + hostTenant, err := common.MatchHostToTenant(nbi, hostName, vc.HostTenantRelations) + if err != nil { + return fmt.Errorf("hostTenant: %s", err) + } + hostAssetTag := host.Summary.Hardware.Uuid + hostModel := host.Summary.Hardware.Model + + var hostSerialNumber string + // find serial number from host summary.hardware.OtherIdentifyingInfo (vmware specific logic) + serialInfoTypes := map[string]bool{ + "EnclosureSerialNumberTag": true, + "ServiceTag": true, + "SerialNumberTag": true, + } + for _, info := range host.Summary.Hardware.OtherIdentifyingInfo { + infoType := info.IdentifierType.GetElementDescription().Key + if serialInfoTypes[infoType] { + if info.IdentifierValue != "" { + hostSerialNumber = info.IdentifierValue + break } } + } - manufacturerName := host.Summary.Hardware.Vendor - var hostManufacturer *objects.Manufacturer - if manufacturerName == "" { - manufacturerName = constants.DefaultManufacturer - } - hostManufacturer, err = nbi.AddManufacturer(vc.Ctx, &objects.Manufacturer{ - Name: manufacturerName, - Slug: utils.Slugify(manufacturerName), - }) - if err != nil { - return fmt.Errorf("failed adding vmware Manufacturer %v with error: %s", hostManufacturer, err) - } - - var hostDeviceType *objects.DeviceType - hostDeviceType, err = nbi.AddDeviceType(vc.Ctx, &objects.DeviceType{ - Manufacturer: hostManufacturer, - Model: hostModel, - Slug: utils.Slugify(hostModel), - }) - if err != nil { - return fmt.Errorf("failed adding vmware DeviceType %v with error: %s", hostDeviceType, err) - } - - var hostStatus *objects.DeviceStatus - switch host.Summary.Runtime.ConnectionState { - case "connected": - hostStatus = &objects.DeviceStatusActive - default: - hostStatus = &objects.DeviceStatusOffline - } - - var hostPlatform *objects.Platform - osType := host.Summary.Config.Product.Name - osVersion := host.Summary.Config.Product.Version - platformName := utils.GeneratePlatformName(osType, osVersion) - hostPlatform, err = nbi.AddPlatform(vc.Ctx, &objects.Platform{ - Name: platformName, - Slug: utils.Slugify(platformName), - }) - if err != nil { - return fmt.Errorf("failed adding vmware Platform %v with error: %s", hostPlatform, err) - } + manufacturerName := host.Summary.Hardware.Vendor + var hostManufacturer *objects.Manufacturer + if manufacturerName == "" { + manufacturerName = constants.DefaultManufacturer + } + hostManufacturer, err = nbi.AddManufacturer(vc.Ctx, &objects.Manufacturer{ + Name: manufacturerName, + Slug: utils.Slugify(manufacturerName), + }) + if err != nil { + return fmt.Errorf("failed adding vmware Manufacturer %v with error: %s", hostManufacturer, err) + } - hostCPUCores := host.Summary.Hardware.NumCpuCores - hostMemGB := host.Summary.Hardware.MemorySize / constants.KiB / constants.KiB / constants.KiB - - nbHost := &objects.Device{ - NetboxObject: objects.NetboxObject{Tags: vc.Config.SourceTags, CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - constants.CustomFieldHostCPUCoresName: fmt.Sprintf("%d", hostCPUCores), - constants.CustomFieldHostMemoryName: fmt.Sprintf("%d GB", hostMemGB), - }}, - Name: hostName, - Status: hostStatus, - Platform: hostPlatform, - DeviceRole: nbi.DeviceRolesIndexByName["Server"], - Site: hostSite, - Tenant: hostTenant, - Cluster: hostCluster, - SerialNumber: hostSerialNumber, - AssetTag: hostAssetTag, - DeviceType: hostDeviceType, - } - nbHost, err = nbi.AddDevice(vc.Ctx, nbHost) - if err != nil { - return fmt.Errorf("failed to add vmware host %s with error: %v", host.Name, err) - } + var hostDeviceType *objects.DeviceType + hostDeviceType, err = nbi.AddDeviceType(vc.Ctx, &objects.DeviceType{ + Manufacturer: hostManufacturer, + Model: hostModel, + Slug: utils.Slugify(hostModel), + }) + if err != nil { + return fmt.Errorf("failed adding vmware DeviceType %v with error: %s", hostDeviceType, err) + } - // We also need to sync nics separately, because nic is a separate object in netbox - err = vc.syncHostNics(nbi, host, nbHost) - if err != nil { - return fmt.Errorf("failed to sync vmware host %s nics with error: %v", host.Name, err) - } + var hostStatus *objects.DeviceStatus + switch host.Summary.Runtime.ConnectionState { + case "connected": + hostStatus = &objects.DeviceStatusActive + default: + hostStatus = &objects.DeviceStatusOffline } - } - return nil -} -func (vc *VmwareSource) syncHostNics(nbi *inventory.NetboxInventory, vcHost mo.HostSystem, nbHost *objects.Device) error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - // Variable for storeing all ipAddresses from all host interfaces, - // we use them to determine the primary ip of the host. - hostIPv4Addresses := []*objects.IPAddress{} - hostIPv6Addresses := []*objects.IPAddress{} - - // Sync host's physical interfaces - err := vc.syncHostPhysicalNics(nbi, vcHost, nbHost) + var hostPlatform *objects.Platform + osType := host.Summary.Config.Product.Name + osVersion := host.Summary.Config.Product.Version + platformName := utils.GeneratePlatformName(osType, osVersion) + hostPlatform, err = nbi.AddPlatform(vc.Ctx, &objects.Platform{ + Name: platformName, + Slug: utils.Slugify(platformName), + }) if err != nil { - return fmt.Errorf("physical interfaces sync: %s", err) + return fmt.Errorf("failed adding vmware Platform %v with error: %s", hostPlatform, err) } - // Sync host's virtual interfaces - err = vc.syncHostVirtualNics(nbi, vcHost, nbHost, hostIPv4Addresses, hostIPv6Addresses) + hostCPUCores := host.Summary.Hardware.NumCpuCores + hostMemGB := host.Summary.Hardware.MemorySize / constants.KiB / constants.KiB / constants.KiB + + nbHost := &objects.Device{ + NetboxObject: objects.NetboxObject{Tags: vc.Config.SourceTags, CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, + constants.CustomFieldHostCPUCoresName: fmt.Sprintf("%d", hostCPUCores), + constants.CustomFieldHostMemoryName: fmt.Sprintf("%d GB", hostMemGB), + }}, + Name: hostName, + Status: hostStatus, + Platform: hostPlatform, + DeviceRole: nbi.DeviceRolesIndexByName["Server"], + Site: hostSite, + Tenant: hostTenant, + Cluster: hostCluster, + SerialNumber: hostSerialNumber, + AssetTag: hostAssetTag, + DeviceType: hostDeviceType, + } + nbHost, err = nbi.AddDevice(vc.Ctx, nbHost) if err != nil { - return fmt.Errorf("virtual interfaces sync: %s", err) + return fmt.Errorf("failed to add vmware host %s with error: %v", host.Name, err) } - // Set host's private ip address from collected ips - err = vc.setHostPrimaryIPAddress(nbi, nbHost, hostIPv4Addresses, hostIPv6Addresses) + // We also need to sync nics separately, because nic is a separate object in netbox + err = vc.syncHostNics(nbi, host, nbHost) if err != nil { - return fmt.Errorf("adding host primary ip addresses: %s", err) + return fmt.Errorf("failed to sync vmware host %s nics with error: %v", host.Name, err) } + } + return nil +} - return nil +func (vc *VmwareSource) syncHostNics(nbi *inventory.NetboxInventory, vcHost mo.HostSystem, nbHost *objects.Device) error { + // Variable for storeing all ipAddresses from all host interfaces, + // we use them to determine the primary ip of the host. + hostIPv4Addresses := []*objects.IPAddress{} + hostIPv6Addresses := []*objects.IPAddress{} + + // Sync host's physical interfaces + err := vc.syncHostPhysicalNics(nbi, vcHost, nbHost) + if err != nil { + return fmt.Errorf("physical interfaces sync: %s", err) } + + // Sync host's virtual interfaces + err = vc.syncHostVirtualNics(nbi, vcHost, nbHost, hostIPv4Addresses, hostIPv6Addresses) + if err != nil { + return fmt.Errorf("virtual interfaces sync: %s", err) + } + + // Set host's private ip address from collected ips + err = vc.setHostPrimaryIPAddress(nbi, nbHost, hostIPv4Addresses, hostIPv6Addresses) + if err != nil { + return fmt.Errorf("adding host primary ip addresses: %s", err) + } + + return nil } func (vc *VmwareSource) syncHostPhysicalNics(nbi *inventory.NetboxInventory, vcHost mo.HostSystem, nbHost *objects.Device) error { for _, pnic := range vcHost.Config.Network.Pnic { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - hostPnic, err := vc.collectHostPhysicalNicData(nbi, nbHost, pnic) - if err != nil { - return err - } - // After collecting all of the data add interface to nbi - _, err = nbi.AddInterface(vc.Ctx, hostPnic) - if err != nil { - return fmt.Errorf("failed adding physical interface: %s", err) - } + hostPnic, err := vc.collectHostPhysicalNicData(nbi, nbHost, pnic) + if err != nil { + return err + } + // After collecting all of the data add interface to nbi + _, err = nbi.AddInterface(vc.Ctx, hostPnic) + if err != nil { + return fmt.Errorf("failed adding physical interface: %s", err) } } return nil } func (vc *VmwareSource) collectHostPhysicalNicData(nbi *inventory.NetboxInventory, nbHost *objects.Device, pnic types.PhysicalNic) (*objects.Interface, error) { - select { - case <-vc.Ctx.Done(): - return nil, fmt.Errorf("goroutine ended with context") - default: - pnicName := pnic.Device - var pnicLinkSpeedMb int32 - if pnic.LinkSpeed != nil { + pnicName := pnic.Device + var pnicLinkSpeedMb int32 + if pnic.LinkSpeed != nil { + pnicLinkSpeedMb = pnic.LinkSpeed.SpeedMb + } + if pnicLinkSpeedMb == 0 { + if pnic.Spec.LinkSpeed != nil { pnicLinkSpeedMb = pnic.LinkSpeed.SpeedMb } if pnicLinkSpeedMb == 0 { - if pnic.Spec.LinkSpeed != nil { - pnicLinkSpeedMb = pnic.LinkSpeed.SpeedMb - } - if pnicLinkSpeedMb == 0 { - if pnic.ValidLinkSpecification != nil { - pnicLinkSpeedMb = pnic.ValidLinkSpecification[0].SpeedMb - } + if pnic.ValidLinkSpecification != nil { + pnicLinkSpeedMb = pnic.ValidLinkSpecification[0].SpeedMb } } - var pnicDescription string - if pnicLinkSpeedMb*constants.MB >= constants.GB { - pnicDescription = fmt.Sprintf("%dGB/s", pnicLinkSpeedMb/constants.KB) - } else { - pnicDescription = fmt.Sprintf("%dMB/s", pnicLinkSpeedMb) - } - pnicDescription += " pNIC" - var pnicMtu int - var pnicMode *objects.InterfaceMode - // Check virtual switches for data - for vswitch, vswitchData := range vc.Networks.HostVirtualSwitches[nbHost.Name] { - if slices.Contains(vswitchData.pnics, pnic.Key) { - pnicDescription = fmt.Sprintf("%s (%s)", pnicDescription, vswitch) - pnicMtu = vswitchData.mtu - } + } + var pnicDescription string + if pnicLinkSpeedMb*constants.MB >= constants.GB { + pnicDescription = fmt.Sprintf("%dGB/s", pnicLinkSpeedMb/constants.KB) + } else { + pnicDescription = fmt.Sprintf("%dMB/s", pnicLinkSpeedMb) + } + pnicDescription += " pNIC" + var pnicMtu int + var pnicMode *objects.InterfaceMode + // Check virtual switches for data + for vswitch, vswitchData := range vc.Networks.HostVirtualSwitches[nbHost.Name] { + if slices.Contains(vswitchData.pnics, pnic.Key) { + pnicDescription = fmt.Sprintf("%s (%s)", pnicDescription, vswitch) + pnicMtu = vswitchData.mtu } + } - // Check proxy switches for data - for _, pswitchData := range vc.Networks.HostProxySwitches[nbHost.Name] { - if slices.Contains(pswitchData.pnics, pnic.Key) { - pnicDescription = fmt.Sprintf("%s (%s)", pnicDescription, pswitchData.name) - pnicMtu = pswitchData.mtu - pnicMode = &objects.InterfaceModeTaggedAll - } + // Check proxy switches for data + for _, pswitchData := range vc.Networks.HostProxySwitches[nbHost.Name] { + if slices.Contains(pswitchData.pnics, pnic.Key) { + pnicDescription = fmt.Sprintf("%s (%s)", pnicDescription, pswitchData.name) + pnicMtu = pswitchData.mtu + pnicMode = &objects.InterfaceModeTaggedAll } + } - // Check vlans on this pnic - vlanIDMap := map[int]*objects.Vlan{} // set of vlans - for portgroupName, portgroupData := range vc.Networks.HostPortgroups[nbHost.Name] { - if slices.Contains(portgroupData.nics, pnicName) { - if portgroupData.vlanID == 0 || portgroupData.vlanID > 4094 { - vlanIDMap[portgroupData.vlanID] = &objects.Vlan{Vid: portgroupData.vlanID} - continue + // Check vlans on this pnic + vlanIDMap := map[int]*objects.Vlan{} // set of vlans + for portgroupName, portgroupData := range vc.Networks.HostPortgroups[nbHost.Name] { + if slices.Contains(portgroupData.nics, pnicName) { + if portgroupData.vlanID == 0 || portgroupData.vlanID > 4094 { + vlanIDMap[portgroupData.vlanID] = &objects.Vlan{Vid: portgroupData.vlanID} + continue + } + // Check if vlan with this vid already exists, else create it + if vlanName, ok := vc.Networks.Vid2Name[portgroupData.vlanID]; ok { + vlanGroup, err := common.MatchVlanToGroup(nbi, vlanName, vc.VlanGroupRelations) + if err != nil { + return nil, fmt.Errorf("vlanGroup: %s", err) } - // Check if vlan with this vid already exists, else create it - if vlanName, ok := vc.Networks.Vid2Name[portgroupData.vlanID]; ok { - vlanGroup, err := common.MatchVlanToGroup(nbi, vlanName, vc.VlanGroupRelations) - if err != nil { - return nil, fmt.Errorf("vlanGroup: %s", err) - } - vlanIDMap[portgroupData.vlanID] = nbi.VlansIndexByVlanGroupIDAndVID[vlanGroup.ID][portgroupData.vlanID] - } else { - vlanGroup, err := common.MatchVlanToGroup(nbi, portgroupName, vc.VlanGroupRelations) - if err != nil { - return nil, fmt.Errorf("vlanGroup: %s", err) - } - var newVlan *objects.Vlan - var ok bool - newVlan, ok = nbi.VlansIndexByVlanGroupIDAndVID[vlanGroup.ID][portgroupData.vlanID] - if !ok { - newVlan, err = nbi.AddVlan(vc.Ctx, &objects.Vlan{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, + vlanIDMap[portgroupData.vlanID] = nbi.VlansIndexByVlanGroupIDAndVID[vlanGroup.ID][portgroupData.vlanID] + } else { + vlanGroup, err := common.MatchVlanToGroup(nbi, portgroupName, vc.VlanGroupRelations) + if err != nil { + return nil, fmt.Errorf("vlanGroup: %s", err) + } + var newVlan *objects.Vlan + var ok bool + newVlan, ok = nbi.VlansIndexByVlanGroupIDAndVID[vlanGroup.ID][portgroupData.vlanID] + if !ok { + newVlan, err = nbi.AddVlan(vc.Ctx, &objects.Vlan{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, }, - Status: &objects.VlanStatusActive, - Name: fmt.Sprintf("VLAN%d_%s", portgroupData.vlanID, portgroupName), - Vid: portgroupData.vlanID, - Group: vlanGroup, - }) - if err != nil { - return nil, fmt.Errorf("new vlan: %s", err) - } + }, + Status: &objects.VlanStatusActive, + Name: fmt.Sprintf("VLAN%d_%s", portgroupData.vlanID, portgroupName), + Vid: portgroupData.vlanID, + Group: vlanGroup, + }) + if err != nil { + return nil, fmt.Errorf("new vlan: %s", err) } - vlanIDMap[portgroupData.vlanID] = newVlan } + vlanIDMap[portgroupData.vlanID] = newVlan } } + } - // Determine interface mode for non VM traffic NIC, from vlans data - var taggedVlanList []*objects.Vlan // when mode="tagged" - if len(vlanIDMap) > 0 { - switch { - case len(vlanIDMap) == 1 && vlanIDMap[0] != nil: - pnicMode = &objects.InterfaceModeAccess - case vlanIDMap[4095] != nil: - pnicMode = &objects.InterfaceModeTaggedAll - default: - pnicMode = &objects.InterfaceModeTagged - taggedVlanList = []*objects.Vlan{} - if pnicMode == &objects.InterfaceModeTagged { - for vid, vlan := range vlanIDMap { - if vid == 0 { - continue - } - taggedVlanList = append(taggedVlanList, vlan) + // Determine interface mode for non VM traffic NIC, from vlans data + var taggedVlanList []*objects.Vlan // when mode="tagged" + if len(vlanIDMap) > 0 { + switch { + case len(vlanIDMap) == 1 && vlanIDMap[0] != nil: + pnicMode = &objects.InterfaceModeAccess + case vlanIDMap[4095] != nil: + pnicMode = &objects.InterfaceModeTaggedAll + default: + pnicMode = &objects.InterfaceModeTagged + taggedVlanList = []*objects.Vlan{} + if pnicMode == &objects.InterfaceModeTagged { + for vid, vlan := range vlanIDMap { + if vid == 0 { + continue } + taggedVlanList = append(taggedVlanList, vlan) } } } + } - pnicType := objects.IfaceSpeed2IfaceType[objects.InterfaceSpeed(pnicLinkSpeedMb)] - if pnicType == nil { - pnicType = &objects.OtherInterfaceType - } - return &objects.Interface{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - Description: pnicDescription, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, - }, - Device: nbHost, - Name: pnicName, - Status: true, - Type: pnicType, - Speed: objects.InterfaceSpeed(pnicLinkSpeedMb / constants.KB), - MTU: pnicMtu, - MAC: strings.ToUpper(pnic.Mac), - Mode: pnicMode, - TaggedVlans: taggedVlanList, - }, nil + pnicType := objects.IfaceSpeed2IfaceType[objects.InterfaceSpeed(pnicLinkSpeedMb)] + if pnicType == nil { + pnicType = &objects.OtherInterfaceType } + return &objects.Interface{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + Description: pnicDescription, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, + }, + }, + Device: nbHost, + Name: pnicName, + Status: true, + Type: pnicType, + Speed: objects.InterfaceSpeed(pnicLinkSpeedMb / constants.KB), + MTU: pnicMtu, + MAC: strings.ToUpper(pnic.Mac), + Mode: pnicMode, + TaggedVlans: taggedVlanList, + }, nil } func (vc *VmwareSource) syncHostVirtualNics(nbi *inventory.NetboxInventory, vcHost mo.HostSystem, nbHost *objects.Device, hostIPv4Addresses []*objects.IPAddress, hostIPv6Addresses []*objects.IPAddress) error { // Collect data over all virtual interfaces for _, vnic := range vcHost.Config.Network.Vnic { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - hostVnic, err := vc.collectHostVirtualNicData(nbi, nbHost, vcHost, vnic) - if err != nil { - return err - } + hostVnic, err := vc.collectHostVirtualNicData(nbi, nbHost, vcHost, vnic) + if err != nil { + return err + } - nbVnic, err := nbi.AddInterface(vc.Ctx, hostVnic) - if err != nil { - return err - } + nbVnic, err := nbi.AddInterface(vc.Ctx, hostVnic) + if err != nil { + return err + } - // Get IPv4 address for this vnic. TODO: filter - ipv4Address := vnic.Spec.Ip.IpAddress - ipv4MaskBits, err := utils.MaskToBits(vnic.Spec.Ip.SubnetMask) - if err != nil { - return fmt.Errorf("mask to bits: %s", err) - } - ipv4DNS := utils.ReverseLookup(ipv4Address) - nbIPv4Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, + // Get IPv4 address for this vnic. TODO: filter + ipv4Address := vnic.Spec.Ip.IpAddress + ipv4MaskBits, err := utils.MaskToBits(vnic.Spec.Ip.SubnetMask) + if err != nil { + return fmt.Errorf("mask to bits: %s", err) + } + ipv4DNS := utils.ReverseLookup(ipv4Address) + nbIPv4Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, }, - Address: fmt.Sprintf("%s/%d", ipv4Address, ipv4MaskBits), - Status: &objects.IPAddressStatusActive, // TODO - DNSName: ipv4DNS, - Tenant: nbHost.Tenant, - AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, - AssignedObjectID: nbVnic.ID, - }) - if err != nil { - return err - } - hostIPv4Addresses = append(hostIPv4Addresses, nbIPv4Address) - - if vnic.Spec.Ip.IpV6Config != nil { - for _, ipv6Entry := range vnic.Spec.Ip.IpV6Config.IpV6Address { - ipv6Address := ipv6Entry.IpAddress - ipv6Mask := ipv6Entry.PrefixLength - // TODO: Filter out ipv6 addresses - nbIPv6Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, + }, + Address: fmt.Sprintf("%s/%d", ipv4Address, ipv4MaskBits), + Status: &objects.IPAddressStatusActive, // TODO + DNSName: ipv4DNS, + Tenant: nbHost.Tenant, + AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, + AssignedObjectID: nbVnic.ID, + }) + if err != nil { + return err + } + hostIPv4Addresses = append(hostIPv4Addresses, nbIPv4Address) + + if vnic.Spec.Ip.IpV6Config != nil { + for _, ipv6Entry := range vnic.Spec.Ip.IpV6Config.IpV6Address { + ipv6Address := ipv6Entry.IpAddress + ipv6Mask := ipv6Entry.PrefixLength + // TODO: Filter out ipv6 addresses + nbIPv6Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, }, - Address: fmt.Sprintf("%s/%d", ipv6Address, ipv6Mask), - Status: &objects.IPAddressStatusActive, // TODO - Tenant: nbHost.Tenant, - AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, - AssignedObjectID: nbVnic.ID, - }) - if err != nil { - return err - } - hostIPv6Addresses = append(hostIPv6Addresses, nbIPv6Address) + }, + Address: fmt.Sprintf("%s/%d", ipv6Address, ipv6Mask), + Status: &objects.IPAddressStatusActive, // TODO + Tenant: nbHost.Tenant, + AssignedObjectType: objects.AssignedObjectTypeDeviceInterface, + AssignedObjectID: nbVnic.ID, + }) + if err != nil { + return err } + hostIPv6Addresses = append(hostIPv6Addresses, nbIPv6Address) } } } @@ -566,257 +526,247 @@ func (vc *VmwareSource) setHostPrimaryIPAddress(nbi *inventory.NetboxInventory, } func (vc *VmwareSource) collectHostVirtualNicData(nbi *inventory.NetboxInventory, nbHost *objects.Device, vcHost mo.HostSystem, vnic types.HostVirtualNic) (*objects.Interface, error) { - select { - case <-vc.Ctx.Done(): - return nil, fmt.Errorf("goroutine ended with context") - default: - vnicName := vnic.Device - vnicPortgroupData, vnicPortgroupDataOk := vc.Networks.HostPortgroups[vcHost.Name][vnic.Portgroup] - vnicDvPortgroupKey := "" - if vnic.Spec.DistributedVirtualPort != nil { - vnicDvPortgroupKey = vnic.Spec.DistributedVirtualPort.PortgroupKey - } - vnicDvPortgroupData, vnicDvPortgroupDataOk := vc.Networks.DistributedVirtualPortgroups[vnicDvPortgroupKey] - vnicPortgroupVlanID := 0 - vnicDvPortgroupVlanIDs := []int{} - var vnicMode *objects.InterfaceMode - var vlanDescription, vnicDescription string - - // Get data from local portgroup, or distributed portgroup - if vnicPortgroupDataOk { - vnicPortgroupVlanID = vnicPortgroupData.vlanID - vnicSwitch := vnicPortgroupData.vswitch - vnicDescription = fmt.Sprintf("%s (%s, vlan ID: %d)", vnic.Portgroup, vnicSwitch, vnicPortgroupVlanID) - } else if vnicDvPortgroupDataOk { - vnicDescription = vnicDvPortgroupData.Name - vnicDvPortgroupVlanIDs = vnicDvPortgroupData.VlanIDs - if len(vnicDvPortgroupVlanIDs) == 1 && vnicDvPortgroupData.VlanIDs[0] == 4095 { - vnicDescription = "all vlans" - vnicMode = &objects.InterfaceModeTaggedAll + vnicName := vnic.Device + vnicPortgroupData, vnicPortgroupDataOk := vc.Networks.HostPortgroups[vcHost.Name][vnic.Portgroup] + vnicDvPortgroupKey := "" + if vnic.Spec.DistributedVirtualPort != nil { + vnicDvPortgroupKey = vnic.Spec.DistributedVirtualPort.PortgroupKey + } + vnicDvPortgroupData, vnicDvPortgroupDataOk := vc.Networks.DistributedVirtualPortgroups[vnicDvPortgroupKey] + vnicPortgroupVlanID := 0 + vnicDvPortgroupVlanIDs := []int{} + var vnicMode *objects.InterfaceMode + var vlanDescription, vnicDescription string + + // Get data from local portgroup, or distributed portgroup + if vnicPortgroupDataOk { + vnicPortgroupVlanID = vnicPortgroupData.vlanID + vnicSwitch := vnicPortgroupData.vswitch + vnicDescription = fmt.Sprintf("%s (%s, vlan ID: %d)", vnic.Portgroup, vnicSwitch, vnicPortgroupVlanID) + } else if vnicDvPortgroupDataOk { + vnicDescription = vnicDvPortgroupData.Name + vnicDvPortgroupVlanIDs = vnicDvPortgroupData.VlanIDs + if len(vnicDvPortgroupVlanIDs) == 1 && vnicDvPortgroupData.VlanIDs[0] == 4095 { + vnicDescription = "all vlans" + vnicMode = &objects.InterfaceModeTaggedAll + } else { + if len(vnicDvPortgroupData.VlanIDRanges) > 0 { + vlanDescription = fmt.Sprintf("vlan IDs: %s", strings.Join(vnicDvPortgroupData.VlanIDRanges, ",")) } else { - if len(vnicDvPortgroupData.VlanIDRanges) > 0 { - vlanDescription = fmt.Sprintf("vlan IDs: %s", strings.Join(vnicDvPortgroupData.VlanIDRanges, ",")) - } else { - vlanDescription = fmt.Sprintf("vlan ID: %d", vnicDvPortgroupData.VlanIDs[0]) - } - if len(vnicDvPortgroupData.VlanIDs) == 1 && vnicDvPortgroupData.VlanIDs[0] == 0 { - vnicMode = &objects.InterfaceModeAccess - } else { - vnicMode = &objects.InterfaceModeTagged - } + vlanDescription = fmt.Sprintf("vlan ID: %d", vnicDvPortgroupData.VlanIDs[0]) } - vnicDvPortgroupDwSwitchUUID := vnic.Spec.DistributedVirtualPort.SwitchUuid - vnicVswitch, vnicVswitchOk := vc.Networks.HostVirtualSwitches[vcHost.Name][vnicDvPortgroupDwSwitchUUID] - if vnicVswitchOk { - vnicDescription = fmt.Sprintf("%s (%v, %s)", vnicDescription, vnicVswitch, vlanDescription) + if len(vnicDvPortgroupData.VlanIDs) == 1 && vnicDvPortgroupData.VlanIDs[0] == 0 { + vnicMode = &objects.InterfaceModeAccess + } else { + vnicMode = &objects.InterfaceModeTagged } } + vnicDvPortgroupDwSwitchUUID := vnic.Spec.DistributedVirtualPort.SwitchUuid + vnicVswitch, vnicVswitchOk := vc.Networks.HostVirtualSwitches[vcHost.Name][vnicDvPortgroupDwSwitchUUID] + if vnicVswitchOk { + vnicDescription = fmt.Sprintf("%s (%v, %s)", vnicDescription, vnicVswitch, vlanDescription) + } + } - var vnicUntaggedVlan *objects.Vlan - var vnicTaggedVlans []*objects.Vlan - if vnicPortgroupData != nil && vnicPortgroupVlanID != 0 { - vnicUntaggedVlanGroup, err := common.MatchVlanToGroup(nbi, vc.Networks.Vid2Name[vnicPortgroupVlanID], vc.VlanGroupRelations) + var vnicUntaggedVlan *objects.Vlan + var vnicTaggedVlans []*objects.Vlan + if vnicPortgroupData != nil && vnicPortgroupVlanID != 0 { + vnicUntaggedVlanGroup, err := common.MatchVlanToGroup(nbi, vc.Networks.Vid2Name[vnicPortgroupVlanID], vc.VlanGroupRelations) + if err != nil { + return nil, fmt.Errorf("vlan group: %s", err) + } + vnicUntaggedVlan = nbi.VlansIndexByVlanGroupIDAndVID[vnicUntaggedVlanGroup.ID][vnicPortgroupVlanID] + vnicMode = &objects.InterfaceModeAccess + // vnicUntaggedVlan = &objects.Vlan{ + // Name: fmt.Sprintf("ESXi %s (ID: %d) (%s)", vnic.Portgroup, vnicPortgroupVlanId, nbHost.Site.Name), + // Vid: vnicPortgroupVlanId, + // Tenant: nbHost.Tenant, + // } + } else if vnicDvPortgroupData != nil { + for _, vnicDvPortgroupDataVlanID := range vnicDvPortgroupVlanIDs { + if vnicMode != &objects.InterfaceModeTagged { + break + } + if vnicDvPortgroupDataVlanID == 0 { + continue + } + vnicTaggedVlanGroup, err := common.MatchVlanToGroup(nbi, vc.Networks.Vid2Name[vnicDvPortgroupDataVlanID], vc.VlanGroupRelations) if err != nil { return nil, fmt.Errorf("vlan group: %s", err) } - vnicUntaggedVlan = nbi.VlansIndexByVlanGroupIDAndVID[vnicUntaggedVlanGroup.ID][vnicPortgroupVlanID] - vnicMode = &objects.InterfaceModeAccess - // vnicUntaggedVlan = &objects.Vlan{ - // Name: fmt.Sprintf("ESXi %s (ID: %d) (%s)", vnic.Portgroup, vnicPortgroupVlanId, nbHost.Site.Name), - // Vid: vnicPortgroupVlanId, + vnicTaggedVlans = append(vnicTaggedVlans, nbi.VlansIndexByVlanGroupIDAndVID[vnicTaggedVlanGroup.ID][vnicDvPortgroupDataVlanID]) + // vnicTaggedVlans = append(vnicTaggedVlans, &objects.Vlan{ + // Name: fmt.Sprintf("%s-%d", vnicDvPortgroupData.Name, vnicDvPortgroupDataVlanId), + // Vid: vnicDvPortgroupDataVlanId, // Tenant: nbHost.Tenant, - // } - } else if vnicDvPortgroupData != nil { - for _, vnicDvPortgroupDataVlanID := range vnicDvPortgroupVlanIDs { - if vnicMode != &objects.InterfaceModeTagged { - break - } - if vnicDvPortgroupDataVlanID == 0 { - continue - } - vnicTaggedVlanGroup, err := common.MatchVlanToGroup(nbi, vc.Networks.Vid2Name[vnicDvPortgroupDataVlanID], vc.VlanGroupRelations) - if err != nil { - return nil, fmt.Errorf("vlan group: %s", err) - } - vnicTaggedVlans = append(vnicTaggedVlans, nbi.VlansIndexByVlanGroupIDAndVID[vnicTaggedVlanGroup.ID][vnicDvPortgroupDataVlanID]) - // vnicTaggedVlans = append(vnicTaggedVlans, &objects.Vlan{ - // Name: fmt.Sprintf("%s-%d", vnicDvPortgroupData.Name, vnicDvPortgroupDataVlanId), - // Vid: vnicDvPortgroupDataVlanId, - // Tenant: nbHost.Tenant, - // }) - } + // }) } - return &objects.Interface{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - Description: vnicDescription, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, - }, - Device: nbHost, - Name: vnicName, - Status: true, - Type: &objects.VirtualInterfaceType, - MTU: int(vnic.Spec.Mtu), - Mode: vnicMode, - TaggedVlans: vnicTaggedVlans, - UntaggedVlan: vnicUntaggedVlan, - }, nil } + return &objects.Interface{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + Description: vnicDescription, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, + }, + }, + Device: nbHost, + Name: vnicName, + Status: true, + Type: &objects.VirtualInterfaceType, + MTU: int(vnic.Spec.Mtu), + Mode: vnicMode, + TaggedVlans: vnicTaggedVlans, + UntaggedVlan: vnicUntaggedVlan, + }, nil } func (vc *VmwareSource) syncVms(nbi *inventory.NetboxInventory) error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - for vmKey, vm := range vc.Vms { - // Check if vm is a template, we don't add templates into netbox. - if vm.Config != nil { - if vm.Config.Template { - continue - } + for vmKey, vm := range vc.Vms { + // Check if vm is a template, we don't add templates into netbox. + if vm.Config != nil { + if vm.Config.Template { + continue } + } - vmName := vm.Name - vmHostName := vc.Hosts[vc.VM2Host[vmKey]].Name + vmName := vm.Name + vmHostName := vc.Hosts[vc.VM2Host[vmKey]].Name - // Tenant is received from VmTenantRelations - vmTenant, err := common.MatchVMToTenant(nbi, vmName, vc.VMTenantRelations) - if err != nil { - return fmt.Errorf("vm's Tenant: %s", err) - } + // Tenant is received from VmTenantRelations + vmTenant, err := common.MatchVMToTenant(nbi, vmName, vc.VMTenantRelations) + if err != nil { + return fmt.Errorf("vm's Tenant: %s", err) + } - // Site is the same as the Host - vmSite, err := common.MatchHostToSite(nbi, vmHostName, vc.HostSiteRelations) - if err != nil { - return fmt.Errorf("vm's Site: %s", err) - } - vmHost := nbi.DevicesIndexByNameAndSiteID[vmHostName][vmSite.ID] + // Site is the same as the Host + vmSite, err := common.MatchHostToSite(nbi, vmHostName, vc.HostSiteRelations) + if err != nil { + return fmt.Errorf("vm's Site: %s", err) + } + vmHost := nbi.DevicesIndexByNameAndSiteID[vmHostName][vmSite.ID] - // Cluster of the vm is same as the host - vmCluster := vmHost.Cluster + // Cluster of the vm is same as the host + vmCluster := vmHost.Cluster - // VM status - vmStatus := &objects.VMStatusOffline - vmPowerState := vm.Runtime.PowerState - if vmPowerState == types.VirtualMachinePowerStatePoweredOn { - vmStatus = &objects.VMStatusActive - } + // VM status + vmStatus := &objects.VMStatusOffline + vmPowerState := vm.Runtime.PowerState + if vmPowerState == types.VirtualMachinePowerStatePoweredOn { + vmStatus = &objects.VMStatusActive + } - // vmVCPUs - vmVCPUs := vm.Config.Hardware.NumCPU + // vmVCPUs + vmVCPUs := vm.Config.Hardware.NumCPU - // vmMemory - vmMemory := vm.Config.Hardware.MemoryMB + // vmMemory + vmMemory := vm.Config.Hardware.MemoryMB - // DisksSize - vmDiskSizeB := int64(0) - for _, hwDevice := range vm.Config.Hardware.Device { - if disk, ok := hwDevice.(*types.VirtualDisk); ok { - vmDiskSizeB += disk.CapacityInBytes - } + // DisksSize + vmDiskSizeB := int64(0) + for _, hwDevice := range vm.Config.Hardware.Device { + if disk, ok := hwDevice.(*types.VirtualDisk); ok { + vmDiskSizeB += disk.CapacityInBytes } + } - // vmPlatform - vmPlatformName := vm.Config.GuestFullName - if vmPlatformName == "" { - vmPlatformName = vm.Guest.GuestFullName - } - if vmPlatformName == "" { - vmPlatformName = utils.GeneratePlatformName(constants.DefaultOSName, constants.DefaultOSVersion) - } - vmPlatform, err := nbi.AddPlatform(vc.Ctx, &objects.Platform{ - Name: vmPlatformName, - Slug: utils.Slugify(vmPlatformName), - }) - if err != nil { - return fmt.Errorf("failed adding vmware vm's Platform %v with error: %s", vmPlatform, err) - } + // vmPlatform + vmPlatformName := vm.Config.GuestFullName + if vmPlatformName == "" { + vmPlatformName = vm.Guest.GuestFullName + } + if vmPlatformName == "" { + vmPlatformName = utils.GeneratePlatformName(constants.DefaultOSName, constants.DefaultOSVersion) + } + vmPlatform, err := nbi.AddPlatform(vc.Ctx, &objects.Platform{ + Name: vmPlatformName, + Slug: utils.Slugify(vmPlatformName), + }) + if err != nil { + return fmt.Errorf("failed adding vmware vm's Platform %v with error: %s", vmPlatform, err) + } - // Extract additional info from CustomFields - var vmOwners []string - var vmOwnerEmails []string - var vmDescription string - vmCustomFields := map[string]string{} - if len(vm.Summary.CustomValue) > 0 { - for _, field := range vm.Summary.CustomValue { - if field, ok := field.(*types.CustomFieldStringValue); ok { - fieldName := vc.CustomFieldID2Name[field.Key] - - if mappedField, ok := vc.CustomFieldMappings[fieldName]; ok { - switch mappedField { - case "owner": - vmOwners = strings.Split(field.Value, ",") - case "email": - vmOwnerEmails = strings.Split(field.Value, ",") - case "description": - vmDescription = strings.TrimSpace(field.Value) - } - } else { - fieldName = utils.Alphanumeric(fieldName) - if _, ok := nbi.CustomFieldsIndexByName[fieldName]; !ok { - err := nbi.AddCustomField(vc.Ctx, &objects.CustomField{ - Name: fieldName, - Type: objects.CustomFieldTypeText, - CustomFieldUIVisible: &objects.CustomFieldUIVisibleIfSet, - CustomFieldUIEditable: &objects.CustomFieldUIEditableYes, - ContentTypes: []string{"virtualization.virtualmachine"}, - }) - if err != nil { - return fmt.Errorf("vm's custom field %s: %s", fieldName, err) - } + // Extract additional info from CustomFields + var vmOwners []string + var vmOwnerEmails []string + var vmDescription string + vmCustomFields := map[string]string{} + if len(vm.Summary.CustomValue) > 0 { + for _, field := range vm.Summary.CustomValue { + if field, ok := field.(*types.CustomFieldStringValue); ok { + fieldName := vc.CustomFieldID2Name[field.Key] + + if mappedField, ok := vc.CustomFieldMappings[fieldName]; ok { + switch mappedField { + case "owner": + vmOwners = strings.Split(field.Value, ",") + case "email": + vmOwnerEmails = strings.Split(field.Value, ",") + case "description": + vmDescription = strings.TrimSpace(field.Value) + } + } else { + fieldName = utils.Alphanumeric(fieldName) + if _, ok := nbi.CustomFieldsIndexByName[fieldName]; !ok { + err := nbi.AddCustomField(vc.Ctx, &objects.CustomField{ + Name: fieldName, + Type: objects.CustomFieldTypeText, + CustomFieldUIVisible: &objects.CustomFieldUIVisibleIfSet, + CustomFieldUIEditable: &objects.CustomFieldUIEditableYes, + ContentTypes: []string{"virtualization.virtualmachine"}, + }) + if err != nil { + return fmt.Errorf("vm's custom field %s: %s", fieldName, err) } - vmCustomFields[fieldName] = field.Value } + vmCustomFields[fieldName] = field.Value } } } - vmCustomFields[constants.CustomFieldSourceName] = vc.SourceConfig.Name - vmCustomFields[constants.CustomFieldSourceIDName] = vm.Self.Value - - // netbox description has constraint <= len(200 characters) - // In this case we make a comment - var vmComments string - if len(vmDescription) >= objects.MaxDescriptionLength { - vmDescription = "See comments." - vmComments = vmDescription - } + } + vmCustomFields[constants.CustomFieldSourceName] = vc.SourceConfig.Name + vmCustomFields[constants.CustomFieldSourceIDName] = vm.Self.Value + + // netbox description has constraint <= len(200 characters) + // In this case we make a comment + var vmComments string + if len(vmDescription) >= objects.MaxDescriptionLength { + vmDescription = "See comments." + vmComments = vmDescription + } - newVM, err := nbi.AddVM(vc.Ctx, &objects.VM{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - Description: vmDescription, - CustomFields: vmCustomFields, - }, - Name: vmName, - Cluster: vmCluster, - Site: vmSite, - Tenant: vmTenant, - Status: vmStatus, - Host: vmHost, - Platform: vmPlatform, - VCPUs: float32(vmVCPUs), - Memory: int(vmMemory), // MBs - Disk: int(vmDiskSizeB / constants.KiB / constants.KiB / constants.KiB), // GBs - Comments: vmComments, - }) + newVM, err := nbi.AddVM(vc.Ctx, &objects.VM{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + Description: vmDescription, + CustomFields: vmCustomFields, + }, + Name: vmName, + Cluster: vmCluster, + Site: vmSite, + Tenant: vmTenant, + Status: vmStatus, + Host: vmHost, + Platform: vmPlatform, + VCPUs: float32(vmVCPUs), + Memory: int(vmMemory), // MBs + Disk: int(vmDiskSizeB / constants.KiB / constants.KiB / constants.KiB), // GBs + Comments: vmComments, + }) - if err != nil { - return fmt.Errorf("failed to sync vmware vm: %v", err) - } + if err != nil { + return fmt.Errorf("failed to sync vmware vm: %v", err) + } - err = vc.addVMContact(nbi, newVM, vmOwners, vmOwnerEmails) - if err != nil { - return fmt.Errorf("adding vm's contact: %s", err) - } + err = vc.addVMContact(nbi, newVM, vmOwners, vmOwnerEmails) + if err != nil { + return fmt.Errorf("adding vm's contact: %s", err) + } - // Sync vm interfaces - err = vc.syncVMInterfaces(nbi, vm, newVM) - if err != nil { - return fmt.Errorf("failed to sync vmware vm's interfaces: %v", err) - } + // Sync vm interfaces + err = vc.syncVMInterfaces(nbi, vm, newVM) + if err != nil { + return fmt.Errorf("failed to sync vmware vm's interfaces: %v", err) } } return nil @@ -824,280 +774,262 @@ func (vc *VmwareSource) syncVms(nbi *inventory.NetboxInventory) error { // Syncs VM's interfaces to Netbox. func (vc *VmwareSource) syncVMInterfaces(nbi *inventory.NetboxInventory, vmwareVM mo.VirtualMachine, netboxVM *objects.VM) error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - // Data to determine the primary IP address of the vm - var vmDefaultGatewayIpv4 string - var vmDefaultGatewayIpv6 string - vmIPv4Addresses := make([]*objects.IPAddress, 0) - vmIPv6Addresses := make([]*objects.IPAddress, 0) - - // From vm's routing determine the default interface - if len(vmwareVM.Guest.IpStack) > 0 { - for _, route := range vmwareVM.Guest.IpStack[0].IpRouteConfig.IpRoute { - if route.PrefixLength == 0 { - ipAddress := route.Network - if ipAddress == "" { - continue - } - gatewayIPAddress := route.Gateway.IpAddress - if gatewayIPAddress == "" { - continue - } + // Data to determine the primary IP address of the vm + var vmDefaultGatewayIpv4 string + var vmDefaultGatewayIpv6 string + vmIPv4Addresses := make([]*objects.IPAddress, 0) + vmIPv6Addresses := make([]*objects.IPAddress, 0) + + // From vm's routing determine the default interface + if len(vmwareVM.Guest.IpStack) > 0 { + for _, route := range vmwareVM.Guest.IpStack[0].IpRouteConfig.IpRoute { + if route.PrefixLength == 0 { + ipAddress := route.Network + if ipAddress == "" { + continue + } + gatewayIPAddress := route.Gateway.IpAddress + if gatewayIPAddress == "" { + continue + } - // Get version from ipAddress (v4 or v6) - ipVersion := utils.GetIPVersion(ipAddress) - if ipVersion == constants.IPv4 { - vmDefaultGatewayIpv4 = gatewayIPAddress - } else if ipVersion == constants.IPv6 { - vmDefaultGatewayIpv6 = gatewayIPAddress - } + // Get version from ipAddress (v4 or v6) + ipVersion := utils.GetIPVersion(ipAddress) + if ipVersion == constants.IPv4 { + vmDefaultGatewayIpv4 = gatewayIPAddress + } else if ipVersion == constants.IPv6 { + vmDefaultGatewayIpv6 = gatewayIPAddress } } } + } - for _, vmDevice := range vmwareVM.Config.Hardware.Device { - // TODO: Refactor this to avoid hardcoded typecasting. Ensure all types - // that compose VirtualEthernetCard are properly handled. - var vmEthernetCard *types.VirtualEthernetCard - switch v := vmDevice.(type) { - case *types.VirtualPCNet32: - vmEthernetCard = &v.VirtualEthernetCard - case *types.VirtualVmxnet3: - vmEthernetCard = &v.VirtualEthernetCard - case *types.VirtualVmxnet2: - vmEthernetCard = &v.VirtualEthernetCard - case *types.VirtualVmxnet: - vmEthernetCard = &v.VirtualEthernetCard - case *types.VirtualE1000e: - vmEthernetCard = &v.VirtualEthernetCard - case *types.VirtualE1000: - vmEthernetCard = &v.VirtualEthernetCard - case *types.VirtualSriovEthernetCard: - vmEthernetCard = &v.VirtualEthernetCard - case *types.VirtualEthernetCard: - vmEthernetCard = v - default: - continue - } - - if vmEthernetCard != nil { - nicIPv4Addresses, nicIPv6Addresses, collectedVMIface, err := vc.collectVMInterfaceData(nbi, netboxVM, vmwareVM, vmEthernetCard) - if err != nil { - return err - } + for _, vmDevice := range vmwareVM.Config.Hardware.Device { + // TODO: Refactor this to avoid hardcoded typecasting. Ensure all types + // that compose VirtualEthernetCard are properly handled. + var vmEthernetCard *types.VirtualEthernetCard + switch v := vmDevice.(type) { + case *types.VirtualPCNet32: + vmEthernetCard = &v.VirtualEthernetCard + case *types.VirtualVmxnet3: + vmEthernetCard = &v.VirtualEthernetCard + case *types.VirtualVmxnet2: + vmEthernetCard = &v.VirtualEthernetCard + case *types.VirtualVmxnet: + vmEthernetCard = &v.VirtualEthernetCard + case *types.VirtualE1000e: + vmEthernetCard = &v.VirtualEthernetCard + case *types.VirtualE1000: + vmEthernetCard = &v.VirtualEthernetCard + case *types.VirtualSriovEthernetCard: + vmEthernetCard = &v.VirtualEthernetCard + case *types.VirtualEthernetCard: + vmEthernetCard = v + default: + continue + } - nbVMInterface, err := nbi.AddVMInterface(vc.Ctx, collectedVMIface) - if err != nil { - return fmt.Errorf("adding VmInterface: %s", err) - } + if vmEthernetCard != nil { + nicIPv4Addresses, nicIPv6Addresses, collectedVMIface, err := vc.collectVMInterfaceData(nbi, netboxVM, vmwareVM, vmEthernetCard) + if err != nil { + return err + } - vmIPv4Addresses, vmIPv6Addresses, err = vc.addVMInterfaceIPs(nbi, nbVMInterface, nicIPv4Addresses, nicIPv6Addresses, vmIPv4Addresses, vmIPv6Addresses) - if err != nil { - return err - } + nbVMInterface, err := nbi.AddVMInterface(vc.Ctx, collectedVMIface) + if err != nil { + return fmt.Errorf("adding VmInterface: %s", err) } + + vmIPv4Addresses, vmIPv6Addresses = vc.addVMInterfaceIPs(nbi, nbVMInterface, nicIPv4Addresses, nicIPv6Addresses, vmIPv4Addresses, vmIPv6Addresses) } - err := vc.setVMPrimaryIPAddress(nbi, netboxVM, vmDefaultGatewayIpv4, vmDefaultGatewayIpv6, vmIPv4Addresses, vmIPv6Addresses) - if err != nil { - return fmt.Errorf("setting vm primary ip address: %s", err) - } - return nil } + err := vc.setVMPrimaryIPAddress(nbi, netboxVM, vmDefaultGatewayIpv4, vmDefaultGatewayIpv6, vmIPv4Addresses, vmIPv6Addresses) + if err != nil { + return fmt.Errorf("setting vm primary ip address: %s", err) + } + return nil } func (vc *VmwareSource) collectVMInterfaceData(nbi *inventory.NetboxInventory, netboxVM *objects.VM, vmwareVM mo.VirtualMachine, vmEthernetCard *types.VirtualEthernetCard) ([]string, []string, *objects.VMInterface, error) { - select { - case <-vc.Ctx.Done(): - return nil, nil, nil, fmt.Errorf("goroutine ended with context") - default: - intMac := vmEthernetCard.MacAddress - intConnected := vmEthernetCard.Connectable.Connected - intDeviceBackingInfo := vmEthernetCard.Backing - intDeviceInfo := vmEthernetCard.DeviceInfo - nicIPv4Addresses := []string{} - nicIPv6Addresses := []string{} - var intMtu int - var intNetworkName string - var intNetworkPrivate bool - var intMode *objects.VMInterfaceMode - intNetworkVlanIDs := []int{} - intNetworkVlanIDRanges := []string{} - - // Get info from local vSwitches if possible, else from DistributedPortGroup - if backingInfo, ok := intDeviceBackingInfo.(*types.VirtualEthernetCardNetworkBackingInfo); ok { - intNetworkName = backingInfo.DeviceName - intHostPgroup := vc.Networks.HostPortgroups[netboxVM.Host.Name][intNetworkName] - - if intHostPgroup != nil { - intNetworkVlanIDs = []int{intHostPgroup.vlanID} - intNetworkVlanIDRanges = []string{strconv.Itoa(intHostPgroup.vlanID)} - intVswitchName := intHostPgroup.vswitch - intVswitchData := vc.Networks.HostVirtualSwitches[netboxVM.Host.Name][intVswitchName] - if intVswitchData != nil { - intMtu = intVswitchData.mtu - } - } - } else if backingInfo, ok := intDeviceBackingInfo.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo); ok { - dvsPortgroupKey := backingInfo.Port.PortgroupKey - intPortgroupData := vc.Networks.DistributedVirtualPortgroups[dvsPortgroupKey] - - if intPortgroupData != nil { - intNetworkName = intPortgroupData.Name - intNetworkVlanIDs = intPortgroupData.VlanIDs - intNetworkVlanIDRanges = intPortgroupData.VlanIDRanges - if len(intNetworkVlanIDRanges) == 0 { - intNetworkVlanIDRanges = []string{strconv.Itoa(intNetworkVlanIDs[0])} - } - intNetworkPrivate = intPortgroupData.Private + intMac := vmEthernetCard.MacAddress + intConnected := vmEthernetCard.Connectable.Connected + intDeviceBackingInfo := vmEthernetCard.Backing + intDeviceInfo := vmEthernetCard.DeviceInfo + nicIPv4Addresses := []string{} + nicIPv6Addresses := []string{} + var intMtu int + var intNetworkName string + var intNetworkPrivate bool + var intMode *objects.VMInterfaceMode + intNetworkVlanIDs := []int{} + intNetworkVlanIDRanges := []string{} + + // Get info from local vSwitches if possible, else from DistributedPortGroup + if backingInfo, ok := intDeviceBackingInfo.(*types.VirtualEthernetCardNetworkBackingInfo); ok { + intNetworkName = backingInfo.DeviceName + intHostPgroup := vc.Networks.HostPortgroups[netboxVM.Host.Name][intNetworkName] + + if intHostPgroup != nil { + intNetworkVlanIDs = []int{intHostPgroup.vlanID} + intNetworkVlanIDRanges = []string{strconv.Itoa(intHostPgroup.vlanID)} + intVswitchName := intHostPgroup.vswitch + intVswitchData := vc.Networks.HostVirtualSwitches[netboxVM.Host.Name][intVswitchName] + if intVswitchData != nil { + intMtu = intVswitchData.mtu } + } + } else if backingInfo, ok := intDeviceBackingInfo.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo); ok { + dvsPortgroupKey := backingInfo.Port.PortgroupKey + intPortgroupData := vc.Networks.DistributedVirtualPortgroups[dvsPortgroupKey] + + if intPortgroupData != nil { + intNetworkName = intPortgroupData.Name + intNetworkVlanIDs = intPortgroupData.VlanIDs + intNetworkVlanIDRanges = intPortgroupData.VlanIDRanges + if len(intNetworkVlanIDRanges) == 0 { + intNetworkVlanIDRanges = []string{strconv.Itoa(intNetworkVlanIDs[0])} + } + intNetworkPrivate = intPortgroupData.Private + } - intDvswitchUUID := backingInfo.Port.SwitchUuid - intDvswitchData := vc.Networks.HostProxySwitches[netboxVM.Host.Name][intDvswitchUUID] + intDvswitchUUID := backingInfo.Port.SwitchUuid + intDvswitchData := vc.Networks.HostProxySwitches[netboxVM.Host.Name][intDvswitchUUID] - if intDvswitchData != nil { - intMtu = intDvswitchData.mtu - } + if intDvswitchData != nil { + intMtu = intDvswitchData.mtu } + } - var vlanDescription string - intLabel := intDeviceInfo.GetDescription().Label - splitStr := strings.Split(intLabel, " ") - intName := fmt.Sprintf("vNic %s", splitStr[len(splitStr)-1]) - intFullName := intName - if intNetworkName != "" { - intFullName = fmt.Sprintf("%s (%s)", intFullName, intNetworkName) - } - intDescription := intLabel - if len(intNetworkVlanIDs) > 0 { - if len(intNetworkVlanIDs) == 1 && intNetworkVlanIDs[0] == 4095 { - vlanDescription = "all vlans" - intMode = &objects.VMInterfaceModeTaggedAll + var vlanDescription string + intLabel := intDeviceInfo.GetDescription().Label + splitStr := strings.Split(intLabel, " ") + intName := fmt.Sprintf("vNic %s", splitStr[len(splitStr)-1]) + intFullName := intName + if intNetworkName != "" { + intFullName = fmt.Sprintf("%s (%s)", intFullName, intNetworkName) + } + intDescription := intLabel + if len(intNetworkVlanIDs) > 0 { + if len(intNetworkVlanIDs) == 1 && intNetworkVlanIDs[0] == 4095 { + vlanDescription = "all vlans" + intMode = &objects.VMInterfaceModeTaggedAll + } else { + vlanDescription = fmt.Sprintf("vlan ID: %s", strings.Join(intNetworkVlanIDRanges, ", ")) + if len(intNetworkVlanIDs) == 1 { + intMode = &objects.VMInterfaceModeAccess } else { - vlanDescription = fmt.Sprintf("vlan ID: %s", strings.Join(intNetworkVlanIDRanges, ", ")) - if len(intNetworkVlanIDs) == 1 { - intMode = &objects.VMInterfaceModeAccess - } else { - intMode = &objects.VMInterfaceModeTagged - } + intMode = &objects.VMInterfaceModeTagged } + } - if intNetworkPrivate { - vlanDescription += "(private)" - } - intDescription = fmt.Sprintf("%s (%s)", intDescription, vlanDescription) + if intNetworkPrivate { + vlanDescription += "(private)" } - // Find corresponding guest NIC and get IP addresses and connected status - for _, guestNic := range vmwareVM.Guest.Net { - if intMac != guestNic.MacAddress { - continue - } - intConnected = guestNic.Connected - - if guestNic.IpConfig != nil { - for _, intIP := range guestNic.IpConfig.IpAddress { - intIPAddress := fmt.Sprintf("%s/%d", intIP.IpAddress, intIP.PrefixLength) - ipVersion := utils.GetIPVersion(intIP.IpAddress) - switch ipVersion { - case constants.IPv4: - nicIPv4Addresses = append(nicIPv4Addresses, intIPAddress) - case constants.IPv6: - nicIPv6Addresses = append(nicIPv6Addresses, intIPAddress) - default: - return nicIPv4Addresses, nicIPv6Addresses, nil, fmt.Errorf("unknown ip version: %s", intIPAddress) - } + intDescription = fmt.Sprintf("%s (%s)", intDescription, vlanDescription) + } + // Find corresponding guest NIC and get IP addresses and connected status + for _, guestNic := range vmwareVM.Guest.Net { + if intMac != guestNic.MacAddress { + continue + } + intConnected = guestNic.Connected + + if guestNic.IpConfig != nil { + for _, intIP := range guestNic.IpConfig.IpAddress { + intIPAddress := fmt.Sprintf("%s/%d", intIP.IpAddress, intIP.PrefixLength) + ipVersion := utils.GetIPVersion(intIP.IpAddress) + switch ipVersion { + case constants.IPv4: + nicIPv4Addresses = append(nicIPv4Addresses, intIPAddress) + case constants.IPv6: + nicIPv6Addresses = append(nicIPv6Addresses, intIPAddress) + default: + return nicIPv4Addresses, nicIPv6Addresses, nil, fmt.Errorf("unknown ip version: %s", intIPAddress) } } } - var intUntaggedVlan *objects.Vlan - var intTaggedVlanList []*objects.Vlan - if len(intNetworkVlanIDs) > 0 && intMode != &objects.VMInterfaceModeTaggedAll { - if len(intNetworkVlanIDs) == 1 && intNetworkVlanIDs[0] != 0 { - vidID := intNetworkVlanIDs[0] - nicUntaggedVlanGroup, err := common.MatchVlanToGroup(nbi, vc.Networks.Vid2Name[vidID], vc.VlanGroupRelations) - if err != nil { - return nicIPv4Addresses, nicIPv6Addresses, nil, fmt.Errorf("vlan group: %s", err) - } - intUntaggedVlan = nbi.VlansIndexByVlanGroupIDAndVID[nicUntaggedVlanGroup.ID][vidID] - } else { - intTaggedVlanList = []*objects.Vlan{} - for _, intNetworkVlanID := range intNetworkVlanIDs { - if intNetworkVlanID == 0 { - continue - } - // nicTaggedVlanList = append(nicTaggedVlanList, nbi.get[intNetworkVlanId]) + } + var intUntaggedVlan *objects.Vlan + var intTaggedVlanList []*objects.Vlan + if len(intNetworkVlanIDs) > 0 && intMode != &objects.VMInterfaceModeTaggedAll { + if len(intNetworkVlanIDs) == 1 && intNetworkVlanIDs[0] != 0 { + vidID := intNetworkVlanIDs[0] + nicUntaggedVlanGroup, err := common.MatchVlanToGroup(nbi, vc.Networks.Vid2Name[vidID], vc.VlanGroupRelations) + if err != nil { + return nicIPv4Addresses, nicIPv6Addresses, nil, fmt.Errorf("vlan group: %s", err) + } + intUntaggedVlan = nbi.VlansIndexByVlanGroupIDAndVID[nicUntaggedVlanGroup.ID][vidID] + } else { + intTaggedVlanList = []*objects.Vlan{} + for _, intNetworkVlanID := range intNetworkVlanIDs { + if intNetworkVlanID == 0 { + continue } + // nicTaggedVlanList = append(nicTaggedVlanList, nbi.get[intNetworkVlanId]) } } - return nicIPv4Addresses, nicIPv6Addresses, &objects.VMInterface{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - Description: intDescription, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, - }, - VM: netboxVM, - Name: intFullName, - MACAddress: strings.ToUpper(intMac), - MTU: intMtu, - Mode: intMode, - Enabled: intConnected, - TaggedVlans: intTaggedVlanList, - UntaggedVlan: intUntaggedVlan, - }, nil } + return nicIPv4Addresses, nicIPv6Addresses, &objects.VMInterface{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + Description: intDescription, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, + }, + }, + VM: netboxVM, + Name: intFullName, + MACAddress: strings.ToUpper(intMac), + MTU: intMtu, + Mode: intMode, + Enabled: intConnected, + TaggedVlans: intTaggedVlanList, + UntaggedVlan: intUntaggedVlan, + }, nil } // Function that adds all collected IPs for the vm's interface to netbox. -func (vc *VmwareSource) addVMInterfaceIPs(nbi *inventory.NetboxInventory, nbVMInterface *objects.VMInterface, nicIPv4Addresses []string, nicIPv6Addresses []string, vmIPv4Addresses []*objects.IPAddress, vmIPv6Addresses []*objects.IPAddress) ([]*objects.IPAddress, []*objects.IPAddress, error) { - select { - case <-vc.Ctx.Done(): - return nil, nil, fmt.Errorf("goroutine ended with context") - default: - // Add all collected ipv4 addresses for the interface to netbox - for _, ipv4Address := range nicIPv4Addresses { - nbIPv4Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, +func (vc *VmwareSource) addVMInterfaceIPs(nbi *inventory.NetboxInventory, nbVMInterface *objects.VMInterface, nicIPv4Addresses []string, nicIPv6Addresses []string, vmIPv4Addresses []*objects.IPAddress, vmIPv6Addresses []*objects.IPAddress) ([]*objects.IPAddress, []*objects.IPAddress) { + // Add all collected ipv4 addresses for the interface to netbox + for _, ipv4Address := range nicIPv4Addresses { + nbIPv4Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, }, - Address: ipv4Address, - DNSName: utils.ReverseLookup(ipv4Address), - AssignedObjectType: objects.AssignedObjectTypeVMInterface, - AssignedObjectID: nbVMInterface.ID, - }) - if err != nil { - vc.Logger.Warningf(vc.Ctx, "adding ipv4 address: %s", err) - } - vmIPv4Addresses = append(vmIPv4Addresses, nbIPv4Address) + }, + Address: ipv4Address, + DNSName: utils.ReverseLookup(ipv4Address), + AssignedObjectType: objects.AssignedObjectTypeVMInterface, + AssignedObjectID: nbVMInterface.ID, + }) + if err != nil { + vc.Logger.Warningf(vc.Ctx, "adding ipv4 address: %s", err) } + vmIPv4Addresses = append(vmIPv4Addresses, nbIPv4Address) + } - // Add all collected ipv6 addresses for the interface to netbox - for _, ipv6Address := range nicIPv6Addresses { - nbIPv6Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ - NetboxObject: objects.NetboxObject{ - Tags: vc.Config.SourceTags, - CustomFields: map[string]string{ - constants.CustomFieldSourceName: vc.SourceConfig.Name, - }, + // Add all collected ipv6 addresses for the interface to netbox + for _, ipv6Address := range nicIPv6Addresses { + nbIPv6Address, err := nbi.AddIPAddress(vc.Ctx, &objects.IPAddress{ + NetboxObject: objects.NetboxObject{ + Tags: vc.Config.SourceTags, + CustomFields: map[string]string{ + constants.CustomFieldSourceName: vc.SourceConfig.Name, }, - Address: ipv6Address, - DNSName: utils.ReverseLookup(ipv6Address), - AssignedObjectType: objects.AssignedObjectTypeVMInterface, - AssignedObjectID: nbVMInterface.ID, - }) - if err != nil { - vc.Logger.Warningf(vc.Ctx, "adding ipv6 address: %s", err) - } - vmIPv6Addresses = append(vmIPv6Addresses, nbIPv6Address) + }, + Address: ipv6Address, + DNSName: utils.ReverseLookup(ipv6Address), + AssignedObjectType: objects.AssignedObjectTypeVMInterface, + AssignedObjectID: nbVMInterface.ID, + }) + if err != nil { + vc.Logger.Warningf(vc.Ctx, "adding ipv6 address: %s", err) } - return vmIPv4Addresses, vmIPv6Addresses, nil + vmIPv6Addresses = append(vmIPv6Addresses, nbIPv6Address) } + return vmIPv4Addresses, vmIPv6Addresses } // setVMPrimaryIPAddress updates the vm's primary IP in the following way: @@ -1105,79 +1037,69 @@ func (vc *VmwareSource) addVMInterfaceIPs(nbi *inventory.NetboxInventory, nbVMIn // If any of the ips is in the same subnet as the default gateway, we choose it. // If there is no ip in the subnet of the default gateway, we choose the first one. func (vc *VmwareSource) setVMPrimaryIPAddress(nbi *inventory.NetboxInventory, netboxVM *objects.VM, vmDefaultGatewayIpv4 string, vmDefaultGatewayIpv6 string, vmIPv4Addresses []*objects.IPAddress, vmIPv6Addresses []*objects.IPAddress) error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - if len(vmIPv4Addresses) > 0 || len(vmIPv6Addresses) > 0 { - var vmIPv4PrimaryAddress *objects.IPAddress - for _, addr := range vmIPv4Addresses { - if vmIPv4PrimaryAddress == nil || utils.SubnetContainsIPAddress(vmDefaultGatewayIpv4, addr.Address) { - vmIPv4PrimaryAddress = addr - } - } - var vmIPv6PrimaryAddress *objects.IPAddress - for _, addr := range vmIPv6Addresses { - if vmIPv6PrimaryAddress == nil || utils.SubnetContainsIPAddress(vmDefaultGatewayIpv6, addr.Address) { - vmIPv6PrimaryAddress = addr - } + if len(vmIPv4Addresses) > 0 || len(vmIPv6Addresses) > 0 { + var vmIPv4PrimaryAddress *objects.IPAddress + for _, addr := range vmIPv4Addresses { + if vmIPv4PrimaryAddress == nil || utils.SubnetContainsIPAddress(vmDefaultGatewayIpv4, addr.Address) { + vmIPv4PrimaryAddress = addr } - newNetboxVM := *netboxVM - newNetboxVM.PrimaryIPv4 = vmIPv4PrimaryAddress - newNetboxVM.PrimaryIPv6 = vmIPv6PrimaryAddress - _, err := nbi.AddVM(vc.Ctx, &newNetboxVM) - if err != nil { - return fmt.Errorf("updating vm's primary ip: %s", err) + } + var vmIPv6PrimaryAddress *objects.IPAddress + for _, addr := range vmIPv6Addresses { + if vmIPv6PrimaryAddress == nil || utils.SubnetContainsIPAddress(vmDefaultGatewayIpv6, addr.Address) { + vmIPv6PrimaryAddress = addr } } - return nil + newNetboxVM := *netboxVM + newNetboxVM.PrimaryIPv4 = vmIPv4PrimaryAddress + newNetboxVM.PrimaryIPv6 = vmIPv6PrimaryAddress + _, err := nbi.AddVM(vc.Ctx, &newNetboxVM) + if err != nil { + return fmt.Errorf("updating vm's primary ip: %s", err) + } } + return nil } func (vc *VmwareSource) addVMContact(nbi *inventory.NetboxInventory, nbVM *objects.VM, vmOwners []string, vmOwnerEmails []string) error { - select { - case <-vc.Ctx.Done(): - return fmt.Errorf("goroutine ended with context") - default: - // If vm owner name was found we also add contact assignment to the vm - var vmMailMapFallback bool - if len(vmOwners) > 0 && len(vmOwnerEmails) > 0 && len(vmOwners) != len(vmOwnerEmails) { - vc.Logger.Warningf(vc.Ctx, "vm owner names and emails mismatch (len(vmOwnerEmails) != len(vmOwners), using fallback mechanism") - vmMailMapFallback = true - } - vmOwner2Email := utils.MatchNamesWithEmails(vc.Ctx, vmOwners, vmOwnerEmails, vc.Logger) - for i, vmOwnerName := range vmOwners { - if vmOwnerName != "" { - var vmOwnerEmail string - if len(vmOwnerEmails) > 0 { - if vmMailMapFallback { - if match, ok := vmOwner2Email[vmOwnerName]; ok { - vmOwnerEmail = match - } - } else { - vmOwnerEmail = vmOwnerEmails[i] + // If vm owner name was found we also add contact assignment to the vm + var vmMailMapFallback bool + if len(vmOwners) > 0 && len(vmOwnerEmails) > 0 && len(vmOwners) != len(vmOwnerEmails) { + vc.Logger.Warningf(vc.Ctx, "vm owner names and emails mismatch (len(vmOwnerEmails) != len(vmOwners), using fallback mechanism") + vmMailMapFallback = true + } + vmOwner2Email := utils.MatchNamesWithEmails(vc.Ctx, vmOwners, vmOwnerEmails, vc.Logger) + for i, vmOwnerName := range vmOwners { + if vmOwnerName != "" { + var vmOwnerEmail string + if len(vmOwnerEmails) > 0 { + if vmMailMapFallback { + if match, ok := vmOwner2Email[vmOwnerName]; ok { + vmOwnerEmail = match } - } - contact, err := nbi.AddContact( - vc.Ctx, &objects.Contact{ - Name: strings.TrimSpace(vmOwners[i]), - Email: vmOwnerEmail, - }, - ) - if err != nil { - return fmt.Errorf("creating vm contact: %s", err) - } - _, err = nbi.AddContactAssignment(vc.Ctx, &objects.ContactAssignment{ - ContentType: "virtualization.virtualmachine", - ObjectID: nbVM.ID, - Contact: contact, - Role: nbi.ContactRolesIndexByName[objects.AdminContactRoleName], - }) - if err != nil { - return fmt.Errorf("add contact assignment for vm: %s", err) + } else { + vmOwnerEmail = vmOwnerEmails[i] } } + contact, err := nbi.AddContact( + vc.Ctx, &objects.Contact{ + Name: strings.TrimSpace(vmOwners[i]), + Email: vmOwnerEmail, + }, + ) + if err != nil { + return fmt.Errorf("creating vm contact: %s", err) + } + _, err = nbi.AddContactAssignment(vc.Ctx, &objects.ContactAssignment{ + ContentType: "virtualization.virtualmachine", + ObjectID: nbVM.ID, + Contact: contact, + Role: nbi.ContactRolesIndexByName[objects.AdminContactRoleName], + }) + if err != nil { + return fmt.Errorf("add contact assignment for vm: %s", err) + } } - return nil } + return nil }