github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/cmd/ndm_daemonset/probe/mountprobe.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 probe
    18  
    19  import (
    20  	"k8s.io/klog/v2"
    21  
    22  	"github.com/openebs/node-disk-manager/blockdevice"
    23  	"github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller"
    24  	"github.com/openebs/node-disk-manager/pkg/epoll"
    25  	"github.com/openebs/node-disk-manager/pkg/features"
    26  	"github.com/openebs/node-disk-manager/pkg/mount"
    27  	"github.com/openebs/node-disk-manager/pkg/mount/libmount"
    28  	"github.com/openebs/node-disk-manager/pkg/util"
    29  )
    30  
    31  // mountProbe contains required variables for populating diskInfo
    32  type mountProbe struct {
    33  	// Every new probe needs a controller object to register itself.
    34  	// Here Controller consists of Clientset, kubeClientset, probes, etc which is used to
    35  	// create, update, delete, deactivate the disk resources or list the probes already registered.
    36  	Controller      *controller.Controller
    37  	MountIdentifier *mount.Identifier
    38  	epoll           *epoll.Epoll
    39  	destination     chan controller.EventMessage
    40  	mountsFileName  string
    41  	mountTable      *libmount.MountTab
    42  }
    43  
    44  const (
    45  	mountProbePriority = 4
    46  	mountConfigKey     = "mount-probe"
    47  )
    48  
    49  var (
    50  	mountProbeName  = "mount probe"
    51  	mountProbeState = defaultEnabled
    52  )
    53  
    54  // init is used to get a controller object and then register itself
    55  var mountProbeRegister = func() {
    56  	// Get a controller object
    57  	ctrl := <-controller.ControllerBroadcastChannel
    58  	if ctrl == nil {
    59  		klog.Error("unable to configure", mountProbeName)
    60  		return
    61  	}
    62  	if ctrl.NDMConfig != nil {
    63  		for _, probeConfig := range ctrl.NDMConfig.ProbeConfigs {
    64  			if probeConfig.Key == mountConfigKey {
    65  				mountProbeName = probeConfig.Name
    66  				mountProbeState = util.CheckTruthy(probeConfig.State)
    67  				break
    68  			}
    69  		}
    70  	}
    71  	newRegisterProbe := &registerProbe{
    72  		priority:   mountProbePriority,
    73  		name:       mountProbeName,
    74  		state:      mountProbeState,
    75  		pi:         newMountProbeForRegistration(ctrl),
    76  		controller: ctrl,
    77  	}
    78  	// Here we register the probe (mount probe in this case)
    79  	newRegisterProbe.register()
    80  }
    81  
    82  // newMountProbeForRegistration returns mountprobe struct which helps
    83  // register the probe and start mount-point and fs change detection loop
    84  func newMountProbeForRegistration(c *controller.Controller) *mountProbe {
    85  	return &mountProbe{
    86  		Controller:     c,
    87  		mountsFileName: mount.HostMountsFilePath,
    88  		destination:    controller.EventMessageChannel,
    89  	}
    90  }
    91  
    92  // newMountProbe returns mountProbe struct which helps populate diskInfo struct
    93  // with the mount related details like mountpoint
    94  func newMountProbe(devPath string) *mountProbe {
    95  	mountIdentifier := &mount.Identifier{
    96  		DevPath: devPath,
    97  	}
    98  	mountProbe := &mountProbe{
    99  		MountIdentifier: mountIdentifier,
   100  	}
   101  	return mountProbe
   102  }
   103  
   104  // Start initializes moutprobe and sets up necessary watchers
   105  // for mount change detection
   106  func (mp *mountProbe) Start() {
   107  	if !features.FeatureGates.
   108  		IsEnabled(features.ChangeDetection) {
   109  		return
   110  	}
   111  	if err := mp.setupEpoll(); err != nil {
   112  		klog.Errorf("failed to setup epoll: %v", err)
   113  		return
   114  	}
   115  	mt, err := mp.newMountTable()
   116  	if err != nil {
   117  		klog.Errorf("failed to generate mount table")
   118  		return
   119  	}
   120  	mp.mountTable = mt
   121  	go mp.listen()
   122  }
   123  
   124  // FillBlockDeviceDetails fills details in diskInfo struct using information it gets from probe
   125  func (mp *mountProbe) FillBlockDeviceDetails(blockDevice *blockdevice.BlockDevice) {
   126  	if blockDevice.DevPath == "" {
   127  		klog.Error("mountIdentifier is found empty, mount probe will not fetch mount information.")
   128  		return
   129  	}
   130  	mountProbe := newMountProbe(blockDevice.DevPath)
   131  	basicMountInfo, err := mountProbe.MountIdentifier.DeviceBasicMountInfo(mount.HostMountsFilePath)
   132  	if err != nil {
   133  		if err == mount.ErrAttributesNotFound {
   134  			klog.Infof("no mount point found for %s. clearing mount points if any",
   135  				blockDevice.DevPath)
   136  			blockDevice.FSInfo.MountPoint = nil
   137  			return
   138  		}
   139  		klog.Error(err)
   140  		return
   141  	}
   142  
   143  	blockDevice.FSInfo.MountPoint = basicMountInfo.MountPoint
   144  	if blockDevice.FSInfo.FileSystem == "" {
   145  		blockDevice.FSInfo.FileSystem = basicMountInfo.FileSystem
   146  	}
   147  }
   148  
   149  func (mp *mountProbe) setupEpoll() error {
   150  	// create a buffered epoll so that we don't miss any change events
   151  	// due to slow consumption downstream
   152  	ep, err := epoll.New(epoll.BufferSize(10))
   153  	if err != nil {
   154  		return err
   155  	}
   156  	mp.epoll = &ep
   157  	return ep.AddWatcher(epoll.Watcher{
   158  		FileName:   mp.mountsFileName,
   159  		EventTypes: []epoll.EventType{epoll.EPOLLERR, epoll.EPOLLPRI},
   160  	})
   161  }
   162  
   163  func (mp *mountProbe) listen() {
   164  	eventCh, err := mp.epoll.Start()
   165  	if err != nil {
   166  		klog.Errorf("error while starting epoll: %v", err)
   167  		return
   168  	}
   169  	defer mp.epoll.Stop()
   170  	klog.Info("started mount change detection loop")
   171  	defaultMsg := controller.EventMessage{
   172  		Action:          string(ChangeEA),
   173  		Devices:         nil,
   174  		AllBlockDevices: true,
   175  	}
   176  
   177  	for range eventCh {
   178  		// regenerate mounts table and get the changes
   179  		newMountTable, err := mp.newMountTable()
   180  		if err != nil {
   181  			klog.Error("failed to generate mounts table.")
   182  			mp.destination <- defaultMsg
   183  		}
   184  		mtDiff := libmount.GenerateDiff(mp.mountTable, newMountTable)
   185  		mp.mountTable = newMountTable
   186  		mp.processDiff(mtDiff)
   187  	}
   188  }
   189  
   190  func (mp *mountProbe) newMountTable() (*libmount.MountTab, error) {
   191  	return libmount.NewMountTab(libmount.FromFile(mp.mountsFileName,
   192  		libmount.MntFmtFstab),
   193  		libmount.WithAllowFilter(libmount.SourceContainsFilter("/dev/")),
   194  		libmount.WithDenyFilter(libmount.SourceFilter("overlay")),
   195  		libmount.WithDenyFilter(libmount.TargetContainsFilter("/var/lib/kubelet/pod")),
   196  		libmount.WithDenyFilter(libmount.TargetContainsFilter("/var/lib/docker")),
   197  		libmount.WithDenyFilter(libmount.TargetContainsFilter("/run/docker")))
   198  }
   199  
   200  func (mp *mountProbe) processDiff(diff libmount.MountTabDiff) {
   201  	devices := make([]*blockdevice.BlockDevice, 0)
   202  	changedDevices := diff.ListSources()
   203  	for _, dev := range changedDevices {
   204  		bd := new(blockdevice.BlockDevice)
   205  		bd.DevPath = dev
   206  		devices = append(devices, bd)
   207  	}
   208  	if len(devices) == 0 {
   209  		return
   210  	}
   211  	klog.V(4).Infof("detected mount/fs changes in %d devices", len(devices))
   212  	mp.destination <- controller.EventMessage{
   213  		Action:          string(ChangeEA),
   214  		Devices:         devices,
   215  		RequestedProbes: []string{mountProbeName},
   216  	}
   217  
   218  }