diff --git a/api/v1alpha1/localvolumeset_types.go b/api/v1alpha1/localvolumeset_types.go index 31eb97b30..b53c5d75e 100644 --- a/api/v1alpha1/localvolumeset_types.go +++ b/api/v1alpha1/localvolumeset_types.go @@ -75,6 +75,10 @@ type DeviceInclusionSpec struct { // to contain at least one of these strings. // +optional Vendors []string `json:"vendors,omitempty"` + // Serials is a list of device serial IDs. If not empty, the device's serial ID as outputted + // by lsblk needs to have at least one of these strings. + // +optional + Serials []string `json:"serials,omitempty"` } // DeviceExclusionSpec holds the exclusion filter spec diff --git a/pkg/diskmaker/controllers/lvset/matcher.go b/pkg/diskmaker/controllers/lvset/matcher.go index 48fb8cc39..399314153 100644 --- a/pkg/diskmaker/controllers/lvset/matcher.go +++ b/pkg/diskmaker/controllers/lvset/matcher.go @@ -30,6 +30,7 @@ const ( inMechanicalPropertyList = "inMechanicalPropertyList" inVendorList = "inVendorList" inModelList = "inModelList" + inSerialList = "inSerialList" // exclusion matcher names: notInDeviceNameFilter = "notInDeviceNameFilter" @@ -200,6 +201,30 @@ var matcherMap = map[string]func(internal.BlockDevice, *localv1alpha1.DeviceIncl } return matched, nil }, + inSerialList: func(dev internal.BlockDevice, spec *localv1alpha1.DeviceInclusionSpec) (bool, error) { + if spec == nil { + return true, nil + } + if len(spec.Serials) == 0 { + return true, nil + } + deviceSerial := strings.TrimSpace(dev.Serial) + hasConfiguredSerial := false + for _, serial := range spec.Serials { + serial = strings.TrimSpace(serial) + if serial == "" { + continue + } + hasConfiguredSerial = true + if strings.EqualFold(deviceSerial, serial) { + return true, nil + } + } + if !hasConfiguredSerial { + return true, nil + } + return false, nil + }, } // functions that exclude devices by *localv1alpha1.DeviceExclusionSpec diff --git a/pkg/diskmaker/controllers/lvset/matcher_test.go b/pkg/diskmaker/controllers/lvset/matcher_test.go index e0da1bc02..1f0783f14 100644 --- a/pkg/diskmaker/controllers/lvset/matcher_test.go +++ b/pkg/diskmaker/controllers/lvset/matcher_test.go @@ -611,6 +611,35 @@ func TestInModelList(t *testing.T) { assertAll(t, results) } +func TestInSerialList(t *testing.T) { + matcherMap := matcherMap + matcher := inSerialList + results := []knownMatcherResult{ + // exact match + { + matcherMap: matcherMap, matcher: matcher, + dev: internal.BlockDevice{Serial: "12345678ABCD"}, + spec: &localv1alpha1.DeviceInclusionSpec{Serials: []string{"12345678ABCD"}}, + expectMatch: true, expectErr: false, + }, + // empty list + { + matcherMap: matcherMap, matcher: matcher, + dev: internal.BlockDevice{Serial: "12345678ABCD"}, + spec: &localv1alpha1.DeviceInclusionSpec{Serials: []string{" "}}, + expectMatch: true, expectErr: false, + }, + // mismatch + { + matcherMap: matcherMap, matcher: matcher, + dev: internal.BlockDevice{Serial: "12345678ABCD"}, + spec: &localv1alpha1.DeviceInclusionSpec{Serials: []string{"ABCDEF012"}}, + expectMatch: false, expectErr: false, + }, + } + assertAll(t, results) +} + func TestNotInDeviceNameFilter(t *testing.T) { em := exclusionMap matcher := notInDeviceNameFilter diff --git a/pkg/internal/diskutil_test.go b/pkg/internal/diskutil_test.go index 00d69e66d..b1e0da608 100644 --- a/pkg/internal/diskutil_test.go +++ b/pkg/internal/diskutil_test.go @@ -17,6 +17,8 @@ NAME="sda1" KNAME="sda1" ROTA="1" TYPE="part" SIZE="62913494528" MODEL="" VENDOR ` lsblkOutput2 = `NAME="sdc" KNAME="sdc" ROTA="1" TYPE="disk" SIZE="62914560000" MODEL="VBOX HARDDISK" VENDOR="ATA" RO="0" RM="1" STATE="running" SERIAL="" NAME="sdc3" KNAME="sdc3" ROTA="1" TYPE="part" SIZE="62913494528" MODEL="" VENDOR="" RO="0" RM="1" STATE="" SERIAL="" +` + lsblkOutput3 = `NAME="nvme0n1" ROTA="0" TYPE="disk" SIZE="1234567890" MODEL="Micron MTFDKBA512TFH" VENDOR="" RO="0" RM="0" STATE="live" KNAME="nvme0n1" SERIAL="221035A4EFA5" PARTLABEL="" ` blkIDOutput1 = `/dev/sdc: TYPE="ext4" /dev/sdc3: TYPE="ext2" @@ -150,6 +152,29 @@ func TestListBlockDevices(t *testing.T) { }, }, }, + { + label: "Case 5: block devices with serial", + lsblkOutput: lsblkOutput3, + blkIDOutput: "", + totalBlockDevices: 1, + totalBadRows: 0, + expected: []BlockDevice{ + { + Name: "nvme0n1", + FSType: "", + Type: "disk", + Size: "1234567890", + Model: "Micron MTFDKBA512TFH", + Vendor: "", + Serial: "221035A4EFA5", + Rotational: "0", + ReadOnly: "0", + Removable: "0", + State: "live", + PartLabel: "", + }, + }, + }, } for _, tc := range testcases {