Skip to content

Commit 9a7a654

Browse files
authored
Merge pull request #7 from shapeblue/vmware-xen-support
Support for identifying device paths for attached volumes on Vmware and XenServer
2 parents 24c75e8 + 8f1795c commit 9a7a654

File tree

3 files changed

+167
-4
lines changed

3 files changed

+167
-4
lines changed

cmd/cloudstack-csi-driver/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ RUN apk add --no-cache \
1414
# blkid, mount and umount are required by k8s.io/mount-utils \
1515
blkid \
1616
mount \
17-
umount
17+
umount \
18+
# Provides udevadm for device path detection \
19+
udev
1820

1921
COPY ./bin/cloudstack-csi-driver /cloudstack-csi-driver
2022
ENTRYPOINT ["/cloudstack-csi-driver"]

deploy/k8s/node-daemonset.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ spec:
6464
mountPath: /dev
6565
- name: cloud-init-dir
6666
mountPath: /run/cloud-init/
67+
- name: sys-dir
68+
mountPath: /sys
6769
# Comment the above 2 lines and uncomment the next 2 lines for Ignition support
6870
# - name: ignition-dir
6971
# mountPath: /run/metadata
@@ -177,6 +179,10 @@ spec:
177179
hostPath:
178180
path: /run/cloud-init/
179181
type: Directory
182+
- name: sys-dir
183+
hostPath:
184+
path: /sys
185+
type: Directory
180186
# Comment the above 4 lines and uncomment the next 4 lines for Ignition support
181187
# - name: ignition-dir
182188
# hostPath:

pkg/mount/mount.go

