beats icon indicating copy to clipboard operation
beats copied to clipboard

[meraki] Add switchport status filtering for device health metrics

Open gpop63 opened this issue 2 weeks ago • 6 comments

Proposed commit message

This PR adds a new switchport_statuses config option (defaulting to ["connected"]) to filter switchport metrics by port status, reducing number of documents.

Checklist

  • [ ] My code follows the style guidelines of this project
  • [ ] I have commented my code, particularly in hard-to-understand areas
  • [ ] I have made corresponding changes to the documentation
  • [ ] I have made corresponding change to the default configuration files
  • [ ] I have added tests that prove my fix is effective or that my feature works. Where relevant, I have used the stresstest.sh script to run them under stress conditions and race detector to verify their stability.
  • [ ] I have added an entry in ./changelog/fragments using the changelog tool.

Disruptive User Impact

Author's Checklist

  • [ ]

How to test this PR locally

Related issues

Use cases

Screenshots

Logs

gpop63 avatar Dec 09 '25 13:12 gpop63

:robot: GitHub comments

Just comment with:

  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)

github-actions[bot] avatar Dec 09 '25 13:12 github-actions[bot]

This pull request does not have a backport label. If this is a bug or security fix, could you label this PR @gpop63? 🙏. For such, you'll need to label your PR with:

  • The upcoming major version of the Elastic Stack
  • The upcoming minor version of the Elastic Stack (if you're not pushing a breaking change)

To fixup this pull request, you need to add the backport labels for the needed branches, such as:

  • backport-8./d is the label to automatically backport to the 8./d branch. /d is the digit
  • backport-active-all is the label that automatically backports to all active branches.
  • backport-active-8 is the label that automatically backports to all active minor branches for the 8 major.
  • backport-active-9 is the label that automatically backports to all active minor branches for the 9 major.

mergify[bot] avatar Dec 09 '25 13:12 mergify[bot]

🔍 Preview links for changed docs

github-actions[bot] avatar Dec 09 '25 13:12 github-actions[bot]

make sure to run nilaway

shmsr avatar Dec 09 '25 17:12 shmsr

diff --git a/x-pack/metricbeat/module/meraki/config_test.go b/x-pack/metricbeat/module/meraki/config_test.go
index 363ae89755..03ef925d44 100644
--- a/x-pack/metricbeat/module/meraki/config_test.go
+++ b/x-pack/metricbeat/module/meraki/config_test.go
@@ -21,33 +21,33 @@ func TestDefaultConfig(t *testing.T) {
 
 func TestConfigValidate(t *testing.T) {
 	// Missing BaseURL
-	cfg := &config{ApiKey: "key", Organizations: []string{"org"}, Period: 10 * time.Second}
+	cfg := &config{ApiKey: "key", Organizations: []string{"org"}, Period: 10 * time.Second, SwitchportStatuses: []string{"connected"}}
 	cfg.BaseURL = ""
 	err := cfg.Validate()
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "apiBaseURL is required")
 
 	// Missing ApiKey
-	cfg = &config{BaseURL: "url", Organizations: []string{"org"}, Period: 10 * time.Second}
+	cfg = &config{BaseURL: "url", Organizations: []string{"org"}, Period: 10 * time.Second, SwitchportStatuses: []string{"connected"}}
 	cfg.ApiKey = ""
 	err = cfg.Validate()
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "apiKey is required")
 
 	// Missing Organizations
-	cfg = &config{BaseURL: "url", ApiKey: "key", Organizations: []string{}, Period: 10 * time.Second}
+	cfg = &config{BaseURL: "url", ApiKey: "key", Organizations: []string{}, Period: 10 * time.Second, SwitchportStatuses: []string{"connected"}}
 	err = cfg.Validate()
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "organizations is required")
 
 	// Period too long
