diff --git a/infrastructure/devicepathresolver/id_device_path_resolver.go b/infrastructure/devicepathresolver/id_device_path_resolver.go index ef91fcafc..746c51b40 100644 --- a/infrastructure/devicepathresolver/id_device_path_resolver.go +++ b/infrastructure/devicepathresolver/id_device_path_resolver.go @@ -3,6 +3,7 @@ package devicepathresolver import ( "fmt" "path" + "regexp" "time" boshudev "github.com/cloudfoundry/bosh-agent/platform/udevdevice" @@ -12,24 +13,29 @@ import ( ) type idDevicePathResolver struct { - diskWaitTimeout time.Duration - udev boshudev.UdevDevice - fs boshsys.FileSystem + diskWaitTimeout time.Duration + udev boshudev.UdevDevice + fs boshsys.FileSystem + stripVolumeRegex string + stripVolumeCompiled *regexp.Regexp } func NewIDDevicePathResolver( diskWaitTimeout time.Duration, udev boshudev.UdevDevice, fs boshsys.FileSystem, + stripVolumeRegex string, ) DevicePathResolver { - return idDevicePathResolver{ - diskWaitTimeout: diskWaitTimeout, - udev: udev, - fs: fs, + return &idDevicePathResolver{ + diskWaitTimeout: diskWaitTimeout, + udev: udev, + fs: fs, + stripVolumeRegex: stripVolumeRegex, + stripVolumeCompiled: nil, } } -func (idpr idDevicePathResolver) GetRealDevicePath(diskSettings boshsettings.DiskSettings) (string, bool, error) { +func (idpr *idDevicePathResolver) GetRealDevicePath(diskSettings boshsettings.DiskSettings) (string, bool, error) { if diskSettings.ID == "" { return "", false, bosherr.Errorf("Disk ID is not set") } @@ -54,7 +60,12 @@ func (idpr idDevicePathResolver) GetRealDevicePath(diskSettings boshsettings.Dis var realPath string diskID := diskSettings.ID - deviceGlobPattern := fmt.Sprintf("*%s", diskID) + strippedDiskID, err := idpr.stripVolumeIfRequired(diskID) + if err != nil { + return "", false, err + } + + deviceGlobPattern := fmt.Sprintf("*%s", strippedDiskID) deviceIDPathGlobPattern := path.Join("/", "dev", "disk", "by-id", deviceGlobPattern) for !found { @@ -87,3 +98,18 @@ func (idpr idDevicePathResolver) GetRealDevicePath(diskSettings boshsettings.Dis return realPath, false, nil } + +func (idpr *idDevicePathResolver) stripVolumeIfRequired(diskID string) (string, error) { + if idpr.stripVolumeRegex == "" { + return diskID, nil + } + + if idpr.stripVolumeCompiled == nil { + var err error + idpr.stripVolumeCompiled, err = regexp.Compile(idpr.stripVolumeRegex) + if err != nil { + return "", bosherr.WrapError(err, "Compiling stripVolumeRegex") + } + } + return idpr.stripVolumeCompiled.ReplaceAllLiteralString(diskID, ""), nil +} diff --git a/infrastructure/devicepathresolver/id_device_path_resolver_test.go b/infrastructure/devicepathresolver/id_device_path_resolver_test.go index 543d62fdc..c76ae32c6 100644 --- a/infrastructure/devicepathresolver/id_device_path_resolver_test.go +++ b/infrastructure/devicepathresolver/id_device_path_resolver_test.go @@ -18,10 +18,11 @@ import ( var _ = Describe("IDDevicePathResolver", func() { var ( - fs *fakesys.FakeFileSystem - udev *fakeudev.FakeUdevDevice - diskSettings boshsettings.DiskSettings - pathResolver DevicePathResolver + fs *fakesys.FakeFileSystem + udev *fakeudev.FakeUdevDevice + diskSettings boshsettings.DiskSettings + pathResolver DevicePathResolver + stripVolumeRegex string ) BeforeEach(func() { @@ -33,7 +34,7 @@ var _ = Describe("IDDevicePathResolver", func() { }) JustBeforeEach(func() { - pathResolver = NewIDDevicePathResolver(500*time.Millisecond, udev, fs) + pathResolver = NewIDDevicePathResolver(500*time.Millisecond, udev, fs, stripVolumeRegex) }) Describe("GetRealDevicePath", func() { @@ -204,5 +205,36 @@ var _ = Describe("IDDevicePathResolver", func() { Expect(timeout).To(BeFalse()) }) }) + + Context("when stripVolumeRegex option is used to remove mismatched disk ID prefix", func() { + BeforeEach(func() { + diskSettings = boshsettings.DiskSettings{ + ID: "vol-fake-disk-id-include-longname", + } + stripVolumeRegex = "^vol-" + err := fs.MkdirAll("/dev/fake-device-path", os.FileMode(0750)) + Expect(err).ToNot(HaveOccurred()) + + err = fs.Symlink("/dev/fake-device-path", "/dev/intermediate/fake-device-path") + Expect(err).ToNot(HaveOccurred()) + + err = fs.Symlink("/dev/intermediate/fake-device-path", "/dev/disk/by-id/virtio-fake-disk-id-include-longname") + Expect(err).ToNot(HaveOccurred()) + + fs.SetGlob("/dev/disk/by-id/*fake-disk-id-include-longname", []string{"/dev/disk/by-id/virtio-fake-disk-id-include-longname"}) + }) + + It("removes prefix and returns fully resolved path", func() { + path, timeout, err := pathResolver.GetRealDevicePath(diskSettings) + Expect(err).ToNot(HaveOccurred()) + + devicePath, err := filepath.Abs("/dev/fake-device-path") + Expect(err).ToNot(HaveOccurred()) + + Expect(path).To(Equal(devicePath)) + Expect(timeout).To(BeFalse()) + }) + }) + }) }) diff --git a/platform/linux_platform.go b/platform/linux_platform.go index 9fe718280..be09d741c 100644 --- a/platform/linux_platform.go +++ b/platform/linux_platform.go @@ -78,6 +78,10 @@ type LinuxOptions struct { // Strategy for resolving ephemeral & persistent disk partitioners; // possible values: parted, "" (default is sfdisk if disk < 2TB, parted otherwise) PartitionerType string + + // Regular expression specifying what part of volume ID to strip. + // possible values: valid RE2 regex e.g. "^vol-", "" (default is not to strip) + StripVolumeRegex string } type linux struct { diff --git a/platform/provider.go b/platform/provider.go index f74af2449..faba74d2b 100644 --- a/platform/provider.go +++ b/platform/provider.go @@ -115,7 +115,7 @@ func NewProvider(logger boshlog.Logger, dirProvider boshdirs.Provider, statsColl switch options.Linux.DevicePathResolutionType { case "virtio": udev := boshudev.NewConcreteUdevDevice(runner, logger) - idDevicePathResolver := devicepathresolver.NewIDDevicePathResolver(500*time.Millisecond, udev, fs) + idDevicePathResolver := devicepathresolver.NewIDDevicePathResolver(500*time.Millisecond, udev, fs, options.Linux.StripVolumeRegex) mappedDevicePathResolver := devicepathresolver.NewMappedDevicePathResolver(30000*time.Millisecond, fs) devicePathResolver = devicepathresolver.NewVirtioDevicePathResolver(idDevicePathResolver, mappedDevicePathResolver, logger) case "scsi":