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  }