Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/cloudstack-csi-driver/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ RUN apk add --no-cache \
# blkid, mount and umount are required by k8s.io/mount-utils \
blkid \
mount \
umount
umount \
# Provides udevadm for device path detection
udev

COPY ./bin/cloudstack-csi-driver /cloudstack-csi-driver
ENTRYPOINT ["/cloudstack-csi-driver"]
6 changes: 6 additions & 0 deletions deploy/k8s/node-daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ spec:
mountPath: /dev
- name: cloud-init-dir
mountPath: /run/cloud-init/
- name: sys-dir
mountPath: /sys
# Comment the above 2 lines and uncomment the next 2 lines for Ignition support
# - name: ignition-dir
# mountPath: /run/metadata
Expand Down Expand Up @@ -177,6 +179,10 @@ spec:
hostPath:
path: /run/cloud-init/
type: Directory
- name: sys-dir
hostPath:
path: /sys
type: Directory
# Comment the above 4 lines and uncomment the next 4 lines for Ignition support
# - name: ignition-dir
# hostPath:
Expand Down
161 changes: 158 additions & 3 deletions pkg/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ func (m *mounter) GetBlockSizeBytes(devicePath string) (int64, error) {

func (m *mounter) GetDevicePath(ctx context.Context, volumeID string) (string, error) {
backoff := wait.Backoff{
Duration: 1 * time.Second,
Factor: 1.1,
Steps: 15,
Duration: 2 * time.Second,
Factor: 1.5,
Steps: 20,
}

var devicePath string
Expand Down Expand Up @@ -111,6 +111,24 @@ func (m *mounter) GetDevicePath(ctx context.Context, volumeID string) (string, e
}

func (m *mounter) getDevicePathBySerialID(volumeID string) (string, error) {
// First try XenServer device paths
xenDevicePath, err := m.getDevicePathForXenServer(volumeID)
if err != nil {
fmt.Printf("Failed to get VMware device path: %v\n", err)
}
if xenDevicePath != "" {
return xenDevicePath, nil
}

// Try VMware device paths
vmwareDevicePath, err := m.getDevicePathForVMware(volumeID)
if err != nil {
fmt.Printf("Failed to get VMware device path: %v\n", err)
}
if vmwareDevicePath != "" {
return vmwareDevicePath, nil
}
// Fall back to standard device paths (for KVM)
sourcePathPrefixes := []string{"virtio-", "scsi-", "scsi-0QEMU_QEMU_HARDDISK_"}
serial := diskUUIDToSerial(volumeID)
for _, prefix := range sourcePathPrefixes {
Expand All @@ -120,13 +138,150 @@ func (m *mounter) getDevicePathBySerialID(volumeID string) (string, error) {
return source, nil
}
if !os.IsNotExist(err) {
fmt.Printf("Not found: %s\n", err.Error())
return "", err
}
}

return "", nil
}

func (m *mounter) getDevicePathForXenServer(volumeID string) (string, error) {
for i := 'b'; i <= 'z'; i++ {
devicePath := fmt.Sprintf("/dev/xvd%c", i)
fmt.Printf("Checking XenServer device path: %s\n", devicePath)

if _, err := os.Stat(devicePath); err == nil {
isBlock, err := m.IsBlockDevice(devicePath)
if err == nil && isBlock {
if m.verifyXenServerDevice(devicePath, volumeID) {
fmt.Printf("Found and verified XenServer device: %s\n", devicePath)
return devicePath, nil
}
}
}
}
return "", fmt.Errorf("device not found for volume %s", volumeID)
}

func (m *mounter) verifyXenServerDevice(devicePath string, volumeID string) bool {
size, err := m.GetBlockSizeBytes(devicePath)
if err != nil {
fmt.Printf("Failed to get device size: %v\n", err)
return false
}
fmt.Printf("Device size: %d bytes\n", size)

mounted, err := m.isDeviceMounted(devicePath)
if err != nil {
fmt.Printf("Failed to check if device is mounted: %v\n", err)
return false
}
if mounted {
fmt.Printf("Device is already mounted: %s\n", devicePath)
return false
}

props, err := m.getDeviceProperties(devicePath)
if err != nil {
fmt.Printf("Failed to get device properties: %v\n", err)
return false
}
fmt.Printf("Device properties: %v\n", props)

return true
}

func (m *mounter) getDevicePathForVMware(volumeID string) (string, error) {
// Loop through /dev/sdb to /dev/sdz (/dev/sda -> the root disk)
for i := 'b'; i <= 'z'; i++ {
devicePath := fmt.Sprintf("/dev/sd%c", i)
fmt.Printf("Checking VMware device path: %s\n", devicePath)

if _, err := os.Stat(devicePath); err == nil {
isBlock, err := m.IsBlockDevice(devicePath)
if err == nil && isBlock {
// Use the same verification as for XenServer
if m.verifyVMwareDevice(devicePath, volumeID) {
fmt.Printf("Found and verified VMware device: %s\n", devicePath)
return devicePath, nil
}
}
}
}
return "", fmt.Errorf("device not found for volume %s", volumeID)
}

func (m *mounter) verifyVMwareDevice(devicePath string, volumeID string) bool {
size, err := m.GetBlockSizeBytes(devicePath)
if err != nil {
fmt.Printf("Failed to get device size: %v\n", err)
return false
}
fmt.Printf("Device size: %d bytes\n", size)

mounted, err := m.isDeviceMounted(devicePath)
if err != nil {
fmt.Printf("Failed to check if device is mounted: %v\n", err)
return false
}
if mounted {
fmt.Printf("Device is already mounted: %s\n", devicePath)
return false
}

props, err := m.getDeviceProperties(devicePath)
if err != nil {
fmt.Printf("Failed to get device properties: %v\n", err)
return false
}
fmt.Printf("Device properties: %v\n", props)

return true
}

func (m *mounter) isDeviceMounted(devicePath string) (bool, error) {
output, err := m.Exec.Command("grep", devicePath, "/proc/mounts").Output()
if err != nil {
if strings.Contains(err.Error(), "exit status 1") {
return false, nil
}
return false, err
}
return len(output) > 0, nil
}

func (m *mounter) isDeviceInUse(devicePath string) (bool, error) {
output, err := m.Exec.Command("lsof", devicePath).Output()
if err != nil {
if strings.Contains(err.Error(), "exit status 1") {
return false, nil
}
return false, err
}
return len(output) > 0, nil
}

func (m *mounter) getDeviceProperties(devicePath string) (map[string]string, error) {
output, err := m.Exec.Command("udevadm", "info", "--query=property", devicePath).Output()
if err != nil {
return nil, err
}

props := make(map[string]string)
for _, line := range strings.Split(string(output), "\n") {
if line == "" {
continue
}
parts := strings.Split(line, "=")
if len(parts) == 2 {
props[parts[0]] = parts[1]
}
}

return props, nil
}

func (m *mounter) probeVolume(ctx context.Context) {
logger := klog.FromContext(ctx)
logger.V(2).Info("Scanning SCSI host")
Expand Down
Loading