Lines changed: 158 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ func (m *mounter) GetBlockSizeBytes(devicePath string) (int64, error) {
8080

8181
func (m *mounter) GetDevicePath(ctx context.Context, volumeID string) (string, error) {
8282
backoff := wait.Backoff{
83-
Duration: 1 * time.Second,
84-
Factor: 1.1,
85-
Steps: 15,
83+
Duration: 2 * time.Second,
84+
Factor: 1.5,
85+
Steps: 20,
8686
}
8787

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

113113
func (m *mounter) getDevicePathBySerialID(volumeID string) (string, error) {
114+
// First try XenServer device paths
115+
xenDevicePath, err := m.getDevicePathForXenServer(volumeID)
116+
if err != nil {
117+
fmt.Printf("Failed to get XenServer device path: %v\n", err)
118+
}
119+
if xenDevicePath != "" {
120+
return xenDevicePath, nil
121+
}
122+
123+
// Try VMware device paths
124+
vmwareDevicePath, err := m.getDevicePathForVMware(volumeID)
125+
if err != nil {
126+
fmt.Printf("Failed to get VMware device path: %v\n", err)
127+
}
128+
if vmwareDevicePath != "" {
129+
return vmwareDevicePath, nil
130+
}
131+
// Fall back to standard device paths (for KVM)
114132
sourcePathPrefixes := []string{"virtio-", "scsi-", "scsi-0QEMU_QEMU_HARDDISK_"}
115133
serial := diskUUIDToSerial(volumeID)
116134
for _, prefix := range sourcePathPrefixes {
@@ -120,13 +138,150 @@ func (m *mounter) getDevicePathBySerialID(volumeID string) (string, error) {
120138
return source, nil
121139
}
122140
if !os.IsNotExist(err) {
141+
fmt.Printf("Not found: %s\n", err.Error())
123142
return "", err
124143
}
125144
}
126145

127146
return "", nil
128147
}
129148

149+
func (m *mounter) getDevicePathForXenServer(volumeID string) (string, error) {
150+
for i := 'b'; i <= 'z'; i++ {
151+
devicePath := fmt.Sprintf("/dev/xvd%c", i)
152+
fmt.Printf("Checking XenServer device path: %s\n", devicePath)
153+
154+
if _, err := os.Stat(devicePath); err == nil {
155+
isBlock, err := m.IsBlockDevice(devicePath)
156+
if err == nil && isBlock {
157+
if m.verifyXenServerDevice(devicePath, volumeID) {
158+
fmt.Printf("Found and verified XenServer device: %s\n", devicePath)
159+
return devicePath, nil
160+
}
161+
}
162+
}
163+
}
164+
return "", fmt.Errorf("device not found for volume %s", volumeID)
165+
}
166+
167+
func (m *mounter) verifyXenServerDevice(devicePath string, volumeID string) bool {
168+
size, err := m.GetBlockSizeBytes(devicePath)
169+
if err != nil {
170+
fmt.Printf("Failed to get device size: %v\n", err)
171+
return false
172+
}
173+
fmt.Printf("Device size: %d bytes\n", size)
174+
175+
mounted, err := m.isDeviceMounted(devicePath)
176+
if err != nil {
177+
fmt.Printf("Failed to check if device is mounted: %v\n", err)
178+
return false
179+
}
180+
if mounted {
181+
fmt.Printf("Device is already mounted: %s\n", devicePath)
182+
return false
183+
}
184+
185+
props, err := m.getDeviceProperties(devicePath)
186+
if err != nil {
187+
fmt.Printf("Failed to get device properties: %v\n", err)
188+
return false
189+
}
190+
fmt.Printf("Device properties: %v\n", props)
191+
192+
return true
193+
}
194+
195+
func (m *mounter) getDevicePathForVMware(volumeID string) (string, error) {
196+
// Loop through /dev/sdb to /dev/sdz (/dev/sda -> the root disk)
197+
for i := 'b'; i <= 'z'; i++ {
198+
devicePath := fmt.Sprintf("/dev/sd%c", i)
199+
fmt.Printf("Checking VMware device path: %s\n", devicePath)
200+
201+
if _, err := os.Stat(devicePath); err == nil {
202+
isBlock, err := m.IsBlockDevice(devicePath)
203+
if err == nil && isBlock {
204+
// Use the same verification as for XenServer
205+
if m.verifyVMwareDevice(devicePath, volumeID) {
206+
fmt.Printf("Found and verified VMware device: %s\n", devicePath)
207+
return devicePath, nil
208+
}
209+
}
210+
}
211+
}
212+
return "", fmt.Errorf("device not found for volume %s", volumeID)
213+
}
214+
215+
func (m *mounter) verifyVMwareDevice(devicePath string, volumeID string) bool {
216+
size, err := m.GetBlockSizeBytes(devicePath)
217+
if err != nil {
218+
fmt.Printf("Failed to get device size: %v\n", err)
219+
return false
220+
}
221+
fmt.Printf("Device size: %d bytes\n", size)
222+
223+
mounted, err := m.isDeviceMounted(devicePath)
224+
if err != nil {
225+
fmt.Printf("Failed to check if device is mounted: %v\n", err)
226+
return false
227+
}
228+
if mounted {
229+
fmt.Printf("Device is already mounted: %s\n", devicePath)
230+
return false
231+
}
232+
233+
props, err := m.getDeviceProperties(devicePath)
234+
if err != nil {
235+
fmt.Printf("Failed to get device properties: %v\n", err)
236+
return false
237+
}
238+
fmt.Printf("Device properties: %v\n", props)
239+
240+
return true
241+
}
242+
243+
func (m *mounter) isDeviceMounted(devicePath string) (bool, error) {
244+
output, err := m.Exec.Command("grep", devicePath, "/proc/mounts").Output()
245+
if err != nil {
246+
if strings.Contains(err.Error(), "exit status 1") {
247+
return false, nil
248+
}
249+
return false, err
250+
}
251+
return len(output) > 0, nil
252+
}
253+
254+
func (m *mounter) isDeviceInUse(devicePath string) (bool, error) {
255+
output, err := m.Exec.Command("lsof", devicePath).Output()
256+
if err != nil {
257+
if strings.Contains(err.Error(), "exit status 1") {
258+
return false, nil
259+
}
260+
return false, err
261+
}
262+
return len(output) > 0, nil
263+
}
264+
265+
func (m *mounter) getDeviceProperties(devicePath string) (map[string]string, error) {
266+
output, err := m.Exec.Command("udevadm", "info", "--query=property", devicePath).Output()
267+
if err != nil {
268+
return nil, err
269+
}
270+
271+
props := make(map[string]string)
272+
for _, line := range strings.Split(string(output), "\n") {
273+
if line == "" {
274+
continue
275+
}
276+
parts := strings.Split(line, "=")
277+
if len(parts) == 2 {
278+
props[parts[0]] = parts[1]
279+
}
280+
}
281+
282+
return props, nil
283+
}
284+
130285
func (m *mounter) probeVolume(ctx context.Context) {
131286
logger := klog.FromContext(ctx)
132287
logger.V(2).Info("Scanning SCSI host")

0 commit comments

Comments
 (0)