From f2e71fb0a3944fbee81f95abeac8c3888e1ecca0 Mon Sep 17 00:00:00 2001 From: Stefan Kurek Date: Mon, 24 Jun 2024 15:12:25 -0400 Subject: [PATCH] vcenterreceiver fixes multiple datacenter bug --- ...nterreceiver_fix_multiple_datacenters.yaml | 27 ++++++++++ receiver/vcenterreceiver/client.go | 54 ++++++++++++++----- receiver/vcenterreceiver/client_test.go | 36 +++++++++++-- receiver/vcenterreceiver/scraper.go | 35 +++++++++--- 4 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 .chloggen/vcenterreceiver_fix_multiple_datacenters.yaml diff --git a/.chloggen/vcenterreceiver_fix_multiple_datacenters.yaml b/.chloggen/vcenterreceiver_fix_multiple_datacenters.yaml new file mode 100644 index 0000000000000..8e556140f4b19 --- /dev/null +++ b/.chloggen/vcenterreceiver_fix_multiple_datacenters.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: vcenterreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: vcenterreceiver fixes errors when multiple datacenters in some of the client calls. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [33734] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: | + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/receiver/vcenterreceiver/client.go b/receiver/vcenterreceiver/client.go index 6bf61883dd9b7..fca06406523e4 100644 --- a/receiver/vcenterreceiver/client.go +++ b/receiver/vcenterreceiver/client.go @@ -224,29 +224,55 @@ func (vc *vcenterClient) VMs(ctx context.Context, containerMoRef vt.ManagedObjec return vms, nil } -// ResourcePoolInventoryListObjects returns the ResourcePools (with populated InventoryLists) of the vSphere SDK -func (vc *vcenterClient) ResourcePoolInventoryListObjects(ctx context.Context) ([]*object.ResourcePool, error) { - rps, err := vc.finder.ResourcePoolList(ctx, "*") +// DatacenterInventoryListObjects returns the Datacenters (with populated InventoryLists) of the vSphere SDK +func (vc *vcenterClient) DatacenterInventoryListObjects(ctx context.Context) ([]*object.Datacenter, error) { + dcs, err := vc.finder.DatacenterList(ctx, "*") if err != nil { - return nil, fmt.Errorf("unable to retrieve ResourcePools with InventoryLists: %w", err) + return nil, fmt.Errorf("unable to retrieve Datacenters with InventoryLists: %w", err) } - return rps, nil + return dcs, nil } -// VAppInventoryListObjects returns the vApps (with populated InventoryLists) of the vSphere SDK -func (vc *vcenterClient) VAppInventoryListObjects(ctx context.Context) ([]*object.VirtualApp, error) { - vApps, err := vc.finder.VirtualAppList(ctx, "*") - if err == nil { - return vApps, nil +// ResourcePoolInventoryListObjects returns the ResourcePools (with populated InventoryLists) of the vSphere SDK +func (vc *vcenterClient) ResourcePoolInventoryListObjects( + ctx context.Context, + dcs []*object.Datacenter, +) ([]*object.ResourcePool, error) { + allRPools := []*object.ResourcePool{} + for _, dc := range dcs { + vc.finder.SetDatacenter(dc) + rps, err := vc.finder.ResourcePoolList(ctx, "*") + if err != nil { + return nil, fmt.Errorf("unable to retrieve ResourcePools with InventoryLists for datacenter %s: %w", dc.InventoryPath, err) + } + allRPools = append(allRPools, rps...) } - var notFoundErr *find.NotFoundError - if errors.As(err, ¬FoundErr) { - return []*object.VirtualApp{}, nil + return allRPools, nil +} + +// VAppInventoryListObjects returns the vApps (with populated InventoryLists) of the vSphere SDK +func (vc *vcenterClient) VAppInventoryListObjects( + ctx context.Context, + dcs []*object.Datacenter, +) ([]*object.VirtualApp, error) { + allVApps := []*object.VirtualApp{} + for _, dc := range dcs { + vc.finder.SetDatacenter(dc) + vApps, err := vc.finder.VirtualAppList(ctx, "*") + var notFoundErr *find.NotFoundError + if err == nil { + allVApps = append(allVApps, vApps...) + continue + } + + if !errors.As(err, ¬FoundErr) { + return nil, fmt.Errorf("unable to retrieve vApps with InventoryLists for datacenter %s: %w", dc.InventoryPath, err) + } } - return nil, fmt.Errorf("unable to retrieve vApps with InventoryLists: %w", err) + return allVApps, nil } // PerfMetricsQueryResult contains performance metric related data diff --git a/receiver/vcenterreceiver/client_test.go b/receiver/vcenterreceiver/client_test.go index 1576b4f2da3af..7cb30b7e60f16 100644 --- a/receiver/vcenterreceiver/client_test.go +++ b/receiver/vcenterreceiver/client_test.go @@ -219,17 +219,36 @@ func TestPerfMetricsQuery(t *testing.T) { }, esx) } +func TestDatacenterInventoryListObjects(t *testing.T) { + vpx := simulator.VPX() + vpx.Datacenter = 2 + simulator.Test(func(ctx context.Context, c *vim25.Client) { + finder := find.NewFinder(c) + client := vcenterClient{ + vimDriver: c, + finder: finder, + } + dcs, err := client.DatacenterInventoryListObjects(ctx) + require.NoError(t, err) + require.Equal(t, len(dcs), 2) + }, vpx) +} + func TestResourcePoolInventoryListObjects(t *testing.T) { + vpx := simulator.VPX() + vpx.Datacenter = 2 simulator.Test(func(ctx context.Context, c *vim25.Client) { finder := find.NewFinder(c) client := vcenterClient{ vimDriver: c, finder: finder, } - rps, err := client.ResourcePoolInventoryListObjects(ctx) + dcs, err := finder.DatacenterList(ctx, "*") + require.NoError(t, err) + rps, err := client.ResourcePoolInventoryListObjects(ctx, dcs) require.NoError(t, err) require.NotEmpty(t, rps, 0) - }) + }, vpx) } func TestVAppInventoryListObjects(t *testing.T) { @@ -237,6 +256,7 @@ func TestVAppInventoryListObjects(t *testing.T) { // vApps appears to be broken t.Skip() vpx := simulator.VPX() + vpx.Datacenter = 2 vpx.App = 2 simulator.Test(func(ctx context.Context, c *vim25.Client) { finder := find.NewFinder(c) @@ -244,23 +264,29 @@ func TestVAppInventoryListObjects(t *testing.T) { vimDriver: c, finder: finder, } - vApps, err := client.VAppInventoryListObjects(ctx) + dcs, err := finder.DatacenterList(ctx, "*") + require.NoError(t, err) + vApps, err := client.VAppInventoryListObjects(ctx, dcs) require.NoError(t, err) require.NotEmpty(t, vApps, 0) }, vpx) } func TestEmptyVAppInventoryListObjects(t *testing.T) { + vpx := simulator.VPX() + vpx.Datacenter = 2 simulator.Test(func(ctx context.Context, c *vim25.Client) { finder := find.NewFinder(c) client := vcenterClient{ vimDriver: c, finder: finder, } - vApps, err := client.VAppInventoryListObjects(ctx) + dcs, err := finder.DatacenterList(ctx, "*") + require.NoError(t, err) + vApps, err := client.VAppInventoryListObjects(ctx, dcs) require.NoError(t, err) require.Empty(t, vApps, 0) - }) + }, vpx) } func TestSessionReestablish(t *testing.T) { diff --git a/receiver/vcenterreceiver/scraper.go b/receiver/vcenterreceiver/scraper.go index a99ac707d44a3..a62185b3c092f 100644 --- a/receiver/vcenterreceiver/scraper.go +++ b/receiver/vcenterreceiver/scraper.go @@ -5,6 +5,7 @@ import ( "context" "fmt" + "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" @@ -108,8 +109,9 @@ func (v *vcenterMetricScraper) scrape(ctx context.Context) (pmetric.Metrics, err // scrapeAndProcessAllMetrics collects & converts all relevant resources managed by vCenter to OTEL resources & metrics func (v *vcenterMetricScraper) scrapeAndProcessAllMetrics(ctx context.Context, errs *scrapererror.ScrapeErrors) error { v.scrapeData = newVcenterScrapeData() - v.scrapeResourcePoolInventoryListObjects(ctx, errs) - v.scrapeVAppInventoryListObjects(ctx, errs) + dcObjects := v.scrapeDatacenterInventoryListObjects(ctx, errs) + v.scrapeResourcePoolInventoryListObjects(ctx, dcObjects, errs) + v.scrapeVAppInventoryListObjects(ctx, dcObjects, errs) v.scrapeDatacenters(ctx, errs) for _, dc := range v.scrapeData.datacenters { @@ -128,13 +130,30 @@ func (v *vcenterMetricScraper) scrapeAndProcessAllMetrics(ctx context.Context, e return errs.Combine() } +// scrapeDatacenterInventoryListObjects scrapes and store all Datacenter objects with their InventoryLists +func (v *vcenterMetricScraper) scrapeDatacenterInventoryListObjects( + ctx context.Context, + errs *scrapererror.ScrapeErrors, +) []*object.Datacenter { + // Get Datacenters with InventoryLists and store for later retrieval + dcs, err := v.client.DatacenterInventoryListObjects(ctx) + if err != nil { + errs.AddPartial(1, err) + } + return dcs +} + // scrapeResourcePoolInventoryListObjects scrapes and store all ResourcePool objects with their InventoryLists -func (v *vcenterMetricScraper) scrapeResourcePoolInventoryListObjects(ctx context.Context, errs *scrapererror.ScrapeErrors) { +func (v *vcenterMetricScraper) scrapeResourcePoolInventoryListObjects( + ctx context.Context, + dcs []*object.Datacenter, + errs *scrapererror.ScrapeErrors, +) { // Init for current collection v.scrapeData.rPoolIPathsByRef = make(map[string]*string) // Get ResourcePools with InventoryLists and store for later retrieval - rPools, err := v.client.ResourcePoolInventoryListObjects(ctx) + rPools, err := v.client.ResourcePoolInventoryListObjects(ctx, dcs) if err != nil { errs.AddPartial(1, err) return @@ -145,12 +164,16 @@ func (v *vcenterMetricScraper) scrapeResourcePoolInventoryListObjects(ctx contex } // scrapeVAppInventoryListObjects scrapes and stores all vApp objects with their InventoryLists -func (v *vcenterMetricScraper) scrapeVAppInventoryListObjects(ctx context.Context, errs *scrapererror.ScrapeErrors) { +func (v *vcenterMetricScraper) scrapeVAppInventoryListObjects( + ctx context.Context, + dcs []*object.Datacenter, + errs *scrapererror.ScrapeErrors, +) { // Init for current collection v.scrapeData.vAppIPathsByRef = make(map[string]*string) // Get vApps with InventoryLists and store for later retrieval - vApps, err := v.client.VAppInventoryListObjects(ctx) + vApps, err := v.client.VAppInventoryListObjects(ctx, dcs) if err != nil { errs.AddPartial(1, err) return