Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core, uuid): add dm support #495

Merged
merged 15 commits into from
Nov 2, 2020
18 changes: 18 additions & 0 deletions blockdevice/blockdevice.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ type BlockDevice struct {
// information on the disk
DeviceAttributes DeviceAttribute

// DMInfo is filled if the device is a DM device
DMInfo DeviceMapperInformation

DevUse DeviceUsage

// PartitionInfo contains details if this blockdevice is a partition
Expand Down Expand Up @@ -209,13 +212,19 @@ const (
// BlockDeviceTypeLoop represents a loop device
BlockDeviceTypeLoop = "loop"

// BlockDeviceTypeDMDevice is a dm device
BlockDeviceTypeDMDevice = "dm"

// BlockDeviceTypeLVM is an lvm device type
BlockDeviceTypeLVM = "lvm"

// BlockDeviceTypeCrypt is a LUKS volume
BlockDeviceTypeCrypt = "crypt"
)

// DeviceMapperDeviceTypes is the slice of device types that uses a device mapper
var DeviceMapperDeviceTypes = []string{BlockDeviceTypeDMDevice, BlockDeviceTypeLVM, BlockDeviceTypeCrypt}

const (
// DriveTypeHDD represents a rotating hard disk drive
DriveTypeHDD = "HDD"
Expand Down Expand Up @@ -356,6 +365,15 @@ type PartitionInformation struct {
PartitionTableType string
}

type DeviceMapperInformation struct {
// DMUUID is the UUID of the device as present in <dev-sys-path>/dm/uuid
DMUUID string

// DevMapperPath is the device mapper path. It will be of the format /dev/mapper/<mapper>
// This path will be a symlink to the actual dm-X device node
DevMapperPath string
}

// DependentBlockDevices contains path of all devices that are
// related to this BlockDevice
type DependentBlockDevices struct {
Expand Down
1 change: 1 addition & 0 deletions changelogs/unreleased/495-akhilerm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add support for device-mapper(dm) devices.
23 changes: 16 additions & 7 deletions cmd/ndm_daemonset/probe/udevprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,13 @@ func (up *udevProbe) scan() error {
deviceDetails.PartitionInfo.PartitionTableUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_PARTITION_TABLE_UUID)
deviceDetails.PartitionInfo.PartitionEntryUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_PARTITION_UUID)
deviceDetails.FSInfo.FileSystemUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_FS_UUID)
deviceDetails.DMInfo.DMUUID = newUdevice.GetPropertyValue(libudevwrapper.UDEV_DM_UUID)
} else {
uuid := newUdevice.GetUid()
disksUid = append(disksUid, uuid)
deviceDetails.UUID = uuid
}
deviceDetails.DeviceAttributes.DeviceType = newUdevice.GetPropertyValue(libudevwrapper.UDEV_DEVTYPE)
udevDeviceType := newUdevice.GetPropertyValue(libudevwrapper.UDEV_DEVTYPE)
deviceDetails.SysPath = newUdevice.GetSyspath()
deviceDetails.DevPath = newUdevice.GetPath()

Expand All @@ -211,15 +212,13 @@ func (up *udevProbe) scan() error {
deviceDetails.DevPath, deviceDetails.FSInfo.FileSystemUUID)
}

diskInfo = append(diskInfo, deviceDetails)

