Skip to content

feat: add SolarWinds Orion provider with pull (SWIS API) and webhook support#6235

Open
alexanderxfgl-bit wants to merge 9 commits intokeephq:mainfrom
alexanderxfgl-bit:feat/solarwinds-provider
Open

feat: add SolarWinds Orion provider with pull (SWIS API) and webhook support#6235
alexanderxfgl-bit wants to merge 9 commits intokeephq:mainfrom
alexanderxfgl-bit:feat/solarwinds-provider

Conversation

@alexanderxfgl-bit
Copy link
Copy Markdown

@alexanderxfgl-bit alexanderxfgl-bit commented Apr 10, 2026

Summary

Adds comprehensive SolarWinds Orion provider to Keep with both pull-based (SWIS REST API) and push-based (webhook) alert ingestion.

Features

  • Pull-based alerts: Fetches active alerts from SolarWinds Orion via SWIS API using SWQL queries

    • Queries Orion.AlertActive view for active alerts
    • Maps severity levels (0-5) to Keep severities
    • Maps node status and acknowledgement state
    • Includes node details (caption, IP, group)
    • Supports both username/password and API token authentication
  • Push-based alerts (webhook): Receives alerts via webhooks

    • Case-insensitive field matching for flexibility
    • Supports multiple payload formats
    • Maps SolarWinds statuses (Up, Down, Warning, Critical) to Keep
    • Includes URL and IP address support
  • Comprehensive configuration:

    • HTTP Basic Auth (username + password)
    • Bearer token auth (API token)
    • SSL verification toggle
    • Proper error handling
  • Full test coverage:

    • Webhook formatting tests (15+ test cases)
    • SWIS API pull tests
    • Edge cases (timestamps, minimal payloads, etc.)
    • Auth validation tests

Related Issue

Closes #3526

/claim #3526


Note

Medium Risk
Adds a new external-integration provider with credential handling and outbound HTTP calls to the SolarWinds SWIS API, which could affect alert ingestion correctness and operational reliability if mappings or error handling are off.

Overview
Introduces a new SolarWinds Orion provider (keep/providers/solarwinds_provider) that supports both pull-based alert fetching via SWIS/SWQL and push-based ingestion via webhooks, including severity/status mapping, timestamp parsing, and optional basic-auth vs bearer-token authentication.

Adds provider documentation (README.md) and a comprehensive test suite covering webhook payload normalization and SWIS polling behavior, including auth validation and error cases.

Reviewed by Cursor Bugbot for commit 005a6a8. Bugbot is set up for automated code reviews on this repo. Configure here.

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Apr 10, 2026
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@alexanderxfgl-bit
Copy link
Copy Markdown
Author

