[lxc-devel] [lxd/master] Storage package
tomponline on Github
lxc-bot at linuxcontainers.org
Mon Oct 7 15:08:38 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191007/4751d9b0/attachment-0001.bin>
-------------- next part --------------
From 2d1839e328d4785bbf0940312328ffcdc1513ad2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 4 Oct 2019 19:02:38 -0400
Subject: [PATCH 1/5] lxd/storage: Add new interfaces
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/migration/interfaces.go | 12 ++
lxd/storage/backend_lxd.go | 201 +++++++++++++++++++++++++++++++
lxd/storage/backend_mock.go | 192 +++++++++++++++++++++++++++++
lxd/storage/drivers/driver.go | 45 +++++++
lxd/storage/drivers/errors.go | 14 +++
lxd/storage/drivers/interface.go | 50 ++++++++
lxd/storage/errors.go | 11 ++
lxd/storage/interfaces.go | 95 +++++++++++++++
lxd/storage/load.go | 85 +++++++++++++
9 files changed, 705 insertions(+)
create mode 100644 lxd/migration/interfaces.go
create mode 100644 lxd/storage/backend_lxd.go
create mode 100644 lxd/storage/backend_mock.go
create mode 100644 lxd/storage/drivers/driver.go
create mode 100644 lxd/storage/drivers/errors.go
create mode 100644 lxd/storage/drivers/interface.go
create mode 100644 lxd/storage/errors.go
create mode 100644 lxd/storage/interfaces.go
create mode 100644 lxd/storage/load.go
diff --git a/lxd/migration/interfaces.go b/lxd/migration/interfaces.go
new file mode 100644
index 0000000000..7264e9ef91
--- /dev/null
+++ b/lxd/migration/interfaces.go
@@ -0,0 +1,12 @@
+package migration
+
+// FIXME: empty stubs until we're ready to move the migration code over
+
+type SourceArgs struct {
+}
+
+type SinkArgs struct {
+}
+
+type StorageSourceDriver interface {
+}
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
new file mode 100644
index 0000000000..730be6615c
--- /dev/null
+++ b/lxd/storage/backend_lxd.go
@@ -0,0 +1,201 @@
+package storage
+
+import (
+ "os"
+
+ "github.com/gorilla/websocket"
+
+ "github.com/lxc/lxd/lxd/migration"
+ "github.com/lxc/lxd/lxd/operations"
+ "github.com/lxc/lxd/lxd/state"
+ "github.com/lxc/lxd/lxd/storage/drivers"
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
+)
+
+type lxdBackend struct {
+ driver drivers.Driver
+ id int64
+ name string
+ state *state.State
+}
+
+func (b *lxdBackend) DaemonState() *state.State {
+ return b.state
+}
+
+func (b *lxdBackend) ID() int64 {
+ return b.id
+}
+
+func (b *lxdBackend) Name() string {
+ return b.name
+}
+
+func (b *lxdBackend) Driver() drivers.Driver {
+ return b.driver
+}
+
+func (b *lxdBackend) create(dbPool api.StoragePool, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) GetResources() (*api.ResourcesStoragePool, error) {
+ return nil, ErrNotImplemented
+}
+
+func (b *lxdBackend) Delete(op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) Mount() (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) Unmount() (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateInstance(i Instance, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateInstanceFromMigration(i Instance, conn *websocket.Conn, args migration.SinkArgs, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) RenameInstance(i Instance, newName string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) DeleteInstance(i Instance, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) MigrateInstance(i Instance, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error) {
+ return nil, ErrNotImplemented
+}
+
+func (b *lxdBackend) RefreshInstance(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) GetInstanceUsage(i Instance) (uint64, error) {
+ return 0, ErrNotImplemented
+}
+
+func (b *lxdBackend) SetInstanceQuota(i Instance, quota uint64) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) MountInstance(i Instance) (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) UnmountInstance(i Instance) (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) GetInstanceDisk(i Instance) (string, string, error) {
+ return "", "", ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateInstanceSnapshot(i Instance, name string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) DeleteInstanceSnapshot(i Instance, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) RestoreInstanceSnapshot(i Instance, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) MountInstanceSnapshot(i Instance) (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) UnmountInstanceSnapshot(i Instance) (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateImage(img api.Image, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) DeleteImage(img api.Image, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateCustomVolume(vol api.StorageVolume, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateCustomVolumeFromCopy(vol api.StorageVolume, src api.StorageVolume, snapshots bool, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateCustomVolumeFromMigration(vol api.StorageVolume, conn *websocket.Conn, args migration.SinkArgs, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) RenameCustomVolume(vol api.StorageVolume, newName string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) DeleteCustomVolume(vol api.StorageVolume, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) MigrateCustomVolume(vol api.StorageVolume, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error) {
+ return nil, ErrNotImplemented
+}
+
+func (b *lxdBackend) GetCustomVolumeUsage(vol api.StorageVolume) (uint64, error) {
+ return 0, ErrNotImplemented
+}
+
+func (b *lxdBackend) SetCustomVolumeQuota(vol api.StorageVolume, quota uint64) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) MountCustomVolume(vol api.StorageVolume) (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) UnmountCustomVolume(vol api.StorageVolume) (bool, error) {
+ return true, ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateCustomVolumeSnapshot(vol api.StorageVolume, name string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) RenameCustomVolumeSnapshot(vol api.StorageVolume, newName string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (b *lxdBackend) DeleteCustomVolumeSnapshot(vol api.StorageVolume, op *operations.Operation) error {
+ return ErrNotImplemented
+}
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
new file mode 100644
index 0000000000..e6082782b7
--- /dev/null
+++ b/lxd/storage/backend_mock.go
@@ -0,0 +1,192 @@
+package storage
+
+import (
+ "github.com/gorilla/websocket"
+
+ "github.com/lxc/lxd/lxd/migration"
+ "github.com/lxc/lxd/lxd/operations"
+ "github.com/lxc/lxd/lxd/state"
+ "github.com/lxc/lxd/lxd/storage/drivers"
+ "github.com/lxc/lxd/shared/api"
+)
+
+type mockBackend struct {
+ name string
+ state *state.State
+}
+
+func (b *mockBackend) DaemonState() *state.State {
+ return b.state
+}
+
+func (b *mockBackend) ID() int64 {
+ return -1
+}
+
+func (b *mockBackend) Name() string {
+ return b.name
+}
+
+func (b *mockBackend) Driver() drivers.Driver {
+ return nil
+}
+
+func (b *mockBackend) GetResources() (*api.ResourcesStoragePool, error) {
+ return nil, nil
+}
+
+func (b *mockBackend) Delete(op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) Mount() (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) Unmount() (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) CreateInstance(i Instance, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) CreateInstanceFromMigration(i Instance, conn *websocket.Conn, args migration.SinkArgs, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) RenameInstance(i Instance, newName string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) DeleteInstance(i Instance, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) MigrateInstance(i Instance, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error) {
+ return nil, nil
+}
+
+func (b *mockBackend) RefreshInstance(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) GetInstanceUsage(i Instance) (uint64, error) {
+ return 0, nil
+}
+
+func (b *mockBackend) SetInstanceQuota(i Instance, quota uint64) error {
+ return nil
+}
+
+func (b *mockBackend) MountInstance(i Instance) (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) UnmountInstance(i Instance) (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) GetInstanceDisk(i Instance) (string, string, error) {
+ return "", "", nil
+}
+
+func (b *mockBackend) CreateInstanceSnapshot(i Instance, name string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) DeleteInstanceSnapshot(i Instance, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) RestoreInstanceSnapshot(i Instance, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) MountInstanceSnapshot(i Instance) (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) UnmountInstanceSnapshot(i Instance) (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) CreateImage(img api.Image, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) DeleteImage(img api.Image, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) CreateCustomVolume(vol api.StorageVolume, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) CreateCustomVolumeFromCopy(vol api.StorageVolume, src api.StorageVolume, snapshots bool, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) CreateCustomVolumeFromMigration(vol api.StorageVolume, conn *websocket.Conn, args migration.SinkArgs, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) RenameCustomVolume(vol api.StorageVolume, newName string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) DeleteCustomVolume(vol api.StorageVolume, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) MigrateCustomVolume(vol api.StorageVolume, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error) {
+ return nil, nil
+}
+
+func (b *mockBackend) GetCustomVolumeUsage(vol api.StorageVolume) (uint64, error) {
+ return 0, nil
+}
+
+func (b *mockBackend) SetCustomVolumeQuota(vol api.StorageVolume, quota uint64) error {
+ return nil
+}
+
+func (b *mockBackend) MountCustomVolume(vol api.StorageVolume) (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) UnmountCustomVolume(vol api.StorageVolume) (bool, error) {
+ return true, nil
+}
+
+func (b *mockBackend) CreateCustomVolumeSnapshot(vol api.StorageVolume, name string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) RenameCustomVolumeSnapshot(vol api.StorageVolume, newName string, op *operations.Operation) error {
+ return nil
+}
+
+func (b *mockBackend) DeleteCustomVolumeSnapshot(vol api.StorageVolume, op *operations.Operation) error {
+ return nil
+}
diff --git a/lxd/storage/drivers/driver.go b/lxd/storage/drivers/driver.go
new file mode 100644
index 0000000000..ff55d6e250
--- /dev/null
+++ b/lxd/storage/drivers/driver.go
@@ -0,0 +1,45 @@
+package drivers
+
+import (
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
+)
+
+var drivers = map[string]func(name string, path string, config map[string]string) driver{}
+
+// Create performs the initial validation and alteration of the configuration and creates the low-level storage pool, returning a Driver.
+func Create(dbPool *api.StoragePool) (Driver, error) {
+ if dbPool == nil {
+ return nil, ErrNilValue
+ }
+
+ // Locate the driver
+ _, ok := drivers[dbPool.Driver]
+ if !ok {
+ return nil, ErrUnknownDriver
+ }
+
+ path := shared.VarPath("storage-pools", dbPool.Name)
+ d := drivers[dbPool.Driver](dbPool.Name, path, dbPool.Config)
+
+ // Create the low level pool
+ err := d.create(dbPool)
+ if err != nil {
+ return nil, err
+ }
+
+ return d, nil
+}
+
+// Load returns a Driver for an existing low-level storage pool.
+func Load(driverName string, name string, path string, config map[string]string) (Driver, error) {
+ // Locate the driver
+ _, ok := drivers[driverName]
+ if !ok {
+ return nil, ErrUnknownDriver
+ }
+
+ d := drivers[driverName](name, path, config)
+
+ return d, nil
+}
diff --git a/lxd/storage/drivers/errors.go b/lxd/storage/drivers/errors.go
new file mode 100644
index 0000000000..7cca047d15
--- /dev/null
+++ b/lxd/storage/drivers/errors.go
@@ -0,0 +1,14 @@
+package drivers
+
+import (
+ "fmt"
+)
+
+// ErrNilValue is the "Nil value provided" error
+var ErrNilValue = fmt.Errorf("Nil value provided")
+
+// ErrNotImplemented is the "Not implemented" error
+var ErrNotImplemented = fmt.Errorf("Not implemented")
+
+// ErrUnknownDriver is the "Unknown driver" error
+var ErrUnknownDriver = fmt.Errorf("Unknown driver")
diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go
new file mode 100644
index 0000000000..105be9cc9f
--- /dev/null
+++ b/lxd/storage/drivers/interface.go
@@ -0,0 +1,50 @@
+package drivers
+
+import (
+ "github.com/lxc/lxd/lxd/migration"
+ "github.com/lxc/lxd/lxd/operations"
+ "github.com/lxc/lxd/shared/api"
+)
+
+// VolumeType represents a storage volume type.
+type VolumeType string
+
+// VolumeTypeCustom represents a custom storage volume.
+const VolumeTypeCustom = VolumeType("custom")
+
+// VolumeTypeContainer represents a container storage volume.
+const VolumeTypeContainer = VolumeType("containers")
+
+// VolumeTypeImage represents an image storage volume.
+const VolumeTypeImage = VolumeType("images")
+
+// VolumeTypeVM represents a virtual-machine storage volume.
+const VolumeTypeVM = VolumeType("virtual-machines")
+
+// driver is the extended internal interface
+type driver interface {
+ Driver
+
+ create(dbPool *api.StoragePool) error
+}
+
+// Driver repreents a low-level storage driver.
+type Driver interface {
+ // Internal
+ Name() string
+ Version() string
+
+ // Pool
+ Delete(op *operations.Operation) error
+ Mount() (bool, error)
+ Unmount() (bool, error)
+ GetResources() (*api.ResourcesStoragePool, error)
+
+ // Volumes
+ DeleteVolume(volType VolumeType, name string, op *operations.Operation) error
+ RenameVolume(volType VolumeType, name string, newName string, op *operations.Operation) error
+
+ // Migration
+ MigrationType() migration.MigrationFSType
+ PreservesInodes() bool
+}
diff --git a/lxd/storage/errors.go b/lxd/storage/errors.go
new file mode 100644
index 0000000000..efbe5c6c75
--- /dev/null
+++ b/lxd/storage/errors.go
@@ -0,0 +1,11 @@
+package storage
+
+import (
+ "fmt"
+)
+
+// ErrNilValue is the "Nil value provided" error
+var ErrNilValue = fmt.Errorf("Nil value provided")
+
+// ErrNotImplemented is the "Not implemented" error
+var ErrNotImplemented = fmt.Errorf("Not implemented")
diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
new file mode 100644
index 0000000000..3246bb034e
--- /dev/null
+++ b/lxd/storage/interfaces.go
@@ -0,0 +1,95 @@
+package storage
+
+import (
+ "github.com/gorilla/websocket"
+
+ "github.com/lxc/lxd/lxd/instance/instancetype"
+ "github.com/lxc/lxd/lxd/migration"
+ "github.com/lxc/lxd/lxd/operations"
+ "github.com/lxc/lxd/lxd/state"
+ "github.com/lxc/lxd/lxd/storage/drivers"
+ "github.com/lxc/lxd/shared/api"
+)
+
+// Instance represents the storage relevant subset of a LXD instance
+type Instance interface {
+ Name() string
+ Project() string
+ Type() instancetype.Type
+ Path() string
+
+ IsRunning() bool
+ Snapshots() ([]Instance, error)
+ TemplateApply(trigger string) error
+}
+
+// Pool represents a LXD storage pool
+type Pool interface {
+ // Internal
+ DaemonState() *state.State
+
+ // Pool
+ ID() int64
+ Name() string
+ Driver() drivers.Driver
+
+ GetResources() (*api.ResourcesStoragePool, error)
+ Delete(op *operations.Operation) error
+
+ Mount() (bool, error)
+ Unmount() (bool, error)
+
+ // Instances
+ CreateInstance(i Instance, op *operations.Operation) error
+ CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error
+ CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error
+ CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error
+ CreateInstanceFromMigration(i Instance, conn *websocket.Conn, args migration.SinkArgs, op *operations.Operation) error
+ RenameInstance(i Instance, newName string, op *operations.Operation) error
+ DeleteInstance(i Instance, op *operations.Operation) error
+
+ MigrateInstance(i Instance, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error)
+ RefreshInstance(i Instance, src Instance, snapshots bool, op *operations.Operation) error
+ BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error
+
+ GetInstanceUsage(i Instance) (uint64, error)
+ SetInstanceQuota(i Instance, quota uint64) error
+
+ MountInstance(i Instance) (bool, error)
+ UnmountInstance(i Instance) (bool, error)
+ GetInstanceDisk(i Instance) (string, string, error)
+
+ // Instance snapshots
+ CreateInstanceSnapshot(i Instance, name string, op *operations.Operation) error
+ RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error
+ DeleteInstanceSnapshot(i Instance, op *operations.Operation) error
+
+ RestoreInstanceSnapshot(i Instance, op *operations.Operation) error
+
+ MountInstanceSnapshot(i Instance) (bool, error)
+ UnmountInstanceSnapshot(i Instance) (bool, error)
+
+ // Images
+ CreateImage(img api.Image, op *operations.Operation) error
+ DeleteImage(img api.Image, op *operations.Operation) error
+
+ // Custom volumes
+ CreateCustomVolume(vol api.StorageVolume, op *operations.Operation) error
+ CreateCustomVolumeFromCopy(vol api.StorageVolume, src api.StorageVolume, snapshots bool, op *operations.Operation) error
+ CreateCustomVolumeFromMigration(vol api.StorageVolume, conn *websocket.Conn, args migration.SinkArgs, op *operations.Operation) error
+ RenameCustomVolume(vol api.StorageVolume, newName string, op *operations.Operation) error
+ DeleteCustomVolume(vol api.StorageVolume, op *operations.Operation) error
+
+ MigrateCustomVolume(vol api.StorageVolume, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error)
+
+ GetCustomVolumeUsage(vol api.StorageVolume) (uint64, error)
+ SetCustomVolumeQuota(vol api.StorageVolume, quota uint64) error
+
+ MountCustomVolume(vol api.StorageVolume) (bool, error)
+ UnmountCustomVolume(vol api.StorageVolume) (bool, error)
+
+ // Custom volume snapshots
+ CreateCustomVolumeSnapshot(vol api.StorageVolume, name string, op *operations.Operation) error
+ RenameCustomVolumeSnapshot(vol api.StorageVolume, newName string, op *operations.Operation) error
+ DeleteCustomVolumeSnapshot(vol api.StorageVolume, op *operations.Operation) error
+}
diff --git a/lxd/storage/load.go b/lxd/storage/load.go
new file mode 100644
index 0000000000..e88131c585
--- /dev/null
+++ b/lxd/storage/load.go
@@ -0,0 +1,85 @@
+package storage
+
+import (
+ "github.com/lxc/lxd/lxd/operations"
+ "github.com/lxc/lxd/lxd/state"
+ "github.com/lxc/lxd/lxd/storage/drivers"
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
+)
+
+// MockBackend controls whether to run the storage logic in mock mode.
+var MockBackend = false
+
+// CreatePool creates a new storage pool on disk and returns a Pool interface.
+func CreatePool(state *state.State, id int64, dbPool *api.StoragePool, op *operations.Operation) (Pool, error) {
+ // Sanity checks
+ if dbPool == nil {
+ return nil, ErrNilValue
+ }
+
+ // Ensure a config map exists
+ if dbPool.Config == nil {
+ dbPool.Config = map[string]string{}
+ }
+
+ // Handle mock requests
+ if MockBackend {
+ pool := mockBackend{}
+ pool.name = dbPool.Name
+ pool.state = state
+ return &pool, nil
+ }
+
+ // Setup the pool struct
+ pool := lxdBackend{}
+ pool.id = id
+ pool.name = dbPool.Name
+ pool.state = state
+
+ // Create the pool itself (also responsible for setting driver)
+ err := pool.create(dbPool, op)
+ if err != nil {
+ return nil, err
+ }
+
+ return &pool, nil
+}
+
+// GetPoolByName retrieves the pool from the database by its name and returns a Pool interface.
+func GetPoolByName(state *state.State, name string) (Pool, error) {
+ // Handle mock requests
+ if MockBackend {
+ pool := mockBackend{}
+ pool.name = name
+ pool.state = state
+ return &pool, nil
+ }
+
+ // Load the database record
+ id, dbPool, err := state.Cluster.StoragePoolGet(name)
+ if err != nil {
+ return nil, err
+ }
+
+ // Ensure a config map exists
+ if dbPool.Config == nil {
+ dbPool.Config = map[string]string{}
+ }
+
+ // Load the storage driver
+ path := shared.VarPath("storage-pools", name)
+ driver, err := drivers.Load(dbPool.Driver, dbPool.Name, path, dbPool.Config)
+ if err != nil {
+ return nil, err
+ }
+
+ // Setup the pool struct
+ pool := lxdBackend{}
+ pool.driver = driver
+ pool.id = id
+ pool.name = dbPool.Name
+ pool.state = state
+
+ return &pool, nil
+}
From ecd05b420108f8a68047a8c7d30ffd4eab27d733 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 6 Oct 2019 22:44:13 -0400
Subject: [PATCH 2/5] lxd/storage/drivers: Add common functions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/storage/drivers/utils.go | 143 +++++++++++++++++++++++++++++++++++
1 file changed, 143 insertions(+)
create mode 100644 lxd/storage/drivers/utils.go
diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
new file mode 100644
index 0000000000..cd54b45d49
--- /dev/null
+++ b/lxd/storage/drivers/utils.go
@@ -0,0 +1,143 @@
+package drivers
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "golang.org/x/sys/unix"
+
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
+)
+
+func wipeDirectory(path string) error {
+ // List all entries
+ entries, err := ioutil.ReadDir(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return nil
+ }
+ }
+
+ // Individually wipe all entries
+ for _, entry := range entries {
+ entryPath := filepath.Join(path, entry.Name())
+ err := os.RemoveAll(entryPath)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func forceUnmount(path string) (bool, error) {
+ unmounted := false
+
+ for {
+ // Check if already unmounted
+ if !shared.IsMountPoint(path) {
+ return unmounted, nil
+ }
+
+ // Try a clean unmount first
+ err := unix.Unmount(path, 0)
+ if err != nil {
+ // Fallback to lazy unmounting
+ err = unix.Unmount(path, unix.MNT_DETACH)
+ if err != nil {
+ return false, err
+ }
+ }
+
+ unmounted = true
+ }
+
+ return unmounted, nil
+}
+
+func sameMount(srcPath string, dstPath string) bool {
+ // Get the source vfs path information
+ var srcFsStat unix.Statfs_t
+ err := unix.Statfs(srcPath, &srcFsStat)
+ if err != nil {
+ return false
+ }
+
+ // Get the destination vfs path information
+ var dstFsStat unix.Statfs_t
+ err = unix.Statfs(dstPath, &dstFsStat)
+ if err != nil {
+ return false
+ }
+
+ // Compare statfs
+ if srcFsStat.Type != dstFsStat.Type || srcFsStat.Fsid != dstFsStat.Fsid {
+ return false
+ }
+
+ // Get the source path information
+ var srcStat unix.Stat_t
+ err = unix.Stat(srcPath, &srcStat)
+ if err != nil {
+ return false
+ }
+
+ // Get the destination path information
+ var dstStat unix.Stat_t
+ err = unix.Stat(dstPath, &dstStat)
+ if err != nil {
+ return false
+ }
+
+ // Compare inode
+ if srcStat.Ino != dstStat.Ino {
+ return false
+ }
+
+ return true
+}
+
+func tryMount(src string, dst string, fs string, flags uintptr, options string) error {
+ var err error
+
+ // Attempt 20 mounts over 10s
+ for i := 0; i < 20; i++ {
+ err = unix.Mount(src, dst, fs, flags, options)
+ if err == nil {
+ break
+ }
+
+ time.Sleep(500 * time.Millisecond)
+ }
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func vfsResources(path string) (*api.ResourcesStoragePool, error) {
+ // Get the VFS information
+ st, err := shared.Statvfs(path)
+ if err != nil {
+ return nil, err
+ }
+
+ // Fill in the struct
+ res := api.ResourcesStoragePool{}
+ res.Space.Total = st.Blocks * uint64(st.Bsize)
+ res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)
+
+ // Some filesystems don't report inodes since they allocate them
+ // dynamically e.g. btrfs.
+ if st.Files > 0 {
+ res.Inodes.Total = st.Files
+ res.Inodes.Used = st.Files - st.Ffree
+ }
+
+ return &res, nil
+}
From 14663cb5b98264bd5131a6e4a5ad1d37d382f28b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 6 Oct 2019 20:42:09 -0400
Subject: [PATCH 3/5] lxd/storage/drivers: Add dir backend
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/storage/drivers/driver.go | 6 +-
lxd/storage/drivers/driver_dir.go | 119 ++++++++++++++++++++++++++++++
2 files changed, 124 insertions(+), 1 deletion(-)
create mode 100644 lxd/storage/drivers/driver_dir.go
diff --git a/lxd/storage/drivers/driver.go b/lxd/storage/drivers/driver.go
index ff55d6e250..f9360c127d 100644
--- a/lxd/storage/drivers/driver.go
+++ b/lxd/storage/drivers/driver.go
@@ -5,7 +5,11 @@ import (
"github.com/lxc/lxd/shared/api"
)
-var drivers = map[string]func(name string, path string, config map[string]string) driver{}
+var drivers = map[string]func(name string, path string, config map[string]string) driver{
+ "dir": func(name string, path string, config map[string]string) driver {
+ return &dir{name: name, path: path, config: config}
+ },
+}
// Create performs the initial validation and alteration of the configuration and creates the low-level storage pool, returning a Driver.
func Create(dbPool *api.StoragePool) (Driver, error) {
diff --git a/lxd/storage/drivers/driver_dir.go b/lxd/storage/drivers/driver_dir.go
new file mode 100644
index 0000000000..368b6daea8
--- /dev/null
+++ b/lxd/storage/drivers/driver_dir.go
@@ -0,0 +1,119 @@
+package drivers
+
+import (
+ "fmt"
+
+ "golang.org/x/sys/unix"
+
+ "github.com/lxc/lxd/lxd/migration"
+ "github.com/lxc/lxd/lxd/operations"
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
+)
+
+type dir struct {
+ name string
+ path string
+ config map[string]string
+}
+
+// Functions used by the loader
+func (d *dir) create(dbPool *api.StoragePool) error {
+ // WARNING: The create() function cannot rely on any of the struct attributes being set
+
+ // Set default source if missing
+ if dbPool.Config["source"] == "" {
+ dbPool.Config["source"] = shared.VarPath("storage-pools", dbPool.Name)
+ }
+
+ if !shared.PathExists(dbPool.Config["source"]) {
+ return fmt.Errorf("Source path '%s' doesn't exist", dbPool.Config["source"])
+ }
+
+ // Check that the path is currently empty
+ isEmpty, err := shared.PathIsEmpty(dbPool.Config["source"])
+ if err != nil {
+ return err
+ }
+
+ if !isEmpty {
+ return fmt.Errorf("Source path '%s' isn't empty", dbPool.Config["source"])
+ }
+
+ return nil
+}
+
+func (d *dir) Name() string {
+ return "dir"
+}
+
+func (d *dir) Version() string {
+ return "1"
+}
+
+func (d *dir) Delete(op *operations.Operation) error {
+ // On delete, wipe everything in the directory
+ err := wipeDirectory(d.path)
+ if err != nil {
+ return err
+ }
+
+ // Unmount the path
+ _, err = d.Unmount()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (d *dir) Mount() (bool, error) {
+ // Check if we're dealing with an external mount
+ if d.config["source"] == d.path {
+ return false, nil
+ }
+
+ // Check if already mounted
+ if sameMount(d.config["source"], d.path) {
+ return false, nil
+ }
+
+ // Setup the bind-mount
+ err := tryMount(d.config["source"], d.path, "none", unix.MS_BIND, "")
+ if err != nil {
+ return false, err
+ }
+
+ return true, nil
+}
+
+func (d *dir) Unmount() (bool, error) {
+ // Check if we're dealing with an external mount
+ if d.config["source"] == d.path {
+ return false, nil
+ }
+
+ // Unmount until nothing is left mounted
+ return forceUnmount(d.path)
+}
+
+func (d *dir) GetResources() (*api.ResourcesStoragePool, error) {
+ // Use the generic VFS resources
+ return vfsResources(d.path)
+}
+
+func (d *dir) DeleteVolume(volType VolumeType, name string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (d *dir) RenameVolume(volType VolumeType, name string, newName string, op *operations.Operation) error {
+ return ErrNotImplemented
+}
+
+func (d *dir) MigrationType() migration.MigrationFSType {
+ return migration.MigrationFSType_RSYNC
+}
+
+func (d *dir) PreservesInodes() bool {
+ return false
+}
From 71838908e00e46fc04ba4e3667af8dcfb3d61267 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 6 Oct 2019 20:56:17 -0400
Subject: [PATCH 4/5] lxd/storage: Add common helpers to utils
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/storage/utils.go | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index d23d4f4171..e04c4fe345 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -3,6 +3,7 @@ package storage
import (
"fmt"
"os"
+ "path/filepath"
"strings"
"time"
@@ -14,6 +15,27 @@ import (
"github.com/lxc/lxd/shared/logger"
)
+var baseDirectories = []string{
+ "containers",
+ "containers-snapshots",
+ "custom",
+ "custom-snapshots",
+ "images",
+ "virtual-machines",
+ "virtual-machines-snapshots",
+}
+
+func createStorageStructure(path string) error {
+ for _, name := range baseDirectories {
+ err := os.MkdirAll(filepath.Join(path, name), 0711)
+ if err != nil && !os.IsExist(err) {
+ return err
+ }
+ }
+
+ return nil
+}
+
// MkfsOptions represents options for filesystem creation.
type MkfsOptions struct {
Label string
From 4ffb702c5ec72092e9c554bfeb81ccefd9708f29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 6 Oct 2019 21:04:14 -0400
Subject: [PATCH 5/5] lxd/storage: Implement lxd backend
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/storage/backend_lxd.go | 66 ++++++++++++++++++++++++++++++++++----
1 file changed, 60 insertions(+), 6 deletions(-)
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 730be6615c..27d73f3301 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -36,24 +36,78 @@ func (b *lxdBackend) Driver() drivers.Driver {
return b.driver
}
-func (b *lxdBackend) create(dbPool api.StoragePool, op *operations.Operation) error {
- return ErrNotImplemented
+func (b *lxdBackend) create(dbPool *api.StoragePool, op *operations.Operation) error {
+ revertPath := true
+
+ // Create the storage path
+ path := shared.VarPath("storage-pools", b.name)
+ err := os.MkdirAll(path, 0711)
+ if err != nil && !os.IsExist(err) {
+ return err
+ }
+ defer func() {
+ if !revertPath {
+ return
+ }
+
+ os.RemoveAll(path)
+ }()
+
+ // Create the low-level storage pool
+ driver, err := drivers.Create(dbPool)
+ if err != nil {
+ return err
+ }
+
+ // Mount the storage pool
+ ourMount, err := driver.Mount()
+ if err != nil {
+ return err
+ }
+ if ourMount {
+ defer driver.Unmount()
+ }
+
+ // Create the directory structure
+ err = createStorageStructure(path)
+ if err != nil {
+ return err
+ }
+
+ // Set the driver
+ b.driver = driver
+ revertPath = false
+
+ return nil
}
func (b *lxdBackend) GetResources() (*api.ResourcesStoragePool, error) {
- return nil, ErrNotImplemented
+ return b.driver.GetResources()
}
func (b *lxdBackend) Delete(op *operations.Operation) error {
- return ErrNotImplemented
+ // Delete the low-level storage
+ err := b.driver.Delete(op)
+ if err != nil {
+ return err
+ }
+
+ // Delete the mountpoint
+ path := shared.VarPath("storage-pools", b.name)
+ err = os.Remove(path)
+ if err != nil {
+ return err
+ }
+
+ return nil
}
func (b *lxdBackend) Mount() (bool, error) {
- return true, ErrNotImplemented
+ return b.driver.Mount()
}
func (b *lxdBackend) Unmount() (bool, error) {
- return true, ErrNotImplemented
+ return b.driver.Unmount()
}
func (b *lxdBackend) CreateInstance(i Instance, op *operations.Operation) error {
More information about the lxc-devel
mailing list