diff --git a/.chloggen/fix_vcenter-vapp-attrs-on-vms.yaml b/.chloggen/fix_vcenter-vapp-attrs-on-vms.yaml new file mode 100644 index 000000000000..c95fcd4a8e1c --- /dev/null +++ b/.chloggen/fix_vcenter-vapp-attrs-on-vms.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: "Adds new `vcenter.virtual_app.name` and `vcenter.virtual_app.inventory_path` resource attributes to appropriate VM Resources" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [32557] + +# (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: [user] diff --git a/receiver/vcenterreceiver/client.go b/receiver/vcenterreceiver/client.go index 6d459e164961..14686a5e9acb 100644 --- a/receiver/vcenterreceiver/client.go +++ b/receiver/vcenterreceiver/client.go @@ -5,6 +5,7 @@ package vcenterreceiver // import "github.com/open-telemetry/opentelemetry-colle import ( "context" + "errors" "fmt" "net/url" @@ -112,6 +113,20 @@ func (vc *vcenterClient) ResourcePools(ctx context.Context) ([]*object.ResourceP return rps, err } +// VirtualApps returns the VirtualApps in the vSphere SDK +func (vc *vcenterClient) VirtualApps(ctx context.Context) ([]*object.VirtualApp, error) { + vApps, err := vc.finder.VirtualAppList(ctx, "*") + if err != nil { + var notFoundErr *find.NotFoundError + if errors.As(err, ¬FoundErr) { + return []*object.VirtualApp{}, nil + } + + return nil, fmt.Errorf("unable to retrieve vApps: %w", err) + } + return vApps, err +} + func (vc *vcenterClient) VMs(ctx context.Context) ([]mo.VirtualMachine, error) { v, err := vc.vm.CreateContainerView(ctx, vc.vimDriver.ServiceContent.RootFolder, []string{"VirtualMachine"}, true) if err != nil { @@ -135,6 +150,7 @@ func (vc *vcenterClient) VMs(ctx context.Context) ([]mo.VirtualMachine, error) { "summary.storage.uncommitted", "summary.runtime.host", "resourcePool", + "parentVApp", }, &vms) if err != nil { return nil, fmt.Errorf("unable to retrieve VMs: %w", err) diff --git a/receiver/vcenterreceiver/client_test.go b/receiver/vcenterreceiver/client_test.go index c9f2ae7837f7..426a84e9f541 100644 --- a/receiver/vcenterreceiver/client_test.go +++ b/receiver/vcenterreceiver/client_test.go @@ -47,6 +47,25 @@ func TestGetResourcePools(t *testing.T) { }) } +func TestGetVirtualApps(t *testing.T) { + // Currently some issue with how the simulator creates vApps. + // It is created (VMs show up with it in the inventory path) + // but it is not returned by the finder call in this tested method. + t.Skip() + model := simulator.VPX() + model.App = 1 + simulator.Test(func(ctx context.Context, c *vim25.Client) { + finder := find.NewFinder(c) + client := vcenterClient{ + vimDriver: c, + finder: finder, + } + vApps, err := client.VirtualApps(ctx) + require.NoError(t, err) + require.NotEmpty(t, vApps) + }, model) +} + func TestGetVMs(t *testing.T) { simulator.Test(func(ctx context.Context, c *vim25.Client) { viewManager := view.NewManager(c) diff --git a/receiver/vcenterreceiver/documentation.md b/receiver/vcenterreceiver/documentation.md index 619bcfc09569..01f095513bd4 100644 --- a/receiver/vcenterreceiver/documentation.md +++ b/receiver/vcenterreceiver/documentation.md @@ -471,5 +471,7 @@ The memory utilization of the VM. | vcenter.host.name | The hostname of the vCenter ESXi host. | Any Str | true | | vcenter.resource_pool.inventory_path | The inventory path of the resource pool. | Any Str | true | | vcenter.resource_pool.name | The name of the resource pool. | Any Str | true | +| vcenter.virtual_app.inventory_path | The inventory path of the vApp. | Any Str | false | +| vcenter.virtual_app.name | The name of the vApp. | Any Str | false | | vcenter.vm.id | The instance UUID of the virtual machine. | Any Str | true | | vcenter.vm.name | The name of the virtual machine. | Any Str | true | diff --git a/receiver/vcenterreceiver/internal/metadata/generated_config.go b/receiver/vcenterreceiver/internal/metadata/generated_config.go index 2ce12cd0acb5..98561ed3378c 100644 --- a/receiver/vcenterreceiver/internal/metadata/generated_config.go +++ b/receiver/vcenterreceiver/internal/metadata/generated_config.go @@ -225,6 +225,8 @@ type ResourceAttributesConfig struct { VcenterHostName ResourceAttributeConfig `mapstructure:"vcenter.host.name"` VcenterResourcePoolInventoryPath ResourceAttributeConfig `mapstructure:"vcenter.resource_pool.inventory_path"` VcenterResourcePoolName ResourceAttributeConfig `mapstructure:"vcenter.resource_pool.name"` + VcenterVirtualAppInventoryPath ResourceAttributeConfig `mapstructure:"vcenter.virtual_app.inventory_path"` + VcenterVirtualAppName ResourceAttributeConfig `mapstructure:"vcenter.virtual_app.name"` VcenterVMID ResourceAttributeConfig `mapstructure:"vcenter.vm.id"` VcenterVMName ResourceAttributeConfig `mapstructure:"vcenter.vm.name"` } @@ -249,6 +251,12 @@ func DefaultResourceAttributesConfig() ResourceAttributesConfig { VcenterResourcePoolName: ResourceAttributeConfig{ Enabled: true, }, + VcenterVirtualAppInventoryPath: ResourceAttributeConfig{ + Enabled: false, + }, + VcenterVirtualAppName: ResourceAttributeConfig{ + Enabled: false, + }, VcenterVMID: ResourceAttributeConfig{ Enabled: true, }, diff --git a/receiver/vcenterreceiver/internal/metadata/generated_config_test.go b/receiver/vcenterreceiver/internal/metadata/generated_config_test.go index 8e80f896727b..1702cf3dab73 100644 --- a/receiver/vcenterreceiver/internal/metadata/generated_config_test.go +++ b/receiver/vcenterreceiver/internal/metadata/generated_config_test.go @@ -73,6 +73,8 @@ func TestMetricsBuilderConfig(t *testing.T) { VcenterHostName: ResourceAttributeConfig{Enabled: true}, VcenterResourcePoolInventoryPath: ResourceAttributeConfig{Enabled: true}, VcenterResourcePoolName: ResourceAttributeConfig{Enabled: true}, + VcenterVirtualAppInventoryPath: ResourceAttributeConfig{Enabled: true}, + VcenterVirtualAppName: ResourceAttributeConfig{Enabled: true}, VcenterVMID: ResourceAttributeConfig{Enabled: true}, VcenterVMName: ResourceAttributeConfig{Enabled: true}, }, @@ -129,6 +131,8 @@ func TestMetricsBuilderConfig(t *testing.T) { VcenterHostName: ResourceAttributeConfig{Enabled: false}, VcenterResourcePoolInventoryPath: ResourceAttributeConfig{Enabled: false}, VcenterResourcePoolName: ResourceAttributeConfig{Enabled: false}, + VcenterVirtualAppInventoryPath: ResourceAttributeConfig{Enabled: false}, + VcenterVirtualAppName: ResourceAttributeConfig{Enabled: false}, VcenterVMID: ResourceAttributeConfig{Enabled: false}, VcenterVMName: ResourceAttributeConfig{Enabled: false}, }, @@ -173,6 +177,8 @@ func TestResourceAttributesConfig(t *testing.T) { VcenterHostName: ResourceAttributeConfig{Enabled: true}, VcenterResourcePoolInventoryPath: ResourceAttributeConfig{Enabled: true}, VcenterResourcePoolName: ResourceAttributeConfig{Enabled: true}, + VcenterVirtualAppInventoryPath: ResourceAttributeConfig{Enabled: true}, + VcenterVirtualAppName: ResourceAttributeConfig{Enabled: true}, VcenterVMID: ResourceAttributeConfig{Enabled: true}, VcenterVMName: ResourceAttributeConfig{Enabled: true}, }, @@ -186,6 +192,8 @@ func TestResourceAttributesConfig(t *testing.T) { VcenterHostName: ResourceAttributeConfig{Enabled: false}, VcenterResourcePoolInventoryPath: ResourceAttributeConfig{Enabled: false}, VcenterResourcePoolName: ResourceAttributeConfig{Enabled: false}, + VcenterVirtualAppInventoryPath: ResourceAttributeConfig{Enabled: false}, + VcenterVirtualAppName: ResourceAttributeConfig{Enabled: false}, VcenterVMID: ResourceAttributeConfig{Enabled: false}, VcenterVMName: ResourceAttributeConfig{Enabled: false}, }, diff --git a/receiver/vcenterreceiver/internal/metadata/generated_metrics.go b/receiver/vcenterreceiver/internal/metadata/generated_metrics.go index 5eb8d9bab669..1eff1d22e8d6 100644 --- a/receiver/vcenterreceiver/internal/metadata/generated_metrics.go +++ b/receiver/vcenterreceiver/internal/metadata/generated_metrics.go @@ -2219,6 +2219,12 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSetting if !mbc.ResourceAttributes.VcenterDatacenterName.enabledSetByUser { settings.Logger.Warn("[WARNING] Please set `enabled` field explicitly for `vcenter.datacenter.name`: this attribute will be enabled by default starting in release v0.101.0") } + if !mbc.ResourceAttributes.VcenterVirtualAppInventoryPath.enabledSetByUser { + settings.Logger.Warn("[WARNING] Please set `enabled` field explicitly for `vcenter.virtual_app.inventory_path`: this attribute will be enabled by default starting in release v0.101.0") + } + if !mbc.ResourceAttributes.VcenterVirtualAppName.enabledSetByUser { + settings.Logger.Warn("[WARNING] Please set `enabled` field explicitly for `vcenter.virtual_app.name`: this attribute will be enabled by default starting in release v0.101.0") + } mb := &MetricsBuilder{ config: mbc, startTime: pcommon.NewTimestampFromTime(time.Now()), @@ -2302,6 +2308,18 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSetting if mbc.ResourceAttributes.VcenterResourcePoolName.MetricsExclude != nil { mb.resourceAttributeExcludeFilter["vcenter.resource_pool.name"] = filter.CreateFilter(mbc.ResourceAttributes.VcenterResourcePoolName.MetricsExclude) } + if mbc.ResourceAttributes.VcenterVirtualAppInventoryPath.MetricsInclude != nil { + mb.resourceAttributeIncludeFilter["vcenter.virtual_app.inventory_path"] = filter.CreateFilter(mbc.ResourceAttributes.VcenterVirtualAppInventoryPath.MetricsInclude) + } + if mbc.ResourceAttributes.VcenterVirtualAppInventoryPath.MetricsExclude != nil { + mb.resourceAttributeExcludeFilter["vcenter.virtual_app.inventory_path"] = filter.CreateFilter(mbc.ResourceAttributes.VcenterVirtualAppInventoryPath.MetricsExclude) + } + if mbc.ResourceAttributes.VcenterVirtualAppName.MetricsInclude != nil { + mb.resourceAttributeIncludeFilter["vcenter.virtual_app.name"] = filter.CreateFilter(mbc.ResourceAttributes.VcenterVirtualAppName.MetricsInclude) + } + if mbc.ResourceAttributes.VcenterVirtualAppName.MetricsExclude != nil { + mb.resourceAttributeExcludeFilter["vcenter.virtual_app.name"] = filter.CreateFilter(mbc.ResourceAttributes.VcenterVirtualAppName.MetricsExclude) + } if mbc.ResourceAttributes.VcenterVMID.MetricsInclude != nil { mb.resourceAttributeIncludeFilter["vcenter.vm.id"] = filter.CreateFilter(mbc.ResourceAttributes.VcenterVMID.MetricsInclude) } diff --git a/receiver/vcenterreceiver/internal/metadata/generated_metrics_test.go b/receiver/vcenterreceiver/internal/metadata/generated_metrics_test.go index a29c84af79b1..a7d264ad9dc1 100644 --- a/receiver/vcenterreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/vcenterreceiver/internal/metadata/generated_metrics_test.go @@ -66,6 +66,14 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, "[WARNING] Please set `enabled` field explicitly for `vcenter.datacenter.name`: this attribute will be enabled by default starting in release v0.101.0", observedLogs.All()[expectedWarnings].Message) expectedWarnings++ } + if test.resAttrsSet == testDataSetDefault { + assert.Equal(t, "[WARNING] Please set `enabled` field explicitly for `vcenter.virtual_app.inventory_path`: this attribute will be enabled by default starting in release v0.101.0", observedLogs.All()[expectedWarnings].Message) + expectedWarnings++ + } + if test.resAttrsSet == testDataSetDefault { + assert.Equal(t, "[WARNING] Please set `enabled` field explicitly for `vcenter.virtual_app.name`: this attribute will be enabled by default starting in release v0.101.0", observedLogs.All()[expectedWarnings].Message) + expectedWarnings++ + } assert.Equal(t, expectedWarnings, observedLogs.Len()) @@ -234,6 +242,8 @@ func TestMetricsBuilder(t *testing.T) { rb.SetVcenterHostName("vcenter.host.name-val") rb.SetVcenterResourcePoolInventoryPath("vcenter.resource_pool.inventory_path-val") rb.SetVcenterResourcePoolName("vcenter.resource_pool.name-val") + rb.SetVcenterVirtualAppInventoryPath("vcenter.virtual_app.inventory_path-val") + rb.SetVcenterVirtualAppName("vcenter.virtual_app.name-val") rb.SetVcenterVMID("vcenter.vm.id-val") rb.SetVcenterVMName("vcenter.vm.name-val") res := rb.Emit() diff --git a/receiver/vcenterreceiver/internal/metadata/generated_resource.go b/receiver/vcenterreceiver/internal/metadata/generated_resource.go index abb087dc71ae..2319b184b00c 100644 --- a/receiver/vcenterreceiver/internal/metadata/generated_resource.go +++ b/receiver/vcenterreceiver/internal/metadata/generated_resource.go @@ -63,6 +63,20 @@ func (rb *ResourceBuilder) SetVcenterResourcePoolName(val string) { } } +// SetVcenterVirtualAppInventoryPath sets provided value as "vcenter.virtual_app.inventory_path" attribute. +func (rb *ResourceBuilder) SetVcenterVirtualAppInventoryPath(val string) { + if rb.config.VcenterVirtualAppInventoryPath.Enabled { + rb.res.Attributes().PutStr("vcenter.virtual_app.inventory_path", val) + } +} + +// SetVcenterVirtualAppName sets provided value as "vcenter.virtual_app.name" attribute. +func (rb *ResourceBuilder) SetVcenterVirtualAppName(val string) { + if rb.config.VcenterVirtualAppName.Enabled { + rb.res.Attributes().PutStr("vcenter.virtual_app.name", val) + } +} + // SetVcenterVMID sets provided value as "vcenter.vm.id" attribute. func (rb *ResourceBuilder) SetVcenterVMID(val string) { if rb.config.VcenterVMID.Enabled { diff --git a/receiver/vcenterreceiver/internal/metadata/generated_resource_test.go b/receiver/vcenterreceiver/internal/metadata/generated_resource_test.go index bd6121433168..4216d7d2f453 100644 --- a/receiver/vcenterreceiver/internal/metadata/generated_resource_test.go +++ b/receiver/vcenterreceiver/internal/metadata/generated_resource_test.go @@ -19,6 +19,8 @@ func TestResourceBuilder(t *testing.T) { rb.SetVcenterHostName("vcenter.host.name-val") rb.SetVcenterResourcePoolInventoryPath("vcenter.resource_pool.inventory_path-val") rb.SetVcenterResourcePoolName("vcenter.resource_pool.name-val") + rb.SetVcenterVirtualAppInventoryPath("vcenter.virtual_app.inventory_path-val") + rb.SetVcenterVirtualAppName("vcenter.virtual_app.name-val") rb.SetVcenterVMID("vcenter.vm.id-val") rb.SetVcenterVMName("vcenter.vm.name-val") @@ -29,7 +31,7 @@ func TestResourceBuilder(t *testing.T) { case "default": assert.Equal(t, 7, res.Attributes().Len()) case "all_set": - assert.Equal(t, 8, res.Attributes().Len()) + assert.Equal(t, 10, res.Attributes().Len()) case "none_set": assert.Equal(t, 0, res.Attributes().Len()) return @@ -67,6 +69,16 @@ func TestResourceBuilder(t *testing.T) { if ok { assert.EqualValues(t, "vcenter.resource_pool.name-val", val.Str()) } + val, ok = res.Attributes().Get("vcenter.virtual_app.inventory_path") + assert.Equal(t, test == "all_set", ok) + if ok { + assert.EqualValues(t, "vcenter.virtual_app.inventory_path-val", val.Str()) + } + val, ok = res.Attributes().Get("vcenter.virtual_app.name") + assert.Equal(t, test == "all_set", ok) + if ok { + assert.EqualValues(t, "vcenter.virtual_app.name-val", val.Str()) + } val, ok = res.Attributes().Get("vcenter.vm.id") assert.True(t, ok) if ok { diff --git a/receiver/vcenterreceiver/internal/metadata/testdata/config.yaml b/receiver/vcenterreceiver/internal/metadata/testdata/config.yaml index ae941ef2060a..d212701831d8 100644 --- a/receiver/vcenterreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/vcenterreceiver/internal/metadata/testdata/config.yaml @@ -92,6 +92,10 @@ all_set: enabled: true vcenter.resource_pool.name: enabled: true + vcenter.virtual_app.inventory_path: + enabled: true + vcenter.virtual_app.name: + enabled: true vcenter.vm.id: enabled: true vcenter.vm.name: @@ -189,6 +193,10 @@ none_set: enabled: false vcenter.resource_pool.name: enabled: false + vcenter.virtual_app.inventory_path: + enabled: false + vcenter.virtual_app.name: + enabled: false vcenter.vm.id: enabled: false vcenter.vm.name: @@ -219,6 +227,14 @@ filter_set_include: enabled: true metrics_include: - regexp: ".*" + vcenter.virtual_app.inventory_path: + enabled: true + metrics_include: + - regexp: ".*" + vcenter.virtual_app.name: + enabled: true + metrics_include: + - regexp: ".*" vcenter.vm.id: enabled: true metrics_include: @@ -253,6 +269,14 @@ filter_set_exclude: enabled: true metrics_exclude: - strict: "vcenter.resource_pool.name-val" + vcenter.virtual_app.inventory_path: + enabled: true + metrics_exclude: + - strict: "vcenter.virtual_app.inventory_path-val" + vcenter.virtual_app.name: + enabled: true + metrics_exclude: + - strict: "vcenter.virtual_app.name-val" vcenter.vm.id: enabled: true metrics_exclude: diff --git a/receiver/vcenterreceiver/internal/mockserver/client_mock.go b/receiver/vcenterreceiver/internal/mockserver/client_mock.go index 449877eea164..a15f63dbc2fe 100644 --- a/receiver/vcenterreceiver/internal/mockserver/client_mock.go +++ b/receiver/vcenterreceiver/internal/mockserver/client_mock.go @@ -114,6 +114,13 @@ func routeRetreiveProperties(t *testing.T, body map[string]any) ([]byte, error) switch { case content == "group-d1" && contentType == "Folder": + for _, i := range propSetArray { + m, ok := i.(map[string]any) + require.True(t, ok) + if m["pathSet"] == "parentVApp" && m["type"] == "VirtualMachine" { + return loadResponse("datacenter-list.xml") + } + } return loadResponse("datacenter.xml") case content == "datacenter-3" && contentType == "Datacenter": @@ -199,6 +206,9 @@ func routeRetreiveProperties(t *testing.T, body map[string]any) ([]byte, error) return loadResponse("virtual-app-children.xml") } } + if _, ok := propSet["pathSet"].([]any); ok { + return loadResponse("virtual-app-properties.xml") + } if ps, ok := propSet["pathSet"].(string); ok { if ps == "owner" { return loadResponse("virtual-app-owner.xml") diff --git a/receiver/vcenterreceiver/internal/mockserver/responses/virtual-app-properties.xml b/receiver/vcenterreceiver/internal/mockserver/responses/virtual-app-properties.xml new file mode 100644 index 000000000000..dc60a5088bab --- /dev/null +++ b/receiver/vcenterreceiver/internal/mockserver/responses/virtual-app-properties.xml @@ -0,0 +1,24 @@ + + + + + + resgroup-v10 + + name + v-app-1 + + + vm + + vm-6004 + + + + + + diff --git a/receiver/vcenterreceiver/internal/mockserver/responses/virtual-app.xml b/receiver/vcenterreceiver/internal/mockserver/responses/virtual-app.xml new file mode 100644 index 000000000000..1f854623d778 --- /dev/null +++ b/receiver/vcenterreceiver/internal/mockserver/responses/virtual-app.xml @@ -0,0 +1,14 @@ + + + + + + resgroup-v10 + + name + v-app-1 + + + + + diff --git a/receiver/vcenterreceiver/metadata.yaml b/receiver/vcenterreceiver/metadata.yaml index 595640adb1cd..0642659d31c1 100644 --- a/receiver/vcenterreceiver/metadata.yaml +++ b/receiver/vcenterreceiver/metadata.yaml @@ -33,6 +33,18 @@ resource_attributes: description: The inventory path of the resource pool. enabled: true type: string + vcenter.virtual_app.name: + description: The name of the vApp. + enabled: false + type: string + warnings: + if_enabled_not_set: "this attribute will be enabled by default starting in release v0.101.0" + vcenter.virtual_app.inventory_path: + description: The inventory path of the vApp. + enabled: false + type: string + warnings: + if_enabled_not_set: "this attribute will be enabled by default starting in release v0.101.0" vcenter.datastore.name: description: The name of the vCenter datastore. enabled: true diff --git a/receiver/vcenterreceiver/resources.go b/receiver/vcenterreceiver/resources.go index b212c741e1ed..aa5a24bbbaae 100644 --- a/receiver/vcenterreceiver/resources.go +++ b/receiver/vcenterreceiver/resources.go @@ -16,6 +16,7 @@ func (v *vcenterMetricScraper) createVMResourceBuilder( hs mo.HostSystem, compute *object.ComputeResource, rp *object.ResourcePool, + vApp *object.VirtualApp, ) *metadata.ResourceBuilder { rb := v.mb.NewResourceBuilder() rb.SetVcenterDatacenterName(dcName) @@ -29,5 +30,9 @@ func (v *vcenterMetricScraper) createVMResourceBuilder( rb.SetVcenterResourcePoolName(rp.Name()) rb.SetVcenterResourcePoolInventoryPath(rp.InventoryPath) } + if vApp != nil && vApp.Name() != "" { + rb.SetVcenterVirtualAppName(vApp.Name()) + rb.SetVcenterVirtualAppInventoryPath(vApp.InventoryPath) + } return rb } diff --git a/receiver/vcenterreceiver/scraper.go b/receiver/vcenterreceiver/scraper.go index 17a70822d48c..f498b3e7edae 100644 --- a/receiver/vcenterreceiver/scraper.go +++ b/receiver/vcenterreceiver/scraper.go @@ -44,6 +44,7 @@ type vcenterMetricScraper struct { // map of vm name => compute name vmToComputeMap map[string]string vmToResourcePool map[string]*object.ResourcePool + vmToVirtualApp map[string]*object.VirtualApp } func newVmwareVcenterScraper( @@ -59,6 +60,7 @@ func newVmwareVcenterScraper( mb: metadata.NewMetricsBuilder(config.MetricsBuilderConfig, settings), vmToComputeMap: make(map[string]string), vmToResourcePool: make(map[string]*object.ResourcePool), + vmToVirtualApp: make(map[string]*object.VirtualApp), } } @@ -89,6 +91,7 @@ func (v *vcenterMetricScraper) scrape(ctx context.Context) (pmetric.Metrics, err // cleanup so any inventory moves are accounted for v.vmToComputeMap = make(map[string]string) v.vmToResourcePool = make(map[string]*object.ResourcePool) + v.vmToVirtualApp = make(map[string]*object.VirtualApp) return v.mb.Emit(), err } @@ -115,6 +118,7 @@ func (v *vcenterMetricScraper) collectClusters(ctx context.Context, datacenter * now := pcommon.NewTimestampFromTime(time.Now()) dcName := datacenter.Name() + v.collectVirtualApps(ctx, errs) v.collectResourcePools(ctx, now, dcName, computes, errs) for _, c := range computes { v.collectHosts(ctx, now, dcName, c, errs) @@ -329,6 +333,32 @@ func (v *vcenterMetricScraper) collectResourcePools( } } +func (v *vcenterMetricScraper) collectVirtualApps( + ctx context.Context, + errs *scrapererror.ScrapeErrors, +) { + vApps, err := v.client.VirtualApps(ctx) + if err != nil { + errs.AddPartial(1, err) + return + } + for _, vApp := range vApps { + var moVApp mo.VirtualApp + err = vApp.Properties(ctx, vApp.Reference(), []string{ + "name", + "vm", + }, &moVApp) + if err != nil { + errs.AddPartial(1, err) + continue + } + + for _, vmRef := range moVApp.Vm { + v.vmToVirtualApp[vmRef.Value] = vApp + } + } +} + func (v *vcenterMetricScraper) collectVMs( ctx context.Context, colTime pcommon.Timestamp, @@ -373,6 +403,9 @@ func (v *vcenterMetricScraper) collectVMs( poweredOnVMs++ } + // vApp may not exist for a VM + vApp := v.vmToVirtualApp[vm.Reference().Value] + // vms are optional without a resource pool rpRef := vm.ResourcePool var rp *object.ResourcePool @@ -422,7 +455,7 @@ func (v *vcenterMetricScraper) collectVMs( perfMetrics := vmPerfMetrics.resultsByMoRef[vm.Reference().Value] v.buildVMMetrics(colTime, vm, hwSum, perfMetrics) - rb := v.createVMResourceBuilder(dcName, vm, hwSum, compute, rp) + rb := v.createVMResourceBuilder(dcName, vm, hwSum, compute, rp, vApp) v.mb.EmitForResource(metadata.WithResource(rb.Emit())) } diff --git a/receiver/vcenterreceiver/scraper_test.go b/receiver/vcenterreceiver/scraper_test.go index e438c4139a23..7f47ee4784b9 100644 --- a/receiver/vcenterreceiver/scraper_test.go +++ b/receiver/vcenterreceiver/scraper_test.go @@ -40,6 +40,8 @@ func TestScrapeConfigsEnabled(t *testing.T) { optConfigs := metadata.DefaultMetricsBuilderConfig() optConfigs.ResourceAttributes.VcenterDatacenterName.Enabled = true + optConfigs.ResourceAttributes.VcenterVirtualAppName.Enabled = true + optConfigs.ResourceAttributes.VcenterVirtualAppInventoryPath.Enabled = true optConfigs.Metrics.VcenterVMMemoryUtilization.Enabled = true cfg := &Config{ diff --git a/receiver/vcenterreceiver/testdata/metrics/expected-all-enabled.yaml b/receiver/vcenterreceiver/testdata/metrics/expected-all-enabled.yaml index 23d3d357fca3..11cf0587c7cc 100644 --- a/receiver/vcenterreceiver/testdata/metrics/expected-all-enabled.yaml +++ b/receiver/vcenterreceiver/testdata/metrics/expected-all-enabled.yaml @@ -5651,6 +5651,12 @@ resourceMetrics: - key: vcenter.host.name value: stringValue: esxi-27971.cf5e88ac.australia-southeast1.gve.goog + - key: vcenter.virtual_app.name + value: + stringValue: v-app-1 + - key: vcenter.virtual_app.inventory_path + value: + stringValue: /Datacenter/vm/v-app-1 - key: vcenter.vm.id value: stringValue: 5000bbe0-993e-5813-c56a-198eaa62fb62