forked from open-telemetry/opentelemetry-collector-contrib
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[exporter/bmchelix] New component: BMC Helix Exporter (open-telemetry…
…#36964) #### Description This pull request introduces a new component for exporting metrics to BMC Helix. The changes include adding the new component to various configuration files, creating necessary documentation, and implementing the component's configuration and factory logic. Key changes include: ##### New Component Addition: * Added a new changelog entry for the BMC Helix exporter in `.chloggen/bmchelixexporter-new-component.yaml`. * Updated `.github/CODEOWNERS` to include the new BMC Helix exporter. ##### Documentation: * Created `README.md` for the BMC Helix exporter with detailed setup instructions and examples. ##### Configuration and Factory Implementation: * Implemented configuration struct and validation logic in `config.go`. * Created tests for the configuration in `config_test.go`. * Added factory methods for creating the exporter in `factory.go`. * Created tests for the factory methods in `factory_test.go`. ##### Miscellaneous: * Included the common Makefile in `exporter/bmchelixexporter/Makefile`. * Added package documentation in `doc.go`. #### Link to tracking issue Fixes open-telemetry#36773 --------- Co-authored-by: Bertrand Martin <[email protected]>
- Loading branch information
1 parent
0d6600e
commit 25635d5
Showing
21 changed files
with
826 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Use this changelog template to create an entry for release notes. | ||
|
||
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' | ||
change_type: new_component | ||
|
||
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) | ||
component: exporter/bmchelix | ||
|
||
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). | ||
note: "Add a new component for exporting metrics to BMC Helix" | ||
|
||
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. | ||
issues: [36773] | ||
|
||
# (Optional) One or more lines of additional information to render under the primary note. | ||
# These lines will be padded with 2 spaces and then inserted directly into the document. | ||
# Use pipe (|) for multiline entries. | ||
subtext: | ||
|
||
# If your change doesn't affect end users or the exported elements of any package, | ||
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. | ||
# Optional: The change log or logs in which this entry should be included. | ||
# e.g. '[user]' or '[user, api]' | ||
# Include 'user' if the change is relevant to end users. | ||
# Include 'api' if there is a change to a library API. | ||
# Default: '[user]' | ||
change_logs: [user] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include ../../Makefile.Common |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# BMC Helix Exporter | ||
|
||
<!-- status autogenerated section --> | ||
| Status | | | ||
| ------------- |-----------| | ||
| Stability | [development]: metrics | | ||
| Distributions | [] | | ||
| Issues | [](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fbmchelix) [](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fbmchelix) | | ||
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@bertysentry](https://www.github.com/bertysentry), [@NassimBtk](https://www.github.com/NassimBtk) | | ||
|
||
[development]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#development | ||
<!-- end autogenerated section --> | ||
|
||
This exporter supports sending metrics to [BMC Helix Operations Management](https://www.bmc.com/it-solutions/bmc-helix-operations-management.html) through its [metric ingestion REST API](https://docs.bmc.com/docs/helixoperationsmanagement/244/en/metric-operation-management-endpoints-in-the-rest-api-1392780044.html). | ||
|
||
## Getting Started | ||
|
||
The following settings are **required**: | ||
|
||
- `endpoint`: is the *BMC Helix Portal URL* of your environment, at **onbmc.com** for a BMC Helix SaaS tenant (e.g., `https://company.onbmc.com`), or your own Helix Portal URL for an on-prem instance. | ||
- `api_key`: API key to authenticate the exporter. Connect to BMC Helix Operations Management, go to the Administration > Repository page, and click on the Copy API Key button to get your API Key. Alternatively, it is recommended to create and use a dedicated [authentication key for external integration](https://docs.bmc.com/docs/helixportal244/using-api-keys-for-external-integrations-1391501992.html). | ||
|
||
Example: | ||
|
||
```yaml | ||
exporters: | ||
bmchelix/helix1: | ||
endpoint: https://company.onbmc.com | ||
api_key: <api-key> | ||
``` | ||
### Optional Settings | ||
The following settings can be **optionally configured**: | ||
- `timeout`: (default = `10s`) Timeout for requests made to the BMC Helix. | ||
- `retry_on_failure` [details here](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/exporterhelper#configuration) | ||
- `enabled` (default = true) | ||
- `initial_interval` (default = 5s) Time to wait after the first failure before retrying; ignored if `enabled` is false. | ||
- `max_interval` (default = 30s) The upper bound on backoff; ignored if `enabled` is false. | ||
- `max_elapsed_time` (default = 300s) The maximum amount of time spent trying to send a batch; ignored if `enabled` is false. If set to 0, the retries are never stopped. | ||
|
||
Example: | ||
|
||
```yaml | ||
exporters: | ||
bmchelix/helix2: | ||
endpoint: https://company.onbmc.com | ||
api_key: <api-key> | ||
timeout: 20s | ||
retry_on_failure: | ||
enabled: true | ||
initial_interval: 5s | ||
max_interval: 1m | ||
max_elapsed_time: 8m | ||
``` | ||
|
||
--- | ||
|
||
## Setting Required Attributes for Metrics | ||
|
||
To ensure metrics are correctly populated in BMC Helix, the following attributes must be set either at the *Resource* level, or at the *Metric* level: | ||
|
||
- `entityName`: Unique identifier for the entity. Used as display name if `instanceName` is missing. | ||
- `entityTypeId`: Type identifier for the entity. | ||
- `instanceName`: Display name of the entity. | ||
|
||
> **Note:** If `entityName` or `entityTypeId` is missing, the metric will not be exported. | ||
|
||
To ensure the necessary attributes are present, it is recommended to leverage the [transform processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/transformprocessor) with [OTTL](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl), and include it in the configuration of the telemetry pipeline. | ||
|
||
The minimal pipeline most often looks like: `OTEL metrics --> (batch/memory limit) --> transform processor --> bmchelix exporter`. | ||
|
||
### Transformer Example for Hardware Metrics | ||
|
||
You can use the following OpenTelemetry Transformation Language (OTTL) configuration to map these attributes dynamically: | ||
|
||
```yaml | ||
transform/hw_to_helix: | ||
# Apply transformations to all metrics | ||
metric_statements: | ||
- context: datapoint | ||
statements: | ||
# Create a new attribute 'entityName' with the value of 'id' | ||
- set(attributes["entityName"], attributes["id"]) where attributes["id"] != nil | ||
# Create a new attribute 'instanceName' with the value of 'name' | ||
- set(attributes["instanceName"], attributes["name"]) where attributes["name"] != nil | ||
- context: datapoint | ||
conditions: | ||
- IsMatch(metric.name, ".*\\.agent\\..*") | ||
statements: | ||
- set(attributes["entityName"], attributes["host.id"]) where attributes["host.id"] != nil | ||
- set(attributes["instanceName"], attributes["service.name"]) where attributes["service.name"] != nil | ||
- set(attributes["entityTypeId"], "agent") | ||
- context: datapoint | ||
statements: | ||
# Mapping entityTypeId based on metric names and attributes | ||
- set(attributes["entityTypeId"], "connector") where IsMatch(metric.name, ".*\\.connector\\..*") | ||
- set(attributes["entityTypeId"], "host") where IsMatch(metric.name, ".*\\.host\\..*") or attributes["hw.type"] == "host" | ||
- set(attributes["entityTypeId"], "battery") where IsMatch(metric.name, "hw\\.battery\\..*") or attributes["hw.type"] == "battery" | ||
- set(attributes["entityTypeId"], "blade") where IsMatch(metric.name, "hw\\.blade\\..*") or attributes["hw.type"] == "blade" | ||
- set(attributes["entityTypeId"], "cpu") where IsMatch(metric.name, "hw\\.cpu\\..*") or attributes["hw.type"] == "cpu" | ||
- set(attributes["entityTypeId"], "disk_controller") where IsMatch(metric.name, "hw\\.disk_controller\\..*") or attributes["hw.type"] == "disk_controller" | ||
- set(attributes["entityTypeId"], "enclosure") where IsMatch(metric.name, "hw\\.enclosure\\..*") or attributes["hw.type"] == "enclosure" | ||
- set(attributes["entityTypeId"], "fan") where IsMatch(metric.name, "hw\\.fan\\..*") or attributes["hw.type"] == "fan" | ||
- set(attributes["entityTypeId"], "gpu") where IsMatch(metric.name, "hw\\.gpu\\..*") or attributes["hw.type"] == "gpu" | ||
- set(attributes["entityTypeId"], "led") where IsMatch(metric.name, "hw\\.led\\..*") or attributes["hw.type"] == "led" | ||
- set(attributes["entityTypeId"], "logical_disk") where IsMatch(metric.name, "hw\\.logical_disk\\..*") or attributes["hw.type"] == "logical_disk" | ||
- set(attributes["entityTypeId"], "lun") where IsMatch(metric.name, "hw\\.lun\\..*") or attributes["hw.type"] == "lun" | ||
- set(attributes["entityTypeId"], "memory") where IsMatch(metric.name, "hw\\.memory\\..*") or attributes["hw.type"] == "memory" | ||
- set(attributes["entityTypeId"], "network") where IsMatch(metric.name, "hw\\.network\\..*") or attributes["hw.type"] == "network" | ||
- set(attributes["entityTypeId"], "other_device") where IsMatch(metric.name, "hw\\.other_device\\..*") or attributes["hw.type"] == "other_device" | ||
- set(attributes["entityTypeId"], "physical_disk") where IsMatch(metric.name, "hw\\.physical_disk\\..*") or attributes["hw.type"] == "physical_disk" | ||
- set(attributes["entityTypeId"], "power_supply") where IsMatch(metric.name, "hw\\.power_supply\\..*") or attributes["hw.type"] == "power_supply" | ||
- set(attributes["entityTypeId"], "robotics") where IsMatch(metric.name, "hw\\.robotics\\..*") or attributes["hw.type"] == "robotics" | ||
- set(attributes["entityTypeId"], "tape_drive") where IsMatch(metric.name, "hw\\.tape_drive\\..*") or attributes["hw.type"] == "tape_drive" | ||
- set(attributes["entityTypeId"], "temperature") where IsMatch(metric.name, "hw\\.temperature.*") or attributes["hw.type"] == "temperature" | ||
- set(attributes["entityTypeId"], "vm") where IsMatch(metric.name, "hw\\.vm\\..*") or attributes["hw.type"] == "vm" | ||
- set(attributes["entityTypeId"], "voltage") where IsMatch(metric.name, "hw\\.voltage.*") or attributes["hw.type"] == "voltage" | ||
``` | ||
|
||
This transformer dynamically sets the attributes required for BMC Helix based on metric names and resource attributes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package bmchelixexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter" | ||
|
||
import ( | ||
"errors" | ||
"time" | ||
|
||
"go.opentelemetry.io/collector/config/configretry" | ||
) | ||
|
||
// Config struct is used to store the configuration of the exporter | ||
type Config struct { | ||
Endpoint string `mapstructure:"endpoint"` | ||
APIKey string `mapstructure:"api_key"` | ||
Timeout time.Duration `mapstructure:"timeout"` | ||
RetryConfig configretry.BackOffConfig `mapstructure:"retry_on_failure"` | ||
} | ||
|
||
// validate the configuration | ||
func (c *Config) Validate() error { | ||
if c.Endpoint == "" { | ||
return errors.New("endpoint is required") | ||
} | ||
if c.APIKey == "" { | ||
return errors.New("api key is required") | ||
} | ||
if c.Timeout <= 0 { | ||
return errors.New("timeout must be a positive integer") | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package bmchelixexporter | ||
|
||
import ( | ||
"path/filepath" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/config/configretry" | ||
"go.opentelemetry.io/collector/confmap/confmaptest" | ||
|
||
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter/internal/metadata" | ||
) | ||
|
||
func TestLoadConfig(t *testing.T) { | ||
t.Parallel() | ||
|
||
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) | ||
require.NoError(t, err) | ||
|
||
tests := []struct { | ||
id component.ID | ||
expected component.Config | ||
errorMessage string | ||
}{ | ||
{ | ||
id: component.NewIDWithName(metadata.Type, "helix1"), | ||
expected: &Config{ | ||
Endpoint: "https://helix1:8080", | ||
APIKey: "api_key", | ||
Timeout: 10 * time.Second, | ||
RetryConfig: configretry.NewDefaultBackOffConfig(), | ||
}, | ||
}, | ||
{ | ||
id: component.NewIDWithName(metadata.Type, "helix2"), | ||
expected: &Config{ | ||
Endpoint: "https://helix2:8080", | ||
APIKey: "api_key", | ||
Timeout: 20 * time.Second, | ||
RetryConfig: configretry.BackOffConfig{ | ||
Enabled: true, | ||
InitialInterval: 5 * time.Second, | ||
RandomizationFactor: 0.5, | ||
Multiplier: 1.5, | ||
MaxInterval: 1 * time.Minute, | ||
MaxElapsedTime: 8 * time.Minute, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.id.String(), func(t *testing.T) { | ||
factory := NewFactory() | ||
cfg := factory.CreateDefaultConfig() | ||
|
||
sub, err := cm.Sub(tt.id.String()) | ||
require.NoError(t, err) | ||
require.NoError(t, sub.Unmarshal(cfg)) | ||
|
||
assert.NoError(t, component.ValidateConfig(cfg)) | ||
assert.Equal(t, tt.expected, cfg) | ||
}) | ||
} | ||
} | ||
|
||
func TestValidateConfig(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
config *Config | ||
err string | ||
}{ | ||
{ | ||
name: "valid_config", | ||
config: &Config{ | ||
Endpoint: "https://helix:8080", | ||
APIKey: "api_key", | ||
Timeout: 10 * time.Second, | ||
}, | ||
}, | ||
{ | ||
name: "invalid_config1", | ||
config: &Config{ | ||
APIKey: "api_key", | ||
}, | ||
err: "endpoint is required", | ||
}, | ||
{ | ||
name: "invalid_config2", | ||
config: &Config{ | ||
Endpoint: "https://helix:8080", | ||
}, | ||
err: "api key is required", | ||
}, | ||
{ | ||
name: "invalid_config3", | ||
config: &Config{ | ||
Endpoint: "https://helix:8080", | ||
APIKey: "api_key", | ||
Timeout: -1, | ||
}, | ||
err: "timeout must be a positive integer", | ||
}, | ||
{ | ||
name: "invalid_config4", | ||
config: &Config{ | ||
Endpoint: "https://helix:8080", | ||
APIKey: "api_key", | ||
Timeout: 0, | ||
}, | ||
err: "timeout must be a positive integer", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if tt.err != "" { | ||
err := tt.config.Validate() | ||
assert.Error(t, err) | ||
assert.Equal(t, tt.err, err.Error()) | ||
} else { | ||
assert.NoError(t, tt.config.Validate()) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//go:generate mdatagen metadata.yaml | ||
|
||
// Package bmchelixexporter implements an exporter that sends data to BMC Helix. | ||
package bmchelixexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter" |
Oops, something went wrong.