| 
									
										
										
										
											2015-09-26 08:36:40 -07:00
										 |  |  | // Copyright 2015 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-03 04:35:24 -07:00
										 |  |  | //go:build !nodiskstats
 | 
					
						
							| 
									
										
										
										
											2015-05-12 04:06:41 -07:00
										 |  |  | // +build !nodiskstats
 | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | package collector | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 	"github.com/prometheus/procfs/blockdevice" | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 	secondsPerTick = 1.0 / 1000.0 | 
					
						
							| 
									
										
										
										
											2022-03-07 14:24:08 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-10 16:43:07 -08:00
										 |  |  | 	// Read sectors and write sectors are the "standard UNIX 512-byte sectors, not any device- or filesystem-specific block size."
 | 
					
						
							|  |  |  | 	// See also https://www.kernel.org/doc/Documentation/block/stat.txt
 | 
					
						
							| 
									
										
										
										
											2022-03-07 14:24:08 -08:00
										 |  |  | 	unixSectorSize = 512.0 | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-15 00:32:58 -08:00
										 |  |  | 	diskstatsDefaultIgnoredDevices = "^(z?ram|loop|fd|(h|s|v|xv)d[a-z]|nvme\\d+n\\d+p)\\d+$" | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 02:40:51 -07:00
										 |  |  | 	// See udevadm(8).
 | 
					
						
							|  |  |  | 	udevDevicePropertyPrefix = "E:" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Udev device properties.
 | 
					
						
							|  |  |  | 	udevDMLVLayer               = "DM_LV_LAYER" | 
					
						
							|  |  |  | 	udevDMLVName                = "DM_LV_NAME" | 
					
						
							|  |  |  | 	udevDMName                  = "DM_NAME" | 
					
						
							|  |  |  | 	udevDMUUID                  = "DM_UUID" | 
					
						
							|  |  |  | 	udevDMVGName                = "DM_VG_NAME" | 
					
						
							|  |  |  | 	udevIDATA                   = "ID_ATA" | 
					
						
							|  |  |  | 	udevIDATARotationRateRPM    = "ID_ATA_ROTATION_RATE_RPM" | 
					
						
							|  |  |  | 	udevIDATASATA               = "ID_ATA_SATA" | 
					
						
							|  |  |  | 	udevIDATASATASignalRateGen1 = "ID_ATA_SATA_SIGNAL_RATE_GEN1" | 
					
						
							|  |  |  | 	udevIDATASATASignalRateGen2 = "ID_ATA_SATA_SIGNAL_RATE_GEN2" | 
					
						
							|  |  |  | 	udevIDATAWriteCache         = "ID_ATA_WRITE_CACHE" | 
					
						
							|  |  |  | 	udevIDATAWriteCacheEnabled  = "ID_ATA_WRITE_CACHE_ENABLED" | 
					
						
							|  |  |  | 	udevIDFSType                = "ID_FS_TYPE" | 
					
						
							|  |  |  | 	udevIDFSUsage               = "ID_FS_USAGE" | 
					
						
							|  |  |  | 	udevIDFSUUID                = "ID_FS_UUID" | 
					
						
							|  |  |  | 	udevIDFSVersion             = "ID_FS_VERSION" | 
					
						
							|  |  |  | 	udevIDModel                 = "ID_MODEL" | 
					
						
							|  |  |  | 	udevIDPath                  = "ID_PATH" | 
					
						
							|  |  |  | 	udevIDRevision              = "ID_REVISION" | 
					
						
							|  |  |  | 	udevIDSerialShort           = "ID_SERIAL_SHORT" | 
					
						
							|  |  |  | 	udevIDWWN                   = "ID_WWN" | 
					
						
							| 
									
										
										
										
											2023-05-24 01:19:18 -07:00
										 |  |  | 	udevSCSIIdentSerial         = "SCSI_IDENT_SERIAL" | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-17 08:55:55 -08:00
										 |  |  | type typedFactorDesc struct { | 
					
						
							|  |  |  | 	desc      *prometheus.Desc | 
					
						
							|  |  |  | 	valueType prometheus.ValueType | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | type udevInfo map[string]string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-17 08:55:55 -08:00
										 |  |  | func (d *typedFactorDesc) mustNewConstMetric(value float64, labels ...string) prometheus.Metric { | 
					
						
							|  |  |  | 	return prometheus.MustNewConstMetric(d.desc, d.valueType, value, labels...) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | type diskstatsCollector struct { | 
					
						
							| 
									
										
										
										
											2022-06-17 05:17:35 -07:00
										 |  |  | 	deviceFilter            deviceFilter | 
					
						
							|  |  |  | 	fs                      blockdevice.FS | 
					
						
							|  |  |  | 	infoDesc                typedFactorDesc | 
					
						
							|  |  |  | 	descs                   []typedFactorDesc | 
					
						
							|  |  |  | 	filesystemInfoDesc      typedFactorDesc | 
					
						
							|  |  |  | 	deviceMapperInfoDesc    typedFactorDesc | 
					
						
							|  |  |  | 	ataDescs                map[string]typedFactorDesc | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 	logger                  *slog.Logger | 
					
						
							| 
									
										
										
										
											2022-06-17 05:17:35 -07:00
										 |  |  | 	getUdevDeviceProperties func(uint32, uint32) (udevInfo, error) | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 	registerCollector("diskstats", defaultEnabled, NewDiskstatsCollector) | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 08:44:53 -08:00
										 |  |  | // NewDiskstatsCollector returns a new Collector exposing disk device stats.
 | 
					
						
							| 
									
										
										
										
											2018-10-15 08:24:28 -07:00
										 |  |  | // Docs from https://www.kernel.org/doc/Documentation/iostats.txt
 | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | func NewDiskstatsCollector(logger *slog.Logger) (Collector, error) { | 
					
						
							| 
									
										
										
										
											2014-11-24 18:00:17 -08:00
										 |  |  | 	var diskLabelNames = []string{"device"} | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 	fs, err := blockdevice.NewFS(*procPath, *sysPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("failed to open sysfs: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-11-24 18:00:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-27 23:10:40 -07:00
										 |  |  | 	deviceFilter, err := newDiskstatsDeviceFilter(logger) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("failed to parse device filter flags: %w", err) | 
					
						
							| 
									
										
										
										
											2022-06-06 23:17:31 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 05:17:35 -07:00
										 |  |  | 	collector := diskstatsCollector{ | 
					
						
							| 
									
										
										
										
											2022-06-27 23:10:40 -07:00
										 |  |  | 		deviceFilter: deviceFilter, | 
					
						
							| 
									
										
										
										
											2022-06-06 23:17:31 -07:00
										 |  |  | 		fs:           fs, | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 		infoDesc: typedFactorDesc{ | 
					
						
							|  |  |  | 			desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, diskSubsystem, "info"), | 
					
						
							|  |  |  | 				"Info of /sys/block/<block_device>.", | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 				[]string{"device", "major", "minor", "path", "wwn", "model", "serial", "revision"}, | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 				nil, | 
					
						
							|  |  |  | 			), valueType: prometheus.GaugeValue, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2018-01-17 08:55:55 -08:00
										 |  |  | 		descs: []typedFactorDesc{ | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-06 02:36:22 -08:00
										 |  |  | 				desc: readsCompletedDesc, valueType: prometheus.CounterValue, | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2018-01-17 08:55:55 -08:00
										 |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "reads_merged_total"), | 
					
						
							| 
									
										
										
										
											2018-10-15 08:24:28 -07:00
										 |  |  | 					"The total number of reads merged.", | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-06 02:36:22 -08:00
										 |  |  | 				desc: readBytesDesc, valueType: prometheus.CounterValue, | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-06 02:36:22 -08:00
										 |  |  | 				desc: readTimeSecondsDesc, valueType: prometheus.CounterValue, | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-06 02:36:22 -08:00
										 |  |  | 				desc: writesCompletedDesc, valueType: prometheus.CounterValue, | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2018-01-17 08:55:55 -08:00
										 |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "writes_merged_total"), | 
					
						
							| 
									
										
										
										
											2018-10-15 08:24:28 -07:00
										 |  |  | 					"The number of writes merged.", | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-06 02:36:22 -08:00
										 |  |  | 				desc: writtenBytesDesc, valueType: prometheus.CounterValue, | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-06 02:36:22 -08:00
										 |  |  | 				desc: writeTimeSecondsDesc, valueType: prometheus.CounterValue, | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2017-09-28 06:06:26 -07:00
										 |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "io_now"), | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 					"The number of I/Os currently in progress.", | 
					
						
							|  |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.GaugeValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-06 02:36:22 -08:00
										 |  |  | 				desc: ioTimeSecondsDesc, valueType: prometheus.CounterValue, | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							| 
									
										
										
										
											2018-01-17 08:55:55 -08:00
										 |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "io_time_weighted_seconds_total"), | 
					
						
							| 
									
										
										
										
											2018-10-15 08:24:28 -07:00
										 |  |  | 					"The weighted # of seconds spent doing I/Os.", | 
					
						
							|  |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							|  |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "discards_completed_total"), | 
					
						
							|  |  |  | 					"The total number of discards completed successfully.", | 
					
						
							|  |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							|  |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "discards_merged_total"), | 
					
						
							|  |  |  | 					"The total number of discards merged.", | 
					
						
							|  |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							|  |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "discarded_sectors_total"), | 
					
						
							| 
									
										
										
										
											2018-10-30 10:45:00 -07:00
										 |  |  | 					"The total number of sectors discarded successfully.", | 
					
						
							| 
									
										
										
										
											2018-10-15 08:24:28 -07:00
										 |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							|  |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "discard_time_seconds_total"), | 
					
						
							|  |  |  | 					"This is the total number of seconds spent by all discards.", | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2019-11-25 11:16:15 -08:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							|  |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "flush_requests_total"), | 
					
						
							|  |  |  | 					"The total number of flush requests completed successfully", | 
					
						
							|  |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc( | 
					
						
							|  |  |  | 					prometheus.BuildFQName(namespace, diskSubsystem, "flush_requests_time_seconds_total"), | 
					
						
							|  |  |  | 					"This is the total number of seconds spent by all flush requests.", | 
					
						
							|  |  |  | 					diskLabelNames, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.CounterValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2014-11-24 18:00:17 -08:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 		filesystemInfoDesc: typedFactorDesc{ | 
					
						
							|  |  |  | 			desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, diskSubsystem, "filesystem_info"), | 
					
						
							|  |  |  | 				"Info about disk filesystem.", | 
					
						
							|  |  |  | 				[]string{"device", "type", "usage", "uuid", "version"}, | 
					
						
							|  |  |  | 				nil, | 
					
						
							|  |  |  | 			), valueType: prometheus.GaugeValue, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		deviceMapperInfoDesc: typedFactorDesc{ | 
					
						
							|  |  |  | 			desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, diskSubsystem, "device_mapper_info"), | 
					
						
							|  |  |  | 				"Info about disk device mapper.", | 
					
						
							|  |  |  | 				[]string{"device", "name", "uuid", "vg_name", "lv_name", "lv_layer"}, | 
					
						
							|  |  |  | 				nil, | 
					
						
							|  |  |  | 			), valueType: prometheus.GaugeValue, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		ataDescs: map[string]typedFactorDesc{ | 
					
						
							|  |  |  | 			udevIDATAWriteCache: { | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, diskSubsystem, "ata_write_cache"), | 
					
						
							|  |  |  | 					"ATA disk has a write cache.", | 
					
						
							|  |  |  | 					[]string{"device"}, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.GaugeValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			udevIDATAWriteCacheEnabled: { | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, diskSubsystem, "ata_write_cache_enabled"), | 
					
						
							|  |  |  | 					"ATA disk has its write cache enabled.", | 
					
						
							|  |  |  | 					[]string{"device"}, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.GaugeValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			udevIDATARotationRateRPM: { | 
					
						
							|  |  |  | 				desc: prometheus.NewDesc(prometheus.BuildFQName(namespace, diskSubsystem, "ata_rotation_rate_rpm"), | 
					
						
							|  |  |  | 					"ATA disk rotation rate in RPMs (0 for SSDs).", | 
					
						
							|  |  |  | 					[]string{"device"}, | 
					
						
							|  |  |  | 					nil, | 
					
						
							|  |  |  | 				), valueType: prometheus.GaugeValue, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-12-31 08:19:37 -08:00
										 |  |  | 		logger: logger, | 
					
						
							| 
									
										
										
										
											2022-06-17 05:17:35 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Only enable getting device properties from udev if the directory is readable.
 | 
					
						
							|  |  |  | 	if stat, err := os.Stat(*udevDataPath); err != nil || !stat.IsDir() { | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 		logger.Error("Failed to open directory, disabling udev device properties", "path", *udevDataPath) | 
					
						
							| 
									
										
										
										
											2022-06-17 05:17:35 -07:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		collector.getUdevDeviceProperties = getUdevDeviceProperties | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &collector, nil | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 	diskStats, err := c.fs.ProcDiskstats() | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-15 13:27:14 -07:00
										 |  |  | 		return fmt.Errorf("couldn't get diskstats: %w", err) | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-11-24 18:00:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 	for _, stats := range diskStats { | 
					
						
							|  |  |  | 		dev := stats.DeviceName | 
					
						
							| 
									
										
										
										
											2022-06-06 23:17:31 -07:00
										 |  |  | 		if c.deviceFilter.ignored(dev) { | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 02:40:51 -07:00
										 |  |  | 		info, err := getUdevDeviceProperties(stats.MajorNumber, stats.MinorNumber) | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 			c.logger.Debug("Failed to parse udev info", "err", err) | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-24 01:19:18 -07:00
										 |  |  | 		// This is usually the serial printed on the disk label.
 | 
					
						
							|  |  |  | 		serial := info[udevSCSIIdentSerial] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// If it's undefined, fallback to ID_SERIAL_SHORT instead.
 | 
					
						
							|  |  |  | 		if serial == "" { | 
					
						
							|  |  |  | 			serial = info[udevIDSerialShort] | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 		ch <- c.infoDesc.mustNewConstMetric(1.0, dev, | 
					
						
							|  |  |  | 			fmt.Sprint(stats.MajorNumber), | 
					
						
							|  |  |  | 			fmt.Sprint(stats.MinorNumber), | 
					
						
							|  |  |  | 			info[udevIDPath], | 
					
						
							|  |  |  | 			info[udevIDWWN], | 
					
						
							|  |  |  | 			info[udevIDModel], | 
					
						
							| 
									
										
										
										
											2023-05-24 01:19:18 -07:00
										 |  |  | 			serial, | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 			info[udevIDRevision], | 
					
						
							|  |  |  | 		) | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		statCount := stats.IoStatsCount - 3 // Total diskstats record count, less MajorNumber, MinorNumber and DeviceName
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for i, val := range []float64{ | 
					
						
							|  |  |  | 			float64(stats.ReadIOs), | 
					
						
							|  |  |  | 			float64(stats.ReadMerges), | 
					
						
							| 
									
										
										
										
											2022-03-07 14:24:08 -08:00
										 |  |  | 			float64(stats.ReadSectors) * unixSectorSize, | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 			float64(stats.ReadTicks) * secondsPerTick, | 
					
						
							|  |  |  | 			float64(stats.WriteIOs), | 
					
						
							|  |  |  | 			float64(stats.WriteMerges), | 
					
						
							| 
									
										
										
										
											2022-03-07 14:24:08 -08:00
										 |  |  | 			float64(stats.WriteSectors) * unixSectorSize, | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 			float64(stats.WriteTicks) * secondsPerTick, | 
					
						
							|  |  |  | 			float64(stats.IOsInProgress), | 
					
						
							|  |  |  | 			float64(stats.IOsTotalTicks) * secondsPerTick, | 
					
						
							|  |  |  | 			float64(stats.WeightedIOTicks) * secondsPerTick, | 
					
						
							|  |  |  | 			float64(stats.DiscardIOs), | 
					
						
							|  |  |  | 			float64(stats.DiscardMerges), | 
					
						
							|  |  |  | 			float64(stats.DiscardSectors), | 
					
						
							|  |  |  | 			float64(stats.DiscardTicks) * secondsPerTick, | 
					
						
							|  |  |  | 			float64(stats.FlushRequestsCompleted), | 
					
						
							|  |  |  | 			float64(stats.TimeSpentFlushing) * secondsPerTick, | 
					
						
							|  |  |  | 		} { | 
					
						
							|  |  |  | 			if i >= statCount { | 
					
						
							| 
									
										
										
										
											2018-10-30 10:45:00 -07:00
										 |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-09-28 01:14:12 -07:00
										 |  |  | 			ch <- c.descs[i].mustNewConstMetric(val, dev) | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if fsType := info[udevIDFSType]; fsType != "" { | 
					
						
							|  |  |  | 			ch <- c.filesystemInfoDesc.mustNewConstMetric(1.0, dev, | 
					
						
							|  |  |  | 				fsType, | 
					
						
							|  |  |  | 				info[udevIDFSUsage], | 
					
						
							|  |  |  | 				info[udevIDFSUUID], | 
					
						
							|  |  |  | 				info[udevIDFSVersion], | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if name := info[udevDMName]; name != "" { | 
					
						
							|  |  |  | 			ch <- c.deviceMapperInfoDesc.mustNewConstMetric(1.0, dev, | 
					
						
							|  |  |  | 				name, | 
					
						
							|  |  |  | 				info[udevDMUUID], | 
					
						
							|  |  |  | 				info[udevDMVGName], | 
					
						
							|  |  |  | 				info[udevDMLVName], | 
					
						
							|  |  |  | 				info[udevDMLVLayer], | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ata := info[udevIDATA]; ata != "" { | 
					
						
							|  |  |  | 			for attr, desc := range c.ataDescs { | 
					
						
							|  |  |  | 				str, ok := info[attr] | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 					c.logger.Debug("Udev attribute does not exist", "attribute", attr) | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if value, err := strconv.ParseFloat(str, 64); err == nil { | 
					
						
							|  |  |  | 					ch <- desc.mustNewConstMetric(value, dev) | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2024-09-11 01:51:28 -07:00
										 |  |  | 					c.logger.Error("Failed to parse ATA value", "err", err) | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-28 06:21:31 -08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2014-06-04 04:12:34 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 02:40:51 -07:00
										 |  |  | func getUdevDeviceProperties(major, minor uint32) (udevInfo, error) { | 
					
						
							| 
									
										
										
										
											2022-06-14 11:04:59 -07:00
										 |  |  | 	filename := udevDataFilePath(fmt.Sprintf("b%d:%d", major, minor)) | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	data, err := os.Open(filename) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer data.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	info := make(udevInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	scanner := bufio.NewScanner(data) | 
					
						
							|  |  |  | 	for scanner.Scan() { | 
					
						
							| 
									
										
										
										
											2022-06-16 02:40:51 -07:00
										 |  |  | 		line := scanner.Text() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// We're only interested in device properties.
 | 
					
						
							|  |  |  | 		if !strings.HasPrefix(line, udevDevicePropertyPrefix) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		line = strings.TrimPrefix(line, udevDevicePropertyPrefix) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-14 11:02:10 -07:00
										 |  |  | 		/* TODO: After we drop support for Go 1.17, the condition below can be simplified to: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 02:40:51 -07:00
										 |  |  | 		if name, value, found := strings.Cut(line, "="); found { | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 			info[name] = value | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-14 11:02:10 -07:00
										 |  |  | 		*/ | 
					
						
							| 
									
										
										
										
											2022-06-16 02:40:51 -07:00
										 |  |  | 		if fields := strings.SplitN(line, "=", 2); len(fields) == 2 { | 
					
						
							| 
									
										
										
										
											2022-06-14 11:02:10 -07:00
										 |  |  | 			info[fields[0]] = fields[1] | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-14 00:57:52 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return info, nil | 
					
						
							|  |  |  | } |