diff --git a/packages/orchestrator/pkg/sandbox/cgroup/manager.go b/packages/orchestrator/pkg/sandbox/cgroup/manager.go index 8e272e291b..5f7f66df40 100644 --- a/packages/orchestrator/pkg/sandbox/cgroup/manager.go +++ b/packages/orchestrator/pkg/sandbox/cgroup/manager.go @@ -428,8 +428,7 @@ func (m *managerImpl) Destroy(ctx context.Context, cgroupName string) error { return err } - // Remove kills any remaining processes internally and returns nil once the - // cgroup is gone, so it is the authoritative teardown signal. + // Remove kills any remaining processes and deletes the cgroup. return handle.Remove(ctx) } diff --git a/packages/orchestrator/pkg/sandbox/nbd/path_direct.go b/packages/orchestrator/pkg/sandbox/nbd/path_direct.go index 5d53540fe6..8808d05114 100644 --- a/packages/orchestrator/pkg/sandbox/nbd/path_direct.go +++ b/packages/orchestrator/pkg/sandbox/nbd/path_direct.go @@ -331,6 +331,10 @@ func disconnectNBDWithTimeout(ctx context.Context, deviceIndex uint32, timeout t return nil } +func DisconnectDevice(ctx context.Context, deviceIndex DeviceSlot) error { + return disconnectNBDWithTimeout(ctx, deviceIndex, disconnectTimeout) +} + func closeSocketPairs(socksClient []*os.File, socksServer []io.Closer) error { var errs []error for _, sock := range socksClient { diff --git a/packages/orchestrator/pkg/sandbox/nbd/pool.go b/packages/orchestrator/pkg/sandbox/nbd/pool.go index 112e8969f4..ba6e3e1198 100644 --- a/packages/orchestrator/pkg/sandbox/nbd/pool.go +++ b/packages/orchestrator/pkg/sandbox/nbd/pool.go @@ -25,6 +25,7 @@ import ( const ( waitOnNBDError = 50 * time.Millisecond devicePoolCloseReleaseTimeout = 10 * time.Minute + sysBlockDir = "/sys/block" ) var ( @@ -126,6 +127,43 @@ func getMaxDevices() (uint, error) { return uint(maxDevices), nil } +func ConnectedDevices() ([]DeviceSlot, error) { + maxDevices, err := getMaxDevices() + if err != nil { + return nil, err + } + + devices := make([]DeviceSlot, 0) + for slot := DeviceSlot(0); slot < DeviceSlot(maxDevices); slot++ { + connected, err := isDeviceConnectedIn(sysBlockDir, slot) + if err != nil { + return nil, err + } + if connected { + devices = append(devices, slot) + } + } + + return devices, nil +} + +func IsDeviceConnected(slot DeviceSlot) (bool, error) { + return isDeviceConnectedIn(sysBlockDir, slot) +} + +func isDeviceConnectedIn(blockDir string, slot DeviceSlot) (bool, error) { + pidFile := fmt.Sprintf("%s/nbd%d/pid", blockDir, slot) + _, err := os.Stat(pidFile) + if err == nil { + return true, nil + } + if !os.IsNotExist(err) { + return false, fmt.Errorf("failed to stat pid file: %w", err) + } + + return false, nil +} + func (d *DevicePool) Populate(ctx context.Context) { defer close(d.slots) @@ -178,20 +216,15 @@ func (d *DevicePool) Populate(ctx context.Context) { // https://superuser.com/questions/919895/how-to-get-a-list-of-connected-nbd-devices-on-ubuntu // https://github.com/NetworkBlockDevice/nbd/blob/17043b068f4323078637314258158aebbfff0a6c/nbd-client.c#L254 func (d *DevicePool) isDeviceFree(slot DeviceSlot) (bool, error) { - // Continue only if the file doesn't exist. - pidFile := fmt.Sprintf("/sys/block/nbd%d/pid", slot) - - _, err := os.Stat(pidFile) - if err == nil { + connected, err := isDeviceConnectedIn(sysBlockDir, slot) + if err != nil { + return false, err + } + if connected { // File is present, therefore the device is in use. return false, nil } - if !os.IsNotExist(err) { - // Some other error occurred. - return false, fmt.Errorf("failed to stat pid file: %w", err) - } - sizeFile := fmt.Sprintf("/sys/block/nbd%d/size", slot) data, err := os.ReadFile(sizeFile) diff --git a/packages/orchestrator/pkg/sandbox/nbd/reclaim_test.go b/packages/orchestrator/pkg/sandbox/nbd/reclaim_test.go new file mode 100644 index 0000000000..1d03c73666 --- /dev/null +++ b/packages/orchestrator/pkg/sandbox/nbd/reclaim_test.go @@ -0,0 +1,28 @@ +//go:build linux + +package nbd + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIsDeviceConnectedIn(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + require.NoError(t, os.Mkdir(filepath.Join(dir, "nbd0"), 0o700)) + require.NoError(t, os.Mkdir(filepath.Join(dir, "nbd1"), 0o700)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "nbd0", "pid"), []byte("123"), 0o600)) + + connected, err := isDeviceConnectedIn(dir, 0) + require.NoError(t, err) + require.True(t, connected) + + connected, err = isDeviceConnectedIn(dir, 1) + require.NoError(t, err) + require.False(t, connected) +}