// get the dependents of the block device
// this is done by scanning sysfs
sysfsDevice, err := sysfs.NewSysFsDeviceFromDevPath(deviceDetails.DevPath)
// TODO if error occurs a rescan may be required
if err != nil {
klog.Errorf("could not get sysfs device for %s, err: %v", deviceDetails.DevPath, err)
} else {
// get the dependents of the block device
// this is done by scanning sysfs
dependents, err := sysfsDevice.GetDependents()
// TODO if error occurs need to do a scan from the beginning
if err != nil {
Expand All @@ -228,7 +227,18 @@ func (up *udevProbe) scan() error {
deviceDetails.DependentDevices = dependents
klog.Infof("Dependents of %s : %+v", deviceDetails.DevPath, dependents)
}
// the device type reported by udev will always be disk/partition. Using this info
// and the entries from sysfs, the actual device type is found out.
deviceType, err := sysfsDevice.GetDeviceType(udevDeviceType)
if err != nil {
klog.Errorf("could not get device type for %s, falling back to udev reported type: %s", deviceDetails.DevPath, udevDeviceType)
deviceType = udevDeviceType
}
deviceDetails.DeviceAttributes.DeviceType = deviceType
klog.Infof("Device: %s is of type: %s", deviceDetails.DevPath, deviceDetails.DeviceAttributes.DeviceType)
}

diskInfo = append(diskInfo, deviceDetails)
}
newUdevice.UdevDeviceUnref()
}
Expand Down Expand Up @@ -295,7 +305,6 @@ func (up *udevProbe) FillBlockDeviceDetails(blockDevice *blockdevice.BlockDevice
Links: udevDiskDetails.ByPathDevLinks,
})
}
blockDevice.DeviceAttributes.DeviceType = udevDiskDetails.DiskType

