From 0a294d97d009127df34b7751c219973a6ca85007 Mon Sep 17 00:00:00 2001 From: Niraj Rathod Date: Mon, 9 Sep 2024 21:02:51 +0530 Subject: [PATCH 1/8] add alert names --- metricbeat/module/vsphere/host/data.go | 6 +- metricbeat/module/vsphere/host/host.go | 188 ++++++++++++++++--------- 2 files changed, 129 insertions(+), 65 deletions(-) diff --git a/metricbeat/module/vsphere/host/data.go b/metricbeat/module/vsphere/host/data.go index a6be1d80d07..06ff8ca4980 100644 --- a/metricbeat/module/vsphere/host/data.go +++ b/metricbeat/module/vsphere/host/data.go @@ -23,7 +23,7 @@ import ( "github.com/elastic/elastic-agent-libs/mapstr" ) -func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData) mapstr.M { +func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData, alerts []string) mapstr.M { const bytesMultiplier int64 = 1024 * 1024 event := mapstr.M{ "name": hs.Summary.Config.Name, @@ -34,6 +34,10 @@ func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData) mapstr.M { mapPerfMetricToEvent(event, data.perfMetrics) + if len(alerts) > 0 { + event.Put("alert.names", alerts) + } + if hw := hs.Summary.Hardware; hw != nil { totalCPU := int64(hw.CpuMhz) * int64(hw.NumCpuCores) usedMemory := int64(hs.Summary.QuickStats.OverallMemoryUsage) * bytesMultiplier diff --git a/metricbeat/module/vsphere/host/host.go b/metricbeat/module/vsphere/host/host.go index 2b7e26de72e..430e7c37469 100644 --- a/metricbeat/module/vsphere/host/host.go +++ b/metricbeat/module/vsphere/host/host.go @@ -29,10 +29,15 @@ import ( "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" + "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) +const ( + LiveInterval float64 = 20 +) + func init() { mb.Registry.MustAddMetricSet("vsphere", "host", New, mb.WithHostParser(vsphere.HostParser), @@ -60,9 +65,9 @@ type metricData struct { } type assetNames struct { - outputNetworkNames []string - outputDsNames []string - outputVmNames []string + outputNetworkNames []string + outputDatastoreNames []string + outputVmNames []string } // Define metrics to be collected @@ -90,8 +95,10 @@ var metricSet = map[string]struct{}{ // format. It publishes the event which is then forwarded to the output. In case // of an error set the Error field of mb.Event or simply call report.Error(). func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error { - ctx, cancel := context.WithCancel(ctx) - defer cancel() + period := m.Module().Config().Period + if !isValidPeriod(period.Seconds()) { + return fmt.Errorf("invalid period %v. Please provide one of the following values: 20, 300, 1800, 7200, 86400", period) + } client, err := govmomi.NewClient(ctx, m.HostURL, m.Insecure) if err != nil { @@ -100,23 +107,18 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error defer func() { if err := client.Logout(ctx); err != nil { - m.Logger().Errorf("error trying to log out from vSphere: %w", err) + m.Logger().Errorf("error trying to log out from vSphere: %v", err) } }() - c := client.Client - - // Create a view of HostSystem objects. - mgr := view.NewManager(c) - - v, err := mgr.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true) + v, err := view.NewManager(client.Client).CreateContainerView(ctx, client.Client.ServiceContent.RootFolder, []string{"HostSystem"}, true) if err != nil { - return fmt.Errorf("error in CreateContainerView: %w", err) + return fmt.Errorf("error in creating container view: %w", err) } defer func() { if err := v.Destroy(ctx); err != nil { - m.Logger().Errorf("error trying to destroy view from vSphere: %w", err) + m.Logger().Errorf("error trying to destroy view from vSphere: %v", err) } }() @@ -124,11 +126,11 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error var hst []mo.HostSystem err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "network", "name", "vm", "datastore"}, &hst) if err != nil { - return fmt.Errorf("error in Retrieve: %w", err) + return fmt.Errorf("error in retrieve from vsphere: %w", err) } // Create a performance manager - perfManager := performance.NewManager(c) + perfManager := performance.NewManager(client.Client) // Retrieve all available metrics metrics, err := perfManager.CounterInfoByName(ctx) @@ -146,54 +148,48 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error } } - pc := property.DefaultCollector(c) + pc := property.DefaultCollector(client.Client) for i := range hst { - select { - case <-ctx.Done(): + if ctx.Err() != nil { return ctx.Err() - default: - assetNames, err := getAssetNames(ctx, pc, &hst[i]) - if err != nil { - m.Logger().Errorf("Failed to retrieve object from host %s: %w", hst[i].Name, err) - } - - spec := types.PerfQuerySpec{ - Entity: hst[i].Reference(), - MetricId: metricIds, - MaxSample: 1, - IntervalId: 20, // right now we are only grabbing real time metrics from the performance manager - } + } + assetNames, err := getAssetNames(ctx, pc, &hst[i]) + if err != nil { + m.Logger().Errorf("Failed to retrieve object from host %s: %v", hst[i].Name, err) + } - // Query performance data - samples, err := perfManager.Query(ctx, []types.PerfQuerySpec{spec}) - if err != nil { - m.Logger().Errorf("Failed to query performance data from host %s: %v", hst[i].Name, err) - continue - } + metricMap, err := m.getPerfMetrics(ctx, perfManager, hst[i], metricIds) + if err != nil { + m.Logger().Errorf("Failed to retrieve performance metrics from host %s: %v", hst[i].Name, err) + } - if len(samples) == 0 { - m.Logger().Debug("No samples returned from performance manager") - continue - } + var alerts []string + var alarmManager mo.AlarmManager + err = client.RetrieveOne(ctx, *client.ServiceContent.AlarmManager, nil, &alarmManager) + if err != nil { + m.Logger().Errorf("can not retrive alarm manager from host %s: %v", hst[i].Name, err) + } else { + alarmStates, _ := methods.GetAlarmState(ctx, client, &types.GetAlarmState{ + This: alarmManager.Self, + Entity: hst[i].Self, + }) - results, err := perfManager.ToMetricSeries(ctx, samples) - if err != nil { - m.Logger().Errorf("Failed to convert performance data to metric series for host %s: %v", hst[i].Name, err) - } + for _, alarm := range alarmStates.Returnval { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + m.Logger().Errorf("can not retrive alarm from host %s: %v", hst[i].Name, err) + } - metricMap := make(map[string]interface{}) - for _, result := range results[0].Value { - if len(result.Value) > 0 { - metricMap[result.Name] = result.Value[0] - continue + alerts = append(alerts, triggeredAlarm.Info.Name) } - m.Logger().Debugf("For host %s,Metric %v: No result found", hst[i].Name, result.Name) } - - reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(hst[i], &metricData{perfMetrics: metricMap, assetNames: assetNames}), - }) } + + reporter.Event(mb.Event{ + MetricSetFields: m.mapEvent(hst[i], &metricData{perfMetrics: metricMap, assetNames: assetNames}, alerts), + }) } return nil @@ -202,20 +198,22 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error func getAssetNames(ctx context.Context, pc *property.Collector, hs *mo.HostSystem) (assetNames, error) { referenceList := append(hs.Datastore, hs.Vm...) + if len(referenceList) == 0 { + return assetNames{}, nil + } + var objects []mo.ManagedEntity - if len(referenceList) > 0 { - if err := pc.Retrieve(ctx, referenceList, []string{"name"}, &objects); err != nil { - return assetNames{}, err - } + if err := pc.Retrieve(ctx, referenceList, []string{"name"}, &objects); err != nil { + return assetNames{}, err } - outputDsNames := make([]string, 0, len(hs.Datastore)) + outputDatastoreNames := make([]string, 0, len(hs.Datastore)) outputVmNames := make([]string, 0, len(hs.Vm)) for _, ob := range objects { name := strings.ReplaceAll(ob.Name, ".", "_") switch ob.Reference().Type { case "Datastore": - outputDsNames = append(outputDsNames, name) + outputDatastoreNames = append(outputDatastoreNames, name) case "VirtualMachine": outputVmNames = append(outputVmNames, name) } @@ -235,8 +233,70 @@ func getAssetNames(ctx context.Context, pc *property.Collector, hs *mo.HostSyste } return assetNames{ - outputNetworkNames: outputNetworkNames, - outputDsNames: outputDsNames, - outputVmNames: outputVmNames, + outputNetworkNames: outputNetworkNames, + outputDatastoreNames: outputDatastoreNames, + outputVmNames: outputVmNames, }, nil } + +func (m *HostMetricSet) getPerfMetrics(ctx context.Context, perfManager *performance.Manager, hst mo.HostSystem, metricIds []types.PerfMetricId) (map[string]interface{}, error) { + metricMap := make(map[string]interface{}) + summary, err := perfManager.ProviderSummary(ctx, hst.Reference()) + if err != nil { + return metricMap, fmt.Errorf("failed to get summary: %w", err) + } + + period := m.Module().Config().Period + refreshRate := int32(period.Seconds()) + if period.Seconds() == LiveInterval { + if summary.CurrentSupported { + refreshRate = summary.RefreshRate + if int32(period.Seconds()) != refreshRate { + m.Logger().Warnf("User-provided period %v does not match system's refresh rate %v. Risk of data duplication. Consider adjusting period.", period, refreshRate) + } + } else { + m.Logger().Warnf("Live data collection not supported. Use one of the system's historical interval (300, 1800, 7200, 86400). Risk of data duplication. Consider adjusting period.") + } + } + + spec := types.PerfQuerySpec{ + Entity: hst.Reference(), + MetricId: metricIds, + MaxSample: 1, + IntervalId: refreshRate, + } + + // Query performance data + samples, err := perfManager.Query(ctx, []types.PerfQuerySpec{spec}) + if err != nil { + return metricMap, fmt.Errorf("failed to query performance data: %w", err) + } + + if len(samples) == 0 { + m.Logger().Debug("No samples returned from performance manager") + return metricMap, nil + } + + results, err := perfManager.ToMetricSeries(ctx, samples) + if err != nil { + return metricMap, fmt.Errorf("failed to convert performance data to metric series: %w", err) + } + + for _, result := range results[0].Value { + if len(result.Value) > 0 { + metricMap[result.Name] = result.Value[0] + continue + } + m.Logger().Debugf("For host %s, Metric %s: No result found", hst.Name, result.Name) + } + + return metricMap, nil +} + +func isValidPeriod(period float64) bool { + switch period { + case LiveInterval, 300, 1800, 7200, 86400: + return true + } + return false +} From 84f1be35b8354bb8ed5479fd1e0f509be24c7a99 Mon Sep 17 00:00:00 2001 From: Niraj Rathod Date: Tue, 10 Sep 2024 13:02:59 +0530 Subject: [PATCH 2/8] update host metricset --- metricbeat/docs/fields.asciidoc | 10 + metricbeat/module/vsphere/fields.go | 2 +- .../module/vsphere/host/_meta/data.json | 7 + .../module/vsphere/host/_meta/fields.yml | 4 + metricbeat/module/vsphere/host/data.go | 10 +- metricbeat/module/vsphere/host/host.go | 190 +++++++----------- 6 files changed, 99 insertions(+), 124 deletions(-) diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 8f3ccce6fff..f6caecfb902 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -67202,6 +67202,16 @@ Host information from vSphere environment. +*`vsphere.host.alert.names`*:: ++ +-- +List of all the alert names. + + +type: keyword + +-- + *`vsphere.host.cpu.used.mhz`*:: + -- diff --git a/metricbeat/module/vsphere/fields.go b/metricbeat/module/vsphere/fields.go index 729c11e6264..d16bde25658 100644 --- a/metricbeat/module/vsphere/fields.go +++ b/metricbeat/module/vsphere/fields.go @@ -32,5 +32,5 @@ func init() { // AssetVsphere returns asset data. // This is the base64 encoded zlib format compressed contents of module/vsphere. func AssetVsphere() string { - return "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" + return "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" } diff --git a/metricbeat/module/vsphere/host/_meta/data.json b/metricbeat/module/vsphere/host/_meta/data.json index cb8d7896ce5..5818e2d81e6 100644 --- a/metricbeat/module/vsphere/host/_meta/data.json +++ b/metricbeat/module/vsphere/host/_meta/data.json @@ -15,6 +15,13 @@ }, "vsphere": { "host": { + "alert": { + "names": [ + "Host hardware system board status", + "Host storage status", + "CPU Utilization" + ] + }, "cpu": { "used": { "mhz": 67 diff --git a/metricbeat/module/vsphere/host/_meta/fields.yml b/metricbeat/module/vsphere/host/_meta/fields.yml index b45061b863e..05478014d4c 100644 --- a/metricbeat/module/vsphere/host/_meta/fields.yml +++ b/metricbeat/module/vsphere/host/_meta/fields.yml @@ -4,6 +4,10 @@ Host information from vSphere environment. release: ga fields: + - name: alert.names + type: keyword + description: > + List of all the alert names. - name: cpu.used.mhz type: long description: > diff --git a/metricbeat/module/vsphere/host/data.go b/metricbeat/module/vsphere/host/data.go index 06ff8ca4980..745668af127 100644 --- a/metricbeat/module/vsphere/host/data.go +++ b/metricbeat/module/vsphere/host/data.go @@ -23,7 +23,7 @@ import ( "github.com/elastic/elastic-agent-libs/mapstr" ) -func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData, alerts []string) mapstr.M { +func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData) mapstr.M { const bytesMultiplier int64 = 1024 * 1024 event := mapstr.M{ "name": hs.Summary.Config.Name, @@ -34,10 +34,6 @@ func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData, alerts []st mapPerfMetricToEvent(event, data.perfMetrics) - if len(alerts) > 0 { - event.Put("alert.names", alerts) - } - if hw := hs.Summary.Hardware; hw != nil { totalCPU := int64(hw.CpuMhz) * int64(hw.NumCpuCores) usedMemory := int64(hs.Summary.QuickStats.OverallMemoryUsage) * bytesMultiplier @@ -50,6 +46,10 @@ func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData, alerts []st m.Logger().Debug("'Hardware' or 'Summary' data not found. This is either a parsing error from vsphere library, an error trying to reach host/guest or incomplete information returned from host/guest") } + if len(data.alertNames) > 0 { + event.Put("alert.names", data.alertNames) + } + if len(data.assetNames.outputVmNames) > 0 { event.Put("vm.names", data.assetNames.outputVmNames) event.Put("vm.count", len(data.assetNames.outputVmNames)) diff --git a/metricbeat/module/vsphere/host/host.go b/metricbeat/module/vsphere/host/host.go index 430e7c37469..85626553910 100644 --- a/metricbeat/module/vsphere/host/host.go +++ b/metricbeat/module/vsphere/host/host.go @@ -29,15 +29,10 @@ import ( "github.com/vmware/govmomi/performance" "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" - "github.com/vmware/govmomi/vim25/methods" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" ) -const ( - LiveInterval float64 = 20 -) - func init() { mb.Registry.MustAddMetricSet("vsphere", "host", New, mb.WithHostParser(vsphere.HostParser), @@ -62,12 +57,13 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { type metricData struct { perfMetrics map[string]interface{} assetNames assetNames + alertNames []string } type assetNames struct { - outputNetworkNames []string - outputDatastoreNames []string - outputVmNames []string + outputNetworkNames []string + outputDsNames []string + outputVmNames []string } // Define metrics to be collected @@ -95,10 +91,8 @@ var metricSet = map[string]struct{}{ // format. It publishes the event which is then forwarded to the output. In case // of an error set the Error field of mb.Event or simply call report.Error(). func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error { - period := m.Module().Config().Period - if !isValidPeriod(period.Seconds()) { - return fmt.Errorf("invalid period %v. Please provide one of the following values: 20, 300, 1800, 7200, 86400", period) - } + ctx, cancel := context.WithCancel(ctx) + defer cancel() client, err := govmomi.NewClient(ctx, m.HostURL, m.Insecure) if err != nil { @@ -107,30 +101,35 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error defer func() { if err := client.Logout(ctx); err != nil { - m.Logger().Errorf("error trying to log out from vSphere: %v", err) + m.Logger().Errorf("error trying to log out from vSphere: %w", err) } }() - v, err := view.NewManager(client.Client).CreateContainerView(ctx, client.Client.ServiceContent.RootFolder, []string{"HostSystem"}, true) + c := client.Client + + // Create a view of HostSystem objects. + mgr := view.NewManager(c) + + v, err := mgr.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true) if err != nil { - return fmt.Errorf("error in creating container view: %w", err) + return fmt.Errorf("error in CreateContainerView: %w", err) } defer func() { if err := v.Destroy(ctx); err != nil { - m.Logger().Errorf("error trying to destroy view from vSphere: %v", err) + m.Logger().Errorf("error trying to destroy view from vSphere: %w", err) } }() // Retrieve summary property for all hosts. var hst []mo.HostSystem - err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "network", "name", "vm", "datastore"}, &hst) + err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary", "network", "name", "vm", "datastore", "triggeredAlarmState"}, &hst) if err != nil { - return fmt.Errorf("error in retrieve from vsphere: %w", err) + return fmt.Errorf("error in Retrieve: %w", err) } // Create a performance manager - perfManager := performance.NewManager(client.Client) + perfManager := performance.NewManager(c) // Retrieve all available metrics metrics, err := perfManager.CounterInfoByName(ctx) @@ -148,33 +147,52 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error } } - pc := property.DefaultCollector(client.Client) + pc := property.DefaultCollector(c) for i := range hst { - if ctx.Err() != nil { + select { + case <-ctx.Done(): return ctx.Err() - } - assetNames, err := getAssetNames(ctx, pc, &hst[i]) - if err != nil { - m.Logger().Errorf("Failed to retrieve object from host %s: %v", hst[i].Name, err) - } + default: + assetNames, err := getAssetNames(ctx, pc, &hst[i]) + if err != nil { + m.Logger().Errorf("Failed to retrieve object from host %s: %w", hst[i].Name, err) + } - metricMap, err := m.getPerfMetrics(ctx, perfManager, hst[i], metricIds) - if err != nil { - m.Logger().Errorf("Failed to retrieve performance metrics from host %s: %v", hst[i].Name, err) - } + spec := types.PerfQuerySpec{ + Entity: hst[i].Reference(), + MetricId: metricIds, + MaxSample: 1, + IntervalId: 20, // right now we are only grabbing real time metrics from the performance manager + } - var alerts []string - var alarmManager mo.AlarmManager - err = client.RetrieveOne(ctx, *client.ServiceContent.AlarmManager, nil, &alarmManager) - if err != nil { - m.Logger().Errorf("can not retrive alarm manager from host %s: %v", hst[i].Name, err) - } else { - alarmStates, _ := methods.GetAlarmState(ctx, client, &types.GetAlarmState{ - This: alarmManager.Self, - Entity: hst[i].Self, - }) + // Query performance data + samples, err := perfManager.Query(ctx, []types.PerfQuerySpec{spec}) + if err != nil { + m.Logger().Errorf("Failed to query performance data from host %s: %v", hst[i].Name, err) + continue + } - for _, alarm := range alarmStates.Returnval { + if len(samples) == 0 { + m.Logger().Debug("No samples returned from performance manager") + continue + } + + results, err := perfManager.ToMetricSeries(ctx, samples) + if err != nil { + m.Logger().Errorf("Failed to convert performance data to metric series for host %s: %v", hst[i].Name, err) + } + + metricMap := make(map[string]interface{}) + for _, result := range results[0].Value { + if len(result.Value) > 0 { + metricMap[result.Name] = result.Value[0] + continue + } + m.Logger().Debugf("For host %s,Metric %v: No result found", hst[i].Name, result.Name) + } + + var alerts []string + for _, alarm := range hst[i].TriggeredAlarmState { if alarm.OverallStatus == "red" { var triggeredAlarm mo.Alarm err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) @@ -185,11 +203,11 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error alerts = append(alerts, triggeredAlarm.Info.Name) } } - } - reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(hst[i], &metricData{perfMetrics: metricMap, assetNames: assetNames}, alerts), - }) + reporter.Event(mb.Event{ + MetricSetFields: m.mapEvent(hst[i], &metricData{perfMetrics: metricMap, assetNames: assetNames, alertNames: alerts}), + }) + } } return nil @@ -198,22 +216,20 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error func getAssetNames(ctx context.Context, pc *property.Collector, hs *mo.HostSystem) (assetNames, error) { referenceList := append(hs.Datastore, hs.Vm...) - if len(referenceList) == 0 { - return assetNames{}, nil - } - var objects []mo.ManagedEntity - if err := pc.Retrieve(ctx, referenceList, []string{"name"}, &objects); err != nil { - return assetNames{}, err + if len(referenceList) > 0 { + if err := pc.Retrieve(ctx, referenceList, []string{"name"}, &objects); err != nil { + return assetNames{}, err + } } - outputDatastoreNames := make([]string, 0, len(hs.Datastore)) + outputDsNames := make([]string, 0, len(hs.Datastore)) outputVmNames := make([]string, 0, len(hs.Vm)) for _, ob := range objects { name := strings.ReplaceAll(ob.Name, ".", "_") switch ob.Reference().Type { case "Datastore": - outputDatastoreNames = append(outputDatastoreNames, name) + outputDsNames = append(outputDsNames, name) case "VirtualMachine": outputVmNames = append(outputVmNames, name) } @@ -233,70 +249,8 @@ func getAssetNames(ctx context.Context, pc *property.Collector, hs *mo.HostSyste } return assetNames{ - outputNetworkNames: outputNetworkNames, - outputDatastoreNames: outputDatastoreNames, - outputVmNames: outputVmNames, + outputNetworkNames: outputNetworkNames, + outputDsNames: outputDsNames, + outputVmNames: outputVmNames, }, nil } - -func (m *HostMetricSet) getPerfMetrics(ctx context.Context, perfManager *performance.Manager, hst mo.HostSystem, metricIds []types.PerfMetricId) (map[string]interface{}, error) { - metricMap := make(map[string]interface{}) - summary, err := perfManager.ProviderSummary(ctx, hst.Reference()) - if err != nil { - return metricMap, fmt.Errorf("failed to get summary: %w", err) - } - - period := m.Module().Config().Period - refreshRate := int32(period.Seconds()) - if period.Seconds() == LiveInterval { - if summary.CurrentSupported { - refreshRate = summary.RefreshRate - if int32(period.Seconds()) != refreshRate { - m.Logger().Warnf("User-provided period %v does not match system's refresh rate %v. Risk of data duplication. Consider adjusting period.", period, refreshRate) - } - } else { - m.Logger().Warnf("Live data collection not supported. Use one of the system's historical interval (300, 1800, 7200, 86400). Risk of data duplication. Consider adjusting period.") - } - } - - spec := types.PerfQuerySpec{ - Entity: hst.Reference(), - MetricId: metricIds, - MaxSample: 1, - IntervalId: refreshRate, - } - - // Query performance data - samples, err := perfManager.Query(ctx, []types.PerfQuerySpec{spec}) - if err != nil { - return metricMap, fmt.Errorf("failed to query performance data: %w", err) - } - - if len(samples) == 0 { - m.Logger().Debug("No samples returned from performance manager") - return metricMap, nil - } - - results, err := perfManager.ToMetricSeries(ctx, samples) - if err != nil { - return metricMap, fmt.Errorf("failed to convert performance data to metric series: %w", err) - } - - for _, result := range results[0].Value { - if len(result.Value) > 0 { - metricMap[result.Name] = result.Value[0] - continue - } - m.Logger().Debugf("For host %s, Metric %s: No result found", hst.Name, result.Name) - } - - return metricMap, nil -} - -func isValidPeriod(period float64) bool { - switch period { - case LiveInterval, 300, 1800, 7200, 86400: - return true - } - return false -} From 80de0aebfcd8cc1567e8a1e8549416096849701c Mon Sep 17 00:00:00 2001 From: Niraj Rathod Date: Tue, 10 Sep 2024 20:20:46 +0530 Subject: [PATCH 3/8] add alert metrics to all the metricsets --- metricbeat/docs/fields.asciidoc | 60 +++++++++++++++++++ .../module/vsphere/cluster/_meta/fields.yml | 4 ++ metricbeat/module/vsphere/cluster/cluster.go | 39 ++++++++++-- metricbeat/module/vsphere/cluster/data.go | 18 +++--- .../module/vsphere/datastore/_meta/fields.yml | 4 ++ metricbeat/module/vsphere/datastore/data.go | 4 ++ .../module/vsphere/datastore/datastore.go | 25 +++++++- .../vsphere/datastorecluster/_meta/fields.yml | 4 ++ .../module/vsphere/datastorecluster/data.go | 8 ++- .../datastorecluster/datastorecluster.go | 27 ++++++++- metricbeat/module/vsphere/fields.go | 2 +- .../module/vsphere/host/_meta/data.json | 14 ++--- metricbeat/module/vsphere/host/host.go | 30 ++++++---- .../module/vsphere/network/_meta/fields.yml | 4 ++ metricbeat/module/vsphere/network/data.go | 4 ++ metricbeat/module/vsphere/network/network.go | 27 ++++++++- .../vsphere/resourcepool/_meta/fields.yml | 4 ++ .../module/vsphere/resourcepool/data.go | 4 ++ .../vsphere/resourcepool/resourcepool.go | 27 ++++++++- .../vsphere/virtualmachine/_meta/fields.yml | 4 ++ .../module/vsphere/virtualmachine/data.go | 3 + .../vsphere/virtualmachine/virtualmachine.go | 25 +++++++- 22 files changed, 300 insertions(+), 41 deletions(-) diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index fda8952bbe8..7dce614139e 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -66861,6 +66861,16 @@ Cluster information. +*`vsphere.cluster.alert.names`*:: ++ +-- +List of all the alert names. + + +type: keyword + +-- + *`vsphere.cluster.datastore.names`*:: + @@ -66962,6 +66972,16 @@ datastore +*`vsphere.datastore.alert.names`*:: ++ +-- +List of all the alert names. + + +type: keyword + +-- + *`vsphere.datastore.capacity.free.bytes`*:: + -- @@ -67141,6 +67161,16 @@ Datastore Cluster +*`vsphere.datastorecluster.alert.names`*:: ++ +-- +List of all the alert names. + + +type: keyword + +-- + *`vsphere.datastorecluster.name`*:: + -- @@ -67609,6 +67639,16 @@ type: boolean -- +*`vsphere.network.alert.names`*:: ++ +-- +List of all the alert names. + + +type: keyword + +-- + *`vsphere.network.config.status`*:: + -- @@ -67698,6 +67738,16 @@ Resource pool information from vSphere environment. +*`vsphere.resourcepool.alert.names`*:: ++ +-- +List of all the alert names. + + +type: keyword + +-- + *`vsphere.resourcepool.cpu.usage.mhz`*:: + @@ -67924,6 +67974,16 @@ virtualmachine +*`vsphere.virtualmachine.alert.names`*:: ++ +-- +List of all the alert names. + + +type: keyword + +-- + *`vsphere.virtualmachine.host.id`*:: + diff --git a/metricbeat/module/vsphere/cluster/_meta/fields.yml b/metricbeat/module/vsphere/cluster/_meta/fields.yml index 7afce736285..697ae4171ee 100644 --- a/metricbeat/module/vsphere/cluster/_meta/fields.yml +++ b/metricbeat/module/vsphere/cluster/_meta/fields.yml @@ -4,6 +4,10 @@ description: > Cluster information. fields: + - name: alert.names + type: keyword + description: > + List of all the alert names. - name: datastore type: group fields: diff --git a/metricbeat/module/vsphere/cluster/cluster.go b/metricbeat/module/vsphere/cluster/cluster.go index 10184c55c90..481e3b1f16f 100644 --- a/metricbeat/module/vsphere/cluster/cluster.go +++ b/metricbeat/module/vsphere/cluster/cluster.go @@ -29,6 +29,7 @@ import ( "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" ) // init registers the MetricSet with the central registry as soon as the program @@ -53,6 +54,11 @@ type assetNames struct { outputHostNames []string } +type metricData struct { + assetNames assetNames + alertNames []string +} + // New creates a new instance of the MetricSet. func New(base mb.BaseMetricSet) (mb.MetricSet, error) { ms, err := vsphere.NewMetricSet(base) @@ -97,7 +103,7 @@ func (m *ClusterMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) er // Retrieve summary property for all Clusters var clt []mo.ClusterComputeResource - err = v.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"name", "host", "network", "datastore", "configuration"}, &clt) + err = v.Retrieve(ctx, []string{"ClusterComputeResource"}, []string{"name", "host", "network", "datastore", "configuration", "triggeredAlarmState"}, &clt) if err != nil { return fmt.Errorf("error in Retrieve: %w", err) } @@ -120,8 +126,13 @@ func (m *ClusterMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) er m.Logger().Warn("Metric das_config.enabled not found") } + alerts, err := getAlertNames(ctx, pc, clt[i].TriggeredAlarmState) + if err != nil { + m.Logger().Errorf("Failed to retrieve alerts from cluster %s: %w", clt[i].Name, err) + } + reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(clt[i], assetNames), + MetricSetFields: m.mapEvent(clt[i], &metricData{assetNames: assetNames, alertNames: alerts}), }) } } @@ -129,7 +140,7 @@ func (m *ClusterMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) er } -func getAssetNames(ctx context.Context, pc *property.Collector, cl *mo.ClusterComputeResource) (*assetNames, error) { +func getAssetNames(ctx context.Context, pc *property.Collector, cl *mo.ClusterComputeResource) (assetNames, error) { referenceList := append(cl.Datastore, cl.Host...) outputDatastoreNames := make([]string, 0, len(cl.Datastore)) @@ -137,7 +148,7 @@ func getAssetNames(ctx context.Context, pc *property.Collector, cl *mo.ClusterCo if len(referenceList) > 0 { var objects []mo.ManagedEntity if err := pc.Retrieve(ctx, referenceList, []string{"name"}, &objects); err != nil { - return nil, fmt.Errorf("failed to retrieve managed entities: %w", err) + return assetNames{}, fmt.Errorf("failed to retrieve managed entities: %w", err) } for _, ob := range objects { @@ -157,7 +168,7 @@ func getAssetNames(ctx context.Context, pc *property.Collector, cl *mo.ClusterCo if len(cl.Network) > 0 { var netObjects []mo.Network if err := pc.Retrieve(ctx, cl.Network, []string{"name"}, &netObjects); err != nil { - return nil, fmt.Errorf("failed to retrieve network objects: %w", err) + return assetNames{}, fmt.Errorf("failed to retrieve network objects: %w", err) } for _, ob := range netObjects { @@ -166,9 +177,25 @@ func getAssetNames(ctx context.Context, pc *property.Collector, cl *mo.ClusterCo } } - return &assetNames{ + return assetNames{ outputNetworkNames: outputNetworkNames, outputDatastoreNames: outputDatastoreNames, outputHostNames: outputHostNames, }, nil } + +func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { + var alerts []string + for _, alarm := range triggeredAlarmState { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + return nil, err + } + + alerts = append(alerts, triggeredAlarm.Info.Name) + } + } + return alerts, nil +} diff --git a/metricbeat/module/vsphere/cluster/data.go b/metricbeat/module/vsphere/cluster/data.go index 37c2ac5c319..60998b2d149 100644 --- a/metricbeat/module/vsphere/cluster/data.go +++ b/metricbeat/module/vsphere/cluster/data.go @@ -23,23 +23,27 @@ import ( "github.com/elastic/elastic-agent-libs/mapstr" ) -func (m *ClusterMetricSet) mapEvent(cl mo.ClusterComputeResource, data *assetNames) mapstr.M { +func (m *ClusterMetricSet) mapEvent(cl mo.ClusterComputeResource, data *metricData) mapstr.M { event := mapstr.M{ "host": mapstr.M{ - "count": len(data.outputHostNames), - "names": data.outputHostNames, + "count": len(data.assetNames.outputHostNames), + "names": data.assetNames.outputHostNames, }, "datastore": mapstr.M{ - "count": len(data.outputDatastoreNames), - "names": data.outputDatastoreNames, + "count": len(data.assetNames.outputDatastoreNames), + "names": data.assetNames.outputDatastoreNames, }, "network": mapstr.M{ - "count": len(data.outputNetworkNames), - "names": data.outputNetworkNames, + "count": len(data.assetNames.outputNetworkNames), + "names": data.assetNames.outputNetworkNames, }, "name": cl.Name, } + if len(data.alertNames) > 0 { + event.Put("alert.names", data.alertNames) + } + if cl.Configuration.DasConfig.Enabled != nil { event.Put("das_config.enabled", *cl.Configuration.DasConfig.Enabled) } diff --git a/metricbeat/module/vsphere/datastore/_meta/fields.yml b/metricbeat/module/vsphere/datastore/_meta/fields.yml index e9fb22a486d..ffdf86ab905 100644 --- a/metricbeat/module/vsphere/datastore/_meta/fields.yml +++ b/metricbeat/module/vsphere/datastore/_meta/fields.yml @@ -4,6 +4,10 @@ datastore release: ga fields: + - name: alert.names + type: keyword + description: > + List of all the alert names. - name: capacity.free.bytes type: long description: > diff --git a/metricbeat/module/vsphere/datastore/data.go b/metricbeat/module/vsphere/datastore/data.go index 0b438f96268..9934f21058a 100644 --- a/metricbeat/module/vsphere/datastore/data.go +++ b/metricbeat/module/vsphere/datastore/data.go @@ -49,6 +49,10 @@ func (m *DataStoreMetricSet) mapEvent(ds mo.Datastore, data *metricData) mapstr. }, } + if len(data.alertNames) > 0 { + event.Put("alert.names", data.alertNames) + } + if ds.Summary.Capacity > 0 { usedSpacePercent := float64(ds.Summary.Capacity-ds.Summary.FreeSpace) / float64(ds.Summary.Capacity) event.Put("capacity.used.pct", usedSpacePercent) diff --git a/metricbeat/module/vsphere/datastore/datastore.go b/metricbeat/module/vsphere/datastore/datastore.go index ee2d08feff1..d2796d2b963 100644 --- a/metricbeat/module/vsphere/datastore/datastore.go +++ b/metricbeat/module/vsphere/datastore/datastore.go @@ -57,6 +57,7 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { type metricData struct { perfMetrics map[string]interface{} assetNames assetNames + alertNames []string } type assetNames struct { @@ -108,7 +109,7 @@ func (m *DataStoreMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) // Retrieve summary property for all datastores var dst []mo.Datastore - err = v.Retrieve(ctx, []string{"Datastore"}, []string{"summary", "host", "vm", "overallStatus"}, &dst) + err = v.Retrieve(ctx, []string{"Datastore"}, []string{"summary", "host", "vm", "overallStatus", "triggeredAlarmState"}, &dst) if err != nil { return fmt.Errorf("error in Retrieve: %w", err) } @@ -178,10 +179,16 @@ func (m *DataStoreMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) m.Logger().Debugf("For host %s,Metric %v: No result found", dst[i].Name, result.Name) } + alerts, err := getAlertNames(ctx, pc, dst[i].TriggeredAlarmState) + if err != nil { + m.Logger().Errorf("Failed to retrieve alerts from datastore %s: %w", dst[i].Name, err) + } + reporter.Event(mb.Event{ MetricSetFields: m.mapEvent(dst[i], &metricData{ perfMetrics: metricMap, assetNames: *assetNames, + alertNames: alerts, }), }) } @@ -235,3 +242,19 @@ func getAssetNames(ctx context.Context, pc *property.Collector, ds *mo.Datastore outputVmNames: outputVmNames, }, nil } + +func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { + var alerts []string + for _, alarm := range triggeredAlarmState { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + return nil, err + } + + alerts = append(alerts, triggeredAlarm.Info.Name) + } + } + return alerts, nil +} diff --git a/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml b/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml index 50ce1ca9d44..2035ff246c4 100644 --- a/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml +++ b/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml @@ -4,6 +4,10 @@ description: > Datastore Cluster fields: + - name: alert.names + type: keyword + description: > + List of all the alert names. - name: name type: keyword description: > diff --git a/metricbeat/module/vsphere/datastorecluster/data.go b/metricbeat/module/vsphere/datastorecluster/data.go index d7c7903c62c..6cfe02c3533 100644 --- a/metricbeat/module/vsphere/datastorecluster/data.go +++ b/metricbeat/module/vsphere/datastorecluster/data.go @@ -24,7 +24,7 @@ import ( ) func (m *DatastoreClusterMetricSet) mapEvent(datastoreCluster mo.StoragePod, data *metricData) mapstr.M { - return mapstr.M{ + event := mapstr.M{ "name": datastoreCluster.Name, "capacity": mapstr.M{ "bytes": datastoreCluster.Summary.Capacity, @@ -37,4 +37,10 @@ func (m *DatastoreClusterMetricSet) mapEvent(datastoreCluster mo.StoragePod, dat "count": len(data.assetNames.outputDsNames), }, } + + if len(data.alertNames) > 0 { + event.Put("alert.names", data.alertNames) + } + + return event } diff --git a/metricbeat/module/vsphere/datastorecluster/datastorecluster.go b/metricbeat/module/vsphere/datastorecluster/datastorecluster.go index dd26fa7ba04..e38beaca34d 100644 --- a/metricbeat/module/vsphere/datastorecluster/datastorecluster.go +++ b/metricbeat/module/vsphere/datastorecluster/datastorecluster.go @@ -26,6 +26,7 @@ import ( "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -59,6 +60,7 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { type metricData struct { assetNames assetNames + alertNames []string } type assetNames struct { @@ -94,7 +96,7 @@ func (m *DatastoreClusterMetricSet) Fetch(ctx context.Context, reporter mb.Repor }() var datastoreCluster []mo.StoragePod - err = v.Retrieve(ctx, []string{"StoragePod"}, []string{"name", "summary", "childEntity"}, &datastoreCluster) + err = v.Retrieve(ctx, []string{"StoragePod"}, []string{"name", "summary", "childEntity", "triggeredAlarmState"}, &datastoreCluster) if err != nil { return fmt.Errorf("error in retrieve from vsphere: %w", err) } @@ -110,7 +112,12 @@ func (m *DatastoreClusterMetricSet) Fetch(ctx context.Context, reporter mb.Repor m.Logger().Errorf("Failed to retrieve object from host %s: %w", datastoreCluster[i].Name, err) } - reporter.Event(mb.Event{MetricSetFields: m.mapEvent(datastoreCluster[i], &metricData{assetNames: assetNames})}) + alerts, err := getAlertNames(ctx, pc, datastoreCluster[i].TriggeredAlarmState) + if err != nil { + m.Logger().Errorf("Failed to retrieve alerts from datastore cluster %s: %w", datastoreCluster[i].Name, err) + } + + reporter.Event(mb.Event{MetricSetFields: m.mapEvent(datastoreCluster[i], &metricData{assetNames: assetNames, alertNames: alerts})}) } return nil @@ -136,3 +143,19 @@ func getAssetNames(ctx context.Context, pc *property.Collector, dsc *mo.StorageP outputDsNames: outputDsNames, }, nil } + +func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { + var alerts []string + for _, alarm := range triggeredAlarmState { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + return nil, err + } + + alerts = append(alerts, triggeredAlarm.Info.Name) + } + } + return alerts, nil +} diff --git a/metricbeat/module/vsphere/fields.go b/metricbeat/module/vsphere/fields.go index d16bde25658..45789a5a4cc 100644 --- a/metricbeat/module/vsphere/fields.go +++ b/metricbeat/module/vsphere/fields.go @@ -32,5 +32,5 @@ func init() { // AssetVsphere returns asset data. // This is the base64 encoded zlib format compressed contents of module/vsphere. func AssetVsphere() string { - return "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" + return "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" } diff --git a/metricbeat/module/vsphere/host/_meta/data.json b/metricbeat/module/vsphere/host/_meta/data.json index 5818e2d81e6..4805a6f90a9 100644 --- a/metricbeat/module/vsphere/host/_meta/data.json +++ b/metricbeat/module/vsphere/host/_meta/data.json @@ -15,13 +15,13 @@ }, "vsphere": { "host": { - "alert": { - "names": [ - "Host hardware system board status", - "Host storage status", - "CPU Utilization" - ] - }, + "alert": { + "names": [ + "Host hardware system board status", + "Host storage status", + "CPU Utilization" + ] + }, "cpu": { "used": { "mhz": 67 diff --git a/metricbeat/module/vsphere/host/host.go b/metricbeat/module/vsphere/host/host.go index 85626553910..c98cc923adc 100644 --- a/metricbeat/module/vsphere/host/host.go +++ b/metricbeat/module/vsphere/host/host.go @@ -191,17 +191,9 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error m.Logger().Debugf("For host %s,Metric %v: No result found", hst[i].Name, result.Name) } - var alerts []string - for _, alarm := range hst[i].TriggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - m.Logger().Errorf("can not retrive alarm from host %s: %v", hst[i].Name, err) - } - - alerts = append(alerts, triggeredAlarm.Info.Name) - } + alerts, err := getAlertNames(ctx, pc, hst[i].TriggeredAlarmState) + if err != nil { + m.Logger().Errorf("Failed to retrieve alerts from host %s: %w", hst[i].Name, err) } reporter.Event(mb.Event{ @@ -254,3 +246,19 @@ func getAssetNames(ctx context.Context, pc *property.Collector, hs *mo.HostSyste outputVmNames: outputVmNames, }, nil } + +func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { + var alerts []string + for _, alarm := range triggeredAlarmState { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + return nil, err + } + + alerts = append(alerts, triggeredAlarm.Info.Name) + } + } + return alerts, nil +} diff --git a/metricbeat/module/vsphere/network/_meta/fields.yml b/metricbeat/module/vsphere/network/_meta/fields.yml index c3c0903c34f..60cabaf96d8 100644 --- a/metricbeat/module/vsphere/network/_meta/fields.yml +++ b/metricbeat/module/vsphere/network/_meta/fields.yml @@ -8,6 +8,10 @@ type: boolean description: > Indicates whether at least one host is configured to provide this network. + - name: alert.names + type: keyword + description: > + List of all the alert names. - name: config.status type: keyword description: > diff --git a/metricbeat/module/vsphere/network/data.go b/metricbeat/module/vsphere/network/data.go index 6243441cee6..30a273c256d 100644 --- a/metricbeat/module/vsphere/network/data.go +++ b/metricbeat/module/vsphere/network/data.go @@ -32,6 +32,10 @@ func (m *NetworkMetricSet) mapEvent(net mo.Network, data *metricData) mapstr.M { event.Put("config.status", net.ConfigStatus) event.Put("type", net.Self.Type) + if len(data.alertNames) > 0 { + event.Put("alert.names", data.alertNames) + } + if len(data.assetNames.outputHostNames) > 0 { event.Put("host.names", data.assetNames.outputHostNames) event.Put("host.count", len(data.assetNames.outputHostNames)) diff --git a/metricbeat/module/vsphere/network/network.go b/metricbeat/module/vsphere/network/network.go index 1adbf8f5d33..0384fecbfe2 100644 --- a/metricbeat/module/vsphere/network/network.go +++ b/metricbeat/module/vsphere/network/network.go @@ -26,6 +26,7 @@ import ( "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -58,6 +59,7 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { type metricData struct { assetNames assetNames + alertNames []string } type assetNames struct { @@ -98,7 +100,7 @@ func (m *NetworkMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) er // Retrieve property for all networks var networks []mo.Network - err = v.Retrieve(ctx, []string{"Network"}, []string{"summary", "name", "overallStatus", "configStatus", "vm", "host"}, &networks) + err = v.Retrieve(ctx, []string{"Network"}, []string{"summary", "name", "overallStatus", "configStatus", "vm", "host", "triggeredAlarmState"}, &networks) if err != nil { return fmt.Errorf("error in Retrieve: %w", err) } @@ -115,8 +117,13 @@ func (m *NetworkMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) er continue } + alerts, err := getAlertNames(ctx, pc, networks[i].TriggeredAlarmState) + if err != nil { + m.Logger().Errorf("Failed to retrieve alerts from network %s: %w", networks[i].Name, err) + } + reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(networks[i], &metricData{assetNames: assetNames}), + MetricSetFields: m.mapEvent(networks[i], &metricData{assetNames: assetNames, alertNames: alerts}), }) } } @@ -154,3 +161,19 @@ func getAssetNames(ctx context.Context, pc *property.Collector, net *mo.Network) outputHostNames: outputHostNames, }, nil } + +func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { + var alerts []string + for _, alarm := range triggeredAlarmState { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + return nil, err + } + + alerts = append(alerts, triggeredAlarm.Info.Name) + } + } + return alerts, nil +} diff --git a/metricbeat/module/vsphere/resourcepool/_meta/fields.yml b/metricbeat/module/vsphere/resourcepool/_meta/fields.yml index 8b376d34472..b8dd6f02d50 100644 --- a/metricbeat/module/vsphere/resourcepool/_meta/fields.yml +++ b/metricbeat/module/vsphere/resourcepool/_meta/fields.yml @@ -4,6 +4,10 @@ description: > Resource pool information from vSphere environment. fields: + - name: alert.names + type: keyword + description: > + List of all the alert names. - name: cpu type: group fields: diff --git a/metricbeat/module/vsphere/resourcepool/data.go b/metricbeat/module/vsphere/resourcepool/data.go index c63c1883478..dcec65269a1 100644 --- a/metricbeat/module/vsphere/resourcepool/data.go +++ b/metricbeat/module/vsphere/resourcepool/data.go @@ -60,5 +60,9 @@ func (m *ResourcePoolMetricSet) mapEvent(rp mo.ResourcePool, data *metricData) m event.Put("vm.count", len(data.assetNames.outputVmNames)) } + if len(data.alertNames) > 0 { + event.Put("alert.names", data.alertNames) + } + return event } diff --git a/metricbeat/module/vsphere/resourcepool/resourcepool.go b/metricbeat/module/vsphere/resourcepool/resourcepool.go index ed54b6907fd..247955ff8b5 100644 --- a/metricbeat/module/vsphere/resourcepool/resourcepool.go +++ b/metricbeat/module/vsphere/resourcepool/resourcepool.go @@ -26,6 +26,7 @@ import ( "github.com/vmware/govmomi/property" "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/types" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -60,6 +61,7 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // metricData holds performance metrics values. type metricData struct { assetNames assetNames + alertNames []string } type assetNames struct { @@ -102,7 +104,7 @@ func (m *ResourcePoolMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV // Retrieve property for all ResourcePools. var rps []mo.ResourcePool - err = v.Retrieve(ctx, []string{"ResourcePool"}, []string{"name", "overallStatus", "vm", "summary"}, &rps) + err = v.Retrieve(ctx, []string{"ResourcePool"}, []string{"name", "overallStatus", "vm", "summary", "triggeredAlarmState"}, &rps) if err != nil { return fmt.Errorf("error in Retrieve: %w", err) } @@ -118,8 +120,13 @@ func (m *ResourcePoolMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV m.Logger().Errorf("Failed to retrieve object from resource pool %s: %w", rps[i].Name, err) } + alerts, err := getAlertNames(ctx, pc, rps[i].TriggeredAlarmState) + if err != nil { + m.Logger().Errorf("Failed to retrieve alerts from resource pool %s: %w", rps[i].Name, err) + } + reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(rps[i], &metricData{assetNames: assetNames}), + MetricSetFields: m.mapEvent(rps[i], &metricData{assetNames: assetNames, alertNames: alerts}), }) } } @@ -148,3 +155,19 @@ func getAssetNames(ctx context.Context, pc *property.Collector, rp *mo.ResourceP return assetNames{outputVmNames: outputVmNames}, nil } + +func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { + var alerts []string + for _, alarm := range triggeredAlarmState { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + return nil, err + } + + alerts = append(alerts, triggeredAlarm.Info.Name) + } + } + return alerts, nil +} diff --git a/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml b/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml index 2e0d61050df..2aee87540e5 100644 --- a/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml +++ b/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml @@ -4,6 +4,10 @@ virtualmachine release: ga fields: + - name: alert.names + type: keyword + description: > + List of all the alert names. - name: host type: group fields: diff --git a/metricbeat/module/vsphere/virtualmachine/data.go b/metricbeat/module/vsphere/virtualmachine/data.go index f7854888923..4aa330c41d5 100644 --- a/metricbeat/module/vsphere/virtualmachine/data.go +++ b/metricbeat/module/vsphere/virtualmachine/data.go @@ -74,6 +74,9 @@ func (m *MetricSet) mapEvent(data VMData) mapstr.M { event.Put("snapshot.count", len(data.Snapshots)) event.Put("snapshot.info", data.Snapshots) } + if len(data.alertNames) > 0 { + event.Put("alert.names", data.alertNames) + } return event } diff --git a/metricbeat/module/vsphere/virtualmachine/virtualmachine.go b/metricbeat/module/vsphere/virtualmachine/virtualmachine.go index e704061f859..e0f67e750aa 100644 --- a/metricbeat/module/vsphere/virtualmachine/virtualmachine.go +++ b/metricbeat/module/vsphere/virtualmachine/virtualmachine.go @@ -58,6 +58,7 @@ type VMData struct { DatastoreNames []string CustomFields mapstr.M Snapshots []VMSnapshotData + alertNames []string } type VMSnapshotData struct { @@ -136,7 +137,7 @@ func (m *MetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error { // Retrieve summary property for all machines var vmt []mo.VirtualMachine - err = v.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary", "datastore"}, &vmt) + err = v.Retrieve(ctx, []string{"VirtualMachine"}, []string{"summary", "datastore", "triggeredAlarmState"}, &vmt) if err != nil { return fmt.Errorf("virtualmachine: error in Retrieve: %w", err) } @@ -194,6 +195,11 @@ func (m *MetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error { snapshots = fetchSnapshots(vm.Snapshot.RootSnapshotList) } + alerts, err := getAlertNames(ctx, pc, vm.TriggeredAlarmState) + if err != nil { + m.Logger().Errorf("Failed to retrieve alerts from host %s: %w", vm.Name, err) + } + data := VMData{ VM: vm, HostID: hostID, @@ -202,6 +208,7 @@ func (m *MetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error { DatastoreNames: datastoreNames, CustomFields: customFields, Snapshots: snapshots, + alertNames: alerts, } reporter.Event(mb.Event{ @@ -305,3 +312,19 @@ func fetchSnapshots(snapshotTree []types.VirtualMachineSnapshotTree) []VMSnapsho } return snapshots } + +func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { + var alerts []string + for _, alarm := range triggeredAlarmState { + if alarm.OverallStatus == "red" { + var triggeredAlarm mo.Alarm + err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) + if err != nil { + return nil, err + } + + alerts = append(alerts, triggeredAlarm.Info.Name) + } + } + return alerts, nil +} From c58257b147e82d0b6e7e4f99ed51336ba4a521f4 Mon Sep 17 00:00:00 2001 From: Niraj Rathod Date: Tue, 10 Sep 2024 20:30:14 +0530 Subject: [PATCH 4/8] add changelog entry --- CHANGELOG.next.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 6eceb119ea9..9a2f3c6be98 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -329,6 +329,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - Add metrics for the vSphere Virtualmachine metricset. {pull}40485[40485] - Add support for snapshot in vSphere virtualmachine metricset {pull}40683[40683] - Update fields to use mapstr in vSphere virtualmachine metricset {pull}40707[40707] +- Add metrics related to alert in all the vSphere metricsets. {pull}40714[40714] *Metricbeat* From 6759e89bfefcba096482741c192d3640a460bcc8 Mon Sep 17 00:00:00 2001 From: Niraj Rathod Date: Tue, 10 Sep 2024 20:47:30 +0530 Subject: [PATCH 5/8] update data_test --- metricbeat/module/vsphere/cluster/data_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/metricbeat/module/vsphere/cluster/data_test.go b/metricbeat/module/vsphere/cluster/data_test.go index 097b7254283..6736b8b33f7 100644 --- a/metricbeat/module/vsphere/cluster/data_test.go +++ b/metricbeat/module/vsphere/cluster/data_test.go @@ -49,7 +49,11 @@ func TestEventMapping(t *testing.T) { outputNetworkNames: []string{"Network_0"}, } - outputEvent := m.mapEvent(ClusterTest, &assetNames) + var metricDataTest = metricData{ + assetNames: assetNames, + } + + outputEvent := m.mapEvent(ClusterTest, &metricDataTest) testEvent := mapstr.M{ "das_config": mapstr.M{ "enabled": false, From 17b531ab98fe8ac75e7d5df93373fccf4c2e0916 Mon Sep 17 00:00:00 2001 From: Niraj Rathod Date: Wed, 11 Sep 2024 18:35:44 +0530 Subject: [PATCH 6/8] add triggerd alarms --- metricbeat/docs/fields.asciidoc | 132 +++++++++--------- .../module/vsphere/cluster/_meta/fields.yml | 11 +- metricbeat/module/vsphere/cluster/cluster.go | 46 +++--- metricbeat/module/vsphere/cluster/data.go | 4 +- .../module/vsphere/datastore/_meta/fields.yml | 9 +- metricbeat/module/vsphere/datastore/data.go | 4 +- .../module/vsphere/datastore/datastore.go | 52 ++++--- .../vsphere/datastorecluster/_meta/fields.yml | 11 +- .../module/vsphere/datastorecluster/data.go | 4 +- .../datastorecluster/datastorecluster.go | 48 ++++--- metricbeat/module/vsphere/fields.go | 2 +- .../module/vsphere/host/_meta/data.json | 37 ++++- .../module/vsphere/host/_meta/fields.yml | 9 +- metricbeat/module/vsphere/host/data.go | 4 +- metricbeat/module/vsphere/host/host.go | 51 ++++--- .../module/vsphere/network/_meta/fields.yml | 9 +- metricbeat/module/vsphere/network/data.go | 4 +- metricbeat/module/vsphere/network/network.go | 46 +++--- .../vsphere/resourcepool/_meta/fields.yml | 9 +- .../module/vsphere/resourcepool/data.go | 4 +- .../vsphere/resourcepool/resourcepool.go | 46 +++--- .../vsphere/virtualmachine/_meta/fields.yml | 11 +- .../module/vsphere/virtualmachine/data.go | 4 +- .../vsphere/virtualmachine/virtualmachine.go | 45 +++--- 24 files changed, 364 insertions(+), 238 deletions(-) diff --git a/metricbeat/docs/fields.asciidoc b/metricbeat/docs/fields.asciidoc index 7dce614139e..3875fc9872e 100644 --- a/metricbeat/docs/fields.asciidoc +++ b/metricbeat/docs/fields.asciidoc @@ -66861,16 +66861,6 @@ Cluster information. -*`vsphere.cluster.alert.names`*:: -+ --- -List of all the alert names. - - -type: keyword - --- - *`vsphere.cluster.datastore.names`*:: + @@ -66965,22 +66955,22 @@ type: keyword -- -[float] -=== datastore - -datastore +*`vsphere.cluster.triggerd_alarms.*`*:: ++ +-- +List of all the triggerd alarms. +type: object -*`vsphere.datastore.alert.names`*:: -+ -- -List of all the alert names. +[float] +=== datastore + +datastore -type: keyword --- *`vsphere.datastore.capacity.free.bytes`*:: + @@ -67112,6 +67102,16 @@ type: keyword -- +*`vsphere.datastore.triggerd_alarms.*`*:: ++ +-- +List of all the triggerd alarms. + + +type: object + +-- + *`vsphere.datastore.vm.count`*:: + -- @@ -67161,16 +67161,6 @@ Datastore Cluster -*`vsphere.datastorecluster.alert.names`*:: -+ --- -List of all the alert names. - - -type: keyword - --- - *`vsphere.datastorecluster.name`*:: + -- @@ -67225,22 +67215,22 @@ type: long -- -[float] -=== host - -Host information from vSphere environment. +*`vsphere.datastorecluster.triggerd_alarms.*`*:: ++ +-- +List of all the triggerd alarms. +type: object -*`vsphere.host.alert.names`*:: -+ -- -List of all the alert names. +[float] +=== host + +Host information from vSphere environment. -type: keyword --- *`vsphere.host.cpu.used.mhz`*:: + @@ -67592,6 +67582,16 @@ type: keyword -- +*`vsphere.host.triggerd_alarms.*`*:: ++ +-- +List of all the triggerd alarms. + + +type: object + +-- + *`vsphere.host.uptime`*:: + -- @@ -67639,16 +67639,6 @@ type: boolean -- -*`vsphere.network.alert.names`*:: -+ --- -List of all the alert names. - - -type: keyword - --- - *`vsphere.network.config.status`*:: + -- @@ -67731,22 +67721,22 @@ type: long -- -[float] -=== resourcepool - -Resource pool information from vSphere environment. +*`vsphere.network.triggerd_alarms.*`*:: ++ +-- +List of all the triggerd alarms. +type: object -*`vsphere.resourcepool.alert.names`*:: -+ -- -List of all the alert names. +[float] +=== resourcepool + +Resource pool information from vSphere environment. -type: keyword --- *`vsphere.resourcepool.cpu.usage.mhz`*:: @@ -67967,22 +67957,22 @@ type: keyword -- -[float] -=== virtualmachine - -virtualmachine +*`vsphere.resourcepool.triggerd_alarms.*`*:: ++ +-- +List of all the triggerd alarms. +type: object -*`vsphere.virtualmachine.alert.names`*:: -+ -- -List of all the alert names. +[float] +=== virtualmachine + +virtualmachine -type: keyword --- *`vsphere.virtualmachine.host.id`*:: @@ -68204,6 +68194,16 @@ type: long -- +*`vsphere.virtualmachine.triggerd_alarms.*`*:: ++ +-- +List of all the triggerd alarms. + + +type: object + +-- + [[exported-fields-windows]] == Windows fields diff --git a/metricbeat/module/vsphere/cluster/_meta/fields.yml b/metricbeat/module/vsphere/cluster/_meta/fields.yml index 697ae4171ee..ded927ba59e 100644 --- a/metricbeat/module/vsphere/cluster/_meta/fields.yml +++ b/metricbeat/module/vsphere/cluster/_meta/fields.yml @@ -4,10 +4,6 @@ description: > Cluster information. fields: - - name: alert.names - type: keyword - description: > - List of all the alert names. - name: datastore type: group fields: @@ -55,4 +51,9 @@ - name: names type: keyword description: > - List of all the Network names associated with the cluster. \ No newline at end of file + List of all the Network names associated with the cluster. + - name: triggerd_alarms.* + type: object + object_type: keyword + description: > + List of all the triggerd alarms. \ No newline at end of file diff --git a/metricbeat/module/vsphere/cluster/cluster.go b/metricbeat/module/vsphere/cluster/cluster.go index 5ec5271a0d5..8a3d9459b77 100644 --- a/metricbeat/module/vsphere/cluster/cluster.go +++ b/metricbeat/module/vsphere/cluster/cluster.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -54,9 +55,17 @@ type assetNames struct { outputHostNames []string } +type triggerdAlarm struct { + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + Time time.Time `json:"time"` + Description string `json:"description"` +} + type metricData struct { - assetNames assetNames - alertNames []string + assetNames assetNames + triggerdAlarms []triggerdAlarm } // New creates a new instance of the MetricSet. @@ -126,13 +135,13 @@ func (m *ClusterMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) er m.Logger().Warn("Metric das_config.enabled not found") } - alerts, err := getAlertNames(ctx, pc, clt[i].TriggeredAlarmState) + triggerdAlarm, err := getTriggerdAlarm(ctx, pc, clt[i].TriggeredAlarmState) if err != nil { m.Logger().Errorf("Failed to retrieve alerts from cluster %s: %w", clt[i].Name, err) } reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(clt[i], &metricData{assetNames: assetNames, alertNames: alerts}), + MetricSetFields: m.mapEvent(clt[i], &metricData{assetNames: assetNames, triggerdAlarms: triggerdAlarm}), }) } } @@ -184,18 +193,23 @@ func getAssetNames(ctx context.Context, pc *property.Collector, cl *mo.ClusterCo }, nil } -func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { - var alerts []string - for _, alarm := range triggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - return nil, err - } - - alerts = append(alerts, triggeredAlarm.Info.Name) +func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]triggerdAlarm, error) { + var triggeredAlarms []triggerdAlarm + for _, alarmState := range triggeredAlarmState { + var triggeredAlarm triggerdAlarm + var alarm mo.Alarm + err := pc.RetrieveOne(ctx, alarmState.Alarm, nil, &alarm) + if err != nil { + return nil, err } + triggeredAlarm.Name = alarm.Info.Name + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.ID = alarmState.Key + triggeredAlarm.Status = string(alarmState.OverallStatus) + triggeredAlarm.Time = alarmState.Time + + triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } - return alerts, nil + + return triggeredAlarms, nil } diff --git a/metricbeat/module/vsphere/cluster/data.go b/metricbeat/module/vsphere/cluster/data.go index 60998b2d149..06cf976499b 100644 --- a/metricbeat/module/vsphere/cluster/data.go +++ b/metricbeat/module/vsphere/cluster/data.go @@ -40,8 +40,8 @@ func (m *ClusterMetricSet) mapEvent(cl mo.ClusterComputeResource, data *metricDa "name": cl.Name, } - if len(data.alertNames) > 0 { - event.Put("alert.names", data.alertNames) + if len(data.triggerdAlarms) > 0 { + event.Put("triggerd_alarms", data.triggerdAlarms) } if cl.Configuration.DasConfig.Enabled != nil { diff --git a/metricbeat/module/vsphere/datastore/_meta/fields.yml b/metricbeat/module/vsphere/datastore/_meta/fields.yml index ffdf86ab905..aec0e0955be 100644 --- a/metricbeat/module/vsphere/datastore/_meta/fields.yml +++ b/metricbeat/module/vsphere/datastore/_meta/fields.yml @@ -4,10 +4,6 @@ datastore release: ga fields: - - name: alert.names - type: keyword - description: > - List of all the alert names. - name: capacity.free.bytes type: long description: > @@ -61,6 +57,11 @@ type: keyword description: > Status of the datastore. + - name: triggerd_alarms.* + type: object + object_type: keyword + description: > + List of all the triggerd alarms. - name: vm.count type: long description: > diff --git a/metricbeat/module/vsphere/datastore/data.go b/metricbeat/module/vsphere/datastore/data.go index 9934f21058a..1cb4f11d3cf 100644 --- a/metricbeat/module/vsphere/datastore/data.go +++ b/metricbeat/module/vsphere/datastore/data.go @@ -49,8 +49,8 @@ func (m *DataStoreMetricSet) mapEvent(ds mo.Datastore, data *metricData) mapstr. }, } - if len(data.alertNames) > 0 { - event.Put("alert.names", data.alertNames) + if len(data.triggerdAlarms) > 0 { + event.Put("triggerd_alarms", data.triggerdAlarms) } if ds.Summary.Capacity > 0 { diff --git a/metricbeat/module/vsphere/datastore/datastore.go b/metricbeat/module/vsphere/datastore/datastore.go index dd33a2401cc..5b944edfbca 100644 --- a/metricbeat/module/vsphere/datastore/datastore.go +++ b/metricbeat/module/vsphere/datastore/datastore.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -54,10 +55,18 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return &DataStoreMetricSet{ms}, nil } +type triggerdAlarm struct { + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + Time time.Time `json:"time"` + Description string `json:"description"` +} + type metricData struct { - perfMetrics map[string]interface{} - assetNames assetNames - alertNames []string + perfMetrics map[string]interface{} + assetNames assetNames + triggerdAlarms []triggerdAlarm } type assetNames struct { @@ -149,16 +158,16 @@ func (m *DataStoreMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) m.Logger().Errorf("Failed to retrieve performance metrics from datastore %s: %v", dst[i].Name, err) } - alerts, err := getAlertNames(ctx, pc, dst[i].TriggeredAlarmState) + triggerdAlarm, err := getTriggerdAlarm(ctx, pc, dst[i].TriggeredAlarmState) if err != nil { m.Logger().Errorf("Failed to retrieve alerts from datastore %s: %w", dst[i].Name, err) } reporter.Event(mb.Event{ MetricSetFields: m.mapEvent(dst[i], &metricData{ - perfMetrics: metricMap, - alertNames: alerts, - assetNames: assetNames, + perfMetrics: metricMap, + triggerdAlarms: triggerdAlarm, + assetNames: assetNames, }), }) } @@ -212,20 +221,25 @@ func getAssetNames(ctx context.Context, pc *property.Collector, ds *mo.Datastore }, nil } -func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { - var alerts []string - for _, alarm := range triggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - return nil, err - } - - alerts = append(alerts, triggeredAlarm.Info.Name) +func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]triggerdAlarm, error) { + var triggeredAlarms []triggerdAlarm + for _, alarmState := range triggeredAlarmState { + var triggeredAlarm triggerdAlarm + var alarm mo.Alarm + err := pc.RetrieveOne(ctx, alarmState.Alarm, nil, &alarm) + if err != nil { + return nil, err } + triggeredAlarm.Name = alarm.Info.Name + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.ID = alarmState.Key + triggeredAlarm.Status = string(alarmState.OverallStatus) + triggeredAlarm.Time = alarmState.Time + + triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } - return alerts, nil + + return triggeredAlarms, nil } func (m *DataStoreMetricSet) getPerfMetrics(ctx context.Context, perfManager *performance.Manager, dst mo.Datastore, metricIds []types.PerfMetricId) (metricMap map[string]interface{}, err error) { diff --git a/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml b/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml index 2035ff246c4..43939b60691 100644 --- a/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml +++ b/metricbeat/module/vsphere/datastorecluster/_meta/fields.yml @@ -4,10 +4,6 @@ description: > Datastore Cluster fields: - - name: alert.names - type: keyword - description: > - List of all the alert names. - name: name type: keyword description: > @@ -29,4 +25,9 @@ - name: datastore.count type: long description: > - Number of datastores in the Datastore Cluster. \ No newline at end of file + Number of datastores in the Datastore Cluster. + - name: triggerd_alarms.* + type: object + object_type: keyword + description: > + List of all the triggerd alarms. \ No newline at end of file diff --git a/metricbeat/module/vsphere/datastorecluster/data.go b/metricbeat/module/vsphere/datastorecluster/data.go index 6cfe02c3533..fc727a6d01a 100644 --- a/metricbeat/module/vsphere/datastorecluster/data.go +++ b/metricbeat/module/vsphere/datastorecluster/data.go @@ -38,8 +38,8 @@ func (m *DatastoreClusterMetricSet) mapEvent(datastoreCluster mo.StoragePod, dat }, } - if len(data.alertNames) > 0 { - event.Put("alert.names", data.alertNames) + if len(data.triggerdAlarms) > 0 { + event.Put("triggerd_alarms", data.triggerdAlarms) } return event diff --git a/metricbeat/module/vsphere/datastorecluster/datastorecluster.go b/metricbeat/module/vsphere/datastorecluster/datastorecluster.go index 15c841caa8a..b460113d832 100644 --- a/metricbeat/module/vsphere/datastorecluster/datastorecluster.go +++ b/metricbeat/module/vsphere/datastorecluster/datastorecluster.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/property" @@ -59,8 +60,16 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } type metricData struct { - assetNames assetNames - alertNames []string + assetNames assetNames + triggerdAlarms []triggerdAlarm +} + +type triggerdAlarm struct { + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + Time time.Time `json:"time"` + Description string `json:"description"` } type assetNames struct { @@ -109,15 +118,15 @@ func (m *DatastoreClusterMetricSet) Fetch(ctx context.Context, reporter mb.Repor assetNames, err := getAssetNames(ctx, pc, &datastoreCluster[i]) if err != nil { - m.Logger().Errorf("Failed to retrieve object from host %s: v", datastoreCluster[i].Name, err) + m.Logger().Errorf("Failed to retrieve object from datastore cluster %s: v", datastoreCluster[i].Name, err) } - alerts, err := getAlertNames(ctx, pc, datastoreCluster[i].TriggeredAlarmState) + triggerdAlarm, err := getTriggerdAlarm(ctx, pc, datastoreCluster[i].TriggeredAlarmState) if err != nil { m.Logger().Errorf("Failed to retrieve alerts from datastore cluster %s: %w", datastoreCluster[i].Name, err) } - reporter.Event(mb.Event{MetricSetFields: m.mapEvent(datastoreCluster[i], &metricData{assetNames: assetNames, alertNames: alerts})}) + reporter.Event(mb.Event{MetricSetFields: m.mapEvent(datastoreCluster[i], &metricData{assetNames: assetNames, triggerdAlarms: triggerdAlarm})}) } return nil @@ -144,18 +153,23 @@ func getAssetNames(ctx context.Context, pc *property.Collector, dsc *mo.StorageP }, nil } -func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { - var alerts []string - for _, alarm := range triggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - return nil, err - } - - alerts = append(alerts, triggeredAlarm.Info.Name) +func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]triggerdAlarm, error) { + var triggeredAlarms []triggerdAlarm + for _, alarmState := range triggeredAlarmState { + var triggeredAlarm triggerdAlarm + var alarm mo.Alarm + err := pc.RetrieveOne(ctx, alarmState.Alarm, nil, &alarm) + if err != nil { + return nil, err } + triggeredAlarm.Name = alarm.Info.Name + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.ID = alarmState.Key + triggeredAlarm.Status = string(alarmState.OverallStatus) + triggeredAlarm.Time = alarmState.Time + + triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } - return alerts, nil + + return triggeredAlarms, nil } diff --git a/metricbeat/module/vsphere/fields.go b/metricbeat/module/vsphere/fields.go index 45789a5a4cc..b8277665b9f 100644 --- a/metricbeat/module/vsphere/fields.go +++ b/metricbeat/module/vsphere/fields.go @@ -32,5 +32,5 @@ func init() { // AssetVsphere returns asset data. // This is the base64 encoded zlib format compressed contents of module/vsphere. func AssetVsphere() string { - return "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" + return "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" } diff --git a/metricbeat/module/vsphere/host/_meta/data.json b/metricbeat/module/vsphere/host/_meta/data.json index 21d03b0b5d5..6121e143b0e 100644 --- a/metricbeat/module/vsphere/host/_meta/data.json +++ b/metricbeat/module/vsphere/host/_meta/data.json @@ -15,13 +15,36 @@ }, "vsphere": { "host": { - "alert": { - "names": [ - "Host hardware system board status", - "Host storage status", - "CPU Utilization" - ] - }, + "triggerd_alarms": [ + { + "name": "Host hardware system board status", + "id": "alarm-121.host-12", + "status": "red", + "time": "2024-09-09T13:23:00.786759Z", + "description": "Default alarm to monitor system boards. See the host's Hardware Status tab for more details." + }, + { + "id": "alarm-124.host-12", + "status": "red", + "time": "2024-09-09T13:23:00.786773Z", + "description": "Default alarm to monitor storage. See the host's Hardware Status tab for more details.", + "name": "Host storage status" + }, + { + "name": "Host memory usage", + "id": "alarm-4.host-12", + "status": "yellow", + "time": "2024-08-28T10:31:26.621636Z", + "description": "Default alarm to monitor host memory usage" + }, + { + "name": "CPU Utilization", + "id": "alarm-703.host-12", + "status": "red", + "time": "2024-08-28T10:31:26.621643Z", + "description": "" + } + ], "cpu": { "free": { "mhz": 4521 diff --git a/metricbeat/module/vsphere/host/_meta/fields.yml b/metricbeat/module/vsphere/host/_meta/fields.yml index 05478014d4c..584a907a850 100644 --- a/metricbeat/module/vsphere/host/_meta/fields.yml +++ b/metricbeat/module/vsphere/host/_meta/fields.yml @@ -4,10 +4,6 @@ Host information from vSphere environment. release: ga fields: - - name: alert.names - type: keyword - description: > - List of all the alert names. - name: cpu.used.mhz type: long description: > @@ -150,6 +146,11 @@ type: keyword description: > The overall health status of a host in the vSphere environment. + - name: triggerd_alarms.* + type: object + object_type: keyword + description: > + List of all the triggerd alarms. - name: uptime type: long description: > diff --git a/metricbeat/module/vsphere/host/data.go b/metricbeat/module/vsphere/host/data.go index 745668af127..45d9a69d2ee 100644 --- a/metricbeat/module/vsphere/host/data.go +++ b/metricbeat/module/vsphere/host/data.go @@ -46,8 +46,8 @@ func (m *HostMetricSet) mapEvent(hs mo.HostSystem, data *metricData) mapstr.M { m.Logger().Debug("'Hardware' or 'Summary' data not found. This is either a parsing error from vsphere library, an error trying to reach host/guest or incomplete information returned from host/guest") } - if len(data.alertNames) > 0 { - event.Put("alert.names", data.alertNames) + if len(data.triggerdAlarms) > 0 { + event.Put("triggerd_alarms", data.triggerdAlarms) } if len(data.assetNames.outputVmNames) > 0 { diff --git a/metricbeat/module/vsphere/host/host.go b/metricbeat/module/vsphere/host/host.go index 2eeb24c30ab..345ae4b4cfb 100644 --- a/metricbeat/module/vsphere/host/host.go +++ b/metricbeat/module/vsphere/host/host.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -54,10 +55,18 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return &HostMetricSet{ms}, nil } +type triggerdAlarm struct { + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + Time time.Time `json:"time"` + Description string `json:"description"` +} + type metricData struct { - perfMetrics map[string]interface{} - assetNames assetNames - alertNames []string + perfMetrics map[string]interface{} + assetNames assetNames + triggerdAlarms []triggerdAlarm } type assetNames struct { @@ -163,13 +172,13 @@ func (m *HostMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error m.Logger().Errorf("Failed to retrieve performance metrics from host %s: %v", hst[i].Name, err) } - alerts, err := getAlertNames(ctx, pc, hst[i].TriggeredAlarmState) + triggerdAlarm, err := getTriggerdAlarm(ctx, pc, hst[i].TriggeredAlarmState) if err != nil { - m.Logger().Errorf("Failed to retrieve alerts from host %s: %w", hst[i].Name, err) + m.Logger().Errorf("Failed to retrieve triggerd alarms from host %s: %w", hst[i].Name, err) } reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(hst[i], &metricData{perfMetrics: metricMap, assetNames: assetNames, alertNames: alerts}), + MetricSetFields: m.mapEvent(hst[i], &metricData{perfMetrics: metricMap, assetNames: assetNames, triggerdAlarms: triggerdAlarm}), }) } } @@ -219,20 +228,26 @@ func getAssetNames(ctx context.Context, pc *property.Collector, hs *mo.HostSyste }, nil } -func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { - var alerts []string - for _, alarm := range triggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - return nil, err - } - - alerts = append(alerts, triggeredAlarm.Info.Name) +func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]triggerdAlarm, error) { + var triggeredAlarms []triggerdAlarm + for _, alarmState := range triggeredAlarmState { + var triggeredAlarm triggerdAlarm + var alarm mo.Alarm + err := pc.RetrieveOne(ctx, alarmState.Alarm, nil, &alarm) + if err != nil { + return nil, err } + triggeredAlarm.Name = alarm.Info.Name + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.ID = alarmState.Key + triggeredAlarm.Status = string(alarmState.OverallStatus) + triggeredAlarm.Time = alarmState.Time + + triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } - return alerts, nil + + return triggeredAlarms, nil } func (m *HostMetricSet) getPerfMetrics(ctx context.Context, perfManager *performance.Manager, hst mo.HostSystem, metricIds []types.PerfMetricId) (metricMap map[string]interface{}, err error) { diff --git a/metricbeat/module/vsphere/network/_meta/fields.yml b/metricbeat/module/vsphere/network/_meta/fields.yml index 60cabaf96d8..8cc2288ea01 100644 --- a/metricbeat/module/vsphere/network/_meta/fields.yml +++ b/metricbeat/module/vsphere/network/_meta/fields.yml @@ -8,10 +8,6 @@ type: boolean description: > Indicates whether at least one host is configured to provide this network. - - name: alert.names - type: keyword - description: > - List of all the alert names. - name: config.status type: keyword description: > @@ -50,3 +46,8 @@ type: long description: > Number of virtual machines connected to this network. + - name: triggerd_alarms.* + type: object + object_type: keyword + description: > + List of all the triggerd alarms. \ No newline at end of file diff --git a/metricbeat/module/vsphere/network/data.go b/metricbeat/module/vsphere/network/data.go index 30a273c256d..d9db174db77 100644 --- a/metricbeat/module/vsphere/network/data.go +++ b/metricbeat/module/vsphere/network/data.go @@ -32,8 +32,8 @@ func (m *NetworkMetricSet) mapEvent(net mo.Network, data *metricData) mapstr.M { event.Put("config.status", net.ConfigStatus) event.Put("type", net.Self.Type) - if len(data.alertNames) > 0 { - event.Put("alert.names", data.alertNames) + if len(data.triggerdAlarms) > 0 { + event.Put("triggerd_alarms", data.triggerdAlarms) } if len(data.assetNames.outputHostNames) > 0 { diff --git a/metricbeat/module/vsphere/network/network.go b/metricbeat/module/vsphere/network/network.go index 812553b4397..faf65cbd2c3 100644 --- a/metricbeat/module/vsphere/network/network.go +++ b/metricbeat/module/vsphere/network/network.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/property" @@ -57,9 +58,17 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { return &NetworkMetricSet{ms}, nil } +type triggerdAlarm struct { + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + Time time.Time `json:"time"` + Description string `json:"description"` +} + type metricData struct { - assetNames assetNames - alertNames []string + assetNames assetNames + triggerdAlarms []triggerdAlarm } type assetNames struct { @@ -117,13 +126,13 @@ func (m *NetworkMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) er continue } - alerts, err := getAlertNames(ctx, pc, networks[i].TriggeredAlarmState) + triggerdAlarm, err := getTriggerdAlarm(ctx, pc, networks[i].TriggeredAlarmState) if err != nil { m.Logger().Errorf("Failed to retrieve alerts from network %s: %w", networks[i].Name, err) } reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(networks[i], &metricData{assetNames: assetNames, alertNames: alerts}), + MetricSetFields: m.mapEvent(networks[i], &metricData{assetNames: assetNames, triggerdAlarms: triggerdAlarm}), }) } } @@ -162,18 +171,23 @@ func getAssetNames(ctx context.Context, pc *property.Collector, net *mo.Network) }, nil } -func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { - var alerts []string - for _, alarm := range triggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - return nil, err - } - - alerts = append(alerts, triggeredAlarm.Info.Name) +func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]triggerdAlarm, error) { + var triggeredAlarms []triggerdAlarm + for _, alarmState := range triggeredAlarmState { + var triggeredAlarm triggerdAlarm + var alarm mo.Alarm + err := pc.RetrieveOne(ctx, alarmState.Alarm, nil, &alarm) + if err != nil { + return nil, err } + triggeredAlarm.Name = alarm.Info.Name + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.ID = alarmState.Key + triggeredAlarm.Status = string(alarmState.OverallStatus) + triggeredAlarm.Time = alarmState.Time + + triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } - return alerts, nil + + return triggeredAlarms, nil } diff --git a/metricbeat/module/vsphere/resourcepool/_meta/fields.yml b/metricbeat/module/vsphere/resourcepool/_meta/fields.yml index b8dd6f02d50..788a974a7fd 100644 --- a/metricbeat/module/vsphere/resourcepool/_meta/fields.yml +++ b/metricbeat/module/vsphere/resourcepool/_meta/fields.yml @@ -4,10 +4,6 @@ description: > Resource pool information from vSphere environment. fields: - - name: alert.names - type: keyword - description: > - List of all the alert names. - name: cpu type: group fields: @@ -116,3 +112,8 @@ type: keyword description: > Names of virtual machines on the resource pool. + - name: triggerd_alarms.* + type: object + object_type: keyword + description: > + List of all the triggerd alarms. diff --git a/metricbeat/module/vsphere/resourcepool/data.go b/metricbeat/module/vsphere/resourcepool/data.go index dcec65269a1..aa391a3b10c 100644 --- a/metricbeat/module/vsphere/resourcepool/data.go +++ b/metricbeat/module/vsphere/resourcepool/data.go @@ -60,8 +60,8 @@ func (m *ResourcePoolMetricSet) mapEvent(rp mo.ResourcePool, data *metricData) m event.Put("vm.count", len(data.assetNames.outputVmNames)) } - if len(data.alertNames) > 0 { - event.Put("alert.names", data.alertNames) + if len(data.triggerdAlarms) > 0 { + event.Put("triggerd_alarms", data.triggerdAlarms) } return event diff --git a/metricbeat/module/vsphere/resourcepool/resourcepool.go b/metricbeat/module/vsphere/resourcepool/resourcepool.go index 7e989184ebb..b0ba68a858e 100644 --- a/metricbeat/module/vsphere/resourcepool/resourcepool.go +++ b/metricbeat/module/vsphere/resourcepool/resourcepool.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "strings" + "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/property" @@ -59,9 +60,17 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } // metricData holds performance metrics values. +type triggerdAlarm struct { + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + Time time.Time `json:"time"` + Description string `json:"description"` +} + type metricData struct { - assetNames assetNames - alertNames []string + assetNames assetNames + triggerdAlarms []triggerdAlarm } type assetNames struct { @@ -120,13 +129,13 @@ func (m *ResourcePoolMetricSet) Fetch(ctx context.Context, reporter mb.ReporterV m.Logger().Errorf("Failed to retrieve object from resource pool %s: %v", rps[i].Name, err) } - alerts, err := getAlertNames(ctx, pc, rps[i].TriggeredAlarmState) + triggerdAlarm, err := getTriggerdAlarm(ctx, pc, rps[i].TriggeredAlarmState) if err != nil { m.Logger().Errorf("Failed to retrieve alerts from resource pool %s: %w", rps[i].Name, err) } reporter.Event(mb.Event{ - MetricSetFields: m.mapEvent(rps[i], &metricData{assetNames: assetNames, alertNames: alerts}), + MetricSetFields: m.mapEvent(rps[i], &metricData{assetNames: assetNames, triggerdAlarms: triggerdAlarm}), }) } } @@ -156,18 +165,23 @@ func getAssetNames(ctx context.Context, pc *property.Collector, rp *mo.ResourceP return assetNames{outputVmNames: outputVmNames}, nil } -func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { - var alerts []string - for _, alarm := range triggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - return nil, err - } - - alerts = append(alerts, triggeredAlarm.Info.Name) +func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]triggerdAlarm, error) { + var triggeredAlarms []triggerdAlarm + for _, alarmState := range triggeredAlarmState { + var triggeredAlarm triggerdAlarm + var alarm mo.Alarm + err := pc.RetrieveOne(ctx, alarmState.Alarm, nil, &alarm) + if err != nil { + return nil, err } + triggeredAlarm.Name = alarm.Info.Name + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.ID = alarmState.Key + triggeredAlarm.Status = string(alarmState.OverallStatus) + triggeredAlarm.Time = alarmState.Time + + triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } - return alerts, nil + + return triggeredAlarms, nil } diff --git a/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml b/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml index 2aee87540e5..3327c693adb 100644 --- a/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml +++ b/metricbeat/module/vsphere/virtualmachine/_meta/fields.yml @@ -4,10 +4,6 @@ virtualmachine release: ga fields: - - name: alert.names - type: keyword - description: > - List of all the alert names. - name: host type: group fields: @@ -107,5 +103,8 @@ - name: count type: long description: The number of snapshots of this virtualmachine. - - + - name: triggerd_alarms.* + type: object + object_type: keyword + description: > + List of all the triggerd alarms. diff --git a/metricbeat/module/vsphere/virtualmachine/data.go b/metricbeat/module/vsphere/virtualmachine/data.go index 4aa330c41d5..62d2866d8bb 100644 --- a/metricbeat/module/vsphere/virtualmachine/data.go +++ b/metricbeat/module/vsphere/virtualmachine/data.go @@ -74,8 +74,8 @@ func (m *MetricSet) mapEvent(data VMData) mapstr.M { event.Put("snapshot.count", len(data.Snapshots)) event.Put("snapshot.info", data.Snapshots) } - if len(data.alertNames) > 0 { - event.Put("alert.names", data.alertNames) + if len(data.triggerdAlarms) > 0 { + event.Put("triggerd_alarms", data.triggerdAlarms) } return event diff --git a/metricbeat/module/vsphere/virtualmachine/virtualmachine.go b/metricbeat/module/vsphere/virtualmachine/virtualmachine.go index 384e2cb9228..f13896fbe1d 100644 --- a/metricbeat/module/vsphere/virtualmachine/virtualmachine.go +++ b/metricbeat/module/vsphere/virtualmachine/virtualmachine.go @@ -50,6 +50,14 @@ type MetricSet struct { GetCustomFields bool } +type triggerdAlarm struct { + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + Time time.Time `json:"time"` + Description string `json:"description"` +} + type VMData struct { VM mo.VirtualMachine HostID string @@ -58,7 +66,7 @@ type VMData struct { DatastoreNames []string CustomFields mapstr.M Snapshots []VMSnapshotData - alertNames []string + triggerdAlarms []triggerdAlarm } type VMSnapshotData struct { @@ -195,9 +203,9 @@ func (m *MetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error { snapshots = fetchSnapshots(vm.Snapshot.RootSnapshotList) } - alerts, err := getAlertNames(ctx, pc, vm.TriggeredAlarmState) + triggerdAlarm, err := getTriggerdAlarm(ctx, pc, vm.TriggeredAlarmState) if err != nil { - m.Logger().Errorf("Failed to retrieve alerts from host %s: %w", vm.Name, err) + m.Logger().Errorf("Failed to retrieve alerts from VM %s: %w", vm.Name, err) } data := VMData{ @@ -208,7 +216,7 @@ func (m *MetricSet) Fetch(ctx context.Context, reporter mb.ReporterV2) error { DatastoreNames: datastoreNames, CustomFields: customFields, Snapshots: snapshots, - alertNames: alerts, + triggerdAlarms: triggerdAlarm, } reporter.Event(mb.Event{ @@ -313,18 +321,23 @@ func fetchSnapshots(snapshotTree []types.VirtualMachineSnapshotTree) []VMSnapsho return snapshots } -func getAlertNames(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]string, error) { - var alerts []string - for _, alarm := range triggeredAlarmState { - if alarm.OverallStatus == "red" { - var triggeredAlarm mo.Alarm - err := pc.RetrieveOne(ctx, alarm.Alarm, nil, &triggeredAlarm) - if err != nil { - return nil, err - } - - alerts = append(alerts, triggeredAlarm.Info.Name) +func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlarmState []types.AlarmState) ([]triggerdAlarm, error) { + var triggeredAlarms []triggerdAlarm + for _, alarmState := range triggeredAlarmState { + var triggeredAlarm triggerdAlarm + var alarm mo.Alarm + err := pc.RetrieveOne(ctx, alarmState.Alarm, nil, &alarm) + if err != nil { + return nil, err } + triggeredAlarm.Name = alarm.Info.Name + triggeredAlarm.Description = alarm.Info.Description + triggeredAlarm.ID = alarmState.Key + triggeredAlarm.Status = string(alarmState.OverallStatus) + triggeredAlarm.Time = alarmState.Time + + triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } - return alerts, nil + + return triggeredAlarms, nil } From 7dacfe8887eef14b1b1af6452302a0cd1f77b0de Mon Sep 17 00:00:00 2001 From: Niraj Rathod Date: Thu, 12 Sep 2024 16:18:06 +0530 Subject: [PATCH 7/8] added entity name --- .../module/vsphere/cluster/_meta/data.json | 118 +++++++++++------- metricbeat/module/vsphere/cluster/cluster.go | 34 +++-- .../module/vsphere/datastore/datastore.go | 34 +++-- .../datastorecluster/datastorecluster.go | 34 +++-- .../module/vsphere/host/_meta/data.json | 46 +++---- metricbeat/module/vsphere/host/host.go | 35 ++++-- metricbeat/module/vsphere/network/network.go | 34 +++-- .../vsphere/resourcepool/resourcepool.go | 34 +++-- .../vsphere/virtualmachine/virtualmachine.go | 33 ++++- 9 files changed, 290 insertions(+), 112 deletions(-) diff --git a/metricbeat/module/vsphere/cluster/_meta/data.json b/metricbeat/module/vsphere/cluster/_meta/data.json index 6e165063e85..7c8c435c0d9 100644 --- a/metricbeat/module/vsphere/cluster/_meta/data.json +++ b/metricbeat/module/vsphere/cluster/_meta/data.json @@ -1,47 +1,81 @@ { - "@timestamp": "2017-10-12T08:05:34.853Z", - "event": { - "dataset": "vsphere.cluster", - "duration": 115000, - "module": "vsphere" - }, - "metricset": { - "name": "cluster", - "period": 10000 - }, - "service": { - "address": "127.0.0.1:33365", - "type": "vsphere" - }, - "vsphere": { + "@timestamp": "2017-10-12T08:05:34.853Z", + "event": { + "dataset": "vsphere.cluster", + "duration": 115000, + "module": "vsphere" + }, + "metricset": { + "name": "cluster", + "period": 10000 + }, + "service": { + "address": "127.0.0.1:33365", + "type": "vsphere" + }, + "vsphere": { "cluster": { - "name": "Cluster_1", - "das_config": { - "enabled": false, - "admission": { - "control": { - "enabled": true - } + "triggerd_alarms": [ + { + "status": "red", + "triggered_time": "2024-09-09T13:23:00.786Z", + "description": "Default alarm to monitor system boards. See the host's Hardware Status tab for more details.", + "entity_name": "121.0.0.0", + "name": "Host hardware system board status", + "id": "alarm-121.host-12" + }, + { + "triggered_time": "2024-09-09T13:23:00.786Z", + "description": "Default alarm to monitor storage. See the host's Hardware Status tab for more details.", + "entity_name": "121.0.0.0", + "name": "Host storage status", + "id": "alarm-124.host-12", + "status": "red" + }, + { + "entity_name": "121.0.0.0", + "name": "Host memory usage", + "id": "alarm-4.host-12", + "status": "yellow", + "triggered_time": "2024-08-28T10:31:26.621Z", + "description": "Default alarm to monitor host memory usage" + }, + { + "name": "CPU Utilization", + "id": "alarm-703.host-12", + "status": "red", + "triggered_time": "2024-08-28T10:31:26.621Z", + "description": "", + "entity_name": "121.0.0.0" + } + ], + "name": "Cluster_1", + "das_config": { + "enabled": false, + "admission": { + "control": { + "enabled": true + } + } + }, + "host": { + "count": 1, + "names": [ + "Host_1" + ] + }, + "datastore": { + "count": 1, + "names": [ + "Datastore_1" + ] + }, + "network": { + "count": 1, + "names": [ + "Network_1" + ] } - }, - "host": { - "count": 1, - "names": [ - "Host_1" - ] - }, - "datastore": { - "count": 1, - "names": [ - "Datastore_1" - ] - }, - "network": { - "count": 1, - "names": [ - "Network_1" - ] - } } - } + } } \ No newline at end of file diff --git a/metricbeat/module/vsphere/cluster/cluster.go b/metricbeat/module/vsphere/cluster/cluster.go index 8a3d9459b77..21d51f936d7 100644 --- a/metricbeat/module/vsphere/cluster/cluster.go +++ b/metricbeat/module/vsphere/cluster/cluster.go @@ -21,8 +21,8 @@ import ( "context" "fmt" "strings" - "time" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -56,11 +56,12 @@ type assetNames struct { } type triggerdAlarm struct { - Name string `json:"name"` - ID string `json:"id"` - Status string `json:"status"` - Time time.Time `json:"time"` - Description string `json:"description"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + TriggeredTime common.Time `json:"triggered_time"` + Description string `json:"description"` + EntityName string `json:"entity_name"` } type metricData struct { @@ -203,10 +204,29 @@ func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlar return nil, err } triggeredAlarm.Name = alarm.Info.Name + + var entityName string + if alarmState.Entity.Type == "Network" { + var entity mo.Network + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } else { + var entity mo.ManagedEntity + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } + triggeredAlarm.EntityName = entityName + triggeredAlarm.Description = alarm.Info.Description triggeredAlarm.ID = alarmState.Key triggeredAlarm.Status = string(alarmState.OverallStatus) - triggeredAlarm.Time = alarmState.Time + triggeredAlarm.TriggeredTime = common.Time(alarmState.Time) triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } diff --git a/metricbeat/module/vsphere/datastore/datastore.go b/metricbeat/module/vsphere/datastore/datastore.go index 5b944edfbca..d26e2e94ace 100644 --- a/metricbeat/module/vsphere/datastore/datastore.go +++ b/metricbeat/module/vsphere/datastore/datastore.go @@ -21,8 +21,8 @@ import ( "context" "fmt" "strings" - "time" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -56,11 +56,12 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } type triggerdAlarm struct { - Name string `json:"name"` - ID string `json:"id"` - Status string `json:"status"` - Time time.Time `json:"time"` - Description string `json:"description"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + TriggeredTime common.Time `json:"triggered_time"` + Description string `json:"description"` + EntityName string `json:"entity_name"` } type metricData struct { @@ -231,10 +232,29 @@ func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlar return nil, err } triggeredAlarm.Name = alarm.Info.Name + + var entityName string + if alarmState.Entity.Type == "Network" { + var entity mo.Network + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } else { + var entity mo.ManagedEntity + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } + triggeredAlarm.EntityName = entityName + triggeredAlarm.Description = alarm.Info.Description triggeredAlarm.ID = alarmState.Key triggeredAlarm.Status = string(alarmState.OverallStatus) - triggeredAlarm.Time = alarmState.Time + triggeredAlarm.TriggeredTime = common.Time(alarmState.Time) triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } diff --git a/metricbeat/module/vsphere/datastorecluster/datastorecluster.go b/metricbeat/module/vsphere/datastorecluster/datastorecluster.go index b460113d832..762888d06f0 100644 --- a/metricbeat/module/vsphere/datastorecluster/datastorecluster.go +++ b/metricbeat/module/vsphere/datastorecluster/datastorecluster.go @@ -21,7 +21,6 @@ import ( "context" "fmt" "strings" - "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/property" @@ -29,6 +28,7 @@ import ( "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" ) @@ -65,11 +65,12 @@ type metricData struct { } type triggerdAlarm struct { - Name string `json:"name"` - ID string `json:"id"` - Status string `json:"status"` - Time time.Time `json:"time"` - Description string `json:"description"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + TriggeredTime common.Time `json:"triggered_time"` + Description string `json:"description"` + EntityName string `json:"entity_name"` } type assetNames struct { @@ -163,10 +164,29 @@ func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlar return nil, err } triggeredAlarm.Name = alarm.Info.Name + + var entityName string + if alarmState.Entity.Type == "Network" { + var entity mo.Network + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } else { + var entity mo.ManagedEntity + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } + triggeredAlarm.EntityName = entityName + triggeredAlarm.Description = alarm.Info.Description triggeredAlarm.ID = alarmState.Key triggeredAlarm.Status = string(alarmState.OverallStatus) - triggeredAlarm.Time = alarmState.Time + triggeredAlarm.TriggeredTime = common.Time(alarmState.Time) triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } diff --git a/metricbeat/module/vsphere/host/_meta/data.json b/metricbeat/module/vsphere/host/_meta/data.json index 6121e143b0e..40f34f9760a 100644 --- a/metricbeat/module/vsphere/host/_meta/data.json +++ b/metricbeat/module/vsphere/host/_meta/data.json @@ -17,34 +17,38 @@ "host": { "triggerd_alarms": [ { - "name": "Host hardware system board status", - "id": "alarm-121.host-12", - "status": "red", - "time": "2024-09-09T13:23:00.786759Z", - "description": "Default alarm to monitor system boards. See the host's Hardware Status tab for more details." + "status": "red", + "triggered_time": "2024-09-09T13:23:00.786Z", + "description": "Default alarm to monitor system boards. See the host's Hardware Status tab for more details.", + "entity_name": "121.0.0.0", + "name": "Host hardware system board status", + "id": "alarm-121.host-12" }, { - "id": "alarm-124.host-12", - "status": "red", - "time": "2024-09-09T13:23:00.786773Z", - "description": "Default alarm to monitor storage. See the host's Hardware Status tab for more details.", - "name": "Host storage status" + "triggered_time": "2024-09-09T13:23:00.786Z", + "description": "Default alarm to monitor storage. See the host's Hardware Status tab for more details.", + "entity_name": "121.0.0.0", + "name": "Host storage status", + "id": "alarm-124.host-12", + "status": "red" }, { - "name": "Host memory usage", - "id": "alarm-4.host-12", - "status": "yellow", - "time": "2024-08-28T10:31:26.621636Z", - "description": "Default alarm to monitor host memory usage" + "entity_name": "121.0.0.0", + "name": "Host memory usage", + "id": "alarm-4.host-12", + "status": "yellow", + "triggered_time": "2024-08-28T10:31:26.621Z", + "description": "Default alarm to monitor host memory usage" }, { - "name": "CPU Utilization", - "id": "alarm-703.host-12", - "status": "red", - "time": "2024-08-28T10:31:26.621643Z", - "description": "" + "name": "CPU Utilization", + "id": "alarm-703.host-12", + "status": "red", + "triggered_time": "2024-08-28T10:31:26.621Z", + "description": "", + "entity_name": "121.0.0.0" } - ], + ], "cpu": { "free": { "mhz": 4521 diff --git a/metricbeat/module/vsphere/host/host.go b/metricbeat/module/vsphere/host/host.go index 345ae4b4cfb..e82eba22647 100644 --- a/metricbeat/module/vsphere/host/host.go +++ b/metricbeat/module/vsphere/host/host.go @@ -21,8 +21,8 @@ import ( "context" "fmt" "strings" - "time" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" @@ -56,11 +56,12 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } type triggerdAlarm struct { - Name string `json:"name"` - ID string `json:"id"` - Status string `json:"status"` - Time time.Time `json:"time"` - Description string `json:"description"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + TriggeredTime common.Time `json:"triggered_time"` + Description string `json:"description"` + EntityName string `json:"entity_name"` } type metricData struct { @@ -238,11 +239,29 @@ func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlar return nil, err } triggeredAlarm.Name = alarm.Info.Name - triggeredAlarm.Description = alarm.Info.Description + + var entityName string + if alarmState.Entity.Type == "Network" { + var entity mo.Network + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } else { + var entity mo.ManagedEntity + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } + triggeredAlarm.EntityName = entityName + triggeredAlarm.Description = alarm.Info.Description triggeredAlarm.ID = alarmState.Key triggeredAlarm.Status = string(alarmState.OverallStatus) - triggeredAlarm.Time = alarmState.Time + triggeredAlarm.TriggeredTime = common.Time(alarmState.Time) triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } diff --git a/metricbeat/module/vsphere/network/network.go b/metricbeat/module/vsphere/network/network.go index faf65cbd2c3..d5cded4d433 100644 --- a/metricbeat/module/vsphere/network/network.go +++ b/metricbeat/module/vsphere/network/network.go @@ -21,7 +21,6 @@ import ( "context" "fmt" "strings" - "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/property" @@ -29,6 +28,7 @@ import ( "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" ) @@ -59,11 +59,12 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { } type triggerdAlarm struct { - Name string `json:"name"` - ID string `json:"id"` - Status string `json:"status"` - Time time.Time `json:"time"` - Description string `json:"description"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + TriggeredTime common.Time `json:"triggered_time"` + Description string `json:"description"` + EntityName string `json:"entity_name"` } type metricData struct { @@ -181,10 +182,29 @@ func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlar return nil, err } triggeredAlarm.Name = alarm.Info.Name + + var entityName string + if alarmState.Entity.Type == "Network" { + var entity mo.Network + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } else { + var entity mo.ManagedEntity + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } + triggeredAlarm.EntityName = entityName + triggeredAlarm.Description = alarm.Info.Description triggeredAlarm.ID = alarmState.Key triggeredAlarm.Status = string(alarmState.OverallStatus) - triggeredAlarm.Time = alarmState.Time + triggeredAlarm.TriggeredTime = common.Time(alarmState.Time) triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } diff --git a/metricbeat/module/vsphere/resourcepool/resourcepool.go b/metricbeat/module/vsphere/resourcepool/resourcepool.go index b0ba68a858e..c512f07c703 100644 --- a/metricbeat/module/vsphere/resourcepool/resourcepool.go +++ b/metricbeat/module/vsphere/resourcepool/resourcepool.go @@ -21,7 +21,6 @@ import ( "context" "fmt" "strings" - "time" "github.com/vmware/govmomi" "github.com/vmware/govmomi/property" @@ -29,6 +28,7 @@ import ( "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/types" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" ) @@ -61,11 +61,12 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { // metricData holds performance metrics values. type triggerdAlarm struct { - Name string `json:"name"` - ID string `json:"id"` - Status string `json:"status"` - Time time.Time `json:"time"` - Description string `json:"description"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + TriggeredTime common.Time `json:"triggered_time"` + Description string `json:"description"` + EntityName string `json:"entity_name"` } type metricData struct { @@ -175,10 +176,29 @@ func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlar return nil, err } triggeredAlarm.Name = alarm.Info.Name + + var entityName string + if alarmState.Entity.Type == "Network" { + var entity mo.Network + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } else { + var entity mo.ManagedEntity + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } + triggeredAlarm.EntityName = entityName + triggeredAlarm.Description = alarm.Info.Description triggeredAlarm.ID = alarmState.Key triggeredAlarm.Status = string(alarmState.OverallStatus) - triggeredAlarm.Time = alarmState.Time + triggeredAlarm.TriggeredTime = common.Time(alarmState.Time) triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } diff --git a/metricbeat/module/vsphere/virtualmachine/virtualmachine.go b/metricbeat/module/vsphere/virtualmachine/virtualmachine.go index f13896fbe1d..de7b014dfe3 100644 --- a/metricbeat/module/vsphere/virtualmachine/virtualmachine.go +++ b/metricbeat/module/vsphere/virtualmachine/virtualmachine.go @@ -24,6 +24,7 @@ import ( "strings" "time" + "github.com/elastic/beats/v7/libbeat/common" "github.com/elastic/beats/v7/metricbeat/mb" "github.com/elastic/beats/v7/metricbeat/module/vsphere" "github.com/elastic/elastic-agent-libs/mapstr" @@ -51,11 +52,12 @@ type MetricSet struct { } type triggerdAlarm struct { - Name string `json:"name"` - ID string `json:"id"` - Status string `json:"status"` - Time time.Time `json:"time"` - Description string `json:"description"` + Name string `json:"name"` + ID string `json:"id"` + Status string `json:"status"` + TriggeredTime common.Time `json:"triggered_time"` + Description string `json:"description"` + EntityName string `json:"entity_name"` } type VMData struct { @@ -331,10 +333,29 @@ func getTriggerdAlarm(ctx context.Context, pc *property.Collector, triggeredAlar return nil, err } triggeredAlarm.Name = alarm.Info.Name + + var entityName string + if alarmState.Entity.Type == "Network" { + var entity mo.Network + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } else { + var entity mo.ManagedEntity + if err := pc.RetrieveOne(ctx, alarmState.Entity, []string{"name"}, &entity); err != nil { + return nil, err + } + + entityName = entity.Name + } + triggeredAlarm.EntityName = entityName + triggeredAlarm.Description = alarm.Info.Description triggeredAlarm.ID = alarmState.Key triggeredAlarm.Status = string(alarmState.OverallStatus) - triggeredAlarm.Time = alarmState.Time + triggeredAlarm.TriggeredTime = common.Time(alarmState.Time) triggeredAlarms = append(triggeredAlarms, triggeredAlarm) } From d04d6d4a9fb45ff83b1efecf839e9ae9fe7192ec Mon Sep 17 00:00:00 2001 From: harnish-elastic Date: Fri, 13 Sep 2024 16:46:28 +0530 Subject: [PATCH 8/8] mage check --- metricbeat/module/vsphere/fields.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metricbeat/module/vsphere/fields.go b/metricbeat/module/vsphere/fields.go index b8277665b9f..09f24acb349 100644 --- a/metricbeat/module/vsphere/fields.go +++ b/metricbeat/module/vsphere/fields.go @@ -32,5 +32,5 @@ func init() { // AssetVsphere returns asset data. // This is the base64 encoded zlib format compressed contents of module/vsphere. func AssetVsphere() string { - return "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" + return "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" }