diff --git a/blockdevice/blockdevice.go b/blockdevice/blockdevice.go index 7aedf1abf..beaecd105 100644 --- a/blockdevice/blockdevice.go +++ b/blockdevice/blockdevice.go @@ -431,6 +431,9 @@ const ( // Jiva Jiva StorageEngine = "jiva" + + // LVMLocalPV + LVMLocalPV StorageEngine = "lvm-localpv" ) // Status is used to represent the status of the blockdevice diff --git a/cmd/ndm_daemonset/probe/addhandler.go b/cmd/ndm_daemonset/probe/addhandler.go index 60e4c21b0..e1b05de1e 100644 --- a/cmd/ndm_daemonset/probe/addhandler.go +++ b/cmd/ndm_daemonset/probe/addhandler.go @@ -317,6 +317,13 @@ func (pe *ProbeEvent) handleUnmanagedDevices(bd blockdevice.BlockDevice, bdAPILi } else if !ok { return false, nil } + + // handle if the device is used by lvm localPV + if ok, err := pe.deviceInUseByLVMLocalPV(bd, bdAPIList); err != nil { + return ok, err + } else if !ok { + return false, nil + } return true, nil } @@ -384,6 +391,42 @@ func (pe *ProbeEvent) deviceInUseByZFSLocalPV(bd blockdevice.BlockDevice, bdAPIL return false, nil } +// deviceInUseByLVMLocalPV check if the device is in use by lvm localPV and returns true if further processing of +// event is required. If the device has lvm pv on it, then a blockdevice resource will be created and lvm PV tag +// will be added on to the resource +func (pe *ProbeEvent) deviceInUseByLVMLocalPV(bd blockdevice.BlockDevice, bdAPIList *apis.BlockDeviceList) (bool, error) { + if !bd.DevUse.InUse { + return true, nil + } + + // not in use by lvm localpv + if bd.DevUse.UsedBy != blockdevice.LVMLocalPV { + return true, nil + } + + klog.Infof("device: %s in use by lvm-localPV", bd.DevPath) + + uuid, ok := generateUUID(bd) + if !ok { + klog.Errorf("unable to generate uuid for lvm-localPV device: %s", bd.DevPath) + return false, fmt.Errorf("error generating uuid for lvm-localPV disk: %s", bd.DevPath) + } + + bd.UUID = uuid + + deviceInfo := pe.Controller.NewDeviceInfoFromBlockDevice(&bd) + bdAPI := deviceInfo.ToDevice() + bdAPI.Labels[kubernetes.BlockDeviceTagLabel] = string(blockdevice.LVMLocalPV) + + err := pe.Controller.CreateBlockDevice(bdAPI) + if err != nil { + klog.Errorf("unable to push %s (%s) to etcd", bd.UUID, bd.DevPath) + return false, err + } + klog.Infof("Pushed lvm-localPV device: %s (%s) to etcd", bd.UUID, bd.DevPath) + return false, nil +} + // upgradeDeviceInUseByCStor handles the upgrade if the device is used by cstor. returns true if further processing // is required func (pe *ProbeEvent) upgradeDeviceInUseByCStor(bd blockdevice.BlockDevice, bdAPIList *apis.BlockDeviceList) (bool, error) { diff --git a/cmd/ndm_daemonset/probe/addhandler_test.go b/cmd/ndm_daemonset/probe/addhandler_test.go index 769a43fcf..5ff6c7d1a 100644 --- a/cmd/ndm_daemonset/probe/addhandler_test.go +++ b/cmd/ndm_daemonset/probe/addhandler_test.go @@ -405,6 +405,143 @@ func TestDeviceInUseByZFSLocalPV(t *testing.T) { } } +func TestProbeEvent_deviceInUseByLVMLocalPV(t *testing.T) { + fakeFsID := "fake-fs-uuid" + fakeBD := blockdevice.BlockDevice{ + FSInfo: blockdevice.FileSystemInformation{ + FileSystemUUID: fakeFsID, + }, + } + fakeUUID, _ := generateUUID(fakeBD) + + tests := map[string]struct { + bd blockdevice.BlockDevice + bdAPIList *apis.BlockDeviceList + createdOrUpdatedBDName string + want bool + wantErr bool + }{ + "device not in use": { + bd: blockdevice.BlockDevice{ + DevUse: blockdevice.DeviceUsage{ + InUse: false, + }, + }, + bdAPIList: &apis.BlockDeviceList{}, + createdOrUpdatedBDName: "", + want: true, + wantErr: false, + }, + "device in use, not by lvm localPV": { + bd: blockdevice.BlockDevice{ + DevUse: blockdevice.DeviceUsage{ + InUse: true, + UsedBy: blockdevice.CStor, + }, + + }, + bdAPIList: &apis.BlockDeviceList{}, + createdOrUpdatedBDName: "", + want: true, + wantErr: false, + }, + "deviceType disk, used by lvm PV and is connected to the cluster for the first time": { + bd: blockdevice.BlockDevice{ + Identifier: blockdevice.Identifier{ + DevPath: "/dev/sda", + }, + DeviceAttributes: blockdevice.DeviceAttribute{ + DeviceType: blockdevice.BlockDeviceTypeDisk, + }, + DevUse: blockdevice.DeviceUsage{ + InUse: true, + UsedBy: blockdevice.LVMLocalPV, + }, + FSInfo: blockdevice.FileSystemInformation{ + FileSystemUUID: fakeFsID, + }, + }, + bdAPIList: &apis.BlockDeviceList{}, + createdOrUpdatedBDName: fakeUUID, + want: false, + wantErr: false, + }, + "deviceType disk, used by lvm PV and is moved from disconnected and reconnected to the node at a different path": { + bd: blockdevice.BlockDevice{ + Identifier: blockdevice.Identifier{ + DevPath: "/dev/sda", + }, + DeviceAttributes: blockdevice.DeviceAttribute{ + DeviceType: blockdevice.BlockDeviceTypeDisk, + }, + DevUse: blockdevice.DeviceUsage{ + InUse: true, + UsedBy: blockdevice.LVMLocalPV, + }, + FSInfo: blockdevice.FileSystemInformation{ + FileSystemUUID: fakeFsID, + }, + }, + bdAPIList: &apis.BlockDeviceList{ + Items: []apis.BlockDevice{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: fakeUUID, + }, + Spec: apis.DeviceSpec{ + Path: "/dev/sdb", + }, + }, + }, + }, + createdOrUpdatedBDName: fakeUUID, + want: false, + wantErr: false, + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + s := scheme.Scheme + s.AddKnownTypes(apis.GroupVersion, &apis.BlockDevice{}) + s.AddKnownTypes(apis.GroupVersion, &apis.BlockDeviceList{}) + cl := fake.NewFakeClientWithScheme(s) + + // initialize client with all the bd resources + for _, bdAPI := range tt.bdAPIList.Items { + cl.Create(context.TODO(), &bdAPI) + } + + ctrl := &controller.Controller{ + Clientset: cl, + } + pe := &ProbeEvent{ + Controller: ctrl, + } + got, err := pe.deviceInUseByLVMLocalPV(tt.bd, tt.bdAPIList) + if (err != nil) != tt.wantErr { + t.Errorf("deviceInUseByLVMLocalPV() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("deviceInUseByLVMLocalPV() got = %v, want %v", got, tt.want) + } + + // check if a BD has been created or updated + if len(tt.createdOrUpdatedBDName) != 0 { + gotBDAPI := &apis.BlockDevice{} + err := cl.Get(context.TODO(), client.ObjectKey{Name: tt.createdOrUpdatedBDName}, gotBDAPI) + if err != nil { + t.Errorf("error in getting blockdevice %s", tt.createdOrUpdatedBDName) + } + // verify the block-device-tag on the resource, also verify the path and node name + assert.Equal(t, string(blockdevice.LVMLocalPV), gotBDAPI.GetLabels()[kubernetes.BlockDeviceTagLabel]) + assert.Equal(t, tt.bd.DevPath, gotBDAPI.Spec.Path) + assert.Equal(t, tt.bd.NodeAttributes[blockdevice.NodeName], gotBDAPI.Spec.NodeAttributes.NodeName) + } + }) + } +} + func TestIsParentDeviceInUse(t *testing.T) { cache := map[string]blockdevice.BlockDevice{ "/dev/sda": { diff --git a/cmd/ndm_daemonset/probe/usedbyprobe.go b/cmd/ndm_daemonset/probe/usedbyprobe.go index 844691060..e43f6d58f 100644 --- a/cmd/ndm_daemonset/probe/usedbyprobe.go +++ b/cmd/ndm_daemonset/probe/usedbyprobe.go @@ -45,6 +45,7 @@ const ( k8sLocalVolumePath1 = "kubernetes.io/local-volume" k8sLocalVolumePath2 = "kubernetes.io~local-volume" zfsFileSystemLabel = "zfs_member" + lvmFileSystemLabel = "LVM2_member" ) var ( @@ -149,6 +150,18 @@ func (sp *usedbyProbe) FillBlockDeviceDetails(blockDevice *blockdevice.BlockDevi } } + // checking for lvm localPV + usedByProbe := newUsedByProbe(blockDevice.DevPath) + // check for LVM file system + fstype := usedByProbe.BlkidIdentifier.GetOnDiskFileSystem() + + if fstype == lvmFileSystemLabel { + blockDevice.DevUse.InUse = true + blockDevice.DevUse.UsedBy = blockdevice.LVMLocalPV + klog.V(4).Infof("device: %s Used by: %s filled by used-by probe", blockDevice.DevPath, blockDevice.DevUse.UsedBy) + return + } + // create a device identifier for reading the spdk super block from the disk spdkIdentifier := &spdk.DeviceIdentifier{ DevPath: blockDevice.DevPath,