├── .envrc
├── VERSION
├── systemd
├── sysconfig.dellhw_exporter
└── dellhw_exporter.service
├── docs
├── images
│ └── favicon.png
├── development.md
├── installation.md
├── troubleshooting.md
├── release.md
├── index.md
├── faq.md
├── caching.md
├── collectors.md
├── configuration.md
├── zsite-notice.md
└── metrics.md
├── charts
├── prometheus-dellhw-exporter
│ ├── Makefile
│ ├── templates
│ │ ├── NOTES.txt
│ │ ├── serviceaccount.yaml
│ │ ├── service.yaml
│ │ ├── prometheusrules.yaml
│ │ ├── psp.yaml
│ │ ├── servicemonitor.yaml
│ │ ├── _helpers.tpl
│ │ └── daemonset.yaml
│ ├── .helmignore
│ ├── Chart.yaml
│ ├── README.md.gotmpl
│ ├── README.md
│ └── values.yaml
└── README.md
├── contrib
└── monitoring
│ ├── README.md
│ ├── prometheus-alerts
│ └── prometheus-alerts.yml
│ └── grafana-dashboards
│ └── Node - Dell Disk Status.json
├── renovate.json
├── NOTICE
├── .vscode
└── launch.json
├── .github
└── workflows
│ ├── test.yml
│ ├── build_release.yml
│ └── documentation.yml
├── .gitignore
├── container
└── entrypoint.sh
├── flake.lock
├── dellhw_exporter.spec.in
├── pkg
└── omreport
│ ├── util_test.go
│ └── util.go
├── flake.nix
├── .promu.yml
├── go.mod
├── mkdocs.yml
├── collector
├── chassis_info.go
├── ps.go
├── fans.go
├── version.go
├── memory.go
├── system.go
├── volts.go
├── chassis.go
├── temps.go
├── processors.go
├── nics.go
├── storage_battery.go
├── collector.go
├── storage_enclosure.go
├── ps_amps_sysboard_pwr.go
├── storage_controller.go
├── storage_vdisk.go
├── chassis_batteries.go
├── storage_pdisk.go
└── firmwares.go
├── Dockerfile
├── CODE_OF_CONDUCT.md
├── README.md
├── Makefile
├── go.sum
├── CHANGELOG.md
├── LICENSE
└── cmd
└── dellhw_exporter
└── dellhw_exporter.go
/.envrc:
--------------------------------------------------------------------------------
1 | use flake
2 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 2.0.0-rc.4
2 |
--------------------------------------------------------------------------------
/systemd/sysconfig.dellhw_exporter:
--------------------------------------------------------------------------------
1 | OPTIONS=""
2 |
--------------------------------------------------------------------------------
/docs/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/galexrt/dellhw_exporter/HEAD/docs/images/favicon.png
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: template
2 | template:
3 | $(HELM) template \
4 | dellhw-exporter \
5 | .
6 |
--------------------------------------------------------------------------------
/contrib/monitoring/README.md:
--------------------------------------------------------------------------------
1 | # Monitoring
2 |
3 | * `prometheus-alerts/` directory contains Prometheus 2 alert rules.
4 | * `grafana-dashboards/` directory contains Grafana dashboards for the dellhw_exporter.
5 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:recommended"
5 | ],
6 | "postUpdateOptions": [
7 | "gomodTidy"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/systemd/dellhw_exporter.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=DellHW Exporter
3 |
4 | [Service]
5 | User=node_exporter
6 | EnvironmentFile=/etc/sysconfig/dellhw_exporter
7 | ExecStart=/usr/sbin/dellhw_exporter $OPTIONS
8 |
9 | [Install]
10 | WantedBy=multi-user.target
11 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | dellhw_exporter
2 | Copyright 2021 Alexander Trost
3 |
4 |
5 | For the components used in this product, please refer to the `go.mod` file.
6 | The following components were used in this product as inspiration:
7 |
8 | Bosun
9 | https://github.com/bosun-monitor/bosun
10 | Copyright 2013 Stack Exchange
11 | Licensed under the MIT License
12 |
--------------------------------------------------------------------------------
/docs/development.md:
--------------------------------------------------------------------------------
1 | ## Documentation
2 |
3 | [`mkdocs`](https://www.mkdocs.org/) is used to generate documentation.
4 |
5 | ## Code
6 |
7 | Please run `go vet` and `gofmt` (+ other tools) to ensure the code you write is cleanly written.
8 |
9 | In addition to that make sure to add tests where feasible and run `go test ...` or `make test` from time to time.
10 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Launch",
6 | "type": "go",
7 | "request": "launch",
8 | "mode": "auto",
9 | "program": "${workspaceFolder}/cmd/dellhw_exporter/",
10 | "env": {},
11 | "args": []
12 | }
13 | ]
14 | }
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | The dellhw_exporter has been configured with:
2 |
3 | * Image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
4 | * (Non-default) ServiceAccount: {{ .Values.serviceAccount.create }}
5 | * PodSecurityPolicy: {{ .Values.psp.create }}
6 | * prometheus-operator ServiceMonitor: {{ .Values.serviceMonitor.enabled }}
7 | * prometheus-operator PrometheusRules: {{ .Values.prometheusRule.enabled }}
8 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create -}}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: {{ include "dellhw_exporter.serviceAccountName" . }}
6 | labels:
7 | {{- include "dellhw_exporter.labels" . | nindent 4 }}
8 | {{- with .Values.serviceAccount.annotations }}
9 | annotations:
10 | {{- toYaml . | nindent 4 }}
11 | {{- end }}
12 | {{- end }}
13 |
--------------------------------------------------------------------------------
/charts/README.md:
--------------------------------------------------------------------------------
1 | # charts
2 |
3 | ## Registry
4 |
5 | Charts are available via `https://galexrt.github.io/dellhw_exporter`:
6 |
7 | ```
8 | helm repo add dellhw_exporter https://galexrt.github.io/dellhw_exporter
9 | helm repo update
10 | ```
11 |
12 | ## dellhw_exporter
13 |
14 | The [prometheus-dellhw-exporter/](prometheus-dellhw-exporter/) chart.
15 |
16 | ***
17 |
18 | The Helm chart documentation is auto generated using [https://github.com/norwoodj/helm-docs](https://github.com/norwoodj/helm-docs).
19 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/.helmignore:
--------------------------------------------------------------------------------
1 | # Patterns to ignore when building packages.
2 | # This supports shell glob matching, relative path matching, and
3 | # negation (prefixed with !). Only one pattern per line.
4 | .DS_Store
5 | # Common VCS dirs
6 | .git/
7 | .gitignore
8 | .bzr/
9 | .bzrignore
10 | .hg/
11 | .hgignore
12 | .svn/
13 | # Common backup files
14 | *.swp
15 | *.bak
16 | *.tmp
17 | *.orig
18 | *~
19 | # Various IDEs
20 | .project
21 | .idea/
22 | *.tmproj
23 | .vscode/
24 | Makefile
25 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ include "dellhw_exporter.fullname" . }}
5 | labels:
6 | {{- include "dellhw_exporter.labels" . | nindent 4 }}
7 | spec:
8 | type: {{ .Values.service.type }}
9 | ports:
10 | - port: {{ .Values.service.port }}
11 | targetPort: http-metrics
12 | protocol: TCP
13 | name: http-metrics
14 | selector:
15 | {{- include "dellhw_exporter.selectorLabels" . | nindent 4 }}
16 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v6
14 | with:
15 | submodules: true
16 |
17 | - uses: actions/setup-go@v6
18 | with:
19 | go-version-file: 'go.mod'
20 |
21 | - name: Run tests
22 | run: |
23 | make test
24 | make promu
25 | make check_license
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 | *.tar.gz
26 | .idea/
27 | .build/
28 | .tarballs/
29 | .package/
30 | /dellhw_exporter
31 | /.bin/
32 | /.build/
33 | /.tarball/
34 |
35 | # RPM
36 | dellhw_exporter.spec
37 | .srcpackage/
38 | RPMBUILD
39 |
40 | # Docs
41 | site/
42 |
43 | # Nix
44 | .direnv/
45 |
--------------------------------------------------------------------------------
/container/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | START_DELL_SRVADMIN_SERVICES="${START_DELL_SRVADMIN_SERVICES:-true}"
4 |
5 | if [ "${START_DELL_SRVADMIN_SERVICES}" = "true" ] || [ "${START_DELL_SRVADMIN_SERVICES}" = "True" ]; then
6 | echo "Starting dell srvadmin services ..."
7 | /opt/dell/srvadmin/sbin/dsm_sa_datamgrd &
8 | /opt/dell/srvadmin/sbin/dsm_sa_eventmgrd &
9 | /opt/dell/srvadmin/sbin/dsm_sa_snmpd &
10 | /usr/libexec/instsvcdrv-helper start &
11 |
12 | wait
13 | echo "Started dell srvadmin services."
14 | else
15 | echo "Skipping start of dell srvadmin services."
16 | fi
17 |
18 | exec dellhw_exporter "$@"
19 |
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | ## Linux Installation
2 |
3 | Either use the container images available or download the binary and run it.
4 |
5 | ## Windows Installation
6 |
7 | The binary supports the proper events and signals for using as a Windows service. Checkout [kardianos/service](https://github.com/kardianos/service) for more information.
8 |
9 | Example to add the executable as a service in Windows:
10 |
11 | ```console
12 | sc.exe create "Dell OMSA Exporter" binPath="C:\Program Files\Dell\dellhw_exporter.exe" start=auto
13 | ```
14 |
15 | ## Kubernetes
16 |
17 | A Helm Chart is available at https://github.com/galexrt/dellhw_exporter/tree/main/charts/
18 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "nixpkgs": {
4 | "locked": {
5 | "lastModified": 1758198701,
6 | "narHash": "sha256-7To75JlpekfUmdkUZewnT6MoBANS0XVypW6kjUOXQwc=",
7 | "owner": "nixos",
8 | "repo": "nixpkgs",
9 | "rev": "0147c2f1d54b30b5dd6d4a8c8542e8d7edf93b5d",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "nixos",
14 | "ref": "nixos-unstable",
15 | "repo": "nixpkgs",
16 | "type": "github"
17 | }
18 | },
19 | "root": {
20 | "inputs": {
21 | "nixpkgs": "nixpkgs"
22 | }
23 | }
24 | },
25 | "root": "root",
26 | "version": 7
27 | }
28 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/prometheusrules.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.prometheusRule.enabled -}}
2 | apiVersion: monitoring.coreos.com/v1
3 | kind: PrometheusRule
4 | metadata:
5 | name: {{ include "dellhw_exporter.fullname" . }}
6 | {{- if .Values.prometheusRule.namespace }}
7 | namespace: {{ .Values.prometheusRule.namespace }}
8 | {{- end }}
9 | labels:
10 | {{- include "dellhw_exporter.labels" . | nindent 4 }}
11 | {{- if .Values.prometheusRule.additionalLabels }}
12 | {{- toYaml .Values.prometheusRule.additionalLabels | nindent 4 }}
13 | {{- end }}
14 | spec:
15 | {{- if .Values.prometheusRule.rules }}
16 | groups:
17 | - name: {{ template "dellhw_exporter.name" . }}
18 | rules: {{- toYaml .Values.prometheusRule.rules | nindent 4 }}
19 | {{- end }}
20 | {{- end }}
21 |
--------------------------------------------------------------------------------
/dellhw_exporter.spec.in:
--------------------------------------------------------------------------------
1 | Name: dellhw_exporter
2 | Version: @VERSION@
3 | Release: 1%{?dist}
4 | Summary: Prometheus exporter for Dell Hardware components using OMSA.
5 |
6 | License: Apache-2.0
7 | URL: https://github.com/galexrt/dellhw_exporter
8 | Source0: dellhw_exporter-@VERSION@.tar.gz
9 |
10 | Prefix: /usr
11 | BuildRequires: golang-bin gawk
12 | #Requires:
13 |
14 | %define debug_package %{nil}
15 |
16 |
17 | %description
18 |
19 | Prometheus exporter for Dell Hardware components using OMSA.
20 |
21 |
22 | %prep
23 | %setup -q
24 |
25 |
26 | %build
27 | make tree
28 |
29 |
30 | %install
31 | rm -rf $RPM_BUILD_ROOT
32 | make install DESTDIR=$RPM_BUILD_ROOT
33 |
34 | %files
35 | %doc
36 | %config(noreplace) %{_sysconfdir}/sysconfig/dellhw_exporter
37 | %{prefix}/lib/systemd/system/dellhw_exporter.service
38 | %{prefix}/sbin/dellhw_exporter
39 |
40 | %changelog
41 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/psp.yaml:
--------------------------------------------------------------------------------
1 | {{- if and .Values.psp.create .Values.psp.spec -}}
2 | apiVersion: policy/v1beta1
3 | kind: PodSecurityPolicy
4 | metadata:
5 | name: {{ include "dellhw_exporter.fullname" . }}
6 | spec:
7 | {{- with .Values.psp.spec }}
8 | {{- toYaml . | nindent 2 }}
9 | {{- end }}
10 | ---
11 | apiVersion: rbac.authorization.k8s.io/v1
12 | kind: ClusterRole
13 | metadata:
14 | name: {{ include "dellhw_exporter.fullname" . }}
15 | rules:
16 | - apiGroups: ['policy']
17 | resources: ['podsecuritypolicies']
18 | verbs: ['use']
19 | resourceNames:
20 | - {{ include "dellhw_exporter.fullname" . }}
21 | ---
22 | apiVersion: rbac.authorization.k8s.io/v1
23 | kind: ClusterRoleBinding
24 | metadata:
25 | name: {{ include "dellhw_exporter.fullname" . }}
26 | roleRef:
27 | kind: ClusterRole
28 | name: {{ include "dellhw_exporter.fullname" . }}
29 | apiGroup: rbac.authorization.k8s.io
30 | subjects:
31 | - kind: ServiceAccount
32 | name: {{ include "dellhw_exporter.serviceAccountName" . }}
33 | namespace: {{ .Release.Name }}
34 | {{- end }}
35 |
--------------------------------------------------------------------------------
/docs/troubleshooting.md:
--------------------------------------------------------------------------------
1 | ## No metrics being exported
2 |
3 | If you are not running the Docker container, it is probably that your OMSA / srvadmin services are not running. Start them using the following commands:
4 |
5 | ```console
6 | /opt/dell/srvadmin/sbin/srvadmin-services.sh status
7 | /opt/dell/srvadmin/sbin/srvadmin-services.sh start
8 | echo "return code: $?"
9 | ```
10 | Please note that the return code should be `0`, if not please investigate the logs of srvadmin services.
11 |
12 | When running inside the container this most of the time means
13 | Be sure to enter the container and run the following commands to verify if the kernel modules have been loaded:
14 |
15 | ```console
16 | /usr/libexec/instsvcdrv-helper status
17 | lsmod | grep -iE 'dell|dsu'
18 | ```
19 |
20 | Should the `lsmod` not contain any module named after `dell_` and / or `dsu_`, be sure to add the following read-only mounts depending on your OS, for the kernel modules directory (`/lib/modules`) and / or the kernel source / headers directory (depends hardly on the OS your are using) to the `dellhw_exporter` Docker container using `-v HOST_PATH:CONTAINER_PATH:ro` flag.
21 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: prometheus-dellhw-exporter
3 | description: A Helm chart for the dellhw_exporter
4 |
5 | # A chart can be either an 'application' or a 'library' chart.
6 | #
7 | # Application charts are a collection of templates that can be packaged into versioned archives
8 | # to be deployed.
9 | #
10 | # Library charts provide useful utilities or functions for the chart developer. They're included as
11 | # a dependency of application charts to inject those utilities and functions into the rendering
12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed.
13 | type: application
14 |
15 | # This is the chart version. This version number should be incremented each time you make changes
16 | # to the chart and its templates, including the app version.
17 | # Versions are expected to follow Semantic Versioning (https://semver.org/)
18 | version: 1.2.1
19 |
20 | # This is the version number of the application being deployed. This version number should be
21 | # incremented each time you make changes to the application. Versions are not expected to
22 | # follow Semantic Versioning. They should reflect the version the application is using.
23 | appVersion: v2.0.0-rc.4
24 |
--------------------------------------------------------------------------------
/docs/release.md:
--------------------------------------------------------------------------------
1 | ## Creating a new Release
2 |
3 | 1. Update the version.
4 | 1. [`VERSION` file](VERSION)
5 | 2. Helm chart: `charts/prometheus-dellhw-exporter/Chart.yaml` `appVersion:` line and bump the Helm chart `version:` by a patch release version.
6 | 1. Make sure to run `make helm-docs` in the root of the repo to update the helm chart docs.
7 | 2. Create an entry in the [`CHANGELOG.md` file](CHANGELOG.md).
8 | Example of a changelog entry:
9 |
10 | ```
11 | ## 1.12.0 / 2022-02-02
12 |
13 | * [ENHANCEMENT] Added Pdisk Remaining Rated Write Endurance Metric by @adityaborgaonkar
14 | * [BUGFIX] ci: fix build routine issues
15 | ```
16 |
17 | The following "kinds" of entries can be used:
18 |
19 | * `CHANGE`
20 | * `FEATURE`
21 | * `ENHANCEMENT`
22 | * `BUGFIX`
23 | 3. Commit the version increase with a commit messages in format: `version: bump to v1.12.0`
24 | 4. Create the `git` tag using `git tag v1.12.0`
25 | 5. Now push the changes and commit using `git push && git push --tags`
26 | 6. In a few minutes the new release should be available for "approval" under the [releases section](https://github.com/galexrt/dellhw_exporter/releases). Edit and save the release on GitHub and the release is complete.
27 |
--------------------------------------------------------------------------------
/pkg/omreport/util_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package omreport
18 |
19 | import (
20 | "testing"
21 |
22 | "github.com/stretchr/testify/assert"
23 | )
24 |
25 | type SeverityTestResult struct {
26 | Input string
27 | Output string
28 | }
29 |
30 | var severityTests = []SeverityTestResult{
31 | {
32 | Input: "Ok",
33 | Output: "0",
34 | },
35 | {
36 | Input: "Unknown",
37 | Output: "1",
38 | },
39 | {
40 | Input: "Non-Critical",
41 | Output: "2",
42 | },
43 | {
44 | Input: "What if a high severity issue walks into a bar?",
45 | Output: "1",
46 | },
47 | }
48 |
49 | func TestSeverity(t *testing.T) {
50 | for _, result := range severityTests {
51 | value := severity(result.Input)
52 | assert.Equal(t, result.Output, value)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | # Based upon https://github.com/the-nix-way/dev-templates
2 | {
3 | description = "Basic flake for development of dellhw_exporter";
4 |
5 | inputs = {
6 | nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
7 | };
8 |
9 | outputs = { self, nixpkgs }:
10 | let
11 | goMajorVersion = 1;
12 | goMinorVersion = 25; # Change this to update the whole stack
13 |
14 | lib = nixpkgs.lib;
15 |
16 | supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
17 | forEachSupportedSystem = f: lib.genAttrs supportedSystems (system: f {
18 | pkgs = import nixpkgs {
19 | inherit system;
20 | overlays = [ self.overlays.default ];
21 | };
22 | });
23 | in
24 | {
25 | overlays.default = final: prev: {
26 | go = final."go_${toString goMajorVersion}_${toString goMinorVersion}";
27 | };
28 |
29 | devShells = forEachSupportedSystem ({ pkgs }: {
30 | default = pkgs.mkShell {
31 | # Workaround CGO issue https://nixos.wiki/wiki/Go#Using_cgo_on_NixOS
32 | hardeningDisable = [ "fortify" ];
33 |
34 | packages = with pkgs; [
35 | # go and tools
36 | go
37 | # goimports, godoc, etc.
38 | gotools
39 | gofumpt
40 | ];
41 | };
42 | });
43 | };
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/.github/workflows/build_release.yml:
--------------------------------------------------------------------------------
1 | name: build_release
2 | on:
3 | push:
4 | tags:
5 | - 'v*'
6 | jobs:
7 | build_release:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v6
11 | with:
12 | submodules: true
13 |
14 | - uses: actions/setup-go@v6
15 | with:
16 | go-version-file: 'go.mod'
17 |
18 | - name: Run tests
19 | run: |
20 | make test
21 | make promu
22 | make check_license
23 |
24 | - name: Build and release
25 | env:
26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27 | run: |
28 | promu crossbuild
29 | promu crossbuild tarballs
30 | promu checksum .tarballs
31 | promu release .tarballs
32 |
33 | - name: Build and push to GHCR.io
34 | uses: elgohr/Publish-Docker-Github-Action@v5
35 | with:
36 | name: ${{ github.repository }}
37 | registry: ghcr.io
38 | username: galexrt
39 | password: ${{ secrets.GITHUB_TOKEN }}
40 | tag_names: true
41 |
42 | - name: Build and push to Quay.io
43 | uses: elgohr/Publish-Docker-Github-Action@v5
44 | with:
45 | name: ${{ github.repository }}
46 | registry: quay.io
47 | username: ${{ secrets.REGISTRY_QUAYIO_USERNAME }}
48 | password: ${{ secrets.REGISTRY_QUAYIO_PASSWORD }}
49 | tag_names: true
50 |
--------------------------------------------------------------------------------
/.promu.yml:
--------------------------------------------------------------------------------
1 | go:
2 | version: 1.25
3 | repository:
4 | path: github.com/galexrt/dellhw_exporter
5 | build:
6 | flags: -a -tags 'netgo static_build'
7 | ldflags: |
8 | -s
9 | -X github.com/prometheus/common/version.Version={{.Version}}
10 | -X github.com/prometheus/common/version.Revision={{.Revision}}
11 | -X github.com/prometheus/common/version.Branch={{.Branch}}
12 | -X github.com/prometheus/common/version.BuildUser={{user}}@{{host}}
13 | -X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}}
14 | binaries:
15 | - name: dellhw_exporter
16 | path: ./cmd/dellhw_exporter
17 | tarball:
18 | files:
19 | - LICENSE
20 | - NOTICE
21 | - systemd/dellhw_exporter.service
22 | - systemd/sysconfig.dellhw_exporter
23 | crossbuild:
24 | platforms:
25 | - linux/amd64
26 | - linux/386
27 | #- darwin/amd64
28 | #- darwin/386
29 | - windows/amd64
30 | #- windows/386
31 | #- freebsd/amd64
32 | #- freebsd/386
33 | #- openbsd/amd64
34 | #- openbsd/386
35 | #- netbsd/amd64
36 | #- netbsd/386
37 | #- dragonfly/amd64
38 | #- linux/arm
39 | #- linux/arm64
40 | #- freebsd/arm
41 | #- openbsd/arm
42 | #- linux/mips64
43 | #- linux/mips64le
44 | #- netbsd/arm
45 | #- linux/ppc64
46 | #- linux/ppc64le
47 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Home
2 |
3 | 
4 |
5 | Prometheus exporter for Dell Hardware components using OMSA.
6 |
7 | The exporter was originally made by [PrFalken](https://github.com/PrFalken). Due to some issues in the code, I rewrote the whole exporter using the ["node_exporter"](https://github.com/prometheus/node_exporter) pattern and therefore moved it from being a fork out, to a standalone repository.
8 |
9 | Omreport parsing functions were borrowed from the [Bosun project](https://github.com/bosun-monitor/bosun/blob/master/cmd/scollector/collectors/dell_hw.go), thank you very much for that, they are the most tedious part of the job.
10 |
11 | This exporter wraps the "omreport" command from Dell OMSA. If you can't run omreport on your system, the exporter won't export any metrics.
12 |
13 | ## Compatibility
14 |
15 | ### Tested Dell OMSA Compatibility
16 |
17 | The dellhw_exporter has been tested with the following OMSA versions:
18 |
19 | * `7.4`
20 | * `8.4`
21 | * `9.1`
22 |
23 | ### Kernel Compatibility
24 |
25 | **Please note that only kernel versions that are supported by DELL DSU / OMSA tools are working!**
26 |
27 | **State 07.06.2019**: Dell OMSA `DSU_19.05.00` is not compatible with 5.x kernel it seems (e.g., Fedora uses that kernel).
28 |
29 | Should you run into issues when using the Docker image, please follow the [Troubleshooting - No metrics being exported](troubleshooting.md#no-metrics-being-exported).
30 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/servicemonitor.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceMonitor.enabled -}}
2 | apiVersion: monitoring.coreos.com/v1
3 | kind: ServiceMonitor
4 | metadata:
5 | name: {{ include "dellhw_exporter.fullname" . }}
6 | {{- if .Values.serviceMonitor.namespace }}
7 | namespace: {{ .Values.serviceMonitor.namespace }}
8 | {{- end }}
9 | labels:
10 | {{- include "dellhw_exporter.labels" . | nindent 4 }}
11 | {{- if .Values.serviceMonitor.additionalLabels }}
12 | {{- toYaml .Values.serviceMonitor.additionalLabels | nindent 4 }}
13 | {{- end }}
14 | spec:
15 | endpoints:
16 | - port: http-metrics
17 | interval: {{ .Values.serviceMonitor.scrapeInterval }}
18 | scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }}
19 | {{- if .Values.serviceMonitor.honorLabels }}
20 | honorLabels: true
21 | {{- end }}
22 | {{- if .Values.serviceMonitor.metricRelabelings }}
23 | metricRelabelings: {{ toYaml .Values.serviceMonitor.metricRelabelings | nindent 8 }}
24 | {{- end }}
25 | {{- if .Values.serviceMonitor.namespaceSelector }}
26 | namespaceSelector: {{ toYaml .Values.serviceMonitor.namespaceSelector | nindent 4 }}
27 | {{ else }}
28 | namespaceSelector:
29 | matchNames:
30 | - {{ .Release.Namespace }}
31 | {{- end }}
32 | {{- if .Values.serviceMonitor.targetLabels }}
33 | targetLabels:
34 | {{- range .Values.serviceMonitor.targetLabels }}
35 | - {{ . }}
36 | {{- end }}
37 | {{- end }}
38 | selector:
39 | matchLabels:
40 | {{- include "dellhw_exporter.selectorLabels" . | nindent 6 }}
41 | {{- end }}
42 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/galexrt/dellhw_exporter
2 |
3 | go 1.25.1
4 |
5 | require (
6 | github.com/kardianos/service v1.2.4
7 | github.com/prometheus/client_golang v1.23.2
8 | github.com/prometheus/common v0.67.3
9 | github.com/prometheus/exporter-toolkit v0.15.0
10 | github.com/spf13/pflag v1.0.10
11 | github.com/stretchr/testify v1.11.1
12 | )
13 |
14 | require (
15 | github.com/beorn7/perks v1.0.1 // indirect
16 | github.com/cespare/xxhash/v2 v2.3.0 // indirect
17 | github.com/coreos/go-systemd/v22 v22.6.0 // indirect
18 | github.com/davecgh/go-spew v1.1.1 // indirect
19 | github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
20 | github.com/google/uuid v1.6.0 // indirect
21 | github.com/jpillora/backoff v1.0.0 // indirect
22 | github.com/mdlayher/socket v0.4.1 // indirect
23 | github.com/mdlayher/vsock v1.2.1 // indirect
24 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
25 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
26 | github.com/pmezard/go-difflib v1.0.0 // indirect
27 | github.com/prometheus/client_model v0.6.2 // indirect
28 | github.com/prometheus/procfs v0.16.1 // indirect
29 | go.yaml.in/yaml/v2 v2.4.3 // indirect
30 | golang.org/x/crypto v0.45.0 // indirect
31 | golang.org/x/net v0.47.0 // indirect
32 | golang.org/x/oauth2 v0.32.0 // indirect
33 | golang.org/x/sync v0.18.0 // indirect
34 | golang.org/x/sys v0.38.0 // indirect
35 | golang.org/x/text v0.31.0 // indirect
36 | golang.org/x/time v0.13.0 // indirect
37 | google.golang.org/protobuf v1.36.10 // indirect
38 | gopkg.in/yaml.v3 v3.0.1 // indirect
39 | )
40 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: galexrt/dellhw_exporter Documentation
2 | repo_url: https://github.com/galexrt/dellhw_exporter
3 | site_author: Alexander Trost
4 | site_description: "GitHub galexrt/dellhw_exporter project documentation"
5 | use_directory_urls: true
6 | copyright: 'Impressum | Privacy Policy | Copyright (c) Alexander Trost 2020'
7 | theme:
8 | name: material
9 | font: false
10 | favicon: images/favicon.png
11 | logo: images/favicon.png
12 | palette:
13 | scheme: 'slate'
14 | primary: 'deep purple'
15 | accent: 'lime'
16 | icon:
17 | repo: fontawesome/brands/github
18 | features:
19 | - instant
20 | - navigation.expand
21 | plugins:
22 | - search
23 | # Removed in material mkdocs 6.0
24 | # See https://squidfunk.github.io/mkdocs-material/deprecations/#bundled-plugins
25 | #- awesome-pages
26 | #- git-revision-date-localized
27 | - minify:
28 | minify_html: true
29 | minify_js: true
30 | htmlmin_opts:
31 | remove_comments: true
32 | #js_files: []
33 | - redirects:
34 | redirect_maps: {}
35 | markdown_extensions:
36 | - attr_list
37 | - meta
38 | - toc:
39 | permalink: true
40 | - tables
41 | - nl2br
42 | - admonition
43 | - pymdownx.details
44 | - pymdownx.highlight:
45 | use_pygments: true
46 | linenums: true
47 | - pymdownx.inlinehilite
48 | - pymdownx.tasklist
49 | - pymdownx.keys
50 | - pymdownx.magiclink
51 | - pymdownx.mark
52 | # Style is broken
53 | #- pymdownx.progressbar
54 | - pymdownx.snippets
55 | - pymdownx.superfences
56 | #- pymdownx.tabbed
57 |
--------------------------------------------------------------------------------
/collector/chassis_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "github.com/prometheus/client_golang/prometheus"
21 | )
22 |
23 | type chassisInfoCollector struct {
24 | current *prometheus.Desc
25 | }
26 |
27 | func init() {
28 | Factories["chassis_info"] = NewChassisInfoCollector
29 | }
30 |
31 | // NewChassisCollector returns a new chassisInfoCollector
32 | func NewChassisInfoCollector(cfg *Config) (Collector, error) {
33 | return &chassisInfoCollector{}, nil
34 | }
35 |
36 | // Update Prometheus metrics
37 | func (c *chassisInfoCollector) Update(ch chan<- prometheus.Metric) error {
38 | chassisInfo, err := or.ChassisInfo()
39 | if err != nil {
40 | return err
41 | }
42 | for _, value := range chassisInfo {
43 | c.current = prometheus.NewDesc(
44 | prometheus.BuildFQName(Namespace, "", value.Name),
45 | "Chassis info details in labels.",
46 | nil, value.Labels)
47 | ch <- prometheus.MustNewConstMetric(
48 | c.current, prometheus.GaugeValue, 0)
49 | }
50 |
51 | return nil
52 | }
53 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/README.md.gotmpl:
--------------------------------------------------------------------------------
1 | {{ template "chart.header" . }}
2 | {{ template "chart.description" . }}
3 |
4 | {{ template "chart.badgesSection" . }}
5 |
6 | ## Get Repo Info
7 |
8 | ```console
9 | helm repo add dellhw_exporter https://galexrt.github.io/dellhw_exporter
10 | helm repo update
11 | ```
12 |
13 | _See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._
14 |
15 | ## Install Chart
16 |
17 | To install the chart with the release name `my-release`:
18 |
19 | ```console
20 | helm install --namespace my-release dellhw_exporter/prometheus-dellhw-exporter
21 | ```
22 |
23 | The command deploys dellhw_exporter on the Kubernetes cluster in the default configuration.
24 |
25 | _See [configuration](#configuration) below._
26 |
27 | _See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._
28 |
29 | ### Development Build
30 |
31 | To deploy from a local build from your development environment:
32 |
33 | ```console
34 | cd charts/prometheus-dellhw-exporter
35 | helm install --namespace my-release . -f values.yaml
36 | ```
37 |
38 | ## Uninstall Chart
39 |
40 | To uninstall/delete the my-release deployment:
41 |
42 | ```console
43 | helm delete --namespace my-release
44 | ```
45 |
46 | This removes all the Kubernetes components associated with the chart and deletes the release.
47 |
48 | _See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._
49 |
50 | {{ template "chart.requirementsSection" . }}
51 |
52 | ## Configuration
53 |
54 | {{ template "chart.valuesTable" . }}
55 |
--------------------------------------------------------------------------------
/collector/ps.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type psCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["ps"] = NewPsCollector
31 | }
32 |
33 | // NewPsCollector returns new psCollector
34 | func NewPsCollector(cfg *Config) (Collector, error) {
35 | return &psCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *psCollector) Update(ch chan<- prometheus.Metric) error {
40 | ps, err := or.Ps()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range ps {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall status of power supplies.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/fans.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type fansCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["fans"] = NewFansCollector
31 | }
32 |
33 | // NewFansCollector returns a new fansCollector
34 | func NewFansCollector(cfg *Config) (Collector, error) {
35 | return &fansCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *fansCollector) Update(ch chan<- prometheus.Metric) error {
40 | fans, err := or.Fans()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range fans {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall status of system fans.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/version.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "github.com/prometheus/client_golang/prometheus"
21 | "github.com/prometheus/common/version"
22 | )
23 |
24 | type versionCollector struct {
25 | metric *prometheus.GaugeVec
26 | }
27 |
28 | func init() {
29 | Factories["version"] = NewVersionCollector
30 | }
31 |
32 | // NewVersionCollector returns a new VersionCollector
33 | func NewVersionCollector(cfg *Config) (Collector, error) {
34 | metric := prometheus.NewGaugeVec(
35 | prometheus.GaugeOpts{
36 | Name: "dell_hw_exporter_version",
37 | Help: "Constant '1' value with version, revision, and branch labels from the dellhw_exporter version info.",
38 | },
39 | []string{"version", "revision", "branch"},
40 | )
41 | metric.WithLabelValues(version.Version, version.Revision, version.Branch).Set(1)
42 |
43 | return &versionCollector{
44 | metric: metric,
45 | }, nil
46 | }
47 |
48 | // Update Prometheus metrics
49 | func (c *versionCollector) Update(ch chan<- prometheus.Metric) error {
50 | c.metric.Collect(ch)
51 |
52 | return nil
53 | }
54 |
--------------------------------------------------------------------------------
/collector/memory.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type memoryCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["memory"] = NewMemoryCollector
31 | }
32 |
33 | // NewMemoryCollector returns a new memoryCollector
34 | func NewMemoryCollector(cfg *Config) (Collector, error) {
35 | return &memoryCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *memoryCollector) Update(ch chan<- prometheus.Metric) error {
40 | memory, err := or.Memory()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range memory {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "System RAM DIMM status.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/system.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type systemCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["system"] = NewSystemCollector
31 | }
32 |
33 | // NewSystemCollector returns a new systemCollector
34 | func NewSystemCollector(cfg *Config) (Collector, error) {
35 | return &systemCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *systemCollector) Update(ch chan<- prometheus.Metric) error {
40 | system, err := or.System()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range system {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall status of system components.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/volts.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type voltsCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["volts"] = NewVoltsCollector
31 | }
32 |
33 | // NewVoltsCollector returns a new voltsCollector
34 | func NewVoltsCollector(cfg *Config) (Collector, error) {
35 | return &voltsCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *voltsCollector) Update(ch chan<- prometheus.Metric) error {
40 | volts, err := or.Volts()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range volts {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall volts and status of power supply volt readings.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/chassis.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type chassisCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["chassis"] = NewChassisCollector
31 | }
32 |
33 | // NewChassisCollector returns a new chassisCollector
34 | func NewChassisCollector(cfg *Config) (Collector, error) {
35 | return &chassisCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *chassisCollector) Update(ch chan<- prometheus.Metric) error {
40 | chassis, err := or.Chassis()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range chassis {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall status of chassis components.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/temps.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type tempsCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["temps"] = NewTempsCollector
31 | }
32 |
33 | // NewTempsCollector returns a new tempsCollector
34 | func NewTempsCollector(cfg *Config) (Collector, error) {
35 | return &tempsCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *tempsCollector) Update(ch chan<- prometheus.Metric) error {
40 | temps, err := or.Temps()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range temps {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall temperatures and status of system temperature readings.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/processors.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type processorsCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["processors"] = NewProcessorsCollector
31 | }
32 |
33 | // NewProcessorsCollector returns a new processorsCollector
34 | func NewProcessorsCollector(cfg *Config) (Collector, error) {
35 | return &processorsCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *processorsCollector) Update(ch chan<- prometheus.Metric) error {
40 | chassis, err := or.Processors()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range chassis {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall status of CPUs.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/nics.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type nicsCollector struct {
26 | current *prometheus.Desc
27 | nicList []string
28 | }
29 |
30 | func init() {
31 | Factories["nics"] = NewNicsCollector
32 | }
33 |
34 | // NewNicsCollector returns a new nicsCollector
35 | func NewNicsCollector(cfg *Config) (Collector, error) {
36 | return &nicsCollector{
37 | nicList: cfg.MonitoredNICs,
38 | }, nil
39 | }
40 |
41 | // Update Prometheus metrics
42 | func (c *nicsCollector) Update(ch chan<- prometheus.Metric) error {
43 | nics, err := or.Nics(c.nicList...)
44 | if err != nil {
45 | return err
46 | }
47 | for _, value := range nics {
48 | float, err := strconv.ParseFloat(value.Value, 64)
49 | if err != nil {
50 | return err
51 | }
52 | c.current = prometheus.NewDesc(
53 | prometheus.BuildFQName(Namespace, "", value.Name),
54 | "Connection status of network cards.",
55 | nil, value.Labels)
56 | ch <- prometheus.MustNewConstMetric(
57 | c.current, prometheus.GaugeValue, float)
58 | }
59 |
60 | return nil
61 | }
62 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/library/rockylinux:9.3
2 |
3 | ARG BUILD_DATE="N/A"
4 | ARG REVISION="N/A"
5 |
6 | ARG DELLHW_EXPORTER_VERSION="N/A"
7 |
8 | LABEL org.opencontainers.image.authors="Alexander Trost " \
9 | org.opencontainers.image.created="${BUILD_DATE}" \
10 | org.opencontainers.image.title="galexrt/dellhw_exporter" \
11 | org.opencontainers.image.description="Prometheus exporter for Dell Hardware components using OMSA." \
12 | org.opencontainers.image.documentation="https://github.com/galexrt/dellhw_exporter/blob/main/README.md" \
13 | org.opencontainers.image.url="https://github.com/galexrt/dellhw_exporter" \
14 | org.opencontainers.image.source="https://github.com/galexrt/dellhw_exporter" \
15 | org.opencontainers.image.revision="${REVISION}" \
16 | org.opencontainers.image.vendor="galexrt" \
17 | org.opencontainers.image.version="${DELLHW_EXPORTER_VERSION}"
18 |
19 | # Environment variables
20 | ENV PATH="$PATH:/opt/dell/srvadmin/bin:/opt/dell/srvadmin/sbin" \
21 | SYSTEMCTL_SKIP_REDIRECT="1" \
22 | START_DELL_SRVADMIN_SERVICES="true"
23 |
24 | # Do overall update and install missing packages needed for OpenManage
25 | RUN yum -y update && \
26 | yum -y install wget perl passwd gcc which tar libstdc++.so.6 glibc.i686 make && \
27 | curl -sS --retry 3 --fail -L -o bootstrap.cgi "https://linux.dell.com/repo/hardware/dsu/bootstrap.cgi" && \
28 | yes | bash bootstrap.cgi && \
29 | rm bootstrap.cgi && \
30 | yum -y install srvadmin-base srvadmin-storageservices && \
31 | yum clean all
32 |
33 | EXPOSE 9137/tcp
34 |
35 | ADD container/entrypoint.sh /bin/entrypoint
36 |
37 | RUN chmod +x /bin/entrypoint
38 |
39 | ADD .build/linux-amd64/dellhw_exporter /bin/dellhw_exporter
40 |
41 | ENTRYPOINT ["/bin/entrypoint"]
42 |
--------------------------------------------------------------------------------
/contrib/monitoring/prometheus-alerts/prometheus-alerts.yml:
--------------------------------------------------------------------------------
1 | groups:
2 | - name: dellhw_exporter-prom-alerts.yaml
3 | rules:
4 | - alert: fan_broken
5 | expr: dell_hw_chassis_fan_status == 1
6 | for: 5m
7 | labels:
8 | severity: Warning
9 | severity_id: "4"
10 | type: basic
11 | annotations:
12 | description: dell_hw_chassis_fan_status == 1
13 | summary: On Host {{ .Labels.instance }} fan in slot {{ .Lables.name }} is broken
14 | - alert: lost_hard_drive
15 | expr: (count(dell_hw_storage_pdisk_status OFFSET 1w) BY (instance)) - (count(dell_hw_storage_pdisk_status)
16 | BY (instance)) > 0
17 | for: 5m
18 | labels:
19 | severity: Warning
20 | severity_id: "4"
21 | type: basic
22 | annotations:
23 | description: (count by (instance) (dell_hw_storage_pdisk_status offset 7d))
24 | - (count by (instance) (dell_hw_storage_pdisk_status)) > 0
25 | summary: On Host {{ .Labels.instance }} {{ .Value }} hard drive(s) lost
26 | - alert: lost_power_supply
27 | expr: dell_hw_ps_status == 1
28 | for: 5m
29 | labels:
30 | severity: Warning
31 | severity_id: "4"
32 | type: basic
33 | annotations:
34 | description: dell_hw_ps_status == 1 FOR 5m
35 | summary: On Host {{ .Labels.instance }} power supply with id {{ .Labels.id
36 | }} is lost
37 | - alert: storage_controller_broken
38 | expr: dell_hw_storage_controller_status == 1
39 | for: 5m
40 | labels:
41 | severity: Warning
42 | severity_id: "4"
43 | type: basic
44 | annotations:
45 | description: dell_hw_storage_controller_status == 1
46 | summary: On Host {{ .Labels.instance }} storage controller with id {{ .Lables.id
47 | }} is broken. Check omreport storage controller command
48 |
--------------------------------------------------------------------------------
/collector/storage_battery.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type storageBatteryCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["storage_battery"] = NewStorageBatteryCollector
31 | }
32 |
33 | // NewStorageBatteryCollector returns a new storageBatteryCollector
34 | func NewStorageBatteryCollector(cfg *Config) (Collector, error) {
35 | return &storageBatteryCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *storageBatteryCollector) Update(ch chan<- prometheus.Metric) error {
40 | storageBattery, err := or.StorageBattery()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range storageBattery {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Status of storage controller backup batteries.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/collector.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "io"
21 | "log/slog"
22 |
23 | "github.com/galexrt/dellhw_exporter/pkg/omreport"
24 | "github.com/prometheus/client_golang/prometheus"
25 | )
26 |
27 | // Namespace holds the metrics namespace/first part
28 | const Namespace = "dell_hw"
29 |
30 | var (
31 | or *omreport.OMReport
32 | logger = slog.New(slog.NewTextHandler(io.Discard, nil))
33 | )
34 |
35 | type Config struct {
36 | MonitoredNICs []string
37 | }
38 |
39 | // Factories contains the list of all available collectors.
40 | var Factories = make(map[string]func(*Config) (Collector, error))
41 |
42 | // Collector is the interface a collector has to implement.
43 | type Collector interface {
44 | // Get new metrics and expose them via prometheus registry.
45 | Update(ch chan<- prometheus.Metric) error
46 | }
47 |
48 | type IsAvailable interface {
49 | // IsAvailable checks if the collector is available for the current system.
50 | IsAvailable() bool
51 | }
52 |
53 | // SetOMReport a given OMReport for the collectors
54 | func SetOMReport(omrep *omreport.OMReport) {
55 | or = omrep
56 | }
57 |
58 | // SetLogger
59 | func SetLogger(l *slog.Logger) {
60 | logger = l
61 | }
62 |
--------------------------------------------------------------------------------
/collector/storage_enclosure.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type storageEnclosureCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["storage_enclosure"] = NewStorageEnclosureCollector
31 | }
32 |
33 | // NewStorageEnclosureCollector returns a new storageEnclosureCollector
34 | func NewStorageEnclosureCollector(cfg *Config) (Collector, error) {
35 | return &storageEnclosureCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *storageEnclosureCollector) Update(ch chan<- prometheus.Metric) error {
40 | storageEnclosure, err := or.StorageEnclosure()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range storageEnclosure {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall status of storage enclosures.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/ps_amps_sysboard_pwr.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type psAmpsSysboardPwrCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["ps_amps_sysboard_pwr"] = NewPsAmpsSysboardPwrCollector
31 | }
32 |
33 | // NewPsAmpsSysboardPwrCollector returns a new psAmpsSysboardPwrCollector
34 | func NewPsAmpsSysboardPwrCollector(cfg *Config) (Collector, error) {
35 | return &psAmpsSysboardPwrCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *psAmpsSysboardPwrCollector) Update(ch chan<- prometheus.Metric) error {
40 | psampssysboardpwr, err := or.PsAmpsSysboardPwr()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range psampssysboardpwr {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "System board power usage.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/storage_controller.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type storageControllerCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["storage_controller"] = NewStorageControllerCollector
31 | }
32 |
33 | // NewStorageControllerCollector returns a new storageControllerCollector
34 | func NewStorageControllerCollector(cfg *Config) (Collector, error) {
35 | return &storageControllerCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *storageControllerCollector) Update(ch chan<- prometheus.Metric) error {
40 | storageController, err := or.StorageController()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range storageController {
45 | float, err := strconv.ParseFloat(value.Value, 64)
46 | if err != nil {
47 | return err
48 | }
49 | c.current = prometheus.NewDesc(
50 | prometheus.BuildFQName(Namespace, "", value.Name),
51 | "Overall status of storage controllers.",
52 | nil, value.Labels)
53 | ch <- prometheus.MustNewConstMetric(
54 | c.current, prometheus.GaugeValue, float)
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/collector/storage_vdisk.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type storageVdiskCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["storage_vdisk"] = NewStorageVdiskCollector
31 | }
32 |
33 | // NewStorageVdiskCollector returns a new storageVdiskCollector
34 | func NewStorageVdiskCollector(cfg *Config) (Collector, error) {
35 | return &storageVdiskCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *storageVdiskCollector) Update(ch chan<- prometheus.Metric) error {
40 | storageVdisk, err := or.StorageVdisk()
41 | if err != nil {
42 | return err
43 | }
44 | for _, value := range storageVdisk {
45 | logger.
46 | With("vdisk", value).
47 | Debug("iterating vdisk values")
48 | float, err := strconv.ParseFloat(value.Value, 64)
49 | if err != nil {
50 | return err
51 | }
52 |
53 | c.current = prometheus.NewDesc(
54 | prometheus.BuildFQName(Namespace, "", value.Name),
55 | "Overall status of virtual disks + RAID level (if available).",
56 | nil, value.Labels)
57 | ch <- prometheus.MustNewConstMetric(
58 | c.current, prometheus.GaugeValue, float)
59 | }
60 |
61 | return nil
62 | }
63 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/*
2 | Expand the name of the chart.
3 | */}}
4 | {{- define "dellhw_exporter.name" -}}
5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6 | {{- end }}
7 |
8 | {{/*
9 | Create a default fully qualified app name.
10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
11 | If release name contains chart name it will be used as a full name.
12 | */}}
13 | {{- define "dellhw_exporter.fullname" -}}
14 | {{- if .Values.fullnameOverride }}
15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
16 | {{- else }}
17 | {{- $name := default .Chart.Name .Values.nameOverride }}
18 | {{- if contains $name .Release.Name }}
19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }}
20 | {{- else }}
21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
22 | {{- end }}
23 | {{- end }}
24 | {{- end }}
25 |
26 | {{/*
27 | Create chart name and version as used by the chart label.
28 | */}}
29 | {{- define "dellhw_exporter.chart" -}}
30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
31 | {{- end }}
32 |
33 | {{/*
34 | Common labels
35 | */}}
36 | {{- define "dellhw_exporter.labels" -}}
37 | helm.sh/chart: {{ include "dellhw_exporter.chart" . }}
38 | {{ include "dellhw_exporter.selectorLabels" . }}
39 | {{- if .Chart.AppVersion }}
40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
41 | {{- end }}
42 | app.kubernetes.io/managed-by: {{ .Release.Service }}
43 | {{- end }}
44 |
45 | {{/*
46 | Selector labels
47 | */}}
48 | {{- define "dellhw_exporter.selectorLabels" -}}
49 | app.kubernetes.io/name: {{ include "dellhw_exporter.name" . }}
50 | app.kubernetes.io/instance: {{ .Release.Name }}
51 | {{- end }}
52 |
53 | {{/*
54 | Create the name of the service account to use
55 | */}}
56 | {{- define "dellhw_exporter.serviceAccountName" -}}
57 | {{- if .Values.serviceAccount.create }}
58 | {{- default (include "dellhw_exporter.fullname" .) .Values.serviceAccount.name }}
59 | {{- else }}
60 | {{- default "default" .Values.serviceAccount.name }}
61 | {{- end }}
62 | {{- end }}
63 |
--------------------------------------------------------------------------------
/collector/chassis_batteries.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 | "strings"
22 |
23 | "github.com/prometheus/client_golang/prometheus"
24 | )
25 |
26 | type chassisBatteriesCollector struct {
27 | current *prometheus.Desc
28 | }
29 |
30 | func init() {
31 | Factories["chassis_batteries"] = NewChassisBatteriesCollector
32 | }
33 |
34 | // NewChassisBatteriesCollector returns a new chassisBatteriesCollector
35 | func NewChassisBatteriesCollector(cfg *Config) (Collector, error) {
36 | return &chassisBatteriesCollector{}, nil
37 | }
38 |
39 | // Update Prometheus metrics
40 | func (c *chassisBatteriesCollector) Update(ch chan<- prometheus.Metric) error {
41 | chassisBatteries, err := or.ChassisBatteries()
42 | if err != nil {
43 | return err
44 | }
45 | for _, value := range chassisBatteries {
46 | float, err := strconv.ParseFloat(value.Value, 64)
47 | if err != nil {
48 | return err
49 | }
50 | c.current = prometheus.NewDesc(
51 | prometheus.BuildFQName(Namespace, "", value.Name),
52 | "Overall status of chassis batteries",
53 | nil, value.Labels)
54 | ch <- prometheus.MustNewConstMetric(
55 | c.current, prometheus.GaugeValue, float)
56 | }
57 |
58 | return nil
59 | }
60 |
61 | // IsAvailable if the collector is available
62 | func (c *chassisBatteriesCollector) IsAvailable() bool {
63 | _, err := or.ChassisBatteries()
64 | if err == nil {
65 | return true
66 | }
67 |
68 | e := strings.ToLower(err.Error())
69 | return strings.Contains(e, "no battery probes found on this system")
70 | }
71 |
--------------------------------------------------------------------------------
/collector/storage_pdisk.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type storagePdiskCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["storage_pdisk"] = NewStoragePdiskCollector
31 | }
32 |
33 | // NewStoragePdiskCollector returns a new storagePdiskCollector
34 | func NewStoragePdiskCollector(cfg *Config) (Collector, error) {
35 | return &storagePdiskCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *storagePdiskCollector) Update(ch chan<- prometheus.Metric) error {
40 | controllers, err := or.StorageController()
41 | if err != nil {
42 | return err
43 | }
44 | for cid := range controllers {
45 | logger := logger.With("controller", cid)
46 | logger.Debug("collecting pdisks from controller")
47 |
48 | storagePdisk, err := or.StoragePdisk(strconv.Itoa(cid))
49 | if err != nil {
50 | return err
51 | }
52 |
53 | for _, value := range storagePdisk {
54 | logger.Debug("iterating pdisks from controller", "pdisk", value)
55 | float, err := strconv.ParseFloat(value.Value, 64)
56 | if err != nil {
57 | return err
58 | }
59 |
60 | c.current = prometheus.NewDesc(
61 | prometheus.BuildFQName(Namespace, "", value.Name),
62 | "Overall status of physical disks + failure prediction (if available).",
63 | nil, value.Labels)
64 | ch <- prometheus.MustNewConstMetric(
65 | c.current, prometheus.GaugeValue, float)
66 | }
67 | }
68 |
69 | return nil
70 | }
71 |
--------------------------------------------------------------------------------
/docs/faq.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | Some frequently asked questions about running and using the `dellhw_exporter`.
4 |
5 | ## `dell_hw_chassis_temps` is reporting `0` and not a temperature?
6 |
7 | The `dell_hw_chassis_temps` metric is reporting the [**status** (as a number)](metrics.md#what-do-the-metrics-mean) reported for each component.
8 | The temperatures are reported by the other metrics, e.g., `dell_hw_chassis_temps_reading`.
9 |
10 | ## `lsmod: command not found` errors on start of exporter
11 |
12 | Especially when running the`dellhw_exporter` using the container image, these error lines can appear.
13 |
14 | The reason for that is that the dellhw_exporter image entrypoint script runs the Dell HW OMSA services start script. This start script attempts to check if some specific kernel modules are loaded/available.
15 | As the container image doesn't contain the `lsmod` command, these "errors" can safely be ignored.
16 |
17 | ## How to disable specific collectors if not applicable to the system?
18 |
19 | The `chassis_batteries` collector might not be available on all Dell systems, due to:
20 |
21 | > Removal of CMOS battery sensor:
22 | > As part of implementing the logging changes above, the CMOS battery sensor has been disabled to avoid inconsistencies in health reporting. As a result, the CMOS battery no longer appears in any management interface, including:
23 | iDRAC web UI
24 | >
25 | > Source:
26 |
27 | To avoid error logs about collectors not being applicable to the system, the new flag `--collectors-check` can be used to specify a comma separated list of collectors to check for applicability.
28 | Please note though that currently only the `chassis_batteries` collector is supported and is currently not checked by default.
29 | To enable having the exporter check if `chassis_batteries` is available, you need to add the flag `--collectors-check=chassis_batteries` or env var `DELLHW_EXPORTER_COLLECTORS_CHECK=chassis_batteries`.
30 |
31 | For more information regarding configuration of collectors, please see the [Configuration](configuration.md#collectors-configuration) documentation page.
32 |
--------------------------------------------------------------------------------
/.github/workflows/documentation.yml:
--------------------------------------------------------------------------------
1 | name: Helm and Docs Publish
2 | on:
3 | push:
4 | branches:
5 | - main
6 |
7 | jobs:
8 | helm-release:
9 | # Depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
10 | # see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
11 | permissions:
12 | contents: write
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v6
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Configure Git
21 | run: |
22 | git config user.name "$GITHUB_ACTOR"
23 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
24 |
25 | - name: Install Helm
26 | uses: azure/setup-helm@v4
27 | with:
28 | version: v3.8.1
29 |
30 | - name: Run chart-releaser
31 | uses: helm/chart-releaser-action@v1.7.0
32 | env:
33 | CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
34 |
35 | docs:
36 | needs: helm-release
37 | runs-on: ubuntu-latest
38 | container:
39 | image: "docker.io/squidfunk/mkdocs-material:9.7.0"
40 | permissions:
41 | contents: write
42 | concurrency:
43 | group: ${{ github.workflow }}-${{ github.ref }}
44 | steps:
45 | - uses: actions/checkout@v6
46 | with:
47 | submodules: false
48 | fetch-depth: 0
49 |
50 | - uses: actions/checkout@v6
51 | with:
52 | submodules: true
53 | sparse-checkout: |
54 | index.yaml
55 | artifacthub-repo.yml
56 | ref: 'gh-pages'
57 | path: 'gh-pages'
58 |
59 | - name: Build docs
60 | run: |
61 | mkdocs build --clean
62 | { for file in index.yaml artifacthub-repo.yml; do cp -v -f "./gh-pages/$file" ./site/ || true; done; }
63 |
64 | - name: Deploy
65 | uses: peaceiris/actions-gh-pages@v4
66 | with:
67 | github_token: ${{ secrets.GITHUB_TOKEN }}
68 | publish_dir: ./site
69 | cname: dellhw-exporter.galexrt.moe
70 |
--------------------------------------------------------------------------------
/collector/firmwares.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package collector
18 |
19 | import (
20 | "strconv"
21 |
22 | "github.com/prometheus/client_golang/prometheus"
23 | )
24 |
25 | type firmwaresCollector struct {
26 | current *prometheus.Desc
27 | }
28 |
29 | func init() {
30 | Factories["firmwares"] = NewFirmwaresCollector
31 | }
32 |
33 | // NewFirmwaresCollector returns a new firmwaresCollector
34 | func NewFirmwaresCollector(cfg *Config) (Collector, error) {
35 | return &firmwaresCollector{}, nil
36 | }
37 |
38 | // Update Prometheus metrics
39 | func (c *firmwaresCollector) Update(ch chan<- prometheus.Metric) error {
40 | chassisBios, err := or.ChassisBios()
41 | if err != nil {
42 | return err
43 | }
44 | chassisFirmware, err := or.ChassisFirmware()
45 | if err != nil {
46 | return err
47 | }
48 | for _, value := range chassisBios {
49 | float, err := strconv.ParseFloat(value.Value, 64)
50 | if err != nil {
51 | return err
52 | }
53 | c.current = prometheus.NewDesc(
54 | prometheus.BuildFQName(Namespace, "", value.Name),
55 | "Version info of firmwares/bios.",
56 | nil, value.Labels)
57 | ch <- prometheus.MustNewConstMetric(
58 | c.current, prometheus.GaugeValue, float)
59 | }
60 | for _, value := range chassisFirmware {
61 | float, err := strconv.ParseFloat(value.Value, 64)
62 | if err != nil {
63 | return err
64 | }
65 | c.current = prometheus.NewDesc(
66 | prometheus.BuildFQName(Namespace, "", value.Name),
67 | "Version info of firmwares/bios.",
68 | nil, value.Labels)
69 | ch <- prometheus.MustNewConstMetric(
70 | c.current, prometheus.GaugeValue, float)
71 | }
72 |
73 | return nil
74 | }
75 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/templates/daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: {{ include "dellhw_exporter.fullname" . }}
5 | labels:
6 | {{- include "dellhw_exporter.labels" . | nindent 4 }}
7 | spec:
8 | selector:
9 | matchLabels:
10 | {{- include "dellhw_exporter.selectorLabels" . | nindent 6 }}
11 | template:
12 | metadata:
13 | {{- with .Values.podAnnotations }}
14 | annotations:
15 | {{- toYaml . | nindent 8 }}
16 | {{- end }}
17 | labels:
18 | {{- include "dellhw_exporter.selectorLabels" . | nindent 8 }}
19 | {{- with .Values.podLabels }}
20 | {{- toYaml . | nindent 8 }}
21 | {{- end }}
22 | spec:
23 | {{- with .Values.imagePullSecrets }}
24 | imagePullSecrets:
25 | {{- toYaml . | nindent 8 }}
26 | {{- end }}
27 | serviceAccountName: {{ include "dellhw_exporter.serviceAccountName" . }}
28 | securityContext:
29 | {{- toYaml .Values.podSecurityContext | nindent 8 }}
30 | containers:
31 | - name: {{ include "dellhw_exporter.fullname" . }}
32 | securityContext:
33 | {{- toYaml .Values.securityContext | nindent 12 }}
34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
35 | imagePullPolicy: {{ .Values.image.pullPolicy }}
36 | ports:
37 | - name: http-metrics
38 | containerPort: 9137
39 | protocol: TCP
40 | livenessProbe:
41 | httpGet:
42 | path: /
43 | port: http-metrics
44 | readinessProbe:
45 | httpGet:
46 | path: /
47 | port: http-metrics
48 | resources:
49 | {{- toYaml .Values.resources | nindent 12 }}
50 | volumeMounts:
51 | {{- toYaml .Values.additionalVolumeMounts | nindent 12 }}
52 | env:
53 | {{- toYaml .Values.additionalEnv | nindent 12 }}
54 | {{- with .Values.nodeSelector }}
55 | nodeSelector:
56 | {{- toYaml . | nindent 8 }}
57 | {{- end }}
58 | {{- with .Values.affinity }}
59 | affinity:
60 | {{- toYaml . | nindent 8 }}
61 | {{- end }}
62 | {{- with .Values.tolerations }}
63 | tolerations:
64 | {{- toYaml . | nindent 8 }}
65 | {{- end }}
66 | volumes:
67 | {{- toYaml .Values.additionalVolumes | nindent 8 }}
68 |
--------------------------------------------------------------------------------
/docs/caching.md:
--------------------------------------------------------------------------------
1 | The dellhw_exporter can be configured to cache the results to prevent unnecessary executions of `omreport`, which can lead to high load.
2 |
3 | ## Caching
4 |
5 | Why do we even need caching in an exporter?
6 |
7 | Unfortunately Prometheus itself does not provide a 'real' High-Availability concept. The Prometheus authors recommend running identical Prometheus instances to achieve HA (see [Prometheus FAQ](https://prometheus.io/docs/introduction/faq/#can-prometheus-be-made-highly-available)), hence the exporters will be scraped multiple times within one scrape interval.
8 |
9 | Besides the problem, that not all instances are retrieving the identical metric values, this can also produce high load if the actual collection of metrics is 'expensive'. While the first problem is not a real use case, because Prometheus does not claim to be consistent, the second problem is a real problem and valid use case for caching.
10 |
11 | In particular the `dellhw_exporter`, since the underlying `omreport` calls produce high load. This is caused by many drivers collecting data from different components.
12 |
13 | ### Configuration
14 |
15 | As you may have seen in [the Configuration doc page](configuration.md) there are two caching related configuration parameters for enablement and how long the cache should be valid.
16 |
17 | ```console
18 | --cache-enabled bool Enable caching (default false)
19 | --cache-duration int Duration in seconds for the cache lifetime (default 20)
20 | ```
21 |
22 | If you want to retrieve new metrics on each scrape, but want to prevent multiple collections because of multiple Prometheus instances, it is a good idea to set the cache-duration equal to your job's `scrape_interval`. If the `scrape_interval` is even less than the default value it can be useful to set a different `cache-duration`, maybe 2-3 times of the `scrape_interval`.
23 |
24 |
25 | ### Implementation details
26 |
27 | An additional adapter channel is used to retrieve the collected metrics and put them into an array if caching is enabled. A mutex is used to prevent concurrent collections and concurrent write operations to the cache.
28 |
29 | Since the metrics are pushed into a "local" channel instead of the channel passed by the Prometheus library directly, we need a second waitgroup. The first waitgroup ensures that all collectors have finished. The second waitgroup ensures that all metrics are written to the outgoing channel before the method returns. This is needed because the Prometheus library will close the channel once the method returns.
30 |
31 | For further details please see the initial [Caching PR](https://github.com/galexrt/dellhw_exporter/pull/46).
32 |
--------------------------------------------------------------------------------
/docs/collectors.md:
--------------------------------------------------------------------------------
1 | Which collectors are enabled is controlled by the `--collectors-enabled` and `--collectors-additional` flags.
2 |
3 | ## Enabled by default
4 |
5 | All collectors are enabled by default. You can disable collectors by specifying the whole list of collectors through the `--collectors-enabled` flag.
6 |
7 | | Name | Description |
8 | | ---------------------- | -------------------------------------------------------------------------------- |
9 | | `chassis` | Overall status of chassis components. |
10 | | `chassis_batteries` | Overall status of chassis CMOS batteries. |
11 | | `fans` | Overall status of system fans. |
12 | | `firmwares` | Information about some firmware versions (DRAC, BIOS) |
13 | | `memory` | System RAM DIMM status. |
14 | | `nics` | NICs connection status. |
15 | | `processors` | Overall status of CPUs. |
16 | | `ps` | Overall status of power supplies. |
17 | | `ps_amps_sysboard_pwr` | System board power usage. |
18 | | `storage_battery` | Status of storage controller backup batteries. |
19 | | `storage_controller` | Overall status of storage controllers. |
20 | | `storage_enclosure` | Overall status of storage enclosures. |
21 | | `storage_pdisk` | Overall status of physical disks + failure prediction (if available). |
22 | | `storage_vdisk` | Overall status of virtual disks. |
23 | | `system` | Overall status of system components. |
24 | | `temps` | Overall temperatures (**in Celsius**) and status of system temperature readings. |
25 | | `version` | Exporter version info with build info as labels. |
26 | | `volts` | Overall volts and status of power supply volt readings. |
27 |
28 | ## Disabled by default
29 |
30 | To make it easier to enable disabled collectors without having to specify the whole enabled list, you can use the `--collectors-additional` flag (commad separated list).
31 |
32 | | Name | Description |
33 | | -------------- | -------------------------------------------------------- |
34 | | `chassis_info` | Information about the chassis (currently chassis model). |
35 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at Galexrt@googlemail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/docs/configuration.md:
--------------------------------------------------------------------------------
1 | The dellhw_exporter can be configured using flags or environment variables.
2 | In case of the container image there are certain specific environment variables, to help running inside a containerized environment.
3 |
4 | ## Flags
5 |
6 | ```console
7 | $ dellhw_exporter --help
8 | Usage of dellhw_exporter:
9 | --cache-duration int Cache duration in seconds (default 20)
10 | --cache-enabled Enable metrics caching to reduce load
11 | --collectors-additional strings Comma separated list of collectors to enable additionally to the collectors-enabled list
12 | --collectors-check strings Check if the specified collectors are applicable to the system and disable it otherwise. E.g., chassis_batteries
13 | --collectors-cmd-timeout int Command execution timeout for omreport (default 15)
14 | --collectors-enabled strings Comma separated list of active collectors (default [chassis,chassis_batteries,fans,firmwares,memory,nics,processors,ps,ps_amps_sysboard_pwr,storage_battery,storage_controller,storage_enclosure,storage_pdisk,storage_vdisk,system,temps,version,volts])
15 | --collectors-omreport string Path to the omreport executable (based on the OS (linux or windows) default paths are used if unset) (default "/opt/dell/srvadmin/bin/omreport")
16 | --collectors-print If true, print available collectors and exit.
17 | --log-level string Set log level (default "INFO")
18 | --monitored-nics strings Comma separated list of nics to monitor (default, empty list, is to monitor all)
19 | --version Show version information
20 | --web-config-file string [EXPERIMENTAL] Path to configuration file that can enable TLS or authentication.
21 | --web-listen-address string The address to listen on for HTTP requests (default ":9137")
22 | --web-telemetry-path string Path the metrics will be exposed under (default "/metrics")
23 | ```
24 |
25 | The `--web-config-file` instructs the exporter to load a separate YAML config file that provides the following abilities:
26 |
27 | - HTTPS
28 | - TLS cert authentication
29 | - HTTP2
30 | - Basic Authentication
31 | - TLS versions and cipher suites
32 | - Headers like `Strict-Transport-Security`, `X-XSS-Protection`, `X-Frame-Options`, etc.
33 |
34 | The exact format of the file and all its options can be found [here](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md).
35 |
36 | ## Environment Variables
37 |
38 | For the description of the env vars, see the above equivalent flags (and their defaults).
39 |
40 | ```console
41 | DELLHW_EXPORTER_CACHE_DURATION
42 | DELLHW_EXPORTER_CACHE_ENABLED
43 | DELLHW_EXPORTER_COLLECTORS_ADDITIONAL
44 | DELLHW_EXPORTER_COLLECTORS_CHECK
45 | DELLHW_EXPORTER_COLLECTORS_CMD_TIMEOUT
46 | DELLHW_EXPORTER_COLLECTORS_ENABLED
47 | DELLHW_EXPORTER_COLLECTORS_OMREPORT
48 | DELLHW_EXPORTER_LOG_LEVEL
49 | DELLHW_EXPORTER_MONITORED_NICS
50 | DELLHW_EXPORTER_WEB_LISTEN_ADDRESS
51 | DELLHW_EXPORTER_WEB_TELEMETRY_PATH
52 | DELLHW_EXPORTER_WEB_CONFIG_FILE
53 | ```
54 |
55 | ### Container Image specific Environment Variables
56 |
57 | | Env | Default | Description |
58 | | ------------------------------ | ------- | --------------------------------------------------------------------------------------- |
59 | | `START_DELL_SRVADMIN_SERVICES` | `true` | Set to false if you don't want the srvadmin services to be started inside the container |
60 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/README.md:
--------------------------------------------------------------------------------
1 | # prometheus-dellhw-exporter
2 |
3 | A Helm chart for the dellhw_exporter
4 |
5 |   
6 |
7 | ## Get Repo Info
8 |
9 | ```console
10 | helm repo add dellhw_exporter https://galexrt.github.io/dellhw_exporter
11 | helm repo update
12 | ```
13 |
14 | _See [helm repo](https://helm.sh/docs/helm/helm_repo/) for command documentation._
15 |
16 | ## Install Chart
17 |
18 | To install the chart with the release name `my-release`:
19 |
20 | ```console
21 | helm install --namespace my-release dellhw_exporter/prometheus-dellhw-exporter
22 | ```
23 |
24 | The command deploys dellhw_exporter on the Kubernetes cluster in the default configuration.
25 |
26 | _See [configuration](#configuration) below._
27 |
28 | _See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._
29 |
30 | ### Development Build
31 |
32 | To deploy from a local build from your development environment:
33 |
34 | ```console
35 | cd charts/prometheus-dellhw-exporter
36 | helm install --namespace my-release . -f values.yaml
37 | ```
38 |
39 | ## Uninstall Chart
40 |
41 | To uninstall/delete the my-release deployment:
42 |
43 | ```console
44 | helm delete --namespace my-release
45 | ```
46 |
47 | This removes all the Kubernetes components associated with the chart and deletes the release.
48 |
49 | _See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._
50 |
51 | ## Configuration
52 |
53 | | Key | Type | Default | Description |
54 | |-----|------|---------|-------------|
55 | | additionalEnv | list | `[{"name":"HOSTNAME","valueFrom":{"fieldRef":{"fieldPath":"spec.nodeName"}}}]` | Additional environments to be added to the dellhw_exporter container, use this to configure the exporter (see https://github.com/galexrt/dellhw_exporter/blob/main/docs/configuration.md#environment-variables) |
56 | | additionalVolumeMounts | list | `[]` | Additional volume mounts for the dellhw_exporter container. |
57 | | additionalVolumes | list | `[]` | Additional volumes to be mounted in the dellhw_exporter container. |
58 | | affinity | object | `{}` | Affinity for the DaemonSet |
59 | | fullnameOverride | string | `""` | Override fully-qualified app name |
60 | | image.pullPolicy | string | `"IfNotPresent"` | Override the `imagePullPolicy` |
61 | | image.repository | string | `"quay.io/galexrt/dellhw_exporter"` | Image repository |
62 | | image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
63 | | imagePullSecrets | list | `[]` | ImagePullSecrets to add to the DaemonSet |
64 | | nameOverride | string | `""` | Override chart name |
65 | | nodeSelector | object | `{}` | NodeSelector for the DaemonSet |
66 | | podAnnotations | object | `{}` | Annotations to add to the Pods created by the DaemonSet |
67 | | podLabels | object | `{}` | Additional labels to add to the Pods created by the DaemonSet |
68 | | podSecurityContext | object | `{}` | Kubernetes PodSecurityContext for the Pods |
69 | | prometheusRule.additionalLabels | object | `{}` | Additional Labels for the PrometheusRule object |
70 | | prometheusRule.enabled | bool | `false` | Specifies whether a prometheus-operator PrometheusRule should be created |
71 | | prometheusRule.rules | list | `[]` | Checkout the https://github.com/galexrt/dellhw_exporter/blob/main/contrib/monitoring/prometheus-alerts/prometheus-alerts.yml for example alerts |
72 | | psp.create | bool | `false` | Specifies whether a PodSecurityPolicy (PSP) should be created |
73 | | psp.spec | object | `{"allowedHostPaths":[],"privileged":true,"volumes":["secret"]}` | PodSecurityPolicy spec |
74 | | resources | object | `{}` | Resources for the dellhw_exporter container |
75 | | securityContext | object | `{"privileged":true}` | SecurityContext for the container |
76 | | service.port | int | `9137` | Service port |
77 | | service.type | string | `"ClusterIP"` | Service type |
78 | | serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
79 | | serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
80 | | serviceAccount.name | string | `""` | The name of the service account to use. |
81 | | serviceMonitor.additionalLabels | object | `{}` | Additional Labels for the ServiceMonitor object |
82 | | serviceMonitor.enabled | bool | `false` | Specifies whether a prometheus-operator ServiceMonitor should be created |
83 | | serviceMonitor.namespaceSelector | string | `nil` | ServiceMonitor namespace selector (check the comments below for examples) |
84 | | serviceMonitor.scrapeInterval | duration | `"30s"` | Interval at which metrics should be scraped |
85 | | serviceMonitor.scrapeTimeout | duration | `"20s"` | Timeout for scraping |
86 | | tolerations | list | `[]` | Tolerations for the DaemonSet |
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dellhw_exporter
2 |
3 | 
4 |
5 | Prometheus exporter for Dell Hardware components using OMSA.
6 |
7 | The exporter was originally made by [PrFalken](https://github.com/PrFalken). Due to some issues in the code, I rewrote the whole exporter using the ["node_exporter"](https://github.com/prometheus/node_exporter) pattern and therefore moved it from being a fork out, to a standalone repository.
8 |
9 | Omreport parsing functions were borrowed from the [Bosun project](https://github.com/bosun-monitor/bosun/blob/master/cmd/scollector/collectors/dell_hw.go), thank you very much for that, they are the most tedious part of the job.
10 |
11 | This exporter wraps the `omreport` command from Dell OMSA. If you can't run or get any output from `omreport` on your system, then the exporter probably won't export any metrics.
12 |
13 | ## Compatibility
14 |
15 | ### Tested Dell OMSA Compatibility
16 |
17 | The dellhw_exporter has been tested with the following OMSA versions:
18 |
19 | * `7.4`
20 | * `8.4`
21 | * `9.1`
22 |
23 | ### Kernel Compatibility
24 |
25 | **Please note that only kernel versions that are supported by DELL DSU / OMSA tools are working!**
26 |
27 | **State 07.06.2019**: Dell OMSA `DSU_19.05.00` is not compatible with 5.x kernel it seems (e.g., Fedora uses that kernel).
28 |
29 | Should you run into issues when using the Container image, please follow the [Troubleshooting - No metrics being exported](#no-metrics-being-exported).
30 |
31 | ## Collectors
32 |
33 | For a list of the available collectors, see [Collectors doc page](docs/collectors.md).
34 |
35 | ## Configuration
36 |
37 | For flags and environment variables, see [Configuration doc page](docs/configuration.md).
38 |
39 | ## Caching
40 |
41 | Optional caching can be enabled to prevent performance issues caused by this exporter, see [Caching doc page](caching.md).
42 |
43 | ## Running in Container
44 |
45 | Container Image available from:
46 |
47 | * [Quay.io](https://quay.io/repository/galexrt/dellhw_exporter)
48 | * [GHCR.io](https://github.com/users/galexrt/packages/container/package/dellhw_exporter)
49 |
50 | (Docker Hub is not receiving new images begining from release `v1.10.0`)
51 |
52 | ### Pull the Container Image
53 |
54 | Each Git tag is built and published as a release and container image.
55 |
56 | #### Quay.io
57 |
58 | ```console
59 | docker pull quay.io/galexrt/dellhw_exporter
60 | ```
61 |
62 | #### GHCR.IO
63 |
64 | ```console
65 | docker pull ghcr.io/galexrt/dellhw_exporter
66 | ```
67 |
68 | ### Run the Container Image
69 |
70 | > **NOTE** The `--privileged` flag is required as the OMSA needs to access the host's devices and other components.
71 |
72 | ```console
73 | docker run -d --name dellhw_exporter --privileged -p 9137:9137 quay.io/galexrt/dellhw_exporter
74 | ```
75 |
76 | I can recommend adding the following flags to the `docker run` command:
77 |
78 | * `--restart=always` to ensure the container is restarted on failure or system reboot.
79 | * `-v /etc/hostname:/etc/hostname:ro -e HOSTNAME=$(cat /etc/hostname)` to set the container's hostname to the host's hostname (this is useful for Omreport data/Prometheus labels).
80 | * `-v /etc/os-release:/etc/os-release:ro` to ensure the container has access to the host's OS release information (this is useful for Omreport data/Prometheus labels).
81 |
82 | Podman should also be able to be used.
83 |
84 | ## Running without Docker / Podman
85 |
86 | To run without Docker / Podman either download a [release binary](https://github.com/galexrt/dellhw_exporter/releases) or build it (using `make build` command):
87 |
88 | ```console
89 | ./dellhw_exporter
90 | ./dellhw_exporter --help
91 | ./dellhw_exporter [YOUR_FLAGS]
92 | ```
93 |
94 | **The DELL OMSA services must already be running for the exporter to be able to collect metrics!**
95 |
96 | E.g., run `/opt/dell/srvadmin/sbin/srvadmin-services.sh start` or `systemctl start SERVICE_NAME` (to enable autostart on use `systemctl enable SERVICE_NAME`; where `SERVICE_NAME` [are the DELL OMSA service(s) you installed](http://linux.dell.com/repo/hardware/omsa.html)).
97 |
98 | ## Prometheus
99 |
100 | The exporter by default runs on port `9137` TCP.
101 |
102 | Example static Prometheus Job config:
103 |
104 | ```yaml
105 | [...]
106 | - job_name: 'dellhw_exporter'
107 | # Override the global default and scrape targets from this job every 60 seconds.
108 | scrape_interval: 60s
109 | static_configs:
110 | - targets:
111 | - 'YOUR_EXPORTER_SERVER_HERE:9137'
112 | [...]
113 | ```
114 |
115 | ## Monitoring
116 |
117 | Checkout the files in the [`contrib/monitoring/`](contrib/monitoring/) directory.
118 |
119 | ## Installation
120 |
121 | See [Installation doc page](docs/installation.md).
122 |
123 | ## Troubleshooting
124 |
125 | See [Troubleshooting doc page](docs/troubleshooting.md).
126 |
127 | ## Development
128 |
129 | Golang version `1.23` is used for testing and building the dellhw_exporter.
130 |
131 | `go mod` is used for "vendoring" of the dependencies.
132 |
--------------------------------------------------------------------------------
/charts/prometheus-dellhw-exporter/values.yaml:
--------------------------------------------------------------------------------
1 | # Default values for dellhw_exporter.
2 | # This is a YAML-formatted file.
3 | # Declare variables to be passed into your templates.
4 |
5 | image:
6 | # -- Image repository
7 | repository: quay.io/galexrt/dellhw_exporter
8 | # -- Override the `imagePullPolicy`
9 | pullPolicy: IfNotPresent
10 | # -- Overrides the image tag whose default is the chart appVersion.
11 | tag: ""
12 |
13 | # -- ImagePullSecrets to add to the DaemonSet
14 | imagePullSecrets: []
15 |
16 | # -- Override chart name
17 | nameOverride: ""
18 | # -- Override fully-qualified app name
19 | fullnameOverride: ""
20 |
21 | serviceAccount:
22 | # -- Specifies whether a service account should be created
23 | create: true
24 | # -- Annotations to add to the service account
25 | annotations: {}
26 | # -- If not set and create is true, a name is generated using the fullname template
27 | # -- The name of the service account to use.
28 | name: ""
29 |
30 | # -- Annotations to add to the Pods created by the DaemonSet
31 | podAnnotations: {}
32 | # -- Additional labels to add to the Pods created by the DaemonSet
33 | podLabels: {}
34 |
35 | # -- Kubernetes PodSecurityContext for the Pods
36 | podSecurityContext: {}
37 | # fsGroup: 2000
38 |
39 | # -- SecurityContext for the container
40 | securityContext:
41 | privileged: true
42 | # capabilities:
43 | # drop:
44 | # - ALL
45 | # readOnlyRootFilesystem: true
46 | # runAsNonRoot: true
47 | # runAsUser: 1000
48 |
49 | psp:
50 | # -- Specifies whether a PodSecurityPolicy (PSP) should be created
51 | create: false
52 | # -- PodSecurityPolicy spec
53 | spec:
54 | privileged: true
55 | allowedHostPaths: []
56 | volumes:
57 | - secret
58 |
59 | service:
60 | # -- Service type
61 | type: ClusterIP
62 | # -- Service port
63 | port: 9137
64 |
65 | # -- Resources for the dellhw_exporter container
66 | resources: {}
67 | # We usually recommend not to specify default resources and to leave this as a conscious
68 | # choice for the user. This also increases chances charts run on environments with little
69 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
70 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
71 | # limits:
72 | # cpu: 100m
73 | # memory: 128Mi
74 | # requests:
75 | # cpu: 100m
76 | # memory: 128Mi
77 |
78 | # -- NodeSelector for the DaemonSet
79 | nodeSelector: {}
80 |
81 | # -- Tolerations for the DaemonSet
82 | tolerations: []
83 |
84 | # -- Affinity for the DaemonSet
85 | affinity: {}
86 |
87 | serviceMonitor:
88 | # -- Specifies whether a prometheus-operator ServiceMonitor should be created
89 | enabled: false
90 | # -- Additional Labels for the ServiceMonitor object
91 | additionalLabels: {}
92 | #namespace: "monitoring"
93 | # -- ServiceMonitor namespace selector (check the comments below for examples)
94 | namespaceSelector:
95 | # Default: scrape .Release.Namespace only
96 | # To scrape all, use the following:
97 | # matchNames:
98 | # - monitoring
99 | # any: true
100 | # -- (duration) Interval at which metrics should be scraped
101 | scrapeInterval: 30s
102 | # -- (duration) Timeout for scraping
103 | scrapeTimeout: 20s
104 | # honorLabels: true
105 |
106 | prometheusRule:
107 | # -- Specifies whether a prometheus-operator PrometheusRule should be created
108 | enabled: false
109 | # -- Additional Labels for the PrometheusRule object
110 | additionalLabels: {}
111 | # Default: .Release.Namespace
112 | # namespace: ""
113 | # -- Checkout the https://github.com/galexrt/dellhw_exporter/blob/main/contrib/monitoring/prometheus-alerts/prometheus-alerts.yml for example alerts
114 | rules: []
115 |
116 | # -- Additional environments to be added to the dellhw_exporter container, use this to configure the exporter (see https://github.com/galexrt/dellhw_exporter/blob/main/docs/configuration.md#environment-variables)
117 | additionalEnv:
118 | - name: HOSTNAME
119 | valueFrom:
120 | fieldRef:
121 | fieldPath: spec.nodeName
122 | # To have the exporter check if certain collectors are available before enabling them,
123 | # you can use the following env var. Currently only the `chassis_batteries` collector.
124 | #- name: DELLHW_EXPORTER_COLLECTORS_CHECK
125 | # value: "chassis_batteries"
126 | # E.g., to enable the `chassis_info` collector you can add this env var
127 | #- name: DELLHW_EXPORTER_COLLECTORS_ADDITIONAL
128 | # value: "chassis_info"
129 | # List of nics to monitor
130 | #- name: DELLHW_EXPORTER_MONITORED_NICS
131 | # value: "nic1,nic2"
132 |
133 | # -- Additional volumes to be mounted in the dellhw_exporter container.
134 | additionalVolumes: []
135 | # Mount the host's `/etc/os-release` file to the container's `/etc/os-release` path.
136 | #- name: host-os-release
137 | # hostPath:
138 | # path: /etc
139 | # type: FileOrCreate
140 |
141 | # -- Additional volume mounts for the dellhw_exporter container.
142 | additionalVolumeMounts: []
143 | # Mount the host's `/etc/os-release` file to the container's `/etc/os-release` path.
144 | #- name: host-os-release
145 | # mountPath: /etc/os-release
146 | # subPath: os-release
147 | # readOnly: true
148 |
--------------------------------------------------------------------------------
/docs/zsite-notice.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Impressum"
3 | url: "/site-notice"
4 | toc: true
5 | ---
6 |
7 | The Privacy Policy / Datenschutzerklärung can be found here: [Privacy Policy / Datenschutzerklärung page](zprivacy-policy.md).
8 |
9 | **[German version of the Site Notice / Impressum below.](#impressum)**
10 |
11 | ***
12 |
13 | ## Site Notice
14 |
15 | ### Information pursuant to Sect. 5 German Telemedia Act (TMG)
16 |
17 |
18 | Alexander Trost
19 | Postfach 11
20 | 75196 Kämpfelbach
21 | Germany
22 |
23 |
24 | ### Contact
25 |
26 |
27 | E-mail: galexrt@googlemail.com
28 |
29 |
30 | ### Responsible for the content according to Sect. 55, paragraph 2 RStV
31 |
32 |
33 | Alexander Trost
34 | Postfach 11
35 | 75196 Kämpfelbach
36 | Germany
37 |
38 |
39 | #### Liability for Contents
40 |
41 | As service providers, we are liable for own contents of these websites according to Paragraph 7, Sect. 1 German Telemedia Act (TMG). However, according to Paragraphs 8 to 10 German Telemedia Act (TMG), service providers are not obligated to permanently monitor submitted or stored information or to search for evidences that indicate illegal activities.
42 |
43 | Legal obligations to removing information or to blocking the use of information remain unchallenged. In this case, liability is only possible at the time of knowledge about a specific violation of law. Illegal contents will be removed immediately at the time we get knowledge of them.
44 |
45 | #### Liability for Links
46 |
47 | Our offer includes links to external third-party websites. We have no influence on the contents of those websites, therefore we cannot guarantee for those contents. Providers or administrators of linked websites are always responsible for their own contents.
48 |
49 | The linked websites had been checked for possible violations of law at the time of the establishment of the link. Illegal contents were not detected at the time of the linking. A permanent monitoring of the contents of linked websites cannot be imposed without reasonable indications that there has been a violation of law. Illegal links will be removed immediately at the time we get knowledge of them.
50 |
51 | #### Copyright
52 |
53 | Contents and compilations published on these websites by the providers are subject to German copyright laws. Reproduction, editing, distribution as well as the use of any kind outside the scope of the copyright law require a written permission of the author or originator. Downloads and copies of these websites are permitted for private use only.
54 |
55 | The commercial use of our contents without permission of the originator is prohibited.
56 |
57 | Copyright laws of third parties are respected as long as the contents on these websites do not originate from the provider. Contributions of third parties on this site are indicated as such. However, if you notice any violations of copyright law, please inform us. Such contents will be removed immediately.
58 |
59 | ***
60 |
61 | ## Impressum
62 |
63 | English version below [Site Notice section](#site-notice).
64 |
65 | ### Angaben gemäß § 5 TMG
66 |
67 |
68 | Alexander Trost
69 | Postfach 11
70 | 75196 Kämpfelbach
71 | Germany
72 |
73 |
74 | ### Kontakt
75 |
76 |
77 | E-Mail: galexrt@googlemail.com
78 |
79 |
80 | ### Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV
81 |
82 |
83 | Alexander Trost
84 | Postfach 11
85 | 75196 Kämpfelbach
86 | Germany
87 |
88 |
89 | #### Haftung für Inhalte
90 |
91 | Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
92 |
93 | Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
94 |
95 | #### Haftung für Links
96 |
97 | Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.
98 |
99 | #### Urheberrecht
100 |
101 | Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet.
102 |
103 | Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.
104 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | PROJECTNAME ?= dellhw_exporter
2 | DESCRIPTION ?= dellhw_exporter - Prometheus exporter for Dell Hardware components using OMSA.
3 | MAINTAINER ?= Alexander Trost
4 | HOMEPAGE ?= https://github.com/galexrt/dellhw_exporter
5 |
6 | GO111MODULE ?= on
7 | GO ?= go
8 | FPM ?= fpm
9 | PREFIX ?= $(shell pwd)
10 | BIN_DIR ?= $(PREFIX)/.bin
11 | TARBALL_DIR ?= $(PREFIX)/.tarball
12 | PACKAGE_DIR ?= $(PREFIX)/.package
13 | ARCH ?= amd64
14 | PACKAGE_ARCH ?= linux-amd64
15 |
16 | VERSION := $(shell cat VERSION)
17 | TOPDIR := $(shell pwd)
18 |
19 | RPMBUILD := $(TOPDIR)/RPMBUILD
20 |
21 | # The GOHOSTARM and PROMU parts have been taken from the prometheus/promu repository
22 | # which is licensed under Apache License 2.0 Copyright 2018 The Prometheus Authors
23 | FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
24 |
25 | GOHOSTOS ?= $(shell $(GO) env GOHOSTOS)
26 | GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH)
27 |
28 | ifeq (arm, $(GOHOSTARCH))
29 | GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM)
30 | GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM)
31 | else
32 | GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)
33 | endif
34 |
35 | PROMU_VERSION ?= 0.7.0
36 | PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
37 |
38 | PROMU := $(FIRST_GOPATH)/bin/promu
39 | # END copied code
40 |
41 | pkgs = $(shell go list ./... | grep -v /vendor/ | grep -v /test/)
42 |
43 | DOCKER_IMAGE_NAME ?= dellhw_exporter
44 | DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
45 |
46 | FILELIST := .promu.yml CHANGELOG.md cmd collector contrib docs go.sum Makefile NOTICE README.md VERSION \
47 | charts CODE_OF_CONDUCT.md container Dockerfile go.mod LICENSE mkdocs.yml pkg systemd
48 |
49 |
50 | all: format style vet test build
51 |
52 | build: promu
53 | @echo ">> building binaries"
54 | GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX)
55 |
56 | check_license:
57 | @OUTPUT="$$($(PROMU) check licenses)"; \
58 | if [[ $$OUTPUT ]]; then \
59 | echo "Found go files without license header:"; \
60 | echo "$$OUTPUT"; \
61 | exit 1; \
62 | else \
63 | echo "All files with license header"; \
64 | fi
65 |
66 | docker:
67 | @echo ">> building docker image"
68 | docker build \
69 | --build-arg BUILD_DATE="$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')" \
70 | --build-arg VCS_REF="$(shell git rev-parse HEAD)" \
71 | -t "$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" \
72 | .
73 |
74 | format:
75 | go fmt $(pkgs)
76 |
77 |
78 | tree: build
79 | mkdir -p -m0755 $(PACKAGE_DIR)/lib/systemd/system $(PACKAGE_DIR)/usr/sbin
80 | mkdir -p $(PACKAGE_DIR)/etc/sysconfig
81 | cp dellhw_exporter $(PACKAGE_DIR)/usr/sbin
82 | cp systemd/dellhw_exporter.service $(PACKAGE_DIR)/lib/systemd/system
83 | cp systemd/sysconfig.dellhw_exporter $(PACKAGE_DIR)/etc/sysconfig/dellhw_exporter
84 |
85 | package-%: tree
86 | cd $(PACKAGE_DIR) && $(FPM) -s dir -t $(patsubst package-%, %, $@) \
87 | --deb-user root --deb-group root \
88 | --name $(PROJECTNAME) \
89 | --version $(shell cat VERSION) \
90 | --architecture $(PACKAGE_ARCH) \
91 | --description "$(DESCRIPTION)" \
92 | --maintainer "$(MAINTAINER)" \
93 | --url $(HOMEPAGE) \
94 | usr/ etc/
95 |
96 |
97 | install: build
98 | mkdir -p -m0755 $(DESTDIR)/usr/lib/systemd/system $(DESTDIR)/usr/sbin
99 | mkdir -p $(DESTDIR)/etc/sysconfig
100 | cp dellhw_exporter $(DESTDIR)/usr/sbin
101 | cp systemd/dellhw_exporter.service $(DESTDIR)/usr/lib/systemd/system
102 | cp systemd/sysconfig.dellhw_exporter $(DESTDIR)/etc/sysconfig/dellhw_exporter
103 |
104 |
105 | $(PROJECTNAME).spec: $(PROJECTNAME).spec.in
106 | sed -e's#@VERSION@#$(VERSION)#g' $< > $@
107 |
108 | rpm: dist $(PROJECTNAME).spec
109 | mkdir -p $(RPMBUILD)/SOURCES \
110 | $(RPMBUILD)/SPECS \
111 | $(RPMBUILD)/BUILD \
112 | $(RPMBUILD)/RPMS $(RPMBUILD)/SRPMS
113 | cp $(PROJECTNAME)-$(VERSION).tar.gz $(RPMBUILD)/SOURCES/
114 | cp $(PROJECTNAME).spec $(RPMBUILD)/SPECS/
115 | rpmbuild -vv --define "_topdir $(RPMBUILD)" -ba $(PROJECTNAME).spec
116 |
117 |
118 | promu:
119 | $(eval PROMU_TMP := $(shell mktemp -d))
120 | curl -s -L $(PROMU_URL) | tar -xvzf - -C $(PROMU_TMP)
121 | mkdir -p $(FIRST_GOPATH)/bin
122 | cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu
123 | rm -r $(PROMU_TMP)
124 |
125 | style:
126 | @echo ">> checking code style"
127 | @! gofmt -d $(shell find . -path ./vendor -prune -o -name '*.go' -print) | grep '^'
128 |
129 | tarball: tree
130 | @echo ">> building release tarball"
131 | @$(PROMU) tarball --prefix $(TARBALL_DIR) $(BIN_DIR)
132 |
133 |
134 | dist:
135 | @rm -rf .srcpackage
136 | @mkdir .srcpackage
137 | cp -r $(FILELIST) .srcpackage/
138 | tar --transform "s/\.srcpackage/$(PROJECTNAME)-$(VERSION)/" -zcvf $(PROJECTNAME)-$(VERSION).tar.gz .srcpackage
139 |
140 | clean:
141 | rm -rf .srcpackage RPMBUILD $(PROJECTNAME) $(PROJECTNAME).spec $(PROJECTNAME)-$(VERSION).tar.gz
142 |
143 |
144 | test:
145 | @$(GO) test $(pkgs)
146 |
147 | test-short:
148 | @echo ">> running short tests"
149 | @$(GO) test -short $(pkgs)
150 |
151 | vet:
152 | @echo ">> vetting code"
153 | @$(GO) vet $(pkgs)
154 |
155 | docs-serve:
156 | docker run --net=host --volume "$$(pwd)":"$$(pwd)" --workdir "$$(pwd)" -it squidfunk/mkdocs-material
157 |
158 | docs-build:
159 | docker run --net=host --volume "$$(pwd)":"$$(pwd)" --workdir "$$(pwd)" -it squidfunk/mkdocs-material build --clean
160 |
161 | helm-docs-binary:
162 | GO111MODULE=on $(GO) install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.11.0
163 |
164 | helm-docs: helm-docs-binary
165 | helm-docs
166 |
167 | .PHONY: all build crossbuild docker format package promu style tarball test vet docs-serve docs-build helm-docs docs
168 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
4 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
5 | github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
6 | github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9 | github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
10 | github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
11 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
12 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
13 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
14 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
15 | github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
16 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
17 | github.com/kardianos/service v1.2.4 h1:XNlGtZOYNx2u91urOdg/Kfmc+gfmuIo1Dd3rEi2OgBk=
18 | github.com/kardianos/service v1.2.4/go.mod h1:E4V9ufUuY82F7Ztlu1eN9VXWIQxg8NoLQlmFe0MtrXc=
19 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
20 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
21 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
22 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
23 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
24 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
25 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
26 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
27 | github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
28 | github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
29 | github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
30 | github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
31 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
32 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
33 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
34 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
35 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
36 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
37 | github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
38 | github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
39 | github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
40 | github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
41 | github.com/prometheus/common v0.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q=
42 | github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
43 | github.com/prometheus/exporter-toolkit v0.15.0 h1:Pcle5sSViwR1x0gdPd0wtYrPQENBieQAM7TmT0qtb2U=
44 | github.com/prometheus/exporter-toolkit v0.15.0/go.mod h1:OyRWd2iTo6Xge9Kedvv0IhCrJSBu36JCfJ2yVniRIYk=
45 | github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
46 | github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
47 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
48 | github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
49 | github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
50 | github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
51 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
52 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
53 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
54 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
55 | go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
56 | go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
57 | golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
58 | golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
59 | golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
60 | golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
61 | golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
62 | golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
63 | golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
64 | golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
65 | golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
66 | golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
67 | golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
68 | golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
69 | golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
70 | golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
71 | google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
72 | google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
73 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
74 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
75 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
76 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
77 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
78 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 2.0.0-rc.4 / 2025-10-24
2 |
3 | * [FIX] Helm Chart Statefulset marshal issues
4 | * [FIX] Negative Waitgroup causing panic during metrics collection
5 |
6 | ## 2.0.0-rc.3 / 2025-09-24
7 |
8 | * [ENHANCEMENT] Added `--collectors-check` flag to check the specified collectors, currently only `chassis_batteries` is "supported" on the system. This is [due to the removal of CMOS battery sensor data in newer firmwares (Dell Support page)](https://www.dell.com/support/kbdoc/en-uk/000227413/14g-intel-poweredge-coin-cell-battery-changes-in-august-2024-firmware). Thanks to [@lewispb](https://github.com/lewispb) for bringing this up!
9 | * [CHORE] Updated dependencies and Golang version to 1.25.1
10 |
11 | ## 2.0.0-rc.2 / 2025-07-17
12 |
13 | * [ENHANCEMENT] Exporter Toolkit: Allows you to easily use TLS and basic auth for the exporter, click here for more details. Thanks to [@AlexandarY](https://github.com/AlexandarY) for implementing this!
14 | * [FIX] Helm Chart: Disable Pod Security Policies by default as they have been deprecated in Kubernetes 1.21+ and removed in 1.25+
15 | * [ENHANCEMENT] Helm Chart: Add `additionalVolumeMounts` and `additionalVolumes` to allow adding additional volumes and mounts to the exporter pods.
16 |
17 | ## 2.0.0-rc.1 / 2024-10-19
18 |
19 | * [CHORE] add a basic nix flake to make development easier for me :-)
20 | * [CHORE] replace logrus with zap logger
21 | * [CHORE] update dependencies
22 | * [CHORE] update golang version to 1.23
23 | * [FEATURE] add `--collectors-additional` flag to allow enabling a disabled exporter on top of the `--collectors-enabled` flag
24 | * [FEATURE] Helm chart: add `additionalEnv` list and update chart documentation
25 | * [FEATURE] rewrite the omreport parser logic to be more flexible and consistent across `omreport` commands and versions (fixes #115 and other parsing related issues)
26 | * [FIX] fix docs page not working (404 errors when opening any page)
27 | * [FIX] updated documentation for new changes and added a FAQ page
28 |
29 | ## 1.13.13 / 2024-09-02
30 |
31 | * [FEATURE] add exporter version metric `dell_hw_exporter_version`
32 | * [SECURITY] update go dependencies
33 |
34 | ## 1.13.12 / 2024-05-15
35 |
36 | * [SECURITY] update github.com/prometheus/client_golang to v1.19.1
37 | * [CHORE] update golang version to 1.22 in CI and 1.22.3 in go.mod
38 |
39 | ## 1.13.11 / 2024-04-15
40 |
41 | * [BUGFIX] add workaround for vdisk rebuilding progress causing parsing errors, see [#106](https://github.com/galexrt/dellhw_exporter/issues/106)
42 |
43 | ## 1.13.10 / 2024-02-28
44 |
45 | * [BUGFIX] ignore exit code 255 for omreport command - should resolve [#99](https://github.com/galexrt/dellhw_exporter/issues/99)
46 |
47 | ## 1.13.9 / 2024-02-16
48 |
49 | * [BUGFIX] log the command that failed to execute
50 |
51 | ## 1.13.8 / 2024-02-16
52 |
53 | * [BUGFIX] fix vdisk for (older?) omreport outputs
54 |
55 | ## 1.13.7 / 2024-02-15
56 |
57 | * [CHORE] updated minimum go version to 1.21
58 |
59 | ## 1.13.6 / 2024-02-15
60 |
61 | * [BUGFIX] add vdisk read and write policy to vdisk collector to address final parts of [#93](https://github.com/galexrt/dellhw_exporter/issues/93)
62 |
63 | ## 1.13.5 / 2024-02-15
64 |
65 | * [BUGFIX] add "Non-Raid" state to pdisk collector to address parts of [#93](https://github.com/galexrt/dellhw_exporter/issues/93)
66 | * [BUGFIX] add logging to vdisk collector
67 |
68 | ## 1.13.4 / 2024-02-13
69 |
70 | * [BUGFIX] add logging to pdisk collector
71 |
72 | ## 1.13.3 / 2024-02-13
73 |
74 | * [BUGFIX] improve log lines to better be able to pin point the recent parsing issues
75 |
76 | ## 1.13.2 / 2024-02-06
77 |
78 | * [BUGFIX] [Consider 'Not Applicable' as healthy for Nic status #95](https://github.com/galexrt/dellhw_exporter/pull/95)
79 | * Thanks to [@B0go](https://github.com/B0go) for fixing this issue!
80 |
81 | ## 1.13.1 / 2023-12-07
82 |
83 | * [BUGFIX] Fix container image build issue caused by wget, use curl now
84 |
85 | ## 1.13.0 / 2023-12-07
86 |
87 | * [ENHANCEMENT] [Allow for user to specify a list of interfaces to monitor #89](https://github.com/galexrt/dellhw_exporter/pull/89)
88 | * [ENHANCEMENT] Added Storage Pdisk Hardware Encryption status
89 | * [SECURITY] Updated dependencies to latest version
90 |
91 | ## 1.12.2 / 2022-05-31
92 |
93 | * [SECURITY] update gopkg.in/yaml.v3 to v3.0.1 (CVE-2022-28948)
94 |
95 | ## 1.12.1 / 2022-05-04
96 |
97 | * [ENHANCEMENT] update deps to latest version
98 | * [ENHANCEMENT] updated minimum go version to 1.18
99 |
100 | ## 1.12.0 / 2022-02-02
101 |
102 | * [ENHANCEMENT] Added Pdisk Remaining Rated Write Endurance Metric by @adityaborgaonkar
103 |
104 | ## 1.11.1 / 2021-10-12
105 |
106 | * [ENHANCEMENT] update go version to 1.16
107 |
108 | ## 1.11.0 / 2021-09-12
109 |
110 | * [ENHANCEMENT] add vdisk raid level metric
111 | * This adds `dell_hw_storage_vdisk_raidlevel` metric, which holds the RAID
112 | level of the VDISK.
113 | Additionally the controller ID label was added to some metrics missing
114 | it. Resolves #8
115 |
116 | ## 1.10.0 / 2021-08-30
117 |
118 | * [ENHANCEMENT] add pdisk "predicted failure" metric
119 |
120 | ## 1.9.0 / 2021-08-29
121 |
122 | * [ENHANCEMENT] update go version and deps
123 |
124 | ## 1.8.0 / 2020-10-07
125 |
126 | * [ENHANCEMENT] Windows Service Support
127 | * Thanks to [@kyle-williams-1](https://github.com/kyle-williams-1) for adding this feature!
128 | * [ENHANCEMENT] Kubernetes Helm chart
129 |
130 | ## 1.7.0 / 2020-09-29
131 |
132 | * [ENHANCEMENT] Metric results can be cached to improve performance.
133 | * Thanks to [@Phil1602](https://github.com/Phil1602) for adding this as a feature!
134 | * [ENHANCEMENT] The default value of the `--collectors-omreport` flag is now dependent on the OS for Linux and Windows.
135 | * Thanks to [@kyle-williams-1](https://github.com/kyle-williams-1) for adding this as a feature!
136 | * [ENHANCEMENT] Enabled `windows/amd64` release binary builds.
137 | * [ENHANCEMENT] Golang 1.15 is used by default for CI and build.
138 | * [ENHANCEMENT] Updated LICENSE file and go code file headers.
139 | * [ENHANCEMENT] Created documentation page using [mkdocs](https://www.mkdocs.org/), available at [dellhw-exporter.galexrt.moe](https://dellhw-exporter.galexrt.moe/).
140 |
141 | ## 1.6.0 / 2020-06-09
142 |
143 | * [ENHANCEMENT] Add support for firmware versions #43 (PR #44).
144 | * Thanks to [@sfudeus](https://github.com/sfudeus) for implementing this!
145 | * [ENHANCEMENT] docker: added expose for 9137/tcp exporter port
146 |
147 | ## 1.5.19 / 2020-06-07
148 |
149 | * [BUGFIX] ci: debug using tmate action
150 |
151 | ## 1.5.18 / 2020-06-07
152 |
153 | * [BUGFIX] ci: debug using tmate action
154 |
155 | ## 1.5.17 / 2020-06-07
156 |
157 | * [BUGFIX] ci: fix build routine issues #42
158 |
159 | ## 1.5.16 / 2020-06-07
160 |
161 | * [ENHANCEMENT] ci: no need to specify docker build dir
162 |
163 | ## 1.5.15 / 2020-06-07
164 |
165 | * [ENHANCEMENT] docker: fix copy path for binary
166 |
167 | ## 1.5.14 / 2020-06-07
168 |
169 | * [ENHANCEMENT] ci: use correct env vars for image name
170 |
171 | ## 1.5.13 / 2020-06-07
172 |
173 | * [ENHANCEMENT] ci: use correct env vars for image name
174 |
175 | ## 1.5.11 / 2020-06-07
176 |
177 | * [ENHANCEMENT] ci: use github actions
178 |
179 | ## 1.5.9 / 2020-06-07
180 |
181 | * [ENHANCEMENT] ci: use github actions
182 |
183 | ## 1.4.2 / 2020-02-24
184 |
185 | * [ENHANCEMENT] ci: fixed CI release upload
186 |
--------------------------------------------------------------------------------
/contrib/monitoring/grafana-dashboards/Node - Dell Disk Status.json:
--------------------------------------------------------------------------------
1 | {
2 | "__inputs": [],
3 | "__requires": [
4 | {
5 | "type": "grafana",
6 | "id": "grafana",
7 | "name": "Grafana",
8 | "version": "4.6.3"
9 | },
10 | {
11 | "type": "panel",
12 | "id": "graph",
13 | "name": "Graph",
14 | "version": ""
15 | }
16 | ],
17 | "annotations": {
18 | "list": [
19 | {
20 | "builtIn": 1,
21 | "datasource": "-- Grafana --",
22 | "enable": true,
23 | "hide": true,
24 | "iconColor": "rgba(0, 211, 255, 1)",
25 | "name": "Annotations & Alerts",
26 | "type": "dashboard"
27 | }
28 | ]
29 | },
30 | "description": "Dell server disk overview.",
31 | "editable": true,
32 | "gnetId": null,
33 | "graphTooltip": 0,
34 | "hideControls": false,
35 | "id": null,
36 | "links": [],
37 | "rows": [
38 | {
39 | "collapse": false,
40 | "height": "250px",
41 | "panels": [
42 | {
43 | "aliasColors": {},
44 | "bars": false,
45 | "dashLength": 10,
46 | "dashes": false,
47 | "datasource": "$Cluster",
48 | "fill": 1,
49 | "height": "250px",
50 | "id": 1,
51 | "legend": {
52 | "avg": false,
53 | "current": false,
54 | "max": false,
55 | "min": false,
56 | "rightSide": true,
57 | "show": true,
58 | "sideWidth": 250,
59 | "total": false,
60 | "values": false
61 | },
62 | "lines": true,
63 | "linewidth": 1,
64 | "links": [],
65 | "nullPointMode": "null",
66 | "percentage": false,
67 | "pointradius": 5,
68 | "points": false,
69 | "renderer": "flot",
70 | "seriesOverrides": [],
71 | "spaceLength": 10,
72 | "span": 12,
73 | "stack": false,
74 | "steppedLine": false,
75 | "targets": [
76 | {
77 | "expr": "count(dell_hw_storage_pdisk_status) BY (instance)",
78 | "format": "time_series",
79 | "intervalFactor": 2,
80 | "legendFormat": "{{ instance }}",
81 | "refId": "A",
82 | "step": 40
83 | }
84 | ],
85 | "thresholds": [],
86 | "timeFrom": null,
87 | "timeShift": null,
88 | "title": "Number of available hard drives",
89 | "tooltip": {
90 | "shared": true,
91 | "sort": 0,
92 | "value_type": "individual"
93 | },
94 | "type": "graph",
95 | "xaxis": {
96 | "buckets": null,
97 | "mode": "time",
98 | "name": null,
99 | "show": true,
100 | "values": []
101 | },
102 | "yaxes": [
103 | {
104 | "format": "short",
105 | "label": null,
106 | "logBase": 1,
107 | "max": null,
108 | "min": null,
109 | "show": true
110 | },
111 | {
112 | "format": "short",
113 | "label": null,
114 | "logBase": 1,
115 | "max": null,
116 | "min": null,
117 | "show": true
118 | }
119 | ]
120 | }
121 | ],
122 | "repeat": null,
123 | "repeatIteration": null,
124 | "repeatRowId": null,
125 | "showTitle": false,
126 | "title": "Dashboard Row",
127 | "titleSize": "h6"
128 | },
129 | {
130 | "collapse": false,
131 | "height": 250,
132 | "panels": [
133 | {
134 | "aliasColors": {},
135 | "bars": false,
136 | "dashLength": 10,
137 | "dashes": false,
138 | "datasource": "$Cluster",
139 | "fill": 1,
140 | "id": 2,
141 | "legend": {
142 | "alignAsTable": true,
143 | "avg": false,
144 | "current": false,
145 | "max": false,
146 | "min": false,
147 | "rightSide": true,
148 | "show": true,
149 | "total": false,
150 | "values": false
151 | },
152 | "lines": true,
153 | "linewidth": 1,
154 | "links": [],
155 | "nullPointMode": "null",
156 | "percentage": false,
157 | "pointradius": 5,
158 | "points": false,
159 | "renderer": "flot",
160 | "seriesOverrides": [],
161 | "spaceLength": 10,
162 | "span": 12,
163 | "stack": false,
164 | "steppedLine": false,
165 | "targets": [
166 | {
167 | "expr": "dell_hw_storage_vdisk_status > 0",
168 | "format": "time_series",
169 | "interval": "",
170 | "intervalFactor": 2,
171 | "legendFormat": "{{instance}} - {{}}",
172 | "refId": "A",
173 | "step": 40
174 | }
175 | ],
176 | "thresholds": [],
177 | "timeFrom": null,
178 | "timeShift": null,
179 | "title": "VDisk Status (>0 is bad)",
180 | "tooltip": {
181 | "shared": true,
182 | "sort": 0,
183 | "value_type": "individual"
184 | },
185 | "type": "graph",
186 | "xaxis": {
187 | "buckets": null,
188 | "mode": "time",
189 | "name": null,
190 | "show": true,
191 | "values": []
192 | },
193 | "yaxes": [
194 | {
195 | "decimals": 0,
196 | "format": "short",
197 | "label": "",
198 | "logBase": 1,
199 | "max": "2",
200 | "min": "1",
201 | "show": true
202 | },
203 | {
204 | "format": "short",
205 | "label": null,
206 | "logBase": 1,
207 | "max": null,
208 | "min": null,
209 | "show": true
210 | }
211 | ]
212 | }
213 | ],
214 | "repeat": null,
215 | "repeatIteration": null,
216 | "repeatRowId": null,
217 | "showTitle": false,
218 | "title": "Dashboard Row",
219 | "titleSize": "h6"
220 | }
221 | ],
222 | "schemaVersion": 14,
223 | "style": "dark",
224 | "tags": [
225 | "prometheus",
226 | "node",
227 | "dell"
228 | ],
229 | "templating": {
230 | "list": [
231 | {
232 | "current": {
233 | "text": "prometheus",
234 | "value": "prometheus"
235 | },
236 | "hide": 0,
237 | "label": null,
238 | "name": "Cluster",
239 | "options": [],
240 | "query": "prometheus",
241 | "refresh": 1,
242 | "regex": "",
243 | "type": "datasource"
244 | }
245 | ]
246 | },
247 | "time": {
248 | "from": "now-12h",
249 | "to": "now"
250 | },
251 | "timepicker": {
252 | "refresh_intervals": [
253 | "5s",
254 | "10s",
255 | "30s",
256 | "1m",
257 | "5m",
258 | "15m",
259 | "30m",
260 | "1h",
261 | "2h",
262 | "1d"
263 | ],
264 | "time_options": [
265 | "5m",
266 | "15m",
267 | "1h",
268 | "6h",
269 | "12h",
270 | "24h",
271 | "2d",
272 | "7d",
273 | "30d"
274 | ]
275 | },
276 | "timezone": "",
277 | "title": "Node - Dell Disk Overview",
278 | "version": 4
279 | }
280 |
--------------------------------------------------------------------------------
/pkg/omreport/util.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package omreport
18 |
19 | // Command executes the named program with the given arguments. If it does not
20 | import (
21 | "bytes"
22 | "errors"
23 | "fmt"
24 | "io"
25 | "log/slog"
26 | "os"
27 | "os/exec"
28 | "regexp"
29 | "slices"
30 | "strings"
31 | "sync/atomic"
32 | "time"
33 | "unicode"
34 | "unicode/utf8"
35 | )
36 |
37 | type Output = []Report
38 |
39 | type Report struct {
40 | Title string
41 | Description string
42 |
43 | Lines []Line
44 | }
45 |
46 | type Line = map[string]string
47 |
48 | var (
49 | // ErrPath is returned by Command if the program is not in the PATH.
50 | ErrPath = errors.New("program not in PATH")
51 | // ErrTimeout is returned by Command if the program timed out.
52 | ErrTimeout = errors.New("program killed after timeout")
53 |
54 | // cmdTimeout configurable timeout for commands.
55 | cmdTimeout int64 = 10
56 |
57 | logger = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{Level: slog.LevelError}))
58 | )
59 |
60 | // clean concatenates arguments with a space and removes extra whitespace.
61 | func clean(ss ...string) string {
62 | v := strings.Join(ss, " ")
63 | fs := strings.Fields(v)
64 | return strings.Join(fs, " ")
65 | }
66 |
67 | // extract tries to return a parsed number from s with given suffix. A space may
68 | // be present between number ond suffix.
69 | func extract(s, suffix string) (string, error) {
70 | if !strings.HasSuffix(s, suffix) {
71 | return "0", fmt.Errorf("extract: suffix not found")
72 | }
73 | s = s[:len(s)-len(suffix)]
74 | return strings.TrimSpace(s), nil
75 | }
76 |
77 | // severity returns 1 if s is not "Ok" or "Non-Critical" (should be "Critical" then in most cases)
78 | // elif is "Non-Critical" 2 else 0.
79 | func severity(s string) string {
80 | if s != "Ok" && s != "Non-Critical" {
81 | return "1"
82 | }
83 | if s == "Non-Critical" {
84 | return "2"
85 | }
86 | return "0"
87 | }
88 |
89 | func pdiskState(s string) string {
90 | states := map[string]string{
91 | "Unknown": "0",
92 | "Ready": "1",
93 | "Online": "2",
94 | "Degraded": "3",
95 | "Failed": "4",
96 | "Offline": "5",
97 | "Rebuilding": "6",
98 | "Incompatible": "7",
99 | "Removed": "8",
100 | "Clear": "9",
101 | "SMART Alert Detected": "10",
102 | "Foreign": "11",
103 | "Unsupported": "12",
104 | "Replacing": "13",
105 | "Non-RAID": "14",
106 | }
107 |
108 | return states[s]
109 | }
110 |
111 | func vdiskState(s string) string {
112 | states := map[string]string{
113 | "Ready": "1",
114 | "Degraded": "2",
115 | "Resynching": "3",
116 | "Resynching Paused": "4",
117 | "Regenerating": "5",
118 | "Reconstructing": "6",
119 | "Failed": "7",
120 | "Failed Redundancy": "8",
121 | "Background Initialization": "9",
122 | "Formatting": "10",
123 | "Initializing": "11",
124 | "Degraded Redundancy": "12",
125 | }
126 |
127 | return states[s]
128 | }
129 |
130 | func vdiskReadPolicy(s string) string {
131 | policies := map[string]string{
132 | "Not Applicable": "0",
133 | "Read Ahead": "1",
134 | "No Read Ahead": "2",
135 | "Read Cache Enabled": "3",
136 | "Read Cache Disabled": "4",
137 | "Adaptive Read Ahead": "5",
138 | }
139 |
140 | return policies[s]
141 | }
142 |
143 | func vdiskWritePolicy(s string) string {
144 | policies := map[string]string{
145 | "Not Applicable": "0",
146 | "Write Ahead": "1",
147 | "Force Write Back": "2",
148 | "Write Back Enabled": "3",
149 | "Write Through": "4",
150 | "Write Cache Enabled Protected": "5",
151 | "Write Cache Disabled": "6",
152 | "Write Back": "7",
153 | }
154 |
155 | return policies[s]
156 | }
157 |
158 | func vdiskCachePolicy(s string) string {
159 | policies := map[string]string{
160 | "Not Applicable": "0",
161 | "Cache I/O": "1",
162 | "Direct I/O": "2",
163 | }
164 |
165 | return policies[s]
166 | }
167 |
168 | // yesNoToBool returns "1" for "Yes" and "0" for "No"
169 | func yesNoToBool(s string) string {
170 | if s == "Yes" {
171 | return "1"
172 | }
173 | return "0"
174 | }
175 |
176 | var getNumberFromStringRegex = regexp.MustCompile("[0-9]+")
177 |
178 | func getNumberFromString(s string) string {
179 | result := getNumberFromStringRegex.FindString(s)
180 | if result != "" {
181 | return result
182 | }
183 | return "-1"
184 | }
185 |
186 | func replace(name string) string {
187 | r, _ := Replace(name, "_")
188 | return r
189 | }
190 |
191 | // Replace certain chars in a string
192 | func Replace(s, replacement string) (string, error) {
193 | var c string
194 | replaced := false
195 | for len(s) > 0 {
196 | r, size := utf8.DecodeRuneInString(s)
197 | if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '_' || r == '.' || r == '/' {
198 | c += string(r)
199 | replaced = false
200 | } else if !replaced {
201 | c += replacement
202 | replaced = true
203 | }
204 | s = s[size:]
205 | }
206 | if len(c) == 0 {
207 | return "", fmt.Errorf("clean result is empty")
208 | }
209 | return c, nil
210 | }
211 |
212 | // Command exit within timeout, it is sent SIGINT (if supported by Go). After
213 | // another timeout, it is killed.
214 | func Command(timeout time.Duration, stdin io.Reader, name string, args ...string) (io.Reader, error) {
215 | if _, err := exec.LookPath(name); err != nil {
216 | return nil, ErrPath
217 | }
218 | logger.Debug("executing command", "command", name, "args", args)
219 | c := exec.Command(name, args...)
220 | b := &bytes.Buffer{}
221 | c.Stdout = b
222 | c.Stdin = stdin
223 | if err := c.Start(); err != nil {
224 | return nil, err
225 | }
226 | timedOut := false
227 | intTimer := time.AfterFunc(timeout, func() {
228 | logger.Error("process taking too long, interrupting: ", "command", name, "args", args)
229 | c.Process.Signal(os.Interrupt)
230 | timedOut = true
231 | })
232 | killTimer := time.AfterFunc(timeout, func() {
233 | logger.Error("process taking too long, killing", "command", name, "args", args)
234 | c.Process.Signal(os.Interrupt)
235 | timedOut = true
236 | })
237 | err := c.Wait()
238 | intTimer.Stop()
239 | killTimer.Stop()
240 | if timedOut {
241 | return nil, ErrTimeout
242 | }
243 | return b, err
244 | }
245 |
246 | // ReadCommand runs command name with args and calls fn for the output from
247 | // stdout. Command is interrupted (if supported by Go) after 10 seconds and
248 | // killed after 20 seconds.
249 | func readCommand(fn func(string) error, name string, arg ...string) error {
250 | timeout := time.Duration(int(atomic.LoadInt64(&cmdTimeout)))
251 | return readCommandTimeout(timeout*time.Second, fn, nil, name, arg...)
252 | }
253 |
254 | // ReadCommandTimeout is the same as ReadCommand with a specifiable timeout.
255 | // It can also take a []byte as input (useful for chaining commands).
256 | func readCommandTimeout(timeout time.Duration, fn func(string) error, stdin io.Reader, name string, args ...string) error {
257 | b, err := Command(timeout, stdin, name, args...)
258 | if err != nil {
259 | if exitErr, ok := err.(*exec.ExitError); ok {
260 | // Skip exit code 255, it should indicate that no devices have been found in some cases
261 | if exitErr.ExitCode() == 255 {
262 | return nil
263 | }
264 | }
265 | return fmt.Errorf("failed to execute command (\"%s %s\"). %w", name, args, err)
266 | }
267 |
268 | out, err := io.ReadAll(b)
269 | if err != nil {
270 | logger.Error("failed to read command output", "command", name, "args", args, "error", err.Error())
271 | }
272 |
273 | if err := fn(string(out[:])); err != nil {
274 | return fmt.Errorf("failed to process command (\"%s %s\") output. %w", name, args, err)
275 | }
276 |
277 | return nil
278 | }
279 |
280 | // SetCommandTimeout this function can be used to atomically set the command execution timeout
281 | func SetCommandTimeout(timeout int64) {
282 | atomic.StoreInt64(&cmdTimeout, timeout)
283 | }
284 |
285 | func hasKeys(in map[string]string, fields ...string) bool {
286 | for _, field := range fields {
287 | field = normalizeName(field)
288 | if _, ok := in[field]; !ok {
289 | return false
290 | }
291 | }
292 |
293 | return true
294 | }
295 |
296 | func parseOutput(mode ReaderMode, input string) Output {
297 | output := Output{
298 | {},
299 | }
300 | ri := 0
301 | gotTitle := false
302 | kvSeparated := false
303 |
304 | keyLine := ""
305 | keys := []string{}
306 |
307 | prevLine := ""
308 | nextLine := ""
309 |
310 | spl := strings.Split(input, "\n")
311 | for i, line := range spl {
312 | if i > 0 {
313 | prevLine = spl[i-1]
314 | }
315 | if len(spl) > i+1 {
316 | nextLine = spl[i+1]
317 | } else {
318 | nextLine = ""
319 | }
320 |
321 | line = clean(line)
322 |
323 | if line == "" {
324 | if strings.Contains(prevLine, ";") {
325 | output = append(output, Report{})
326 | ri++
327 | gotTitle = false
328 | kvSeparated = false
329 | keyLine = ""
330 | keys = []string{}
331 | }
332 | continue
333 | }
334 |
335 | if strings.HasPrefix(line, "For further help") {
336 | continue
337 | }
338 |
339 | if !strings.Contains(line, ";") {
340 | if !gotTitle {
341 | output[ri].Title = line
342 | gotTitle = true
343 | } else {
344 | output[ri].Description = line
345 | }
346 | } else {
347 | sp := strings.Split(line, ";")
348 |
349 | // Handle special cases..
350 | if output[ri].Description == "Version Information" {
351 | keyLine = line
352 | keys = []string{"component", "version"}
353 | kvSeparated = true
354 | } else if output[ri].Title == "Amperage" {
355 | keys = []string{"psu", "amperage"}
356 | } else if output[ri].Title == "BIOS Information" {
357 | kvSeparated = true
358 | } else if strings.Count(line, ";") > 1 {
359 | kvSeparated = true
360 | }
361 |
362 | if len(keys) == 0 {
363 | keyLine = line
364 | keys = append(keys, sp...)
365 |
366 | // Normalize keys to lower case
367 | for i := 0; i < len(keys); i++ {
368 | keys[i] = normalizeName(keys[i])
369 | }
370 |
371 | if prevLine == "" && strings.Contains(nextLine, ";") && keyLine != line {
372 | continue
373 | }
374 | }
375 |
376 | if (mode == KeyValueReaderMode || kvSeparated) && strings.Count(line, ";") == 1 {
377 | output[ri].Lines = append(output[ri].Lines, Line{
378 | normalizeName(sp[0]): sp[1],
379 | })
380 | } else if mode <= TableReaderMode && keyLine != line {
381 | l := Line{}
382 | for i, s := range sp {
383 | if i > len(keys)-1 {
384 | continue
385 | }
386 |
387 | l[keys[i]] = s
388 | }
389 |
390 | output[ri].Lines = append(output[ri].Lines, l)
391 | }
392 | }
393 | }
394 |
395 | if len(output[ri].Lines) == 0 {
396 | output = slices.Delete(output, ri, ri+1)
397 | }
398 |
399 | return output
400 | }
401 |
402 | func normalizeName(in string) string {
403 | return strings.Replace(strings.ToLower(in), " ", "_", -1)
404 | }
405 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/cmd/dellhw_exporter/dellhw_exporter.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2021 The dellhw_exporter Authors. All rights reserved.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "log/slog"
22 | "net/http"
23 | "os"
24 | "runtime"
25 | "slices"
26 | "sort"
27 | "strings"
28 | "sync"
29 | "time"
30 |
31 | flag "github.com/spf13/pflag"
32 |
33 | "github.com/galexrt/dellhw_exporter/collector"
34 | "github.com/galexrt/dellhw_exporter/pkg/omreport"
35 | "github.com/kardianos/service"
36 | "github.com/prometheus/client_golang/prometheus"
37 | "github.com/prometheus/client_golang/prometheus/promhttp"
38 | "github.com/prometheus/common/version"
39 | "github.com/prometheus/exporter-toolkit/web"
40 | )
41 |
42 | var defaultCollectors = []string{
43 | "chassis",
44 | "chassis_batteries",
45 | "fans",
46 | "firmwares",
47 | "memory",
48 | "nics",
49 | "processors",
50 | "ps",
51 | "ps_amps_sysboard_pwr",
52 | "storage_battery",
53 | "storage_controller",
54 | "storage_enclosure",
55 | "storage_pdisk",
56 | "storage_vdisk",
57 | "system",
58 | "temps",
59 | "version",
60 | "volts",
61 | }
62 |
63 | var (
64 | scrapeDurationDesc = prometheus.NewDesc(
65 | prometheus.BuildFQName(collector.Namespace, "scrape", "collector_duration_seconds"),
66 | "dellhw_exporter: Duration of a collector scrape.",
67 | []string{"collector"},
68 | nil,
69 | )
70 |
71 | scrapeSuccessDesc = prometheus.NewDesc(
72 | prometheus.BuildFQName(collector.Namespace, "scrape", "collector_success"),
73 | "dellhw_exporter: Whether a collector succeeded.",
74 | []string{"collector"},
75 | nil,
76 | )
77 | )
78 |
79 | type program struct{}
80 |
81 | // CmdLineOpts holds possible command line options/flags
82 | type CmdLineOpts struct {
83 | version bool
84 | showCollectors bool
85 | logLevel string
86 |
87 | omReportExecutable string
88 | cmdTimeout int64
89 |
90 | checkCollectors []string
91 |
92 | metricsAddr string
93 | metricsPath string
94 | webConfigPath string
95 | enabledCollectors []string
96 | additionalCollectors []string
97 | monitoredNics []string
98 |
99 | cachingEnabled bool
100 | cacheDuration int64
101 | }
102 |
103 | var (
104 | logger *slog.Logger
105 | opts CmdLineOpts
106 | flags = flag.NewFlagSet("dellhw_exporter", flag.ExitOnError)
107 | )
108 |
109 | // DellHWCollector contains the collectors to be used
110 | type DellHWCollector struct {
111 | lastCollectTime time.Time
112 | collectors map[string]collector.Collector
113 |
114 | // Cache related
115 | cachingEnabled bool
116 | cacheDuration time.Duration
117 | cache []prometheus.Metric
118 | cacheMutex sync.Mutex
119 | }
120 |
121 | func main() {
122 | // Service setup
123 | svcConfig := &service.Config{
124 | Name: "DellOMSAExporter",
125 | DisplayName: "Dell OMSA Exporter",
126 | Description: "Prometheus exporter for Dell Hardware components using OMSA",
127 | }
128 |
129 | prg := &program{}
130 | s, err := service.New(prg, svcConfig)
131 | if err != nil {
132 | logger.Error("failed to create service", "error", err.Error())
133 | os.Exit(1)
134 | }
135 |
136 | err = s.Run()
137 | if err != nil {
138 | logger.Error("error while running exporter", "error", err.Error())
139 | }
140 | }
141 |
142 | func setupLogger() *slog.Logger {
143 | var logLevel slog.Level
144 | switch opts.logLevel {
145 | case "debug", "DEBUG":
146 | logLevel = slog.LevelDebug
147 | case "error", "ERROR":
148 | logLevel = slog.LevelError
149 | case "warning", "WARNING":
150 | logLevel = slog.LevelWarn
151 | default:
152 | logLevel = slog.LevelInfo
153 | }
154 |
155 | logHandler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel})
156 | logger := slog.New(logHandler)
157 |
158 | return logger
159 | }
160 |
161 | func (p *program) Start(s service.Service) error {
162 | if err := parseFlagsAndEnvVars(); err != nil {
163 | logger.Error("failed to parse flags and env vars", "error", err.Error())
164 | os.Exit(1)
165 | }
166 |
167 | if opts.version {
168 | fmt.Fprintln(os.Stdout, version.Print("dellhw_exporter"))
169 | os.Exit(0)
170 | }
171 |
172 | if opts.showCollectors {
173 | collectorNames := make(sort.StringSlice, 0, len(collector.Factories))
174 | for n := range collector.Factories {
175 | collectorNames = append(collectorNames, n)
176 | }
177 | collectorNames.Sort()
178 | fmt.Printf("Available collectors:\n")
179 | for _, n := range collectorNames {
180 | fmt.Printf(" - %s\n", n)
181 | }
182 | os.Exit(0)
183 | }
184 |
185 | logger = setupLogger()
186 |
187 | logger.Info("starting dellhw_exporter", "version", version.Info())
188 | logger.Info(fmt.Sprintf("build context: %s", version.BuildContext()))
189 |
190 | if opts.cmdTimeout > 0 {
191 | logger.Debug("setting command timeout", "cmd_timeout", opts.cmdTimeout)
192 | omreport.SetCommandTimeout(opts.cmdTimeout)
193 | } else {
194 | logger.Warn("not setting command timeout because it is zero")
195 | }
196 |
197 | if opts.cachingEnabled {
198 | logger.Info("caching enabled. Cache Duration", "cache_duration", fmt.Sprintf("%ds", opts.cacheDuration))
199 | } else {
200 | logger.Info("caching is disabled by default")
201 | }
202 |
203 | omrOpts := &omreport.Options{
204 | OMReportExecutable: opts.omReportExecutable,
205 | }
206 |
207 | collector.SetLogger(logger)
208 | collector.SetOMReport(omreport.New(omrOpts))
209 |
210 | enabledCollectors := append(opts.enabledCollectors, opts.additionalCollectors...)
211 | collectors, err := loadCollectors(enabledCollectors, opts.checkCollectors)
212 | if err != nil {
213 | logger.Error("couldn't load collectors", "error", err.Error())
214 | os.Exit(1)
215 | }
216 | logger.Info("enabled collectors", "collectors", enabledCollectors)
217 |
218 | if err = prometheus.Register(NewDellHWCollector(collectors, opts.cachingEnabled, opts.cacheDuration)); err != nil {
219 | logger.Error("couldn't register collector", "error", err.Error())
220 | os.Exit(1)
221 | }
222 |
223 | // non-blocking start
224 | go p.run()
225 | return nil
226 | }
227 |
228 | func (p *program) Stop(s service.Service) error {
229 | // non-blocking stop
230 | return nil
231 | }
232 |
233 | func NewDellHWCollector(collectors map[string]collector.Collector, cachingEnabled bool, cacheDurationSeconds int64) *DellHWCollector {
234 | return &DellHWCollector{
235 | cache: make([]prometheus.Metric, 0),
236 | lastCollectTime: time.Unix(0, 0),
237 | collectors: collectors,
238 | cachingEnabled: cachingEnabled,
239 | cacheDuration: time.Duration(cacheDurationSeconds) * time.Second,
240 | }
241 | }
242 |
243 | func init() {
244 | flags.BoolVar(&opts.version, "version", false, "Show version information")
245 | flags.StringVar(&opts.logLevel, "log-level", "INFO", "Set log level")
246 |
247 | flags.BoolVar(&opts.showCollectors, "collectors-print", false, "If true, print available collectors and exit.")
248 | flags.StringSliceVar(&opts.enabledCollectors, "collectors-enabled", defaultCollectors, "Comma separated list of active collectors")
249 | flags.StringSliceVar(&opts.additionalCollectors, "collectors-additional", []string{}, "Comma separated list of collectors to enable additionally to the collectors-enabled list")
250 | flags.StringSliceVar(&opts.monitoredNics, "monitored-nics", []string{}, "Comma separated list of nics to monitor (default, empty list, is to monitor all)")
251 | flags.StringVar(&opts.omReportExecutable, "collectors-omreport", getDefaultOmReportPath(), "Path to the omreport executable (based on the OS (linux or windows) default paths are used if unset)")
252 | flags.Int64Var(&opts.cmdTimeout, "collectors-cmd-timeout", 15, "Command execution timeout for omreport")
253 | flags.StringSliceVar(&opts.checkCollectors, "collectors-check", []string{}, "Check if the specified collectors are applicable to the system and disable it otherwise. E.g., chassis_batteries ")
254 | flags.MarkDeprecated("check-collectors", "Please use collectors-check instead")
255 |
256 | flags.StringVar(&opts.metricsAddr, "web-listen-address", ":9137", "The address to listen on for HTTP requests")
257 | flags.StringVar(&opts.metricsPath, "web-telemetry-path", "/metrics", "Path the metrics will be exposed under")
258 | flags.StringVar(&opts.webConfigPath, "web-config-file", "", "[EXPERIMENTAL] Path to configuration file that can enable TLS or authentication.")
259 |
260 | flags.BoolVar(&opts.cachingEnabled, "cache-enabled", false, "Enable metrics caching to reduce load")
261 | flags.Int64Var(&opts.cacheDuration, "cache-duration", 20, "Cache duration in seconds")
262 |
263 | flags.SetNormalizeFunc(normalizeFlags)
264 | flags.SortFlags = true
265 | }
266 |
267 | // normalizeFlags "normalize" / alias flags that have been deprcated / replaced / removed
268 | func normalizeFlags(f *flag.FlagSet, name string) flag.NormalizedName {
269 | switch name {
270 | case "collectors.print":
271 | name = "collectors-print"
272 | case "web.listen-address":
273 | name = "web-listen-address"
274 | case "web.telemetry-path":
275 | name = "web-telemetry-path"
276 | case "collectors.enabled":
277 | name = "collectors-enabled"
278 | case "collectors.omr-report":
279 | name = "collectors-omreport"
280 | case "collectors.cmd-timeout":
281 | name = "collectors-cmd-timeout"
282 | }
283 | return flag.NormalizedName(name)
284 | }
285 |
286 | func flagNameFromEnvName(s string) string {
287 | s = strings.ToLower(s)
288 | s = strings.ReplaceAll(s, "_", "-")
289 | return s
290 | }
291 |
292 | func parseFlagsAndEnvVars() error {
293 | for _, v := range os.Environ() {
294 | vals := strings.SplitN(v, "=", 2)
295 |
296 | if !strings.HasPrefix(vals[0], "DELLHW_EXPORTER_") {
297 | continue
298 | }
299 | flagName := flagNameFromEnvName(strings.ReplaceAll(vals[0], "DELLHW_EXPORTER_", ""))
300 |
301 | fn := flags.Lookup(flagName)
302 | if fn == nil || fn.Changed {
303 | continue
304 | }
305 |
306 | if err := fn.Value.Set(vals[1]); err != nil {
307 | return err
308 | }
309 | }
310 |
311 | return flags.Parse(os.Args[1:])
312 | }
313 |
314 | // Describe implements the prometheus.Collector interface.
315 | func (n *DellHWCollector) Describe(ch chan<- *prometheus.Desc) {
316 | ch <- scrapeDurationDesc
317 | ch <- scrapeSuccessDesc
318 | }
319 |
320 | // Collect implements the prometheus.Collector interface.
321 | func (n *DellHWCollector) Collect(outgoingCh chan<- prometheus.Metric) {
322 | if n.cachingEnabled {
323 | n.cacheMutex.Lock()
324 | defer n.cacheMutex.Unlock()
325 |
326 | now := time.Now()
327 | expiry := n.lastCollectTime.Add(n.cacheDuration)
328 | if now.Before(expiry) {
329 | logger.Debug(fmt.Sprintf("using cache. now: %s, expiry: %s, lastCollectTime: %s", now.String(), expiry.String(), n.lastCollectTime.String()))
330 | for _, cachedMetric := range n.cache {
331 | logger.Debug(fmt.Sprintf("pushing cached metric %s to outgoingCh", cachedMetric.Desc().String()))
332 | outgoingCh <- cachedMetric
333 | }
334 | return
335 | }
336 | // Clear cache, but keep slice
337 | n.cache = n.cache[:0]
338 | }
339 |
340 | metricsCh := make(chan prometheus.Metric)
341 |
342 | // Wait to ensure outgoingCh is not closed before the goroutine is finished
343 | var wgOutgoing sync.WaitGroup
344 | wgOutgoing.Go(func() {
345 | for metric := range metricsCh {
346 | outgoingCh <- metric
347 | if n.cachingEnabled {
348 | logger.Debug(fmt.Sprintf("appending metric %s to cache", metric.Desc().String()))
349 | n.cache = append(n.cache, metric)
350 | }
351 | }
352 | logger.Debug("finished pushing metrics from metricsCh to outgoingCh")
353 | })
354 |
355 | var wgCollection sync.WaitGroup
356 | for name, coll := range n.collectors {
357 | wgCollection.Go(func() {
358 | execute(name, coll, metricsCh)
359 | })
360 | }
361 |
362 | logger.Debug("waiting for collectors")
363 | wgCollection.Wait()
364 | logger.Debug("finished waiting for collectors")
365 |
366 | n.lastCollectTime = time.Now()
367 | logger.Debug(fmt.Sprintf("updated lastCollectTime to %s", n.lastCollectTime.String()))
368 |
369 | close(metricsCh)
370 |
371 | logger.Debug("waiting for outgoing Adapter")
372 | wgOutgoing.Wait()
373 | logger.Debug("finished waiting for outgoing Adapter")
374 | }
375 |
376 | func execute(name string, c collector.Collector, ch chan<- prometheus.Metric) {
377 | begin := time.Now()
378 | err := c.Update(ch)
379 | duration := time.Since(begin)
380 | var success float64
381 |
382 | if err != nil {
383 | logger.Error("collector failed", "collector", name, "duration", duration.String(), "error", err.Error())
384 | success = 0
385 | } else {
386 | logger.Debug("collector succeeded", "collector", name, "duration", duration.String())
387 | success = 1
388 | }
389 | ch <- prometheus.MustNewConstMetric(scrapeDurationDesc, prometheus.GaugeValue, duration.Seconds(), name)
390 | ch <- prometheus.MustNewConstMetric(scrapeSuccessDesc, prometheus.GaugeValue, success, name)
391 | }
392 |
393 | func getCollectorConfig() *collector.Config {
394 | return &collector.Config{
395 | MonitoredNICs: opts.monitoredNics,
396 | }
397 | }
398 |
399 | func loadCollectors(list []string, check []string) (map[string]collector.Collector, error) {
400 | cfg := getCollectorConfig()
401 |
402 | collectors := map[string]collector.Collector{}
403 | var c collector.Collector
404 | var err error
405 | for _, name := range list {
406 | fn, ok := collector.Factories[name]
407 | if !ok {
408 | return nil, fmt.Errorf("collector %q not available", name)
409 | }
410 |
411 | c, err = fn(cfg)
412 | if err != nil {
413 | return nil, err
414 | }
415 |
416 | if slices.Contains(check, name) {
417 | if cc, ok := c.(collector.IsAvailable); ok {
418 | if !cc.IsAvailable() {
419 | logger.Warn("disabling collector because it is not applicable to the system", "collector", name)
420 | }
421 | continue
422 | }
423 | logger.Debug("collector is applicable to the system", "collector", name)
424 | }
425 |
426 | collectors[name] = c
427 | }
428 |
429 | return collectors, nil
430 | }
431 |
432 | func getDefaultOmReportPath() string {
433 | if runtime.GOOS == "windows" {
434 | return "C:\\Program Files\\Dell\\SysMgt\\oma\\bin\\omreport.exe"
435 | }
436 |
437 | return "/opt/dell/srvadmin/bin/omreport"
438 | }
439 |
440 | func (p *program) run() {
441 | // Background work
442 | handler := promhttp.HandlerFor(prometheus.DefaultGatherer,
443 | promhttp.HandlerOpts{
444 | ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError),
445 | ErrorHandling: promhttp.ContinueOnError,
446 | })
447 |
448 | http.HandleFunc(opts.metricsPath, func(w http.ResponseWriter, r *http.Request) {
449 | handler.ServeHTTP(w, r)
450 | })
451 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
452 | w.Write([]byte(`
453 |
454 | DellHW Exporter
455 |
456 | DellHW Exporter
457 | Metrics
458 |
459 | `))
460 | })
461 |
462 | server := &http.Server{}
463 | if err := web.ListenAndServe(server, &web.FlagConfig{WebListenAddresses: &[]string{opts.metricsAddr}, WebConfigFile: &opts.webConfigPath}, logger); err != nil {
464 | logger.Error("error while serving request", "error", err.Error())
465 | }
466 | }
467 |
--------------------------------------------------------------------------------
/docs/metrics.md:
--------------------------------------------------------------------------------
1 | ## What do the metrics mean?
2 |
3 | Most metrics returned besides temperature, volts, fans RPM count and others, are state indicators which can have the following of the four states:
4 |
5 | * `0` - `OK`, the component should be fine.
6 | * `1` - `Critical`, the component is not okay / has potentially failed / `Unknown` status.
7 | * `2` - `Non-Critical`, the component is not okay, but not critical.
8 |
9 | Some metrics don't follow this pattern as they return, e.g., VDisk RAID level, "if a failure is predicted" (`0` no failure predicted, `1` a failure is predicted).
10 |
11 | ### PDisk and VDisk States, VDisk Policy Values
12 |
13 | Can be found in the [`pkg/omreport/util.go` file](https://github.com/galexrt/dellhw_exporter/blob/main/pkg/omreport/util.go).
14 |
15 | ## Example Metrics Output
16 |
17 | !!! note
18 | Due to my lack of having access to a "fully equipped" DELL server anymore, this example metrics output is not complete.
19 | E.g., `dell_hw_storage_pdisk_state`, `dell_hw_nic_status`, some policy metrics of `dell_hw_storage_vdisk_*`.
20 |
21 | If you want to contribute a `/metrics` output of the exporter, please open an issue or a pull request, thanks!
22 |
23 | ```plain
24 | # HELP dell_hw_exporter_version Constant '1' value with version, revision, and branch labels from the dellhw_exporter version info.
25 | # TYPE dell_hw_exporter_version gauge
26 | dell_hw_exporter_version{branch="",revision="",version=""} 1
27 | # HELP dell_hw_bios Version info of firmwares/bios.
28 | # TYPE dell_hw_bios gauge
29 | dell_hw_bios{manufacturer="dell inc.",release_date="06/26/2020",version="2.8.1"} 0
30 | # HELP dell_hw_chassis_current_reading System board power usage.
31 | # TYPE dell_hw_chassis_current_reading gauge
32 | dell_hw_chassis_current_reading{pwrsupply="PS1"} 0.4
33 | dell_hw_chassis_current_reading{pwrsupply="PS2"} 0.4
34 | # HELP dell_hw_chassis_fan_reading Overall status of system fans.
35 | # TYPE dell_hw_chassis_fan_reading gauge
36 | dell_hw_chassis_fan_reading{fan="System_Board_Fan1A"} 6840
37 | dell_hw_chassis_fan_reading{fan="System_Board_Fan1B"} 6480
38 | dell_hw_chassis_fan_reading{fan="System_Board_Fan2A"} 6840
39 | dell_hw_chassis_fan_reading{fan="System_Board_Fan2B"} 6480
40 | dell_hw_chassis_fan_reading{fan="System_Board_Fan3A"} 6840
41 | dell_hw_chassis_fan_reading{fan="System_Board_Fan3B"} 6360
42 | dell_hw_chassis_fan_reading{fan="System_Board_Fan4A"} 7200
43 | dell_hw_chassis_fan_reading{fan="System_Board_Fan4B"} 6960
44 | dell_hw_chassis_fan_reading{fan="System_Board_Fan5A"} 7080
45 | dell_hw_chassis_fan_reading{fan="System_Board_Fan5B"} 6720
46 | dell_hw_chassis_fan_reading{fan="System_Board_Fan6A"} 6960
47 | dell_hw_chassis_fan_reading{fan="System_Board_Fan6B"} 6480
48 | dell_hw_chassis_fan_reading{fan="System_Board_Fan7A"} 6840
49 | dell_hw_chassis_fan_reading{fan="System_Board_Fan7B"} 6480
50 | dell_hw_chassis_fan_reading{fan="System_Board_Fan8A"} 6840
51 | dell_hw_chassis_fan_reading{fan="System_Board_Fan8B"} 6600
52 | # HELP dell_hw_chassis_fan_status Overall status of system fans.
53 | # TYPE dell_hw_chassis_fan_status gauge
54 | dell_hw_chassis_fan_status{fan="System_Board_Fan1A"} 0
55 | dell_hw_chassis_fan_status{fan="System_Board_Fan1B"} 0
56 | dell_hw_chassis_fan_status{fan="System_Board_Fan2A"} 0
57 | dell_hw_chassis_fan_status{fan="System_Board_Fan2B"} 0
58 | dell_hw_chassis_fan_status{fan="System_Board_Fan3A"} 0
59 | dell_hw_chassis_fan_status{fan="System_Board_Fan3B"} 0
60 | dell_hw_chassis_fan_status{fan="System_Board_Fan4A"} 0
61 | dell_hw_chassis_fan_status{fan="System_Board_Fan4B"} 0
62 | dell_hw_chassis_fan_status{fan="System_Board_Fan5A"} 0
63 | dell_hw_chassis_fan_status{fan="System_Board_Fan5B"} 0
64 | dell_hw_chassis_fan_status{fan="System_Board_Fan6A"} 0
65 | dell_hw_chassis_fan_status{fan="System_Board_Fan6B"} 0
66 | dell_hw_chassis_fan_status{fan="System_Board_Fan7A"} 0
67 | dell_hw_chassis_fan_status{fan="System_Board_Fan7B"} 0
68 | dell_hw_chassis_fan_status{fan="System_Board_Fan8A"} 0
69 | dell_hw_chassis_fan_status{fan="System_Board_Fan8B"} 0
70 | # HELP dell_hw_chassis_memory_status System RAM DIMM status.
71 | # TYPE dell_hw_chassis_memory_status gauge
72 | dell_hw_chassis_memory_status{memory="A1"} 0
73 | dell_hw_chassis_memory_status{memory="A2"} 0
74 | dell_hw_chassis_memory_status{memory="A3"} 0
75 | dell_hw_chassis_memory_status{memory="A4"} 0
76 | dell_hw_chassis_memory_status{memory="A5"} 0
77 | dell_hw_chassis_memory_status{memory="A6"} 0
78 | dell_hw_chassis_memory_status{memory="B1"} 0
79 | dell_hw_chassis_memory_status{memory="B2"} 0
80 | dell_hw_chassis_memory_status{memory="B3"} 0
81 | dell_hw_chassis_memory_status{memory="B4"} 0
82 | dell_hw_chassis_memory_status{memory="B5"} 0
83 | dell_hw_chassis_memory_status{memory="B6"} 0
84 | # HELP dell_hw_chassis_power_fail_level System board power usage.
85 | # TYPE dell_hw_chassis_power_fail_level gauge
86 | dell_hw_chassis_power_fail_level 1300
87 | # HELP dell_hw_chassis_power_reading System board power usage.
88 | # TYPE dell_hw_chassis_power_reading gauge
89 | dell_hw_chassis_power_reading 156
90 | # HELP dell_hw_chassis_power_warn_level System board power usage.
91 | # TYPE dell_hw_chassis_power_warn_level gauge
92 | dell_hw_chassis_power_warn_level 1170
93 | # HELP dell_hw_chassis_processor_status Overall status of CPUs.
94 | # TYPE dell_hw_chassis_processor_status gauge
95 | dell_hw_chassis_processor_status{processor="CPU1"} 0
96 | dell_hw_chassis_processor_status{processor="CPU2"} 0
97 | # HELP dell_hw_chassis_status Overall status of chassis components.
98 | # TYPE dell_hw_chassis_status gauge
99 | dell_hw_chassis_status{component="Batteries"} 0
100 | dell_hw_chassis_status{component="Fans"} 0
101 | dell_hw_chassis_status{component="Hardware_Log"} 0
102 | dell_hw_chassis_status{component="Intrusion"} 0
103 | dell_hw_chassis_status{component="Memory"} 0
104 | dell_hw_chassis_status{component="Power_Management"} 0
105 | dell_hw_chassis_status{component="Power_Supplies"} 0
106 | dell_hw_chassis_status{component="Processors"} 0
107 | dell_hw_chassis_status{component="Temperatures"} 0
108 | dell_hw_chassis_status{component="Voltages"} 0
109 | # HELP dell_hw_chassis_temps Overall temperatures and status of system temperature readings.
110 | # TYPE dell_hw_chassis_temps gauge
111 | dell_hw_chassis_temps{component="CPU1_Temp"} 0
112 | dell_hw_chassis_temps{component="CPU2_Temp"} 0
113 | dell_hw_chassis_temps{component="System_Board_Exhaust_Temp"} 0
114 | dell_hw_chassis_temps{component="System_Board_Inlet_Temp"} 0
115 | # HELP dell_hw_chassis_temps_max_failure Overall temperatures and status of system temperature readings.
116 | # TYPE dell_hw_chassis_temps_max_failure gauge
117 | dell_hw_chassis_temps_max_failure{component="CPU1_Temp"} 97
118 | dell_hw_chassis_temps_max_failure{component="CPU2_Temp"} 97
119 | dell_hw_chassis_temps_max_failure{component="System_Board_Exhaust_Temp"} 80
120 | dell_hw_chassis_temps_max_failure{component="System_Board_Inlet_Temp"} 47
121 | # HELP dell_hw_chassis_temps_max_warning Overall temperatures and status of system temperature readings.
122 | # TYPE dell_hw_chassis_temps_max_warning gauge
123 | dell_hw_chassis_temps_max_warning{component="System_Board_Exhaust_Temp"} 75
124 | dell_hw_chassis_temps_max_warning{component="System_Board_Inlet_Temp"} 43
125 | # HELP dell_hw_chassis_temps_min_failure Overall temperatures and status of system temperature readings.
126 | # TYPE dell_hw_chassis_temps_min_failure gauge
127 | dell_hw_chassis_temps_min_failure{component="CPU1_Temp"} 3
128 | dell_hw_chassis_temps_min_failure{component="CPU2_Temp"} 3
129 | dell_hw_chassis_temps_min_failure{component="System_Board_Exhaust_Temp"} 3
130 | dell_hw_chassis_temps_min_failure{component="System_Board_Inlet_Temp"} -7
131 | # HELP dell_hw_chassis_temps_min_warning Overall temperatures and status of system temperature readings.
132 | # TYPE dell_hw_chassis_temps_min_warning gauge
133 | dell_hw_chassis_temps_min_warning{component="System_Board_Exhaust_Temp"} 8
134 | dell_hw_chassis_temps_min_warning{component="System_Board_Inlet_Temp"} 3
135 | # HELP dell_hw_chassis_temps_reading Overall temperatures and status of system temperature readings.
136 | # TYPE dell_hw_chassis_temps_reading gauge
137 | dell_hw_chassis_temps_reading{component="CPU1_Temp"} 32
138 | dell_hw_chassis_temps_reading{component="CPU2_Temp"} 34
139 | dell_hw_chassis_temps_reading{component="System_Board_Exhaust_Temp"} 29
140 | dell_hw_chassis_temps_reading{component="System_Board_Inlet_Temp"} 19
141 | # HELP dell_hw_chassis_volts_reading Overall volts and status of power supply volt readings.
142 | # TYPE dell_hw_chassis_volts_reading gauge
143 | dell_hw_chassis_volts_reading{component="PS1_Voltage_1"} 228
144 | dell_hw_chassis_volts_reading{component="PS2_Voltage_2"} 228
145 | # HELP dell_hw_chassis_volts_status Overall volts and status of power supply volt readings.
146 | # TYPE dell_hw_chassis_volts_status gauge
147 | dell_hw_chassis_volts_status{component="CPU1_FIVR_PG"} 0
148 | dell_hw_chassis_volts_status{component="CPU1_MEM012_VDDQ_PG"} 0
149 | dell_hw_chassis_volts_status{component="CPU1_MEM012_VPP_PG"} 0
150 | dell_hw_chassis_volts_status{component="CPU1_MEM012_VTT_PG"} 0
151 | dell_hw_chassis_volts_status{component="CPU1_MEM345_VDDQ_PG"} 0
152 | dell_hw_chassis_volts_status{component="CPU1_MEM345_VPP_PG"} 0
153 | dell_hw_chassis_volts_status{component="CPU1_MEM345_VTT_PG"} 0
154 | dell_hw_chassis_volts_status{component="CPU1_VCCIO_PG"} 0
155 | dell_hw_chassis_volts_status{component="CPU1_VCORE_PG"} 0
156 | dell_hw_chassis_volts_status{component="CPU1_VSA_PG"} 0
157 | dell_hw_chassis_volts_status{component="CPU2_FIVR_PG"} 0
158 | dell_hw_chassis_volts_status{component="CPU2_MEM012_VDDQ_PG"} 0
159 | dell_hw_chassis_volts_status{component="CPU2_MEM012_VPP_PG"} 0
160 | dell_hw_chassis_volts_status{component="CPU2_MEM012_VTT_PG"} 0
161 | dell_hw_chassis_volts_status{component="CPU2_MEM345_VDDQ_PG"} 0
162 | dell_hw_chassis_volts_status{component="CPU2_MEM345_VPP_PG"} 0
163 | dell_hw_chassis_volts_status{component="CPU2_MEM345_VTT_PG"} 0
164 | dell_hw_chassis_volts_status{component="CPU2_VCCIO_PG"} 0
165 | dell_hw_chassis_volts_status{component="CPU2_VCORE_PG"} 0
166 | dell_hw_chassis_volts_status{component="CPU2_VSA_PG"} 0
167 | dell_hw_chassis_volts_status{component="PS1_Voltage_1"} 0
168 | dell_hw_chassis_volts_status{component="PS2_Voltage_2"} 0
169 | dell_hw_chassis_volts_status{component="System_Board_1.8V_SW_PG"} 0
170 | dell_hw_chassis_volts_status{component="System_Board_2.5V_SW_PG"} 0
171 | dell_hw_chassis_volts_status{component="System_Board_3.3V_A_PG"} 0
172 | dell_hw_chassis_volts_status{component="System_Board_3.3V_B_PG"} 0
173 | dell_hw_chassis_volts_status{component="System_Board_5V_SW_PG"} 0
174 | dell_hw_chassis_volts_status{component="System_Board_BP0_PG"} 0
175 | dell_hw_chassis_volts_status{component="System_Board_BP1_PG"} 0
176 | dell_hw_chassis_volts_status{component="System_Board_BP2_PG"} 0
177 | dell_hw_chassis_volts_status{component="System_Board_DIMM_PG"} 0
178 | dell_hw_chassis_volts_status{component="System_Board_NDC_PG"} 0
179 | dell_hw_chassis_volts_status{component="System_Board_PS1_PG_FAIL"} 0
180 | dell_hw_chassis_volts_status{component="System_Board_PS2_PG_FAIL"} 0
181 | dell_hw_chassis_volts_status{component="System_Board_PVNN_SW_PG"} 0
182 | dell_hw_chassis_volts_status{component="System_Board_VSB11_SW_PG"} 0
183 | dell_hw_chassis_volts_status{component="System_Board_VSBM_SW_PG"} 0
184 | # HELP dell_hw_cmos_batteries_status Overall status of chassis batteries
185 | # TYPE dell_hw_cmos_batteries_status gauge
186 | dell_hw_cmos_batteries_status{id="0"} 0
187 | # HELP dell_hw_firmware Version info of firmwares/bios.
188 | # TYPE dell_hw_firmware gauge
189 | dell_hw_firmware{idrac9="4.22.00.00 (build 20)"} 0
190 | # HELP dell_hw_nic_status Connection status of network cards.
191 | # TYPE dell_hw_nic_status gauge
192 | dell_hw_nic_status{device="docker0",id="0"} 1
193 | dell_hw_nic_status{device="eno1",id="2"} 0
194 | dell_hw_nic_status{device="eno2",id="3"} 1
195 | dell_hw_nic_status{device="eno3",id="0"} 1
196 | dell_hw_nic_status{device="eno4",id="1"} 1
197 | # HELP dell_hw_ps_rated_input_wattage Overall status of power supplies.
198 | # TYPE dell_hw_ps_rated_input_wattage gauge
199 | dell_hw_ps_rated_input_wattage{id="0"} 1260
200 | dell_hw_ps_rated_input_wattage{id="1"} 1260
201 | # HELP dell_hw_ps_rated_output_wattage Overall status of power supplies.
202 | # TYPE dell_hw_ps_rated_output_wattage gauge
203 | dell_hw_ps_rated_output_wattage{id="0"} 1100
204 | dell_hw_ps_rated_output_wattage{id="1"} 1100
205 | # HELP dell_hw_ps_status Overall status of power supplies.
206 | # TYPE dell_hw_ps_status gauge
207 | dell_hw_ps_status{id="0"} 0
208 | dell_hw_ps_status{id="1"} 0
209 | # HELP dell_hw_scrape_collector_duration_seconds dellhw_exporter: Duration of a collector scrape.
210 | # TYPE dell_hw_scrape_collector_duration_seconds gauge
211 | dell_hw_scrape_collector_duration_seconds{collector="chassis"} 2.516581654
212 | dell_hw_scrape_collector_duration_seconds{collector="chassis_batteries"} 2.408411858
213 | dell_hw_scrape_collector_duration_seconds{collector="fans"} 0.391451297
214 | dell_hw_scrape_collector_duration_seconds{collector="firmwares"} 0.690688131
215 | dell_hw_scrape_collector_duration_seconds{collector="memory"} 2.392257815
216 | dell_hw_scrape_collector_duration_seconds{collector="nics"} 2.501337814
217 | dell_hw_scrape_collector_duration_seconds{collector="processors"} 0.490092584
218 | dell_hw_scrape_collector_duration_seconds{collector="ps"} 0.488725968
219 | dell_hw_scrape_collector_duration_seconds{collector="ps_amps_sysboard_pwr"} 2.410220934
220 | dell_hw_scrape_collector_duration_seconds{collector="storage_battery"} 0.596624274
221 | dell_hw_scrape_collector_duration_seconds{collector="storage_controller"} 1.887127915
222 | dell_hw_scrape_collector_duration_seconds{collector="storage_enclosure"} 0.590577123
223 | dell_hw_scrape_collector_duration_seconds{collector="storage_pdisk"} 2.294178837
224 | dell_hw_scrape_collector_duration_seconds{collector="storage_vdisk"} 0.69400828
225 | dell_hw_scrape_collector_duration_seconds{collector="system"} 0.587728339
226 | dell_hw_scrape_collector_duration_seconds{collector="temps"} 0.488827354
227 | dell_hw_scrape_collector_duration_seconds{collector="volts"} 0.491565389
228 | # HELP dell_hw_scrape_collector_success dellhw_exporter: Whether a collector succeeded.
229 | # TYPE dell_hw_scrape_collector_success gauge
230 | dell_hw_scrape_collector_success{collector="chassis"} 1
231 | dell_hw_scrape_collector_success{collector="chassis_batteries"} 1
232 | dell_hw_scrape_collector_success{collector="fans"} 1
233 | dell_hw_scrape_collector_success{collector="firmwares"} 1
234 | dell_hw_scrape_collector_success{collector="memory"} 1
235 | dell_hw_scrape_collector_success{collector="nics"} 1
236 | dell_hw_scrape_collector_success{collector="processors"} 1
237 | dell_hw_scrape_collector_success{collector="ps"} 1
238 | dell_hw_scrape_collector_success{collector="ps_amps_sysboard_pwr"} 1
239 | dell_hw_scrape_collector_success{collector="storage_battery"} 1
240 | dell_hw_scrape_collector_success{collector="storage_controller"} 1
241 | dell_hw_scrape_collector_success{collector="storage_enclosure"} 1
242 | dell_hw_scrape_collector_success{collector="storage_pdisk"} 1
243 | dell_hw_scrape_collector_success{collector="storage_vdisk"} 1
244 | dell_hw_scrape_collector_success{collector="system"} 1
245 | dell_hw_scrape_collector_success{collector="temps"} 1
246 | dell_hw_scrape_collector_success{collector="volts"} 1
247 | # HELP dell_hw_storage_controller_status Overall status of storage controllers.
248 | # TYPE dell_hw_storage_controller_status gauge
249 | dell_hw_storage_controller_status{controller_name="Dell HBA330 Mini (Slot Embedded)",id="0"} 0
250 | # HELP dell_hw_storage_enclosure_status Overall status of storage enclosures.
251 | # TYPE dell_hw_storage_enclosure_status gauge
252 | dell_hw_storage_enclosure_status{controller_name="Dell HBA330 Mini (Embedded)",enclosure="0_1"} 0
253 | # HELP dell_hw_storage_pdisk_status Overall status of physical disks + failure prediction (if available).
254 | # TYPE dell_hw_storage_pdisk_status gauge
255 | dell_hw_storage_pdisk_status{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_10"} 0
256 | dell_hw_storage_pdisk_status{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_11"} 0
257 | # HELP dell_hw_storage_pdisk_failure_predicted Overall status of physical disks + failure prediction (if available).
258 | # TYPE dell_hw_storage_pdisk_failure_predicted gauge
259 | dell_hw_storage_pdisk_failure_predicted{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_10"} 0
260 | dell_hw_storage_pdisk_failure_predicted{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_11"} 0
261 | # HELP storage_pdisk_remaining_rated_write_endurance Overall status of physical disks + failure prediction (if available).
262 | # TYPE storage_pdisk_remaining_rated_write_endurance gauge
263 | storage_pdisk_remaining_rated_write_endurance{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_10"} 100
264 | storage_pdisk_remaining_rated_write_endurance{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_11"} 100
265 | # HELP storage_pdisk_storage_encrypted Overall status of physical disks + failure prediction (if available).
266 | # TYPE storage_pdisk_storage_encrypted gauge
267 | storage_pdisk_storage_encrypted{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_10"} 0
268 | storage_pdisk_storage_encrypted{controller="0",controller_name="Dell HBA330 Mini (Embedded)",disk="0_1_11"} 0
269 | # HELP dell_hw_storage_vdisk_status Overall status of virtual disks + RAID level (if available).
270 | # TYPE dell_hw_storage_vdisk_status gauge
271 | dell_hw_storage_vdisk_status{controller_name="Dell HBA330 Mini (Embedded)",vdisk="0",vdisk_name="GenericR1_0"} 0
272 | # HELP dell_hw_storage_vdisk_raidlevel Overall status of virtual disks + RAID level (if available).
273 | # TYPE dell_hw_storage_vdisk_raidlevel gauge
274 | dell_hw_storage_vdisk_raidlevel{controller_name="Dell HBA330 Mini (Embedded)",vdisk="0",vdisk_name="GenericR1_0"} 1
275 | # HELP dell_hw_system_status Overall status of system components.
276 | # TYPE dell_hw_system_status gauge
277 | dell_hw_system_status{component="Main_System_Chassis"} 0
278 | # HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
279 | # TYPE go_gc_duration_seconds summary
280 | go_gc_duration_seconds{quantile="0"} 4.4303e-05
281 | go_gc_duration_seconds{quantile="0.25"} 8.1471e-05
282 | go_gc_duration_seconds{quantile="0.5"} 0.000220224
283 | go_gc_duration_seconds{quantile="0.75"} 0.000391777
284 | go_gc_duration_seconds{quantile="1"} 0.00080469
285 | go_gc_duration_seconds_sum 0.692176499
286 | go_gc_duration_seconds_count 1641
287 | # HELP go_goroutines Number of goroutines that currently exist.
288 | # TYPE go_goroutines gauge
289 | go_goroutines 9
290 | # HELP go_info Information about the Go environment.
291 | # TYPE go_info gauge
292 | go_info{version="go1.13.11"} 1
293 | # HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
294 | # TYPE go_memstats_alloc_bytes gauge
295 | go_memstats_alloc_bytes 2.77076e+06
296 | # HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
297 | # TYPE go_memstats_alloc_bytes_total counter
298 | go_memstats_alloc_bytes_total 2.049096176e+09
299 | # HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
300 | # TYPE go_memstats_buck_hash_sys_bytes gauge
301 | go_memstats_buck_hash_sys_bytes 1.607338e+06
302 | # HELP go_memstats_frees_total Total number of frees.
303 | # TYPE go_memstats_frees_total counter
304 | go_memstats_frees_total 9.131498e+06
305 | # HELP go_memstats_gc_cpu_fraction The fraction of this program's available CPU time used by the GC since the program started.
306 | # TYPE go_memstats_gc_cpu_fraction gauge
307 | go_memstats_gc_cpu_fraction 1.1641209236887955e-05
308 | # HELP go_memstats_gc_sys_bytes Number of bytes used for garbage collection system metadata.
309 | # TYPE go_memstats_gc_sys_bytes gauge
310 | go_memstats_gc_sys_bytes 2.394112e+06
311 | # HELP go_memstats_heap_alloc_bytes Number of heap bytes allocated and still in use.
312 | # TYPE go_memstats_heap_alloc_bytes gauge
313 | go_memstats_heap_alloc_bytes 2.77076e+06
314 | # HELP go_memstats_heap_idle_bytes Number of heap bytes waiting to be used.
315 | # TYPE go_memstats_heap_idle_bytes gauge
316 | go_memstats_heap_idle_bytes 5.9318272e+07
317 | # HELP go_memstats_heap_inuse_bytes Number of heap bytes that are in use.
318 | # TYPE go_memstats_heap_inuse_bytes gauge
319 | go_memstats_heap_inuse_bytes 4.612096e+06
320 | # HELP go_memstats_heap_objects Number of allocated objects.
321 | # TYPE go_memstats_heap_objects gauge
322 | go_memstats_heap_objects 5914
323 | # HELP go_memstats_heap_released_bytes Number of heap bytes released to OS.
324 | # TYPE go_memstats_heap_released_bytes gauge
325 | go_memstats_heap_released_bytes 5.7663488e+07
326 | # HELP go_memstats_heap_sys_bytes Number of heap bytes obtained from system.
327 | # TYPE go_memstats_heap_sys_bytes gauge
328 | go_memstats_heap_sys_bytes 6.3930368e+07
329 | # HELP go_memstats_last_gc_time_seconds Number of seconds since 1970 of last garbage collection.
330 | # TYPE go_memstats_last_gc_time_seconds gauge
331 | go_memstats_last_gc_time_seconds 1.60153799910655e+09
332 | # HELP go_memstats_lookups_total Total number of pointer lookups.
333 | # TYPE go_memstats_lookups_total counter
334 | go_memstats_lookups_total 0
335 | # HELP go_memstats_mallocs_total Total number of mallocs.
336 | # TYPE go_memstats_mallocs_total counter
337 | go_memstats_mallocs_total 9.137412e+06
338 | # HELP go_memstats_mcache_inuse_bytes Number of bytes in use by mcache structures.
339 | # TYPE go_memstats_mcache_inuse_bytes gauge
340 | go_memstats_mcache_inuse_bytes 138880
341 | # HELP go_memstats_mcache_sys_bytes Number of bytes used for mcache structures obtained from system.
342 | # TYPE go_memstats_mcache_sys_bytes gauge
343 | go_memstats_mcache_sys_bytes 147456
344 | # HELP go_memstats_mspan_inuse_bytes Number of bytes in use by mspan structures.
345 | # TYPE go_memstats_mspan_inuse_bytes gauge
346 | go_memstats_mspan_inuse_bytes 71944
347 | # HELP go_memstats_mspan_sys_bytes Number of bytes used for mspan structures obtained from system.
348 | # TYPE go_memstats_mspan_sys_bytes gauge
349 | go_memstats_mspan_sys_bytes 98304
350 | # HELP go_memstats_next_gc_bytes Number of heap bytes when next garbage collection will take place.
351 | # TYPE go_memstats_next_gc_bytes gauge
352 | go_memstats_next_gc_bytes 5.202448e+06
353 | # HELP go_memstats_other_sys_bytes Number of bytes used for other system allocations.
354 | # TYPE go_memstats_other_sys_bytes gauge
355 | go_memstats_other_sys_bytes 5.38683e+06
356 | # HELP go_memstats_stack_inuse_bytes Number of bytes in use by the stack allocator.
357 | # TYPE go_memstats_stack_inuse_bytes gauge
358 | go_memstats_stack_inuse_bytes 3.178496e+06
359 | # HELP go_memstats_stack_sys_bytes Number of bytes obtained from system for stack allocator.
360 | # TYPE go_memstats_stack_sys_bytes gauge
361 | go_memstats_stack_sys_bytes 3.178496e+06
362 | # HELP go_memstats_sys_bytes Number of bytes obtained from system.
363 | # TYPE go_memstats_sys_bytes gauge
364 | go_memstats_sys_bytes 7.6742904e+07
365 | # HELP go_threads Number of OS threads created.
366 | # TYPE go_threads gauge
367 | go_threads 55
368 | # HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
369 | # TYPE process_cpu_seconds_total counter
370 | process_cpu_seconds_total 50.7
371 | # HELP process_max_fds Maximum number of open file descriptors.
372 | # TYPE process_max_fds gauge
373 | process_max_fds 1.048576e+06
374 | # HELP process_open_fds Number of open file descriptors.
375 | # TYPE process_open_fds gauge
376 | process_open_fds 64
377 | # HELP process_resident_memory_bytes Resident memory size in bytes.
378 | # TYPE process_resident_memory_bytes gauge
379 | process_resident_memory_bytes 2.9724672e+07
380 | # HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
381 | # TYPE process_start_time_seconds gauge
382 | process_start_time_seconds 1.6014587121e+09
383 | # HELP process_virtual_memory_bytes Virtual memory size in bytes.
384 | # TYPE process_virtual_memory_bytes gauge
385 | process_virtual_memory_bytes 1.22191872e+08
386 | # HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.
387 | # TYPE process_virtual_memory_max_bytes gauge
388 | process_virtual_memory_max_bytes -1
389 | ```
390 |
--------------------------------------------------------------------------------