Key advantages of this implementation over existing PRs:

  1. Pull-based support via SWIS API - Existing PRs (feat: add solarwinds provider #5555, feat: add SolarWinds monitoring provider #5527, feat(providers): add SolarWinds Orion monitoring provider #5566) only have webhook support. This implementation also supports automatic alert polling via the SolarWinds SWIS REST API using SWQL queries.

  2. Better webhook handling - Case-insensitive field matching supports multiple payload formats from different SolarWinds versions.

  3. Proper auth config - Supports both username/password (HTTP Basic) and API token (Bearer) auth. Existing PRs had inconsistent auth configurations.

  4. Comprehensive tests - 20+ test cases covering webhook formatting, SWIS API calls, edge cases, auth validation. Existing PR tests had bugs (referenced fields that don't exist).

  5. Full documentation - Includes README with setup instructions, severity mapping, troubleshooting, and field mappings.

  6. Proper error handling - Connection errors, HTTP errors, SSL issues are handled gracefully.

Implementation details:

  • Queries Orion.AlertActive view for active alerts
  • Maps SolarWinds severities (0-5) to Keep severities (LOW/INFO/WARNING/HIGH/CRITICAL)
  • Maps node status and acknowledgement state
  • Includes node details (caption, IP address, groups)
  • Supports timestamps in multiple formats (ISO 8601, Unix seconds, Unix milliseconds)

@dosubot dosubot bot added Feature A new feature Provider Providers related issues labels Apr 10, 2026
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 5 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 005a6a8. Configure here.

"LastNote": "",
"AlertDefName": "High CPU Load",
"NodeCaption": "server1",
"IP_Address": "10.0.0.1",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test mock uses wrong key for IP address

Medium Severity

The test mock data uses key IP_Address but the SWQL query aliases it as NodeIP (n.IP_Address AS NodeIP), and the production code reads row.get("NodeIP"). Since the mock response contains IP_Address instead of NodeIP, the ip_address field will be None, causing the assertion alerts[0].ip_address == "10.0.0.1" to fail.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 005a6a8. Configure here.

cm = ContextManager(tenant_id="test", workflow_id="test")
provider = SolarwindsProvider(cm, "test-solarwinds", config)
with pytest.raises(ValueError, match="requires either"):
provider.validate_config()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test raises ValueError before pytest.raises context block

Medium Severity

In test_validate_config_no_auth_fails, the SolarwindsProvider constructor calls validate_config() via BaseProvider.__init__, so the ValueError is raised at line 238 during construction — before the with pytest.raises block at line 239. This makes the test fail with an uncaught exception rather than testing what it intends.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 005a6a8. Configure here.

name = alert_name or node_name or "SolarWinds Alert"

# Build fingerprint-compatible id
alert_id = event.get("id") or event.get("AlertObjectID") or event.get("alert_id")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alert ID extraction skips case-insensitive matching

Medium Severity

The alert_id extraction uses event.get() directly (case-sensitive), while all other field extractions use the _get() helper for case-insensitive matching. Webhook payloads with differently-cased ID keys (e.g., alertobjectid or AlertObjectId) will not be recognized, causing unnecessary fallback to generated IDs and potential deduplication issues.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 005a6a8. Configure here.

if key in event:
return event[key]
# Try case-insensitive match
lower_event = {k.lower(): v for k, v in event.items()}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lowercase dict rebuilt per key per _get call

Low Severity

The _get() helper reconstructs lower_event via dict comprehension on every loop iteration for every key checked. Since _get is called 9+ times with up to 5 keys each, this creates up to ~45 redundant dictionary copies. Building the lowercase mapping once before the loop (or once per _format_alert call) would be cleaner and more efficient.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 005a6a8. Configure here.

}
alert = SolarwindsProvider._format_alert(event)
assert alert.lastReceived is not None
assert "2025" in alert.lastReceived
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test asserts wrong year for Unix timestamp value

Low Severity

The Unix timestamp 1705312200 corresponds to approximately January 15, 2024, not 2025. The assertion "2025" in alert.lastReceived will fail because datetime.fromtimestamp(1705312200) produces a 2024 date. Either the timestamp value or the expected year in the assertion is wrong.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 005a6a8. Configure here.

- Fix test assertion: Unix timestamp 1705312200 is 2024 not 2025
- Fix test_validate_config_no_auth_fails: move constructor inside pytest.raises
- Add NodeIP to mock data to match SWQL query alias
- Use _get() for case-insensitive alert_id extraction
- Cache lowercase dict outside _get() loop for efficiency
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 11, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@alexanderxfgl-bit
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@alexanderxfgl-bit
Copy link
Copy Markdown
Author

CLA recheck requested.

- Use case-insensitive lookup for alert_id in pull mode (line 318)
- Fix Unix timestamp in test: 1705312200 (2024) -> 1736899200 (2025)
- Cache lowercase event dict to avoid rebuilding per _get call
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 12, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

- Remove duplicate IP_Address key from test mock data (production code
  reads NodeIP via SWQL alias, not IP_Address)
- Fix incorrect timestamp comment: 1736899200 = Jan 15 2024, not 2025

The other 3 cursor bot findings were false positives:
- pytest.raises placement is correct (ValueError fires inside block)
- alert_id already uses _get() for case-insensitive matching
- lowercase dict is already built once before _get() function
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 13, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

…havior

Pydantic v1 stores Enum fields as their string values (e.g., 'firing'
instead of AlertStatus.FIRING). Updated all test assertions to compare
against string values, matching how other Keep provider tests work
(e.g., uptimekuma_provider).

Changes:
- AlertStatus enum comparisons -> string values ('firing', 'resolved', 'acknowledged')
- AlertSeverity enum comparisons -> string values ('critical', 'high', 'warning', 'info')
- Timestamp assertion relaxed to check date/time components (AlertDto normalizes format)
- Fixed comment: 1736899200 = Jan 15 2025 (not 2024)
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 13, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@alexanderxfgl-bit
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

Improve the case-insensitive field lookup helper to use dict.get()
instead of 'in' + separate key access, reducing redundant lookups.
Add clarifying docstring about _lower_event closure behavior.

Addresses BugBot review feedback (cursor/bugbot).
@cursor
Copy link
Copy Markdown

cursor bot commented Apr 14, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🙋 Bounty claim Feature A new feature Provider Providers related issues size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[🔌 Provider]: Solarwinds

2 participants