github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/sysfs/syspath_test.go (about) 1 /* 2 Copyright 2020 The OpenEBS Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sysfs 18 19 import ( 20 "os" 21 "path/filepath" 22 "testing" 23 24 "github.com/stretchr/testify/assert" 25 26 "github.com/openebs/node-disk-manager/blockdevice" 27 ) 28 29 func TestGetParent(t *testing.T) { 30 tests := map[string]struct { 31 sysfsDevice *Device 32 wantedDeviceName string 33 wantOk bool 34 }{ 35 "[block] given block device is a parent": { 36 sysfsDevice: &Device{ 37 deviceName: "sda", 38 path: "/dev/sda", 39 sysPath: "/sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/", 40 }, 41 wantedDeviceName: "", 42 wantOk: false, 43 }, 44 "[block] given blockdevice is a partition": { 45 sysfsDevice: &Device{ 46 deviceName: "sda4", 47 path: "/dev/sda4", 48 sysPath: "/sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda4/", 49 }, 50 wantedDeviceName: "sda", 51 wantOk: true, 52 }, 53 "[nvme] given blockdevice is a parent": { 54 sysfsDevice: &Device{ 55 deviceName: "nvme0n1", 56 path: "/dev/nvme0n1", 57 sysPath: "/sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1/", 58 }, 59 wantedDeviceName: "", 60 wantOk: false, 61 }, 62 "[nvme] given blockdevice is a partition": { 63 sysfsDevice: &Device{ 64 deviceName: "nvme0n1p1", 65 path: "/dev/nvme0n1p1", 66 sysPath: "/sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1/nvme0n1p1/", 67 }, 68 wantedDeviceName: "nvme0n1", 69 wantOk: true, 70 }, 71 "[nvme-subsystem] given blockdevice is a parent": { 72 sysfsDevice: &Device{ 73 deviceName: "nvme0n1", 74 path: "/dev/nvme0n1", 75 sysPath: "/sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1/", 76 }, 77 wantedDeviceName: "", 78 wantOk: false, 79 }, 80 "[nvme-subsystem] given blockdevice is a partition": { 81 sysfsDevice: &Device{ 82 deviceName: "nvme0n1p1", 83 path: "/dev/nvme0n1p1", 84 sysPath: "/sys/devices/virtual/nvme-subsystem/nvme-subsys0/nvme0n1/nvme0n1p1/", 85 }, 86 wantedDeviceName: "nvme0n1", 87 wantOk: true, 88 }, 89 } 90 for name, test := range tests { 91 t.Run(name, func(t *testing.T) { 92 gotDeviceName, gotOk := test.sysfsDevice.getParent() 93 assert.Equal(t, test.wantedDeviceName, gotDeviceName) 94 assert.Equal(t, test.wantOk, gotOk) 95 }) 96 } 97 } 98 99 func TestGetDeviceSysPath(t *testing.T) { 100 tmp := sysFSDirectoryPath 101 sysFSDirectoryPath = filepath.Join(t.TempDir(), "sys") + "/" 102 t.Cleanup(func() { 103 sysFSDirectoryPath = tmp 104 }) 105 pciPath := "devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda" 106 107 // create top level sys directory 108 os.MkdirAll(sysFSDirectoryPath, 0700) 109 // create block directory 110 os.MkdirAll(filepath.Join(sysFSDirectoryPath, "class", "block"), 0700) 111 // create devices directory 112 os.MkdirAll(filepath.Join(sysFSDirectoryPath, "devices"), 0700) 113 114 // create device directory 115 os.MkdirAll(filepath.Join(sysFSDirectoryPath, pciPath), 0700) 116 os.Symlink(filepath.Join(sysFSDirectoryPath, pciPath), 117 filepath.Join(sysFSDirectoryPath, "class", "block", "sda")) 118 119 tests := map[string]struct { 120 devicePath string 121 want string 122 wantErr bool 123 }{ 124 "devicenode name is used": { 125 devicePath: "/dev/sda", 126 want: filepath.Join(sysFSDirectoryPath, pciPath) + "/", 127 wantErr: false, 128 }, 129 "actual syspath is used": { 130 devicePath: filepath.Join(sysFSDirectoryPath, pciPath) + "/", 131 want: filepath.Join(sysFSDirectoryPath, pciPath) + "/", 132 wantErr: false, 133 }, 134 "class/block path is used": { 135 devicePath: filepath.Join(sysFSDirectoryPath, "class", 136 "block", "sda"), 137 want: filepath.Join(sysFSDirectoryPath, pciPath) + "/", 138 wantErr: false, 139 }, 140 } 141 for name, tt := range tests { 142 t.Run(name, func(t *testing.T) { 143 gotSysPath, err := getDeviceSysPath(tt.devicePath) 144 if err != nil { 145 t.Errorf("error getDeviceSysPath() for %s, error: %v", tt.devicePath, err) 146 } 147 assert.Equal(t, tt.want, gotSysPath) 148 }) 149 } 150 } 151 152 func TestSysFsDeviceGetPartitions(t *testing.T) { 153 tmpDir := t.TempDir() 154 tests := map[string]struct { 155 fileEntries []string 156 sysfsDevice *Device 157 want []string 158 wantOk bool 159 }{ 160 "device is a partition": { 161 fileEntries: nil, 162 sysfsDevice: &Device{ 163 deviceName: "sda1", 164 sysPath: filepath.Join(tmpDir, 165 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/", 166 path: "/dev/sda1", 167 }, 168 want: []string{}, 169 wantOk: true, 170 }, 171 "device is a parent disk, with no partition": { 172 fileEntries: nil, 173 sysfsDevice: &Device{ 174 deviceName: "sda", 175 sysPath: filepath.Join(tmpDir, 176 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 177 path: "/dev/sda", 178 }, 179 want: []string{}, 180 wantOk: true, 181 }, 182 "device is a parent disk, with multiple partitions": { 183 fileEntries: []string{"sdb1", "sdb2", "sdb3"}, 184 sysfsDevice: &Device{ 185 deviceName: "sdb", 186 sysPath: filepath.Join(tmpDir, 187 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/", 188 path: "/dev/sdb", 189 }, 190 want: []string{"sdb1", "sdb2", "sdb3"}, 191 wantOk: true, 192 }, 193 "nvme device, with multiple partitions": { 194 fileEntries: []string{"nvme0n1p1", "nvme0n1p2", "nvme0n1p3"}, 195 sysfsDevice: &Device{ 196 deviceName: "nvme0n1", 197 sysPath: filepath.Join(tmpDir, 198 "sys/devices/pci0000:00/0000:00:0e.0/nvme/nvme0/nvme0n1") + "/", 199 path: "/dev/nvme0n1", 200 }, 201 want: []string{"nvme0n1p1", "nvme0n1p2", "nvme0n1p3"}, 202 wantOk: true, 203 }, 204 } 205 for name, tt := range tests { 206 t.Run(name, func(t *testing.T) { 207 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 208 for _, file := range tt.fileEntries { 209 os.Create(filepath.Join(tt.sysfsDevice.sysPath, file)) 210 } 211 got, gotOk := tt.sysfsDevice.getPartitions() 212 assert.Equal(t, tt.wantOk, gotOk) 213 assert.Equal(t, tt.want, got) 214 os.RemoveAll(tt.sysfsDevice.sysPath) 215 }) 216 } 217 } 218 219 func TestSysFsDeviceGetHolders(t *testing.T) { 220 tmpDir := t.TempDir() 221 tests := map[string]struct { 222 fileEntries []string 223 sysfsDevice *Device 224 createHolderDir bool 225 want []string 226 wantOk bool 227 }{ 228 "device with empty holder directory": { 229 fileEntries: []string{}, 230 sysfsDevice: &Device{ 231 deviceName: "sda", 232 sysPath: filepath.Join(tmpDir, 233 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 234 path: "/dev/sda", 235 }, 236 createHolderDir: true, 237 want: []string{}, 238 wantOk: true, 239 }, 240 "device with holders": { 241 fileEntries: []string{"dm-0", "dm-1"}, 242 sysfsDevice: &Device{ 243 deviceName: "sdb", 244 sysPath: filepath.Join(tmpDir, 245 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/", 246 path: "/dev/sdb", 247 }, 248 createHolderDir: true, 249 want: []string{"dm-0", "dm-1"}, 250 wantOk: true, 251 }, 252 "device without holders and no holder directory": { 253 fileEntries: []string{}, 254 sysfsDevice: &Device{ 255 deviceName: "sdc", 256 sysPath: filepath.Join(tmpDir, 257 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdc") + "/", 258 path: "/dev/sdc", 259 }, 260 createHolderDir: false, 261 want: nil, 262 wantOk: false, 263 }, 264 } 265 for name, tt := range tests { 266 t.Run(name, func(t *testing.T) { 267 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 268 if tt.createHolderDir { 269 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 270 "holders"), 0700) 271 for _, file := range tt.fileEntries { 272 os.Create(filepath.Join(tt.sysfsDevice.sysPath, 273 "holders", file)) 274 } 275 } 276 got, gotOk := tt.sysfsDevice.getHolders() 277 assert.Equal(t, tt.wantOk, gotOk) 278 assert.Equal(t, tt.want, got) 279 os.RemoveAll(tt.sysfsDevice.sysPath) 280 }) 281 } 282 } 283 284 func TestSysFsDeviceGetSlaves(t *testing.T) { 285 tmpDir := t.TempDir() 286 tests := map[string]struct { 287 fileEntries []string 288 sysfsDevice *Device 289 createSlaveDir bool 290 want []string 291 wantOk bool 292 }{ 293 "device with empty slave directory": { 294 fileEntries: []string{}, 295 sysfsDevice: &Device{ 296 deviceName: "dm-0", 297 sysPath: filepath.Join(tmpDir, 298 "sys/devices/virtual/block/dm-0") + "/", 299 path: "/dev/dm-0", 300 }, 301 createSlaveDir: true, 302 want: []string{}, 303 wantOk: true, 304 }, 305 "device with slaves": { 306 fileEntries: []string{"sda", "sdb"}, 307 sysfsDevice: &Device{ 308 deviceName: "dm-1", 309 sysPath: filepath.Join(tmpDir, 310 "sys/devices/virtual/block/dm-1") + "/", 311 path: "/dev/dm-1", 312 }, 313 createSlaveDir: true, 314 want: []string{"sda", "sdb"}, 315 wantOk: true, 316 }, 317 "device without slaves and no slave directory": { 318 fileEntries: []string{}, 319 sysfsDevice: &Device{ 320 deviceName: "dm-2", 321 sysPath: filepath.Join(tmpDir, 322 "sys/devices/virtual/block/dm-2") + "/", 323 path: "/dev/dm-2", 324 }, 325 createSlaveDir: false, 326 want: nil, 327 wantOk: false, 328 }, 329 } 330 for name, tt := range tests { 331 t.Run(name, func(t *testing.T) { 332 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 333 if tt.createSlaveDir { 334 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 335 "slaves"), 0700) 336 for _, file := range tt.fileEntries { 337 os.Create(filepath.Join(tt.sysfsDevice.sysPath, 338 "slaves", file)) 339 } 340 } 341 got, gotOk := tt.sysfsDevice.getSlaves() 342 assert.Equal(t, tt.wantOk, gotOk) 343 assert.Equal(t, tt.want, got) 344 os.RemoveAll(tt.sysfsDevice.sysPath) 345 }) 346 } 347 } 348 349 func TestSysFsDeviceGetLogicalBlockSize(t *testing.T) { 350 tmpDir := t.TempDir() 351 tests := map[string]struct { 352 sysfsDevice *Device 353 createQueueDir bool 354 lbSize string 355 want int64 356 wantErr bool 357 }{ 358 "no queue directory in syspath": { 359 sysfsDevice: &Device{ 360 deviceName: "sda1", 361 sysPath: filepath.Join(tmpDir, 362 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/", 363 path: "/dev/sda1", 364 }, 365 createQueueDir: false, 366 lbSize: "0", 367 want: 0, 368 wantErr: true, 369 }, 370 "valid blocksize present in syspath": { 371 sysfsDevice: &Device{ 372 deviceName: "sda", 373 sysPath: filepath.Join(tmpDir, 374 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 375 path: "/dev/sda", 376 }, 377 createQueueDir: true, 378 lbSize: "512", 379 want: 512, 380 wantErr: false, 381 }, 382 } 383 for name, tt := range tests { 384 t.Run(name, func(t *testing.T) { 385 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 386 if tt.createQueueDir { 387 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 388 "queue"), 0700) 389 file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath, 390 "queue", "logical_block_size")) 391 file.Write([]byte(tt.lbSize)) 392 file.Close() 393 } 394 got, err := tt.sysfsDevice.GetLogicalBlockSize() 395 if (err != nil) != tt.wantErr { 396 t.Errorf("GetLogicalBlockSize() error = %v, wantErr %v", err, tt.wantErr) 397 return 398 } 399 assert.Equal(t, tt.want, got) 400 os.RemoveAll(tt.sysfsDevice.sysPath) 401 }) 402 } 403 } 404 405 func TestSysFsDeviceGetPhysicalBlockSize(t *testing.T) { 406 tmpDir := t.TempDir() 407 tests := map[string]struct { 408 sysfsDevice *Device 409 createQueueDir bool 410 pbSize string 411 want int64 412 wantErr bool 413 }{ 414 "no queue directory in syspath": { 415 sysfsDevice: &Device{ 416 deviceName: "sda1", 417 sysPath: filepath.Join(tmpDir, 418 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/", 419 path: "/dev/sda1", 420 }, 421 createQueueDir: false, 422 pbSize: "0", 423 want: 0, 424 wantErr: true, 425 }, 426 "valid blocksize present in syspath": { 427 sysfsDevice: &Device{ 428 deviceName: "sda", 429 sysPath: filepath.Join(tmpDir, 430 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 431 path: "/dev/sda", 432 }, 433 createQueueDir: true, 434 pbSize: "512", 435 want: 512, 436 wantErr: false, 437 }, 438 } 439 for name, tt := range tests { 440 t.Run(name, func(t *testing.T) { 441 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 442 if tt.createQueueDir { 443 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 444 "queue"), 0700) 445 file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath, 446 "queue", "physical_block_size")) 447 file.Write([]byte(tt.pbSize)) 448 file.Close() 449 } 450 got, err := tt.sysfsDevice.GetPhysicalBlockSize() 451 if (err != nil) != tt.wantErr { 452 t.Errorf("GetPhysicalBlockSize() error = %v, wantErr %v", err, tt.wantErr) 453 return 454 } 455 assert.Equal(t, tt.want, got) 456 os.RemoveAll(tt.sysfsDevice.sysPath) 457 }) 458 } 459 } 460 461 func TestSysFsDeviceGetHardwareSectorSize(t *testing.T) { 462 tmpDir := t.TempDir() 463 tests := map[string]struct { 464 sysfsDevice *Device 465 createQueueDir bool 466 hwSize string 467 want int64 468 wantErr bool 469 }{ 470 "no queue directory in syspath": { 471 sysfsDevice: &Device{ 472 deviceName: "sda1", 473 sysPath: filepath.Join(tmpDir, 474 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/", 475 path: "/dev/sda1", 476 }, 477 createQueueDir: false, 478 hwSize: "0", 479 want: 0, 480 wantErr: true, 481 }, 482 "valid blocksize present in syspath": { 483 sysfsDevice: &Device{ 484 deviceName: "sda", 485 sysPath: filepath.Join(tmpDir, 486 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 487 path: "/dev/sda", 488 }, 489 createQueueDir: true, 490 hwSize: "512", 491 want: 512, 492 wantErr: false, 493 }, 494 } 495 for name, tt := range tests { 496 t.Run(name, func(t *testing.T) { 497 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 498 if tt.createQueueDir { 499 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 500 "queue"), 0700) 501 file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath, 502 "queue", "hw_sector_size")) 503 file.Write([]byte(tt.hwSize)) 504 file.Close() 505 } 506 got, err := tt.sysfsDevice.GetHardwareSectorSize() 507 if (err != nil) != tt.wantErr { 508 t.Errorf("GetHardwareSectorSize() error = %v, wantErr %v", err, tt.wantErr) 509 return 510 } 511 assert.Equal(t, tt.want, got) 512 os.RemoveAll(tt.sysfsDevice.sysPath) 513 }) 514 } 515 } 516 517 func TestSysFsDeviceGetDriveType(t *testing.T) { 518 tmpDir := t.TempDir() 519 tests := map[string]struct { 520 sysfsDevice *Device 521 createQueueDir bool 522 rotational string 523 want string 524 wantErr bool 525 }{ 526 "no queue directory in syspath": { 527 sysfsDevice: &Device{ 528 deviceName: "sda1", 529 sysPath: filepath.Join(tmpDir, 530 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/", 531 path: "/dev/sda1", 532 }, 533 createQueueDir: false, 534 rotational: "0", 535 want: blockdevice.DriveTypeUnknown, 536 wantErr: true, 537 }, 538 "valid rotational value (1) present in syspath": { 539 sysfsDevice: &Device{ 540 deviceName: "sda", 541 sysPath: filepath.Join(tmpDir, 542 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 543 path: "/dev/sda", 544 }, 545 createQueueDir: true, 546 rotational: "1", 547 want: blockdevice.DriveTypeHDD, 548 wantErr: false, 549 }, 550 "valid rotational value (0) present in syspath": { 551 sysfsDevice: &Device{ 552 deviceName: "sdb", 553 sysPath: filepath.Join(tmpDir, 554 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/", 555 path: "/dev/sdb", 556 }, 557 createQueueDir: true, 558 rotational: "0", 559 want: blockdevice.DriveTypeSSD, 560 wantErr: false, 561 }, 562 } 563 for name, tt := range tests { 564 t.Run(name, func(t *testing.T) { 565 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 566 if tt.createQueueDir { 567 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 568 "queue"), 0700) 569 file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath, 570 "queue", "rotational")) 571 file.Write([]byte(tt.rotational)) 572 file.Close() 573 } 574 got, err := tt.sysfsDevice.GetDriveType() 575 if (err != nil) != tt.wantErr { 576 t.Errorf("GetDriveType() error = %v, wantErr %v", err, tt.wantErr) 577 return 578 } 579 assert.Equal(t, tt.want, got) 580 os.RemoveAll(tt.sysfsDevice.sysPath) 581 }) 582 } 583 } 584 585 func TestSysFsDeviceGetCapacityInBytes(t *testing.T) { 586 tmpDir := t.TempDir() 587 tests := map[string]struct { 588 sysfsDevice *Device 589 size string 590 want int64 591 wantErr bool 592 }{ 593 "size 0 in syspath": { 594 sysfsDevice: &Device{ 595 deviceName: "sda1", 596 sysPath: filepath.Join(tmpDir, 597 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda/sda1") + "/", 598 path: "/dev/sda1", 599 }, 600 size: "0", 601 want: 0, 602 wantErr: true, 603 }, 604 "non zero size present in syspath": { 605 sysfsDevice: &Device{ 606 deviceName: "sda", 607 sysPath: filepath.Join(tmpDir, 608 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 609 path: "/dev/sda", 610 }, 611 size: "976773168", 612 want: 500107862016, 613 wantErr: false, 614 }, 615 } 616 for name, tt := range tests { 617 t.Run(name, func(t *testing.T) { 618 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 619 620 file, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath, 621 "size")) 622 file.Write([]byte(tt.size)) 623 file.Close() 624 625 got, err := tt.sysfsDevice.GetCapacityInBytes() 626 if (err != nil) != tt.wantErr { 627 t.Errorf("GetCapacityInBytes() error = %v, wantErr %v", err, tt.wantErr) 628 return 629 } 630 assert.Equal(t, tt.want, got) 631 os.RemoveAll(tt.sysfsDevice.sysPath) 632 }) 633 } 634 } 635 636 func TestSysFsDeviceGetDeviceType(t *testing.T) { 637 tmpDir := t.TempDir() 638 tests := map[string]struct { 639 sysfsDevice *Device 640 // should be either disk / partition 641 devType string 642 643 // used for dm and md devices 644 subDirectoryName string 645 subFileName string 646 subFileContent string 647 648 want string 649 wantErr bool 650 }{ 651 "device is a normal SCSI disk": { 652 sysfsDevice: &Device{ 653 deviceName: "sda", 654 path: "/dev/sda", 655 sysPath: filepath.Join(tmpDir, 656 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 657 }, 658 devType: blockdevice.BlockDeviceTypeDisk, 659 subDirectoryName: "", 660 subFileName: "", 661 subFileContent: "", 662 want: blockdevice.BlockDeviceTypeDisk, 663 wantErr: false, 664 }, 665 "device is SCSI disk partition": { 666 sysfsDevice: &Device{ 667 deviceName: "sdb1", 668 path: "/dev/sdb1", 669 sysPath: filepath.Join(tmpDir, 670 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb/sdb1") + "/", 671 }, 672 devType: blockdevice.BlockDeviceTypePartition, 673 subDirectoryName: "", 674 subFileName: "", 675 subFileContent: "", 676 want: blockdevice.BlockDeviceTypePartition, 677 wantErr: false, 678 }, 679 "device is an LVM": { 680 sysfsDevice: &Device{ 681 deviceName: "dm-0", 682 path: "/dev/dm-0", 683 sysPath: filepath.Join(tmpDir, 684 "sys/devices/virtual/block/dm-0") + "/", 685 }, 686 devType: blockdevice.BlockDeviceTypeDisk, 687 subDirectoryName: "dm", 688 subFileName: "uuid", 689 subFileContent: "LVM-OSlVs5gIXuqSKVPukc2aGPh0AeJw31TJqYIRuRHoodYg9Jwkmyvvk0QNYK4YulHt", 690 want: blockdevice.BlockDeviceTypeLVM, 691 wantErr: false, 692 }, 693 "device is a partition on an LVM device": { 694 sysfsDevice: &Device{ 695 deviceName: "dm-1", 696 path: "/dev/dm-1", 697 sysPath: filepath.Join(tmpDir, 698 "sys/devices/virtual/block/dm-1") + "/", 699 }, 700 devType: blockdevice.BlockDeviceTypeDisk, 701 subDirectoryName: "dm", 702 subFileName: "uuid", 703 subFileContent: "part1-LVM-OSlVs5gIXuqSKVPukc2aGPh0AeJw31TJqYIRuRHoodYg9Jwkmyvvk0QNYK4YulHt", 704 want: blockdevice.BlockDeviceTypePartition, 705 wantErr: false, 706 }, 707 "device is LUKS encrypted device": { 708 sysfsDevice: &Device{ 709 deviceName: "dm-2", 710 path: "/dev/dm-2", 711 sysPath: filepath.Join(tmpDir, 712 "sys/devices/virtual/block/dm-2") + "/", 713 }, 714 devType: blockdevice.BlockDeviceTypeDisk, 715 subDirectoryName: "dm", 716 subFileName: "uuid", 717 subFileContent: "CRYPT-LUKS1-ecc7566437ed483996273d3f50dc5871-backup", 718 want: blockdevice.BlockDeviceTypeCrypt, 719 wantErr: false, 720 }, 721 "device is a partition on LUKS encrypted device": { 722 sysfsDevice: &Device{ 723 deviceName: "dm-3", 724 path: "/dev/dm-3", 725 sysPath: filepath.Join(tmpDir, 726 "sys/devices/virtual/block/dm-3") + "/", 727 }, 728 devType: blockdevice.BlockDeviceTypeDisk, 729 subDirectoryName: "dm", 730 subFileName: "uuid", 731 subFileContent: "part1-CRYPT-LUKS1-ecc7566437ed483996273d3f50dc5871-backup", 732 want: blockdevice.BlockDeviceTypePartition, 733 wantErr: false, 734 }, 735 "device is a loop device": { 736 sysfsDevice: &Device{ 737 deviceName: "loop7", 738 path: "/dev/loop7", 739 sysPath: filepath.Join(tmpDir, 740 "/devices/virtual/block/loop7") + "/", 741 }, 742 devType: blockdevice.BlockDeviceTypeDisk, 743 subDirectoryName: "", 744 subFileName: "", 745 subFileContent: "", 746 want: blockdevice.BlockDeviceTypeLoop, 747 wantErr: false, 748 }, 749 "device is an md device (software raid)": { 750 sysfsDevice: &Device{ 751 deviceName: "md0", 752 path: "/dev/md0", 753 sysPath: filepath.Join(tmpDir, 754 "sys/devices/virtual/block/md0") + "/", 755 }, 756 devType: blockdevice.BlockDeviceTypeDisk, 757 subDirectoryName: "md", 758 subFileName: "level", 759 subFileContent: "raid0", 760 want: "raid0", 761 wantErr: false, 762 }, 763 "device is a dm device with empty uuid": { 764 sysfsDevice: &Device{ 765 deviceName: "dm-16", 766 path: "/dev/dm-16", 767 sysPath: filepath.Join(tmpDir, 768 "sys/devices/virtual/block/dm-16") + "/", 769 }, 770 devType: blockdevice.BlockDeviceTypeDisk, 771 subDirectoryName: "dm", 772 subFileName: "uuid", 773 subFileContent: "\n", 774 want: blockdevice.BlockDeviceTypeDMDevice, 775 wantErr: false, 776 }, 777 } 778 for name, tt := range tests { 779 t.Run(name, func(t *testing.T) { 780 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 781 if len(tt.subDirectoryName) != 0 { 782 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 783 tt.subDirectoryName), 0700) 784 f, _ := os.Create(filepath.Join(tt.sysfsDevice.sysPath, 785 tt.subDirectoryName, tt.subFileName)) 786 f.Write([]byte(tt.subFileContent)) 787 f.Close() 788 } 789 got, err := tt.sysfsDevice.GetDeviceType(tt.devType) 790 if (err != nil) != tt.wantErr { 791 t.Errorf("GetDeviceType() error = %v, wantErr %v", err, tt.wantErr) 792 return 793 } 794 assert.Equal(t, tt.want, got) 795 os.RemoveAll(tt.sysfsDevice.sysPath) 796 }) 797 } 798 } 799 800 func TestSysFsDeviceGetDependents(t *testing.T) { 801 tmpDir := t.TempDir() 802 tests := map[string]struct { 803 partitionEntries []string 804 createHolderDir bool 805 holderEntries []string 806 createSlaveDir bool 807 slaveEntries []string 808 sysfsDevice *Device 809 want blockdevice.DependentBlockDevices 810 wantErr bool 811 }{ 812 "parent disk with no dependents": { 813 partitionEntries: []string{}, 814 createHolderDir: false, 815 holderEntries: []string{}, 816 createSlaveDir: false, 817 slaveEntries: []string{}, 818 sysfsDevice: &Device{ 819 deviceName: "sda", 820 path: "/dev/sda", 821 sysPath: filepath.Join(tmpDir, 822 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda") + "/", 823 }, 824 want: blockdevice.DependentBlockDevices{ 825 Partitions: []string{}, 826 Holders: []string{}, 827 Slaves: []string{}, 828 }, 829 wantErr: false, 830 }, 831 "parent disk with partitions": { 832 partitionEntries: []string{"sdb1", "sdb2"}, 833 createHolderDir: false, 834 holderEntries: []string{}, 835 createSlaveDir: false, 836 slaveEntries: []string{}, 837 sysfsDevice: &Device{ 838 deviceName: "sdb", 839 path: "/dev/sdb", 840 sysPath: filepath.Join(tmpDir, 841 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdb") + "/", 842 }, 843 want: blockdevice.DependentBlockDevices{ 844 Partitions: []string{"/dev/sdb1", "/dev/sdb2"}, 845 Holders: []string{}, 846 Slaves: []string{}, 847 }, 848 wantErr: false, 849 }, 850 "parent disk with holders": { 851 partitionEntries: []string{}, 852 createHolderDir: true, 853 holderEntries: []string{"dm-0", "dm-1"}, 854 createSlaveDir: false, 855 slaveEntries: []string{}, 856 sysfsDevice: &Device{ 857 deviceName: "sdc", 858 path: "/dev/sdc", 859 sysPath: filepath.Join(tmpDir, 860 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdc") + "/", 861 }, 862 want: blockdevice.DependentBlockDevices{ 863 Partitions: []string{}, 864 Holders: []string{"/dev/dm-0", "/dev/dm-1"}, 865 Slaves: []string{}, 866 }, 867 wantErr: false, 868 }, 869 "partition device without holders": { 870 partitionEntries: []string{}, 871 createHolderDir: false, 872 holderEntries: []string{}, 873 createSlaveDir: false, 874 slaveEntries: []string{}, 875 sysfsDevice: &Device{ 876 deviceName: "sdd1", 877 path: "/dev/sdd1", 878 sysPath: filepath.Join(tmpDir, 879 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sdd/sdd1") + "/", 880 }, 881 want: blockdevice.DependentBlockDevices{ 882 Parent: "/dev/sdd", 883 Partitions: []string{}, 884 Holders: []string{}, 885 Slaves: []string{}, 886 }, 887 wantErr: false, 888 }, 889 "partition device with holders": { 890 partitionEntries: []string{}, 891 createHolderDir: true, 892 holderEntries: []string{"dm-0"}, 893 createSlaveDir: false, 894 slaveEntries: []string{}, 895 sysfsDevice: &Device{ 896 deviceName: "sde1", 897 path: "/dev/sde1", 898 sysPath: filepath.Join(tmpDir, 899 "sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sde/sde1") + "/", 900 }, 901 want: blockdevice.DependentBlockDevices{ 902 Parent: "/dev/sde", 903 Partitions: []string{}, 904 Holders: []string{"/dev/dm-0"}, 905 Slaves: []string{}, 906 }, 907 wantErr: false, 908 }, 909 "device with both slaves and holders": { 910 partitionEntries: []string{}, 911 createHolderDir: true, 912 holderEntries: []string{"dm-1", "dm-2"}, 913 createSlaveDir: true, 914 slaveEntries: []string{"sdb", "sdc"}, 915 sysfsDevice: &Device{ 916 deviceName: "dm-0", 917 path: "/dev/dm-0", 918 sysPath: filepath.Join(tmpDir, 919 "sys/devices/virtual/block/dm-0") + "/", 920 }, 921 want: blockdevice.DependentBlockDevices{ 922 Partitions: []string{}, 923 Holders: []string{"/dev/dm-1", "/dev/dm-2"}, 924 Slaves: []string{"/dev/sdb", "/dev/sdc"}, 925 }, 926 wantErr: false, 927 }, 928 } 929 for name, tt := range tests { 930 t.Run(name, func(t *testing.T) { 931 932 os.MkdirAll(tt.sysfsDevice.sysPath, 0700) 933 934 // partition entries 935 for _, file := range tt.partitionEntries { 936 os.Create(filepath.Join(tt.sysfsDevice.sysPath, file)) 937 } 938 939 // holder entries 940 if tt.createHolderDir { 941 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 942 "holders"), 0700) 943 for _, file := range tt.holderEntries { 944 os.Create(filepath.Join(tt.sysfsDevice.sysPath, 945 "holders", file)) 946 } 947 } 948 949 // slave entries 950 if tt.createSlaveDir { 951 os.MkdirAll(filepath.Join(tt.sysfsDevice.sysPath, 952 "slaves"), 0700) 953 for _, file := range tt.slaveEntries { 954 os.Create(filepath.Join(tt.sysfsDevice.sysPath, 955 "slaves", file)) 956 } 957 } 958 959 got, err := tt.sysfsDevice.GetDependents() 960 if (err != nil) != tt.wantErr { 961 t.Errorf("GetDependents() error = %v, wantErr %v", err, tt.wantErr) 962 return 963 } 964 assert.Equal(t, tt.want, got) 965 }) 966 } 967 }