Skip to content

Commit

Permalink
fix a bug with using static passwords and not returning up == 2 for p…
Browse files Browse the repository at this point in the history
…rometheus scrape, add ability to use multiple vault paths for credential retrieval, update variable names for clarity
  • Loading branch information
derrick-dacosta committed Feb 25, 2024
1 parent daa57b5 commit a0f9d74
Show file tree
Hide file tree
Showing 61 changed files with 10,198 additions and 404 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ project adheres to [Semantic Versioning](http://semver.org/) and this change
log is based on the [Keep a CHANGELOG](http://keepachangelog.com/) project.

## Unreleased
- Added HPE DL380 Gen10 support
- Enhanced drive metrics collection for DL380 model servers to include NVME, Storage Disk Drives, and Logical Drives

## Added
- Add ability to reference different vault paths for credential retrieval [#25](https://github.com/Comcast/fishymetrics/issues/25)
- Added HPE DL380 Gen10 support [#17](https://github.com/Comcast/fishymetrics/issues/17)
- Enhanced drive metrics collection for DL380 model servers to include NVME, Storage Disk Drives, and Logical Drives [#17](https://github.com/Comcast/fishymetrics/issues/17)
- add ability to send logs directly to elasticsearch endpoints [#10](https://github.com/Comcast/fishymetrics/issues/10)

## Fixed
- Cisco UCS C220 - add additional edge cases when collecting memory metrics [#2](https://github.com/Comcast/fishymetrics/issues/2)
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1

FROM golang:1.19 as build-env
FROM golang:1.21 as build-env
COPY . /go/src/github.com/comcast/fishymetrics
WORKDIR /go/src/github.com/comcast/fishymetrics

Expand Down
121 changes: 65 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,59 +17,58 @@ Current device models supported
To run it:

```bash
./fishymetrics [flags]
```

Help on flags:

```bash
./fishymetrics --help
```

```bash
-log-path string
directory path where log files are written (default "/var/log/fishymetrics")
-password string
OOB static password
-port string
exporter port (default "9533")
-scheme string
OOB Scheme to use (default "https")
-timeout duration
OOB scrape timeout (default 15s)
-user string
OOB static username
-vault-addr string
Vault address to get chassis credentials from (default "https://vault.com")
-vault-kv2-mount-path string
Vault config path where kv2 secrets are mounted (default "kv2")
-vault-kv2-password-field string
Vault kv2 secret field where we get the password (default "password")
-vault-kv2-path string
Vault path where kv2 secrets will be retreived (default "path/to/secrets")
-vault-kv2-user-field string
Vault kv2 secret field where we get the username (default "user")
-vault-role-id string
Vault Role ID for AppRole
-vault-secret-id string
Vault Secret ID for AppRole
$ ./fishymetrics --help
usage: fishymetrics [<flags>]

redfish api exporter with all the bells and whistles

Flags:
-h, --help Show context-sensitive help (also try --help-long and --help-man).
--user="" BMC static username
--password="" BMC static password
--timeout=15s BMC scrape timeout
--scheme="https" BMC Scheme to use
--log.method=[file|vector]
alternative method for logging in addition to stdout
--log.file-path="/var/log/fishymetrics"
directory path where log files are written if log-method is file
--log.file-max-size=256 max file size in megabytes if log-method is file
--log.file-max-backups=1 max file backups before they are rotated if log-method is file
--log.file-max-age=1 max file age in days before they are rotated if log-method is file
--vector.endpoint="http://0.0.0.0:4444"
vector endpoint to send structured json logs to
--port="9533" exporter port
--vault.addr="https://vault.com"
Vault instance address to get chassis credentials from
--vault.role-id="" Vault Role ID for AppRole
--vault.secret-id="" Vault Secret ID for AppRole
--credential.profiles=CREDENTIAL.PROFILES
profile(s) with all necessary parameters to obtain BMC credential from secrets backend, i.e.

--credential.profiles="
profiles:
- name: profile1
mountPath: "kv2"
path: "path/to/secret"
userField: "user"
passwordField: "password"
...
"

--credential.profiles='{"profiles":[{"name":"profile1","mountPath":"kv2","path":"path/to/secret","userField":"user","passwordField":"password"},...]}'
```
Or set the following ENV Variables:
```bash
USERNAME=<string>
PASSWORD=<string>
OOB_TIMEOUT=<duration> (Default: 15s)
OOB_SCHEME=<string> (Default: https)
BMC_USERNAME=<string>
BMC_PASSWORD=<string>
BMC_TIMEOUT=<duration> (Default: 15s)
BMC_SCHEME=<string> (Default: https)
EXPORTER_PORT=<int> (Default: 9533)
LOG_PATH=<string> (Default: /var/log/fishymetrics)
VAULT_ADDRESS=<string>
VAULT_ROLE_ID=<string>
VAULT_SECRET_ID=<string>
VAULT_KV2_PATH=<string>
VAULT_KV2_MOUNT_PATH=<string>
VAULT_KV2_USER_FIELD=<string>
VAULT_KV2_PASS_FIELD=<string>
```
```bash
./fishymetrics
Expand All @@ -86,7 +85,7 @@ _if deployed on ones localhost_
</aside>
```bash
http://localhost:9533/info
curl http://localhost:9533/info
```
### metrics URL
Expand All @@ -98,32 +97,42 @@ _if deployed on ones localhost_
</aside>
```bash
http://localhost:9533/metrics
curl http://localhost:9533/metrics
```
### redfish API `/scrape`
To test a scrape of a host's redfish API, you can curl `fishymetrics`
```bash
curl 'http://localhost:9533/scrape?module=<module-name>&target=1.2.3.4'
```
If you have a credential profile configured you can add the extra URL query parameter
```bash
curl 'http://localhost:9533/scrape?module=<module-name>&target=1.2.3.4&credential_profile=<profile-name>'
```
### Docker
To run the fishymetrics exporter as a Docker container, run:
To run the fishymetrics exporter as a Docker container using static crdentials, run:
#### Using ENV variables
```bash
docker run --name fishymetrics -d -p <EXPORTER_PORT>:<EXPORTER_PORT> \
-e HP_USERNAME='<user>' \
-e HP_PASSWORD='<password>' \
-e CISCO_USERNAME='<user>' \
-e CISCO_PASSWORD='<password>' \
-e OOB_TIMEOUT=15s \
-e BMC_USERNAME='<user>' \
-e BMC_PASSWORD='<password>' \
-e BMC_TIMEOUT=15s \
-e EXPORTER_PORT=1234 \
comcast/fishymetrics:latest
```
#### Using command line args
```bash
docker run --name fishymetrics -d -p <EXPORTER_PORT>:<EXPORTER_PORT> \
-hp-user '<user>' \
-hp-password '<password>' \
-cisco-user '<user>' \
-cisco-password '<password>' \
-user '<user>' \
-password '<password>' \
-timeout 15s \
-port 1234 \
comcast/fishymetrics:latest
Expand Down
39 changes: 21 additions & 18 deletions cisco/c220/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"io"
"net"
"net/http"
"net/url"
Expand Down Expand Up @@ -77,6 +77,7 @@ type Exporter struct {
mutex sync.RWMutex
pool *pool.Pool
host string
credProfile string
biosVersion string
chassisSerialNumber string

Expand All @@ -85,7 +86,7 @@ type Exporter struct {
}

// NewExporter returns an initialized Exporter for Cisco UCS C220 device.
func NewExporter(ctx context.Context, target, uri string) (*Exporter, error) {
func NewExporter(ctx context.Context, target, uri, profile string) (*Exporter, error) {
var fqdn *url.URL
var tasks []*pool.Task

Expand Down Expand Up @@ -125,7 +126,7 @@ func NewExporter(ctx context.Context, target, uri string) (*Exporter, error) {
fqdn, err := url.ParseRequestURI(target)
if err != nil {
fqdn = &url.URL{
Scheme: config.GetConfig().OOBScheme,
Scheme: config.GetConfig().BMCScheme,
Host: target,
}
}
Expand Down Expand Up @@ -154,18 +155,18 @@ func NewExporter(ctx context.Context, target, uri string) (*Exporter, error) {
}

tasks = append(tasks,
pool.NewTask(common.Fetch(fqdn.String()+uri+"/Managers/CIMC", FIRMWARE, target, retryClient)))
pool.NewTask(common.Fetch(fqdn.String()+uri+"/Managers/CIMC", FIRMWARE, target, profile, retryClient)))

tasks = append(tasks,
pool.NewTask(common.Fetch(fqdn.String()+uri+"/Chassis/1/Thermal", THERMAL, target, retryClient)),
pool.NewTask(common.Fetch(fqdn.String()+uri+"/Chassis/1/Power", POWER, target, retryClient)),
pool.NewTask(common.Fetch(fqdn.String()+sysEndpoint+"/Processors/CPU1", PROCESSOR, target, retryClient)),
pool.NewTask(common.Fetch(fqdn.String()+sysEndpoint+"/Processors/CPU2", PROCESSOR, target, retryClient)))
pool.NewTask(common.Fetch(fqdn.String()+uri+"/Chassis/1/Thermal", THERMAL, target, profile, retryClient)),
pool.NewTask(common.Fetch(fqdn.String()+uri+"/Chassis/1/Power", POWER, target, profile, retryClient)),
pool.NewTask(common.Fetch(fqdn.String()+sysEndpoint+"/Processors/CPU1", PROCESSOR, target, profile, retryClient)),
pool.NewTask(common.Fetch(fqdn.String()+sysEndpoint+"/Processors/CPU2", PROCESSOR, target, profile, retryClient)))

// DIMMs
for _, dimm := range dimms.Members {
tasks = append(tasks,
pool.NewTask(common.Fetch(fqdn.String()+dimm.URL, MEMORY, target, retryClient)))
pool.NewTask(common.Fetch(fqdn.String()+dimm.URL, MEMORY, target, profile, retryClient)))
}

// Raid controller
Expand All @@ -180,12 +181,12 @@ func NewExporter(ctx context.Context, target, uri string) (*Exporter, error) {
pool.NewTask(common.FetchXML(fqdn.String()+"/nuova", DRIVE_XML, target, retryClient)))
} else {
tasks = append(tasks,
pool.NewTask(common.Fetch(fqdn.String()+sysEndpoint+"/Storage/MRAID", STORAGE_CONTROLLER, target, retryClient)))
pool.NewTask(common.Fetch(fqdn.String()+sysEndpoint+"/Storage/MRAID", STORAGE_CONTROLLER, target, profile, retryClient)))

// Disk(s) Status
for _, dsk := range raidCtrl.Drives {
tasks = append(tasks,
pool.NewTask(common.Fetch(fqdn.String()+dsk.Url, DRIVE, target, retryClient)))
pool.NewTask(common.Fetch(fqdn.String()+dsk.Url, DRIVE, target, profile, retryClient)))
}
}

Expand All @@ -198,6 +199,7 @@ func NewExporter(ctx context.Context, target, uri string) (*Exporter, error) {
ctx: ctx,
pool: p,
host: fqdn.Host,
credProfile: profile,
biosVersion: biosVer,
chassisSerialNumber: chassisSN,
up: prometheus.NewGauge(prometheus.GaugeOpts{
Expand Down Expand Up @@ -270,9 +272,10 @@ func (e *Exporter) scrape() {
// If credentials are incorrect we will add host to be ignored until manual intervention
if strings.Contains(task.Err.Error(), "401") {
common.IgnoredDevices[e.host] = common.IgnoredDevice{
Name: e.host,
Endpoint: "https://" + e.host + "/redfish/v1/Chassis",
Module: C220,
Name: e.host,
Endpoint: "https://" + e.host + "/redfish/v1/Chassis",
Module: C220,
CredentialProfile: e.credProfile,
}
log.Info("added host "+e.host+" to ignored list", zap.Any("trace_id", e.ctx.Value("traceID")))
deviceState = 2
Expand Down Expand Up @@ -675,7 +678,7 @@ func getChassisEndpoint(url, host string, client *retryablehttp.Client) (string,
return "", fmt.Errorf("HTTP status %d", resp.StatusCode)
}

body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Error reading Response Body - " + err.Error())
}
Expand Down Expand Up @@ -705,7 +708,7 @@ func getBIOSVersion(url, host string, client *retryablehttp.Client) (string, err
return "", fmt.Errorf("HTTP status %d", resp.StatusCode)
}

body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("Error reading Response Body - " + err.Error())
}
Expand Down Expand Up @@ -747,7 +750,7 @@ func getDIMMEndpoints(url, host string, client *retryablehttp.Client) (Collectio
}
}

body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return dimms, fmt.Errorf("Error reading Response Body - " + err.Error())
}
Expand Down Expand Up @@ -789,7 +792,7 @@ func checkRaidController(url, host string, client *retryablehttp.Client) (Storag
}
}

body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return scm, true, fmt.Errorf("Error reading Response Body - " + err.Error())
}
Expand Down
Loading

0 comments on commit a0f9d74

Please sign in to comment.