// filesystem info of the attached device. Only filesystem data will be filled in the struct,
// as the mountpoint related information will be filled in by the mount probe
Expand All @@ -304,7 +313,7 @@ func (up *udevProbe) FillBlockDeviceDetails(blockDevice *blockdevice.BlockDevice
blockDevice.PartitionInfo.PartitionTableType = udevDiskDetails.PartitionTableType

// if this is a partition, partition number and partition UUID need to be filled
if udevDiskDetails.DiskType == libudevwrapper.UDEV_PARTITION {
if udevDiskDetails.DiskType == blockdevice.BlockDeviceTypePartition {
blockDevice.PartitionInfo.PartitionNumber = udevDiskDetails.PartitionNumber
}
}
Expand Down
8 changes: 3 additions & 5 deletions cmd/ndm_daemonset/probe/udevprobe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,9 @@ func mockOsDiskToAPI() (apis.BlockDevice, error) {
return apis.BlockDevice{}, err
}
fakeDetails := apis.DeviceDetails{
Model: mockOsDiskDetails.Model,
Serial: mockOsDiskDetails.Serial,
Vendor: mockOsDiskDetails.Vendor,
DeviceType: controller.NDMDefaultDiskType,
Model: mockOsDiskDetails.Model,
Serial: mockOsDiskDetails.Serial,
Vendor: mockOsDiskDetails.Vendor,
}
fakeObj := apis.DeviceSpec{
Path: mockOsDiskDetails.DevNode,
Expand Down Expand Up @@ -108,7 +107,6 @@ func TestFillDiskDetails(t *testing.T) {
expectedDiskInfo := &blockdevice.BlockDevice{}
expectedDiskInfo.SysPath = mockOsDiskDetails.SysPath
expectedDiskInfo.DevPath = mockOsDiskDetails.DevNode
expectedDiskInfo.DeviceAttributes.DeviceType = "disk"
expectedDiskInfo.DeviceAttributes.Model = mockOsDiskDetails.Model
expectedDiskInfo.DeviceAttributes.Serial = mockOsDiskDetails.Serial
expectedDiskInfo.DeviceAttributes.Vendor = mockOsDiskDetails.Vendor
Expand Down
12 changes: 12 additions & 0 deletions cmd/ndm_daemonset/probe/uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ func generateUUID(bd blockdevice.BlockDevice) (string, bool) {
// the partition and the unique data will be stored in sectors where consumers do not have access.

switch {
case bd.DeviceAttributes.DeviceType == blockdevice.BlockDeviceTypeLoop:
// hostname and device name, i.e /dev/loopX will be used for generating uuid
hostName, _ := os.Hostname()
klog.Infof("device(%s) is a loop device, using node name: %s and path: %s", bd.DevPath, hostName, bd.DevPath)
uuidField = hostName + bd.DevPath
ok = true
case util.Contains(blockdevice.DeviceMapperDeviceTypes, bd.DeviceAttributes.DeviceType):
// if a DM device, use the DM uuid
klog.Infof("device(%s) is a dm device, using DM UUID: %s", bd.DevPath, bd.DMInfo.DMUUID)
// TODO add a check if DM uuid is present, else may need to add mitigation steps
uuidField = bd.DMInfo.DMUUID
ok = true
case bd.DeviceAttributes.DeviceType == blockdevice.BlockDeviceTypePartition:
// The partition entry UUID is used when a partition (/dev/sda1) is processed. The partition UUID should be used
// if available, other than the partition table UUID, because multiple partitions can have the same partition table
Expand Down
40 changes: 40 additions & 0 deletions cmd/ndm_daemonset/probe/uuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func TestGenerateUUID(t *testing.T) {
fakeSerial := "CT500MX500SSD1"
fakeFileSystemUUID := "149108ca-f404-4556-a263-04943e6cb0b3"
fakePartitionUUID := "065e2357-05"
fakeLVM_DM_UUID := "LVM-j2xmqvbcVWBQK9Jdttte3CyeVTGgxtVV5VcCi3nxdwihZDxSquMOBaGL5eymBNvk"
fakeCRYPT_DM_UUID := "CRYPT-LUKS1-f4608c76343d4b5badaf6651d32f752b-backup"
loopDevicePath := "/dev/loop98"
hostName, _ := os.Hostname()
tests := map[string]struct {
bd blockdevice.BlockDevice
wantUUID string
Expand Down Expand Up @@ -103,6 +107,42 @@ func TestGenerateUUID(t *testing.T) {
wantUUID: "",
wantOk: false,
},
"deviceType-lvm device": {
bd: blockdevice.BlockDevice{
DMInfo: blockdevice.DeviceMapperInformation{
DMUUID: fakeLVM_DM_UUID,
},
DeviceAttributes: blockdevice.DeviceAttribute{
DeviceType: blockdevice.BlockDeviceTypeLVM,
},
},
wantUUID: blockdevice.BlockDevicePrefix + util.Hash(fakeLVM_DM_UUID),
wantOk: true,
},
"deviceType-crypt device": {
bd: blockdevice.BlockDevice{
DMInfo: blockdevice.DeviceMapperInformation{
DMUUID: fakeCRYPT_DM_UUID,
},
DeviceAttributes: blockdevice.DeviceAttribute{
DeviceType: blockdevice.BlockDeviceTypeCrypt,
},
},
wantUUID: blockdevice.BlockDevicePrefix + util.Hash(fakeCRYPT_DM_UUID),
wantOk: true,
},
"deviceType-loop device": {
bd: blockdevice.BlockDevice{
Identifier: blockdevice.Identifier{
DevPath: loopDevicePath,
},
DeviceAttributes: blockdevice.DeviceAttribute{
DeviceType: blockdevice.BlockDeviceTypeLoop,
},
},
wantUUID: blockdevice.BlockDevicePrefix + util.Hash(hostName+loopDevicePath),
wantOk: true,
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions ndm-operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ data:
name: path filter
state: true
include: ""
exclude: loop
exclude: "loop,/dev/fd0,/dev/sr0,/dev/ram,/dev/md,/dev/dm-,/dev/rbd,/dev/zd"

---
# Create NDM Service Account
Expand Down Expand Up @@ -125,7 +125,7 @@ spec:
serviceAccountName: openebs-ndm-operator
containers:
- name: node-disk-manager
image: openebs/node-disk-manager-amd64:ci
image: openebs/node-disk-manager:ci
args:
- -v=4
- --feature-gates="GPTBasedUUID"
Expand Down Expand Up @@ -224,7 +224,7 @@ spec:
serviceAccountName: openebs-ndm-operator
containers:
- name: node-disk-operator
image: openebs/node-disk-operator-amd64:ci
image: openebs/node-disk-operator:ci
ports:
- containerPort: 8080
name: liveness
Expand Down
17 changes: 16 additions & 1 deletion pkg/mount/mountutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,23 @@ func getCmdlineFile() string {
return procCmdLine
}

// getDeviceName gets the blockdevice special file name.
// eg: sda, sdb
// if a mapper device is specified the symlink will be evaluated and the
// dm-X name will be returned
func getDeviceName(devPath string) string {
return strings.Replace(devPath, "/dev/", "", 1)
var err error
var deviceName string

deviceName = devPath
// if the device is a dm device
if strings.HasPrefix(devPath, "/dev/mapper") {
deviceName, err = filepath.EvalSymlinks(devPath)
if err != nil {
return ""
}
}
return strings.Replace(deviceName, "/dev/", "", 1)
}

func fileExists(file string) bool {
Expand Down
1 change: 1 addition & 0 deletions pkg/udev/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const (
UDEV_PARTITION_NUMBER = "ID_PART_ENTRY_NUMBER" // udev attribute to get partition number
UDEV_PARTITION_UUID = "ID_PART_ENTRY_UUID" // udev attribute to get partition uuid
UDEV_PARTITION_TYPE = "ID_PART_ENTRY_TYPE" // udev attribute to get partition type
UDEV_DM_UUID = "DM_UUID" // udev attribute to get the device mapper uuid
)

// UdevDiskDetails struct contain different attribute of disk.
Expand Down
16 changes: 13 additions & 3 deletions pkg/udevevent/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (e *event) process(device *libudevwrapper.UdevDevice) {

// fields used for UUID. These fields will be filled always. But used only if the
// GPTBasedUUID feature-gate is enabled.
deviceDetails.DeviceAttributes.DeviceType = device.GetPropertyValue(libudevwrapper.UDEV_DEVTYPE)
udevDeviceType := device.GetPropertyValue(libudevwrapper.UDEV_DEVTYPE)
deviceDetails.DeviceAttributes.WWN = device.GetPropertyValue(libudevwrapper.UDEV_WWN)
deviceDetails.DeviceAttributes.Serial = device.GetPropertyValue(libudevwrapper.UDEV_SERIAL)

Expand All @@ -66,6 +66,8 @@ func (e *event) process(device *libudevwrapper.UdevDevice) {
deviceDetails.PartitionInfo.PartitionEntryUUID = device.GetPropertyValue(libudevwrapper.UDEV_PARTITION_UUID)
deviceDetails.FSInfo.FileSystemUUID = device.GetPropertyValue(libudevwrapper.UDEV_FS_UUID)

deviceDetails.DMInfo.DMUUID = device.GetPropertyValue(libudevwrapper.UDEV_DM_UUID)

// fields used for dependents. dependents cannot be obtained while
// removing the device since sysfs entry will be absent
if action != libudevwrapper.UDEV_ACTION_REMOVE {
Expand All @@ -77,9 +79,17 @@ func (e *event) process(device *libudevwrapper.UdevDevice) {
// TODO if error occurs need to do a scan from the beginning
if err != nil {
klog.Errorf("could not get dependents for %s, %v", deviceDetails.DevPath, err)
} else {
deviceDetails.DependentDevices = dependents
klog.V(4).Infof("Dependents of %s : %+v", deviceDetails.DevPath, dependents)
}
deviceType, err := sysfsDevice.GetDeviceType(udevDeviceType)
if err != nil {
klog.Errorf("could not get device type for %s, falling back to udev reported type: %s", deviceDetails.DevPath, udevDeviceType)
deviceType = udevDeviceType
}
deviceDetails.DependentDevices = dependents
klog.V(4).Infof("Dependents of %s : %+v", deviceDetails.DevPath, dependents)
deviceDetails.DeviceAttributes.DeviceType = deviceType
klog.Infof("Device: %s is of type: %s", deviceDetails.DevPath, deviceDetails.DeviceAttributes.DeviceType)
}
}

Expand Down