├── .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 | ![build_release](https://github.com/galexrt/dellhw_exporter/workflows/build_release/badge.svg ) 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 | ![Version: 1.2.1](https://img.shields.io/badge/Version-1.2.1-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v2.0.0-rc.4](https://img.shields.io/badge/AppVersion-v2.0.0--rc.4-informational?style=flat-square) 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 | ![build_release](https://github.com/galexrt/dellhw_exporter/workflows/build_release/badge.svg) 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 / Datenschutz­erklärung can be found here: [Privacy Policy / Datenschutz­erklä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 | --------------------------------------------------------------------------------