github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/ndm-exporter/collector/seachest.go (about) 1 /* 2 Copyright 2019 The OpenEBS Authors 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 "fmt" 21 "sync" 22 23 "github.com/openebs/node-disk-manager/blockdevice" 24 "github.com/openebs/node-disk-manager/db/kubernetes" 25 smartmetrics "github.com/openebs/node-disk-manager/pkg/metrics/smart" 26 "github.com/openebs/node-disk-manager/pkg/seachest" 27 28 "github.com/prometheus/client_golang/prometheus" 29 "k8s.io/klog/v2" 30 ) 31 32 const ( 33 // SeachestCollectorNamespace is the namespace field in the prometheus metrics when 34 // seachest is used to collect the metrics. 35 SeachestCollectorNamespace = "seachest" 36 ) 37 38 // SeachestCollector contains the metrics, concurrency handler and client to get the 39 // metrics from seachest 40 type SeachestCollector struct { 41 // Client is the k8s client which will be used to interface with etcd 42 Client kubernetes.Client 43 44 // concurrency handling 45 sync.Mutex 46 requestInProgress bool 47 48 // all metrics collected via seachest 49 metrics *smartmetrics.Metrics 50 } 51 52 // SeachestMetricData is the struct which holds the data from seachest library 53 // corresponding to each blockdevice 54 type SeachestMetricData struct { 55 SeachestIdentifier *seachest.Identifier 56 TempInfo blockdevice.TemperatureInformation 57 Capacity uint64 58 TotalBytesRead uint64 59 TotalBytesWritten uint64 60 DeviceUtilization float64 61 PercentEnduranceUsed float64 62 } 63 64 // NewSeachestMetricCollector creates a new instance of SeachestCollector which 65 // implements Collector interface 66 func NewSeachestMetricCollector(c kubernetes.Client) prometheus.Collector { 67 klog.V(2).Infof("Seachest Metric Collector initialized") 68 sc := &SeachestCollector{ 69 Client: c, 70 metrics: smartmetrics.NewMetrics(SeachestCollectorNamespace), 71 } 72 sc.metrics.WithBlockDeviceCurrentTemperature(). 73 WithBlockDeviceCurrentTemperatureValid(). 74 WithBlockDeviceHighestTemperatureValid(). 75 WithBlockDeviceLowestTemperatureValid(). 76 WithBlockDeviceHighestTemperature(). 77 WithBlockDeviceLowestTemperature(). 78 WithBlockDeviceCapacity(). 79 WithBlockDeviceTotalBytesRead(). 80 WithBlockDeviceTotalBytesWritten(). 81 WithBlockDeviceUtilizationRate(). 82 WithBlockDevicePercentEnduranceUsed(). 83 WithRejectRequest(). 84 WithErrorRequest() 85 return sc 86 } 87 88 // Describe is the implementation of Describe in prometheus.Collector 89 func (sc *SeachestCollector) Describe(ch chan<- *prometheus.Desc) { 90 for _, col := range sc.metrics.Collectors() { 91 col.Describe(ch) 92 } 93 } 94 95 // Collect is the implementation of Collect in prometheus.Collector 96 func (sc *SeachestCollector) Collect(ch chan<- prometheus.Metric) { 97 klog.V(4).Info("Starting to collect smartmetrics metrics for a request") 98 99 sc.Lock() 100 if sc.requestInProgress { 101 klog.V(4).Info("Another request already in progress.") 102 sc.metrics.IncRejectRequestCounter() 103 sc.Unlock() 104 return 105 } 106 107 sc.requestInProgress = true 108 sc.Unlock() 109 110 // once a request is processed, set the progress flag to false 111 defer sc.setRequestProgressToFalse() 112 113 klog.V(4).Info("Setting client for this request.") 114 115 // set the client each time 116 if err := sc.Client.InitClient(); err != nil { 117 klog.Errorf("error setting client. %v", err) 118 sc.metrics.IncErrorRequestCounter() 119 sc.collectErrors(ch) 120 return 121 } 122 123 // get list of blockdevices from etcd 124 blockDevices, err := sc.Client.ListBlockDevice() 125 if err != nil { 126 klog.Errorf("Listing block devices failed %v", err) 127 sc.metrics.IncErrorRequestCounter() 128 sc.collectErrors(ch) 129 return 130 } 131 132 klog.V(4).Info("Blockdevices fetched from etcd") 133 134 err = getMetricData(blockDevices) 135 if err != nil { 136 sc.metrics.IncErrorRequestCounter() 137 sc.collectErrors(ch) 138 return 139 } 140 141 klog.V(4).Infof("metrics data obtained from seachest library") 142 143 sc.setMetricData(blockDevices) 144 145 klog.V(4).Info("Prometheus metrics is set and initializing collection.") 146 147 // collect each metric 148 for _, col := range sc.metrics.Collectors() { 149 col.Collect(ch) 150 } 151 } 152 153 // setRequestProgressToFalse is used to set the progress flag, when a request is 154 // processed or errored 155 func (sc *SeachestCollector) setRequestProgressToFalse() { 156 sc.Lock() 157 sc.requestInProgress = false 158 sc.Unlock() 159 } 160 161 // collectErrors collects only the error metrics and set it on the channel 162 func (sc *SeachestCollector) collectErrors(ch chan<- prometheus.Metric) { 163 for _, col := range sc.metrics.ErrorCollectors() { 164 col.Collect(ch) 165 } 166 } 167 168 // getMetricData gets the seachest metrics for each blockdevice and fills it in the blockdevice struct 169 func getMetricData(bds []blockdevice.BlockDevice) error { 170 var err error 171 ok := false 172 for i, bd := range bds { 173 // do not report metrics for sparse devices 174 if bd.DeviceAttributes.DeviceType == blockdevice.SparseBlockDeviceType { 175 continue 176 } 177 sc := SeachestMetricData{ 178 SeachestIdentifier: &seachest.Identifier{ 179 DevPath: bd.DevPath, 180 }, 181 } 182 err = sc.getSeachestData() 183 if err != nil { 184 klog.Errorf("fetching seachest data for %s failed. %v", bd.DevPath, err) 185 continue 186 } 187 ok = true 188 189 bds[i].SMARTInfo.TemperatureInfo = sc.TempInfo 190 bds[i].Capacity.Storage = sc.Capacity 191 bds[i].SMARTInfo.TotalBytesRead = sc.TotalBytesRead 192 bds[i].SMARTInfo.TotalBytesWritten = sc.TotalBytesWritten 193 bds[i].SMARTInfo.UtilizationRate = sc.DeviceUtilization 194 bds[i].SMARTInfo.PercentEnduranceUsed = sc.PercentEnduranceUsed 195 196 } 197 if !ok { 198 return fmt.Errorf("getting seachest metrics for the blockdevices failed") 199 } 200 return nil 201 } 202 203 // getSeachestData fetches the data for a blockdevice using the seachest library from the disk. 204 func (sc *SeachestMetricData) getSeachestData() error { 205 driveInfo, err := sc.SeachestIdentifier.SeachestBasicDiskInfo() 206 if err != 0 { 207 klog.Errorf("error fetching basic disk info using seachest. %s", seachest.SeachestErrors(err)) 208 return fmt.Errorf("error getting seachest data for metrics. %s", seachest.SeachestErrors(err)) 209 } 210 211 sc.TempInfo.CurrentTemperatureDataValid = sc.SeachestIdentifier.GetTemperatureDataValidStatus(driveInfo) 212 sc.TempInfo.CurrentTemperature = sc.SeachestIdentifier.GetCurrentTemperature(driveInfo) 213 sc.TempInfo.LowestTemperature = sc.SeachestIdentifier.GetLowestTemperature(driveInfo) 214 sc.TempInfo.HighestTemperature = sc.SeachestIdentifier.GetHighestTemperature(driveInfo) 215 sc.TempInfo.HighestTemperatureDataValid = sc.SeachestIdentifier.GetHighestValid(driveInfo) 216 sc.TempInfo.LowestTemperatureDataValid = sc.SeachestIdentifier.GetLowestValid(driveInfo) 217 sc.Capacity = sc.SeachestIdentifier.GetCapacity(driveInfo) 218 sc.TotalBytesRead = sc.SeachestIdentifier.GetTotalBytesRead(driveInfo) 219 sc.TotalBytesWritten = sc.SeachestIdentifier.GetTotalBytesWritten(driveInfo) 220 sc.DeviceUtilization = sc.SeachestIdentifier.GetDeviceUtilizationRate(driveInfo) 221 sc.PercentEnduranceUsed = sc.SeachestIdentifier.GetPercentEnduranceUsed(driveInfo) 222 223 klog.V(4).Infof("Device is : %v", sc.SeachestIdentifier.DevPath) 224 klog.V(4).Infof("Current temperature is %v", sc.TempInfo.CurrentTemperature) 225 klog.V(4).Infof("Lowest temperature is %v", sc.TempInfo.LowestTemperature) 226 klog.V(4).Infof("Highest temperature is %v", sc.TempInfo.HighestTemperature) 227 klog.V(4).Infof("Capacity is %v", sc.Capacity) 228 klog.V(4).Infof("Total bytes read is %v", sc.TotalBytesRead) 229 klog.V(4).Infof("Total bytes written is %v", sc.TotalBytesWritten) 230 klog.V(4).Infof("Device utilization rate is %v", sc.DeviceUtilization) 231 klog.V(4).Infof("Endurance used is %v", sc.PercentEnduranceUsed) 232 233 return nil 234 } 235 236 // setMetricData sets the SMART metric data collected using seachest onto 237 // the prometheus metrics 238 func (sc *SeachestCollector) setMetricData(blockdevices []blockdevice.BlockDevice) { 239 for _, bd := range blockdevices { 240 // sets the label values 241 sc.metrics.WithBlockDeviceUUID(bd.UUID). 242 WithBlockDevicePath(bd.DevPath). 243 WithBlockDeviceHostName(bd.NodeAttributes[blockdevice.HostName]). 244 WithBlockDeviceNodeName(bd.NodeAttributes[blockdevice.NodeName]) 245 // sets the metrics 246 sc.metrics.SetBlockDeviceCurrentTemperature(bd.SMARTInfo.TemperatureInfo.CurrentTemperature). 247 SetBlockDeviceHighestTemperature(bd.SMARTInfo.TemperatureInfo.HighestTemperature). 248 SetBlockDeviceLowestTemperature(bd.SMARTInfo.TemperatureInfo.LowestTemperature). 249 SetBlockDeviceCurrentTemperatureValid(bd.SMARTInfo.TemperatureInfo.CurrentTemperatureDataValid). 250 SetBlockDeviceHighestTemperatureValid(bd.SMARTInfo.TemperatureInfo.HighestTemperatureDataValid). 251 SetBlockDeviceLowestTemperatureValid(bd.SMARTInfo.TemperatureInfo.LowestTemperatureDataValid). 252 SetBlockDeviceCapacity(bd.Capacity.Storage). 253 SetBlockDeviceUtilizationRate(bd.SMARTInfo.UtilizationRate). 254 SetBlockDeviceTotalBytesRead(bd.SMARTInfo.TotalBytesRead). 255 SetBlockDeviceTotalBytesWritten(bd.SMARTInfo.TotalBytesWritten). 256 SetBlockDevicePercentEnduranceUsed(bd.SMARTInfo.PercentEnduranceUsed) 257 258 } 259 }