-
Notifications
You must be signed in to change notification settings - Fork 239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ssh exporter #2138
base: main
Are you sure you want to change the base?
ssh exporter #2138
Changes from 2 commits
757b3c3
3a39d7a
a80b98c
d62de87
82c4e2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,175 @@ | ||||||||||||||||||||
--- | ||||||||||||||||||||
canonical: https://grafana.com/docs/alloy/latest/reference/components/prometheus/prometheus.exporter.ssh/ | ||||||||||||||||||||
aliases: | ||||||||||||||||||||
- ../prometheus.exporter.ssh/ # /docs/alloy/latest/reference/components/prometheus.exporter.ssh/ | ||||||||||||||||||||
description: Learn about prometheus.exporter.ssh | ||||||||||||||||||||
title: prometheus.exporter.ssh | ||||||||||||||||||||
--- | ||||||||||||||||||||
|
||||||||||||||||||||
# prometheus.exporter.ssh | ||||||||||||||||||||
|
||||||||||||||||||||
The `prometheus.exporter.ssh` component embeds an SSH exporter for collecting metrics from remote servers over SSH and exporting them as Prometheus metrics. | ||||||||||||||||||||
|
||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add the experimental snippet to document the fact that the component is experimental |
||||||||||||||||||||
## Usage | ||||||||||||||||||||
|
||||||||||||||||||||
```alloy | ||||||||||||||||||||
prometheus.exporter.ssh "LABEL" { | ||||||||||||||||||||
// Configuration options | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
All the mandatory arguments and blocks need to be shown in the Usage section. |
||||||||||||||||||||
} | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
## Arguments | ||||||||||||||||||||
|
||||||||||||||||||||
The following arguments can be used to configure the exporter's behavior. | ||||||||||||||||||||
All arguments are optional unless specified. Omitted fields take their default values. | ||||||||||||||||||||
Comment on lines
+23
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Observation: In reviewing this I've noticed that the wording of some sections in the Prometheus topics are not consistent with the rest of the components. For example, Arguments in other sections simply state: I'll create an Issue to follow up on this. No action or suggestions for this PR. |
||||||||||||||||||||
|
||||||||||||||||||||
| Name | Type | Description | Default | Required | | ||||||||||||||||||||
| ----------------- | -------- | -------------------------------------------------- | ------- | -------- | | ||||||||||||||||||||
| `verbose_logging` | `bool` | Enable verbose logging for debugging purposes. | `false` | no | | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need such a argument? Normally users would configure the logging level through the logging block. |
||||||||||||||||||||
| `targets` | `block` | One or more target configurations for SSH metrics. | | yes | | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We don't list blocks in the table in the Arguments section. They are listed in the table in the Blocks section. |
||||||||||||||||||||
|
||||||||||||||||||||
## Blocks | ||||||||||||||||||||
|
||||||||||||||||||||
The following blocks are supported inside the definition of `prometheus.exporter.ssh`: | ||||||||||||||||||||
|
||||||||||||||||||||
| Block | Description | Required | | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add/include the |
||||||||||||||||||||
| -------------- | ----------------------------------------------------------- | -------- | | ||||||||||||||||||||
| `targets` | Configures an SSH target to collect metrics from. | yes | | ||||||||||||||||||||
| `custom_metrics` | Defines custom metrics to collect from the target server. | yes | | ||||||||||||||||||||
|
||||||||||||||||||||
### targets block | ||||||||||||||||||||
|
||||||||||||||||||||
The `targets` block defines the remote servers to connect to and the metrics to collect. It supports the following arguments: | ||||||||||||||||||||
|
||||||||||||||||||||
| Name | Type | Description | Default | Required | | ||||||||||||||||||||
| ----------------- | --------------------- | ---------------------------------------------------------------------- | ------- | -------- | | ||||||||||||||||||||
| `address` | `string` | The IP address or hostname of the target server. | | yes | | ||||||||||||||||||||
| `port` | `int` | SSH port number. | `22` | no | | ||||||||||||||||||||
| `username` | `string` | SSH username for authentication. | | yes | | ||||||||||||||||||||
| `password` | `secret` | Password for password-based SSH authentication. | | no | | ||||||||||||||||||||
| `key_file` | `string` | Path to the private key file for key-based SSH authentication. | | no | | ||||||||||||||||||||
| `command_timeout` | `int` | Timeout in seconds for each command execution over SSH. | `30` | no | | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be a duration? We rarely use dedicated seconds. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the expected behavior if command timeout > scrape timeout? |
||||||||||||||||||||
| `custom_metrics` | `block` | One or more custom metrics to collect from the target server. | | yes | | ||||||||||||||||||||
|
||||||||||||||||||||
#### Authentication | ||||||||||||||||||||
|
||||||||||||||||||||
You must provide either `password` or `key_file` for SSH authentication. If both are provided, `key_file` will be used. | ||||||||||||||||||||
|
||||||||||||||||||||
### custom_metrics block | ||||||||||||||||||||
|
||||||||||||||||||||
The `custom_metrics` block defines the metrics to collect from the target server. It supports the following arguments: | ||||||||||||||||||||
|
||||||||||||||||||||
| Name | Type | Description | Default | Required | | ||||||||||||||||||||
| -------------- | --------------------- | ---------------------------------------------------------------------------- | ------- | -------- | | ||||||||||||||||||||
| `name` | `string` | The name of the metric. | | yes | | ||||||||||||||||||||
| `command` | `string` | The command to execute over SSH to collect the metric. | | yes | | ||||||||||||||||||||
| `type` | `string` | The type of the metric (`gauge` or `counter`). | | yes | | ||||||||||||||||||||
| `help` | `string` | Help text for the metric. | | no | | ||||||||||||||||||||
| `labels` | `map(string, string)` | Key-value pairs of labels to associate with the metric. | `{}` | no | | ||||||||||||||||||||
| `parse_regex` | `string` | Regular expression to parse the command output and extract the metric value. | | no | | ||||||||||||||||||||
|
||||||||||||||||||||
#### Metric Types | ||||||||||||||||||||
|
||||||||||||||||||||
- `gauge`: Represents a numerical value that can go up or down. | ||||||||||||||||||||
- `counter`: Represents a cumulative value that only increases. | ||||||||||||||||||||
|
||||||||||||||||||||
#### parse_regex | ||||||||||||||||||||
Comment on lines
+71
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We usually don't use sub-headings under the tables, unless there's too much text and there's no other way to structure the information. |
||||||||||||||||||||
|
||||||||||||||||||||
If the command output is not a simple numeric value, use `parse_regex` to extract the numeric value from the output. | ||||||||||||||||||||
|
||||||||||||||||||||
## Exported fields | ||||||||||||||||||||
|
||||||||||||||||||||
{{< docs/shared lookup="reference/components/exporter-component-exports.md" source="alloy" version="<ALLOY_VERSION>" >}} | ||||||||||||||||||||
|
||||||||||||||||||||
## Component health | ||||||||||||||||||||
|
||||||||||||||||||||
`prometheus.exporter.ssh` is only reported as unhealthy if given an invalid configuration. In those cases, exported fields retain their last healthy values. | ||||||||||||||||||||
|
||||||||||||||||||||
## Debug information | ||||||||||||||||||||
|
||||||||||||||||||||
`prometheus.exporter.ssh` doesn't expose any component-specific debug information. | ||||||||||||||||||||
|
||||||||||||||||||||
## Debug metrics | ||||||||||||||||||||
|
||||||||||||||||||||
`prometheus.exporter.ssh` doesn't expose any component-specific debug metrics. | ||||||||||||||||||||
|
||||||||||||||||||||
## Example | ||||||||||||||||||||
|
||||||||||||||||||||
This example uses a [`prometheus.scrape` component][scrape] to collect metrics from `prometheus.exporter.ssh`: | ||||||||||||||||||||
|
||||||||||||||||||||
```alloy | ||||||||||||||||||||
prometheus.exporter.ssh "example" { | ||||||||||||||||||||
verbose_logging = true | ||||||||||||||||||||
|
||||||||||||||||||||
targets { | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using Alloy syntax arguments and blocks, I wonder if we should just have a I'm not sure what is the best way to go yet... will need to think about it. Using Alloy syntax might be ok with some upcoming features like the foreach block |
||||||||||||||||||||
address = "192.168.1.10" | ||||||||||||||||||||
port = 22 | ||||||||||||||||||||
username = "admin" | ||||||||||||||||||||
password = "password" | ||||||||||||||||||||
command_timeout = 10 | ||||||||||||||||||||
|
||||||||||||||||||||
custom_metrics { | ||||||||||||||||||||
name = "load_average" | ||||||||||||||||||||
command = "cat /proc/loadavg | awk '{print $1}'" | ||||||||||||||||||||
type = "gauge" | ||||||||||||||||||||
help = "Load average over 1 minute" | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
targets { | ||||||||||||||||||||
address = "192.168.1.11" | ||||||||||||||||||||
port = 22 | ||||||||||||||||||||
username = "monitor" | ||||||||||||||||||||
key_file = "/path/to/private.key" | ||||||||||||||||||||
command_timeout = 15 | ||||||||||||||||||||
|
||||||||||||||||||||
custom_metrics { | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the metric come with labels too? E.g. the address? If it does, it would be good to document it. |
||||||||||||||||||||
name = "disk_usage" | ||||||||||||||||||||
command = "df / | tail -1 | awk '{print $5}'" | ||||||||||||||||||||
type = "gauge" | ||||||||||||||||||||
help = "Disk usage percentage" | ||||||||||||||||||||
parse_regex = "(\\d+)%" | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
// Configure a prometheus.scrape component to collect SSH metrics. | ||||||||||||||||||||
prometheus.scrape "demo" { | ||||||||||||||||||||
targets = prometheus.exporter.ssh.example.targets | ||||||||||||||||||||
forward_to = [prometheus.remote_write.demo.receiver] | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
prometheus.remote_write "demo" { | ||||||||||||||||||||
endpoint { | ||||||||||||||||||||
url = PROMETHEUS_REMOTE_WRITE_URL | ||||||||||||||||||||
|
||||||||||||||||||||
basic_auth { | ||||||||||||||||||||
username = USERNAME | ||||||||||||||||||||
password = PASSWORD | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
Replace the following: | ||||||||||||||||||||
|
||||||||||||||||||||
- `PROMETHEUS_REMOTE_WRITE_URL`: The URL of the Prometheus remote_write-compatible server to send metrics to. | ||||||||||||||||||||
- `USERNAME`: The username to use for authentication to the `remote_write` API. | ||||||||||||||||||||
- `PASSWORD`: The password to use for authentication to the `remote_write` API. | ||||||||||||||||||||
|
||||||||||||||||||||
[scrape]: ../prometheus.scrape/ | ||||||||||||||||||||
|
||||||||||||||||||||
<!-- START GENERATED COMPATIBLE COMPONENTS --> | ||||||||||||||||||||
|
||||||||||||||||||||
## Compatible components | ||||||||||||||||||||
|
||||||||||||||||||||
`prometheus.exporter.ssh` has exports that can be consumed by the following components: | ||||||||||||||||||||
|
||||||||||||||||||||
- Components that consume [Targets](../../../compatibility/#targets-consumers) | ||||||||||||||||||||
|
||||||||||||||||||||
{{< admonition type="note" >}} | ||||||||||||||||||||
Connecting some components may not be sensible or components may require further configuration to make the connection work correctly. | ||||||||||||||||||||
Refer to the linked documentation for more details. | ||||||||||||||||||||
{{< /admonition >}} | ||||||||||||||||||||
|
||||||||||||||||||||
<!-- END GENERATED COMPATIBLE COMPONENTS --> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package ssh | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/grafana/alloy/internal/component" | ||
"github.com/grafana/alloy/internal/component/prometheus/exporter" | ||
"github.com/grafana/alloy/internal/featuregate" | ||
"github.com/grafana/alloy/internal/static/integrations" | ||
"github.com/grafana/alloy/internal/static/integrations/ssh_exporter" | ||
) | ||
|
||
func init() { | ||
component.Register(component.Registration{ | ||
Name: "prometheus.exporter.ssh", | ||
Stability: featuregate.StabilityExperimental, | ||
Args: Arguments{}, | ||
Exports: exporter.Exports{}, | ||
Build: exporter.New(createExporter, "ssh"), | ||
}) | ||
} | ||
|
||
func createExporter(opts component.Options, args component.Arguments, defaultInstanceKey string) (integrations.Integration, string, error) { | ||
a := args.(Arguments) | ||
return integrations.NewIntegrationWithInstanceKey(opts.Logger, a.Convert(), defaultInstanceKey) | ||
} | ||
|
||
type Arguments struct { | ||
VerboseLogging bool `alloy:"verbose_logging,attr,optional"` | ||
Targets []Target `alloy:"targets,block"` | ||
} | ||
|
||
func (a *Arguments) Validate() error { | ||
if len(a.Targets) == 0 { | ||
return errors.New("at least one target must be specified") | ||
} | ||
for _, target := range a.Targets { | ||
if err := target.Validate(); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (a *Arguments) Convert() *ssh_exporter.Config { | ||
targets := make([]ssh_exporter.Target, len(a.Targets)) | ||
for i, t := range a.Targets { | ||
targets[i] = t.Convert() | ||
} | ||
return &ssh_exporter.Config{ | ||
VerboseLogging: a.VerboseLogging, | ||
Targets: targets, | ||
} | ||
} | ||
|
||
type Target struct { | ||
Address string `alloy:"address,attr"` | ||
Port int `alloy:"port,attr,optional"` | ||
Username string `alloy:"username,attr,optional"` | ||
Password string `alloy:"password,attr,optional"` | ||
KeyFile string `alloy:"key_file,attr,optional"` | ||
CommandTimeout int `alloy:"command_timeout,attr,optional"` | ||
CustomMetrics []CustomMetric `alloy:"custom_metrics,block,optional"` | ||
} | ||
|
||
func (t *Target) Validate() error { | ||
if t.Address == "" { | ||
return errors.New("target address cannot be empty") | ||
} | ||
if t.Port <= 0 || t.Port > 65535 { | ||
return errors.New("invalid port") | ||
} | ||
if t.Username == "" { | ||
return errors.New("username cannot be empty") | ||
} | ||
for _, cm := range t.CustomMetrics { | ||
if err := cm.Validate(); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (t *Target) Convert() ssh_exporter.Target { | ||
customMetrics := make([]ssh_exporter.CustomMetric, len(t.CustomMetrics)) | ||
for i, cm := range t.CustomMetrics { | ||
customMetrics[i] = cm.Convert() | ||
} | ||
return ssh_exporter.Target{ | ||
Address: t.Address, | ||
Port: t.Port, | ||
Username: t.Username, | ||
Password: t.Password, | ||
KeyFile: t.KeyFile, | ||
CommandTimeout: t.CommandTimeout, | ||
CustomMetrics: customMetrics, | ||
} | ||
} | ||
|
||
type CustomMetric struct { | ||
Name string `alloy:"name,attr"` | ||
Command string `alloy:"command,attr"` | ||
Type string `alloy:"type,attr"` | ||
Help string `alloy:"help,attr,optional"` | ||
Labels map[string]string `alloy:"labels,attr,optional"` | ||
ParseRegex string `alloy:"parse_regex,attr,optional"` | ||
} | ||
|
||
func (cm *CustomMetric) Validate() error { | ||
if cm.Name == "" { | ||
return errors.New("custom metric name cannot be empty") | ||
} | ||
if cm.Command == "" { | ||
return errors.New("custom metric command cannot be empty") | ||
} | ||
if cm.Type != "gauge" && cm.Type != "counter" { | ||
return errors.New("unsupported metric type") | ||
} | ||
return nil | ||
} | ||
|
||
func (cm *CustomMetric) Convert() ssh_exporter.CustomMetric { | ||
return ssh_exporter.CustomMetric{ | ||
Name: cm.Name, | ||
Command: cm.Command, | ||
Type: cm.Type, | ||
Help: cm.Help, | ||
Labels: cm.Labels, | ||
ParseRegex: cm.ParseRegex, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aliases are not need for this component because it's brand new (this was used for mapping after the structure of the doc was changed)