github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/cmd/ndm_daemonset/probe/usedbyprobe.go (about)

     1  /*
     2  Copyright 2020 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 probe
    18  
    19  import (
    20  	"errors"
    21  	"os"
    22  	"path/filepath"
    23  	"strings"
    24  	"syscall"
    25  
    26  	"github.com/openebs/node-disk-manager/blockdevice"
    27  	"github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller"
    28  	"github.com/openebs/node-disk-manager/pkg/blkid"
    29  	"github.com/openebs/node-disk-manager/pkg/spdk"
    30  	libudevwrapper "github.com/openebs/node-disk-manager/pkg/udev"
    31  	"github.com/openebs/node-disk-manager/pkg/util"
    32  
    33  	"k8s.io/klog/v2"
    34  )
    35  
    36  type usedbyProbe struct {
    37  	Controller      *controller.Controller
    38  	BlkidIdentifier *blkid.DeviceIdentifier
    39  }
    40  
    41  const (
    42  	usedbyProbeConfigKey = "used-by-probe"
    43  	usedbyProbePriority  = 5
    44  
    45  	k8sLocalVolumePath1 = "kubernetes.io/local-volume"
    46  	k8sLocalVolumePath2 = "kubernetes.io~local-volume"
    47  	zfsFileSystemLabel  = "zfs_member"
    48  )
    49  
    50  var (
    51  	usedbyProbeName  = "used-by probe"
    52  	usedbyProbeState = defaultEnabled
    53  )
    54  
    55  var usedbyProbeRegister = func() {
    56  	// Get a controller object
    57  	ctrl := <-controller.ControllerBroadcastChannel
    58  	if ctrl == nil {
    59  		klog.Error("unable to configure", usedbyProbeName)
    60  		return
    61  	}
    62  	if ctrl.NDMConfig != nil {
    63  		for _, probeConfig := range ctrl.NDMConfig.ProbeConfigs {
    64  			if probeConfig.Key == usedbyProbeConfigKey {
    65  				usedbyProbeName = probeConfig.Name
    66  				usedbyProbeState = util.CheckTruthy(probeConfig.State)
    67  				break
    68  			}
    69  		}
    70  	}
    71  	newRegisterProbe := &registerProbe{
    72  		priority:   usedbyProbePriority,
    73  		name:       usedbyProbeName,
    74  		state:      usedbyProbeState,
    75  		pi:         &usedbyProbe{Controller: ctrl},
    76  		controller: ctrl,
    77  	}
    78  	// Here we register the used-by probe
    79  	newRegisterProbe.register()
    80  }
    81  
    82  func newUsedByProbe(devPath string) *usedbyProbe {
    83  	usedbyProbe := &usedbyProbe{
    84  		BlkidIdentifier: &blkid.DeviceIdentifier{
    85  			DevPath: devPath,
    86  		},
    87  	}
    88  	return usedbyProbe
    89  }
    90  
    91  func (sp *usedbyProbe) Start() {}
    92  
    93  func (sp *usedbyProbe) FillBlockDeviceDetails(blockDevice *blockdevice.BlockDevice) {
    94  	if blockDevice.DevPath == "" {
    95  		klog.Errorf("device identifier found empty, used-by probe will not fetch information")
    96  		return
    97  	}
    98  
    99  	// checking for local PV on the device
   100  	for _, mountPoint := range blockDevice.FSInfo.MountPoint {
   101  		if strings.Contains(mountPoint, k8sLocalVolumePath1) ||
   102  			strings.Contains(mountPoint, k8sLocalVolumePath2) {
   103  			blockDevice.DevUse.InUse = true
   104  			blockDevice.DevUse.UsedBy = blockdevice.LocalPV
   105  			klog.V(4).Infof("device: %s Used by: %s filled by used-by probe", blockDevice.DevPath, blockDevice.DevUse.UsedBy)
   106  			return
   107  		}
   108  	}
   109  
   110  	// checking for cstor and zfs localPV
   111  	// we start with the assumption that device has a zfs file system
   112  	lookupZFS := true
   113  	devPath := blockDevice.DevPath
   114  
   115  	// if device is not a partition, check whether the zfs partitions are available on the disk
   116  	if blockDevice.DeviceAttributes.DeviceType != libudevwrapper.UDEV_PARTITION {
   117  		path, ok := getBlockDeviceZFSPartition(*blockDevice)
   118  		if ok {
   119  			devPath = path
   120  		} else {
   121  			lookupZFS = false
   122  			klog.V(4).Infof("device: %s is not having any zfs partitions", blockDevice.DevPath)
   123  		}
   124  	}
   125  
   126  	// only if lookupZFS is true, we need to check for the zfs filesystem on the disk.
   127  	if lookupZFS {
   128  		usedByProbe := newUsedByProbe(devPath)
   129  
   130  		// check for ZFS file system
   131  		fstype := usedByProbe.BlkidIdentifier.GetOnDiskFileSystem()
   132  		zpool := usedByProbe.BlkidIdentifier.GetOnDiskLabel()
   133  		if fstype == zfsFileSystemLabel {
   134  			blockDevice.DevUse.InUse = true
   135  			blockDevice.FSInfo.FileSystem = zfsFileSystemLabel
   136  			blockDevice.Labels[controller.NDMZpoolName] = zpool
   137  			// the disk can either be in use by cstor or zfs local PV
   138  			ok, err := isBlockDeviceInUseByKernel(blockDevice.DevPath)
   139  
   140  			if err != nil {
   141  				klog.Errorf("error checking block device: %s: %v", blockDevice.DevPath, err)
   142  			}
   143  			if ok {
   144  				blockDevice.DevUse.UsedBy = blockdevice.ZFSLocalPV
   145  			} else {
   146  				blockDevice.DevUse.UsedBy = blockdevice.CStor
   147  			}
   148  			klog.V(4).Infof("device: %s Used by: %s filled by used-by probe", blockDevice.DevPath, blockDevice.DevUse.UsedBy)
   149  			return
   150  		}
   151  	}
   152  
   153  	// create a device identifier for reading the spdk super block from the disk
   154  	spdkIdentifier := &spdk.DeviceIdentifier{
   155  		DevPath: blockDevice.DevPath,
   156  	}
   157  
   158  	signature, err := spdkIdentifier.GetSPDKSuperBlockSignature()
   159  	if err != nil {
   160  		klog.Errorf("error reading spdk signature from device: %s, %v", blockDevice.DevPath, err)
   161  	}
   162  	if spdk.IsSPDKSignatureExist(signature) {
   163  		blockDevice.DevUse.InUse = true
   164  		blockDevice.DevUse.UsedBy = blockdevice.Mayastor
   165  		klog.V(4).Infof("device: %s Used by: %s filled by used-by probe", blockDevice.DevPath, blockDevice.DevUse.UsedBy)
   166  		return
   167  	}
   168  
   169  	// TODO jiva disk detection
   170  }
   171  
   172  // getBlockDeviceZFSPartition is used to get the zfs partition if it exist in a
   173  // given BD
   174  func getBlockDeviceZFSPartition(bd blockdevice.BlockDevice) (string, bool) {
   175  
   176  	// check for zfs partitions only if there are 2 partitions on the block device
   177  	if len(bd.DependentDevices.Partitions) != 2 {
   178  		return "", false
   179  	}
   180  
   181  	zfsDataPartitionNumber := "1"
   182  	zfsMetaPartitionNumber := "9"
   183  
   184  	// to handle cases of devices with nvme drives, the device name will be
   185  	// nvme0n1
   186  	if util.IsMatchRegex(".+[0-9]+$", bd.DevPath) {
   187  		zfsDataPartitionNumber = "p" + zfsDataPartitionNumber
   188  		zfsMetaPartitionNumber = "p" + zfsMetaPartitionNumber
   189  	}
   190  
   191  	dataPartition := bd.DevPath + zfsDataPartitionNumber
   192  	metaPartition := bd.DevPath + zfsMetaPartitionNumber
   193  
   194  	// check if device has the 2 partitions
   195  	if bd.DependentDevices.Partitions[0] == dataPartition &&
   196  		bd.DependentDevices.Partitions[1] == metaPartition {
   197  		return dataPartition, true
   198  	}
   199  	return "", false
   200  }
   201  
   202  // isBlockDeviceInUseByKernel tries to open the device exclusively to check if the device is
   203  // being held by some process. eg: If kernel zfs uses the disk, the open will fail
   204  func isBlockDeviceInUseByKernel(path string) (bool, error) {
   205  	f, err := os.OpenFile(filepath.Clean(path), os.O_EXCL, 0444)
   206  
   207  	if errors.Is(err, syscall.EBUSY) {
   208  		return true, nil
   209  	}
   210  	if err != nil {
   211  		return false, err
   212  	}
   213  	defer f.Close()
   214  	return false, nil
   215  }