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

     1  /*
     2  Copyright 2018 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  
    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/features"
    25  	libudevwrapper "github.com/openebs/node-disk-manager/pkg/udev"
    26  	"github.com/openebs/node-disk-manager/pkg/util"
    27  	"k8s.io/klog/v2"
    28  )
    29  
    30  // EventAction action type for disk events like attach or detach events
    31  type EventAction string
    32  
    33  const (
    34  	// AttachEA is attach disk event name
    35  	AttachEA EventAction = libudevwrapper.UDEV_ACTION_ADD
    36  	// DetachEA is detach disk event name
    37  	DetachEA EventAction = libudevwrapper.UDEV_ACTION_REMOVE
    38  	// ChangeEA event is generated whenever some property of the blockdevice
    39  	// changes in udev
    40  	ChangeEA EventAction = libudevwrapper.UDEV_ACTION_CHANGE
    41  )
    42  
    43  var (
    44  	ErrNeedRescan = errors.New("need rescan")
    45  )
    46  
    47  // ProbeEvent struct contain a copy of controller it will update disk resources
    48  type ProbeEvent struct {
    49  	Controller *controller.Controller
    50  }
    51  
    52  // addBlockDeviceEvent fill block device details from different probes and push it to etcd
    53  func (pe *ProbeEvent) addBlockDeviceEvent(msg controller.EventMessage) {
    54  	// bdAPIList is the list of all the BlockDevice resources in the cluster
    55  	bdAPIList, err := pe.Controller.ListBlockDeviceResource(true)
    56  	if err != nil {
    57  		klog.Error(err)
    58  		go Rescan(pe.Controller)
    59  		return
    60  	}
    61  
    62  	isGPTBasedUUIDEnabled := features.FeatureGates.IsEnabled(features.GPTBasedUUID)
    63  
    64  	isNeedRescan := false
    65  	erroredDevices := make([]string, 0)
    66  
    67  	// iterate through each block device and perform the add/update operation
    68  	for _, device := range msg.Devices {
    69  		klog.Infof("Processing details for %s", device.DevPath)
    70  		pe.Controller.FillBlockDeviceDetails(device, msg.RequestedProbes...)
    71  
    72  		// add all devices to the hierarchy cache, irrespective of whether they will be
    73  		// filtered at a later stage. This is done so that a complete disk hierarchy is available
    74  		// at all times by NDM. It also helps in device processing when complex filter configurations
    75  		// are provided. Ref: https://github.com/openebs/openebs/issues/3321
    76  		pe.addBlockDeviceToHierarchyCache(*device)
    77  
    78  		// if ApplyFilter returns true then we process the event further
    79  		if !pe.Controller.ApplyFilter(device) {
    80  			continue
    81  		}
    82  		klog.Infof("Processed details for %s", device.DevPath)
    83  
    84  		if isGPTBasedUUIDEnabled {
    85  			if isParentOrSlaveDevice(*device, erroredDevices) {
    86  				klog.Warningf("device: %s skipped, because the parent / slave device has errored", device.DevPath)
    87  				continue
    88  			}
    89  			err := pe.addBlockDevice(*device, bdAPIList)
    90  			if err != nil {
    91  				isNeedRescan = true
    92  				if !errors.Is(err, ErrNeedRescan) {
    93  					erroredDevices = append(erroredDevices, device.DevPath)
    94  					klog.Error(err)
    95  				}
    96  			}
    97  		} else {
    98  			// if GPTBasedUUID is disabled and the device type is partition,
    99  			// the event can be skipped.
   100  			if device.DeviceAttributes.DeviceType == blockdevice.BlockDeviceTypePartition {
   101  				klog.Info("GPTBasedUUID disabled. skip creating block device resource for partition.")
   102  				continue
   103  			}
   104  			deviceInfo := pe.Controller.NewDeviceInfoFromBlockDevice(device)
   105  
   106  			existingBlockDeviceResource := pe.Controller.GetExistingBlockDeviceResource(bdAPIList, deviceInfo.UUID)
   107  			err := pe.Controller.PushBlockDeviceResource(existingBlockDeviceResource, deviceInfo)
   108  			if err != nil {
   109  				isNeedRescan = true
   110  				klog.Error(err)
   111  			}
   112  		}
   113  	}
   114  
   115  	if isNeedRescan {
   116  		go Rescan(pe.Controller)
   117  	}
   118  }
   119  
   120  // deleteBlockDeviceEvent deactivate blockdevice resource using uuid from etcd
   121  func (pe *ProbeEvent) deleteBlockDeviceEvent(msg controller.EventMessage) {
   122  	bdAPIList, err := pe.Controller.ListBlockDeviceResource(false)
   123  	if err != nil {
   124  		klog.Error(err)
   125  	}
   126  
   127  	isDeactivated := true
   128  	isGPTBasedUUIDEnabled := features.FeatureGates.IsEnabled(features.GPTBasedUUID)
   129  
   130  	for _, device := range msg.Devices {
   131  		if isGPTBasedUUIDEnabled {
   132  			_ = pe.deleteBlockDevice(*device, bdAPIList)
   133  		} else {
   134  			if device.DeviceAttributes.DeviceType == blockdevice.BlockDeviceTypePartition {
   135  				klog.Info("GPTBasedUUID disabled. skip delete block device resource for partition.")
   136  				continue
   137  			}
   138  			existingBlockDeviceResource := pe.Controller.GetExistingBlockDeviceResource(bdAPIList, device.UUID)
   139  			if existingBlockDeviceResource == nil {
   140  				// do nothing, may be the disk was filtered, or it was not created
   141  				isDeactivated = false
   142  				continue
   143  			}
   144  			pe.Controller.DeactivateBlockDevice(*existingBlockDeviceResource)
   145  		}
   146  	}
   147  
   148  	// rescan only if GPT based UUID is disabled.
   149  	if !isDeactivated && !isGPTBasedUUIDEnabled {
   150  		go Rescan(pe.Controller)
   151  	}
   152  }
   153  
   154  func (pe *ProbeEvent) changeBlockDeviceEvent(msg controller.EventMessage) {
   155  	var err error
   156  
   157  	if msg.AllBlockDevices {
   158  		for _, bd := range pe.Controller.BDHierarchy {
   159  			klog.Infof("Processing changes for %s", bd.DevPath)
   160  			err = pe.changeBlockDevice(&bd, msg.RequestedProbes...)
   161  			if err != nil {
   162  				klog.Errorf("failed to update blockdevice: %v", err)
   163  			}
   164  		}
   165  		return
   166  	}
   167  
   168  	for _, bd := range msg.Devices {
   169  		// The bd in `msg.Devices` mostly doesn't contain any information other than the
   170  		// DevPath. Get corresponding bd from cache since cache will have latest info
   171  		// for the bd.
   172  		cacheBD, ok := pe.Controller.BDHierarchy[bd.DevPath]
   173  		klog.Infof("Processing changes for %s", cacheBD.DevPath)
   174  		if ok {
   175  			err = pe.changeBlockDevice(&cacheBD, msg.RequestedProbes...)
   176  		} else {
   177  			// Device not in heiracrhy cache. this shouldn't happen, but to recover
   178  			// We use the mostly empty bd and run it through all probes
   179  			klog.V(4).Info("could not find bd in heirarchy cache. ",
   180  				"This shouldn't happen. Abandoning change.")
   181  			err = nil
   182  		}
   183  		if err != nil {
   184  			klog.Errorf("failed to update blockdevice: %v", err)
   185  		}
   186  	}
   187  
   188  }
   189  
   190  // isParentOrSlaveDevice check if any of the errored device is a parent / slave to the
   191  // given blockdevice
   192  func isParentOrSlaveDevice(bd blockdevice.BlockDevice, erroredDevices []string) bool {
   193  	for _, erroredDevice := range erroredDevices {
   194  		if bd.DependentDevices.Parent == erroredDevice {
   195  			return true
   196  		}
   197  		if util.Contains(bd.DependentDevices.Slaves, erroredDevice) {
   198  			return true
   199  		}
   200  	}
   201  	return false
   202  }