The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6285

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===

From 2d1839e328d4785bbf0940312328ffcdc1513ad2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@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 <stgra...@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?= <stgra...@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 <stgra...@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?= <stgra...@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 <stgra...@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?= <stgra...@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 <stgra...@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?= <stgra...@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 <stgra...@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 {
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to