-	cfg = &config{BaseURL: "url", ApiKey: "key", Organizations: []string{"org"}, Period: 301 * time.Second}
+	cfg = &config{BaseURL: "url", ApiKey: "key", Organizations: []string{"org"}, Period: 301 * time.Second, SwitchportStatuses: []string{"connected"}}
 	err = cfg.Validate()
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "maximum allowed collection period")
 
 	// Valid config
-	cfg = &config{BaseURL: "url", ApiKey: "key", Organizations: []string{"org"}, Period: 10 * time.Second}
+	cfg = &config{BaseURL: "url", ApiKey: "key", Organizations: []string{"org"}, Period: 10 * time.Second, SwitchportStatuses: []string{"connected"}}
 	err = cfg.Validate()
 	assert.NoError(t, err)
 }
diff --git a/x-pack/metricbeat/module/meraki/device_health/switchports.go b/x-pack/metricbeat/module/meraki/device_health/switchports.go
index 6b1ca764c9..bd3621d8f5 100644
--- a/x-pack/metricbeat/module/meraki/device_health/switchports.go
+++ b/x-pack/metricbeat/module/meraki/device_health/switchports.go
@@ -28,17 +28,19 @@ type switchport struct {
 // filterSwitchportsByStatus filters switchports by their status, comparing against the allowed statuses.
 // The comparison is case insensitive. Switchports with nil portStatus are excluded.
 func filterSwitchportsByStatus(switchports []*switchport, statusesToReport []string) []*switchport {
+	// Pre-compute lowercase allowed statuses for efficient lookup
+	allowedStatuses := make(map[string]struct{}, len(statusesToReport))
+	for _, status := range statusesToReport {
+		allowedStatuses[strings.ToLower(status)] = struct{}{}
+	}
+
 	var filtered []*switchport
 	for _, sp := range switchports {
 		if sp.portStatus == nil {
 			continue
 		}
-		portStatus := strings.ToLower(sp.portStatus.Status)
-		for _, allowed := range statusesToReport {
-			if portStatus == strings.ToLower(allowed) {
-				filtered = append(filtered, sp)
-				break
-			}
+		if _, ok := allowedStatuses[strings.ToLower(sp.portStatus.Status)]; ok {
+			filtered = append(filtered, sp)
 		}
 	}
 	return filtered
diff --git a/x-pack/metricbeat/module/meraki/device_health/switchports_test.go b/x-pack/metricbeat/module/meraki/device_health/switchports_test.go
index 5062ffb2d7..3e23c792da 100644
--- a/x-pack/metricbeat/module/meraki/device_health/switchports_test.go
+++ b/x-pack/metricbeat/module/meraki/device_health/switchports_test.go
@@ -154,6 +154,22 @@ func TestFilterSwitchportsByStatus(t *testing.T) {
 			expectedCount:    0,
 			expectedPortIDs:  []string{},
 		},
+		{
+			name: "empty statusesToReport returns no ports",
+			switchports: []*switchport{
+				{
+					port:       &sdk.ResponseItemSwitchGetOrganizationSwitchPortsBySwitchPorts{PortID: "1"},
+					portStatus: &sdk.ResponseItemSwitchGetDeviceSwitchPortsStatuses{PortID: "1", Status: "Connected"},
+				},
+				{
+					port:       &sdk.ResponseItemSwitchGetOrganizationSwitchPortsBySwitchPorts{PortID: "2"},
+					portStatus: &sdk.ResponseItemSwitchGetDeviceSwitchPortsStatuses{PortID: "2", Status: "Disconnected"},
+				},
+			},
+			statusesToReport: []string{},
+			expectedCount:    0,
+			expectedPortIDs:  []string{},
+		},
 	}
 
 	for _, tt := range tests {

shmsr avatar Dec 09 '25 20:12 shmsr

Pinging @elastic/elastic-agent-data-plane (Team:Elastic-Agent-Data-Plane)

elasticmachine avatar Dec 10 '25 07:12 elasticmachine