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

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 8d34e3adcbbbcc507f3b93330b9edc5145438d7a Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 11:37:38 +0000
Subject: [PATCH 01/38] lxd/db: un-export StorageVolumeNodeGet

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   | 2 +-
 lxd/db/storage_volumes.go | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 3dcc8e6a16..3ccd6ade3e 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -828,7 +828,7 @@ func (c *Cluster) StoragePoolVolumeGetType(project string, 
volumeName string, vo
                return -1, nil, err
        }
 
-       volumeNode, err := c.StorageVolumeNodeGet(volumeID)
+       volumeNode, err := c.storageVolumeNodeGet(volumeID)
        if err != nil {
                return -1, nil, err
        }
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index b377d7a630..4ef0e08c1b 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -85,8 +85,8 @@ SELECT nodes.id, nodes.address
        return addresses, nil
 }
 
-// StorageVolumeNodeGet returns the name of the node a storage volume is on.
-func (c *Cluster) StorageVolumeNodeGet(volumeID int64) (string, error) {
+// Return the name of the node a storage volume is on.
+func (c *Cluster) storageVolumeNodeGet(volumeID int64) (string, error) {
        name := ""
        query := `
 SELECT nodes.name FROM storage_volumes

From c0958a973b8c3578ca435da093f04563a61205a9 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 12:15:35 +0000
Subject: [PATCH 02/38] lxd/db: un-export StoragePoolVolumesGetType

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 3ccd6ade3e..60f8db160e 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -730,7 +730,7 @@ func (c *Cluster) storagePoolVolumesGet(project string, 
poolID, nodeID int64, vo
        // pool.
        result := []*api.StorageVolume{}
        for _, volumeType := range volumeTypes {
-               volumeNames, err := c.StoragePoolVolumesGetType(project, 
volumeType, poolID, nodeID)
+               volumeNames, err := c.storagePoolVolumesGetType(project, 
volumeType, poolID, nodeID)
                if err != nil && err != sql.ErrNoRows {
                        return nil, errors.Wrap(err, "failed to fetch volume 
types")
                }
@@ -750,9 +750,9 @@ func (c *Cluster) storagePoolVolumesGet(project string, 
poolID, nodeID int64, vo
        return result, nil
 }
 
-// StoragePoolVolumesGetType get all storage volumes attached to a given
-// storage pool of a given volume type, on the given node.
-func (c *Cluster) StoragePoolVolumesGetType(project string, volumeType int, 
poolID, nodeID int64) ([]string, error) {
+// Get all storage volumes attached to a given storage pool of a given volume
+// type, on the given node.
+func (c *Cluster) storagePoolVolumesGetType(project string, volumeType int, 
poolID, nodeID int64) ([]string, error) {
        var poolName string
        query := `
 SELECT storage_volumes.name
@@ -811,7 +811,7 @@ func (c *Cluster) 
StoragePoolVolumeSnapshotsGetType(volumeName string, volumeTyp
 // StoragePoolNodeVolumesGetType returns all storage volumes attached to a
 // given storage pool of a given volume type, on the current node.
 func (c *Cluster) StoragePoolNodeVolumesGetType(volumeType int, poolID int64) 
([]string, error) {
-       return c.StoragePoolVolumesGetType("default", volumeType, poolID, 
c.nodeID)
+       return c.storagePoolVolumesGetType("default", volumeType, poolID, 
c.nodeID)
 }
 
 // StoragePoolVolumeGetType returns a single storage volume attached to a

From 0958ad0c1d304b651e41f1eae0dd6e83210017dc Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 12:56:21 +0000
Subject: [PATCH 03/38] lxd/db: un-export StoragePoolVolumeGetTypeID

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 60f8db160e..2dac1f632c 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -823,7 +823,7 @@ func (c *Cluster) StoragePoolVolumeGetType(project string, 
volumeName string, vo
                project = "default"
        }
 
-       volumeID, err := c.StoragePoolVolumeGetTypeID(project, volumeName, 
volumeType, poolID, nodeID)
+       volumeID, err := c.storagePoolVolumeGetTypeID(project, volumeName, 
volumeType, poolID, nodeID)
        if err != nil {
                return -1, nil, err
        }
@@ -1018,9 +1018,9 @@ INSERT INTO storage_volumes (storage_pool_id, node_id, 
type, snapshot, name, des
        return thisVolumeID, err
 }
 
-// StoragePoolVolumeGetTypeID returns the ID of a storage volume on a given
-// storage pool of a given storage volume type, on the given node.
-func (c *Cluster) StoragePoolVolumeGetTypeID(project string, volumeName 
string, volumeType int, poolID, nodeID int64) (int64, error) {
+// Return the ID of a storage volume on a given storage pool of a given storage
+// volume type, on the given node.
+func (c *Cluster) storagePoolVolumeGetTypeID(project string, volumeName 
string, volumeType int, poolID, nodeID int64) (int64, error) {
        volumeID := int64(-1)
        query := `SELECT storage_volumes.id
 FROM storage_volumes
@@ -1045,13 +1045,13 @@ AND storage_volumes.name=? AND storage_volumes.type=?`
 // StoragePoolNodeVolumeGetTypeID get the ID of a storage volume on a given
 // storage pool of a given storage volume type, on the current node.
 func (c *Cluster) StoragePoolNodeVolumeGetTypeID(volumeName string, volumeType 
int, poolID int64) (int64, error) {
-       return c.StoragePoolVolumeGetTypeID("default", volumeName, volumeType, 
poolID, c.nodeID)
+       return c.storagePoolVolumeGetTypeID("default", volumeName, volumeType, 
poolID, c.nodeID)
 }
 
 // StoragePoolNodeVolumeGetTypeIDByProject gets the ID of a storage volume on 
a given storage pool
 // of a given storage volume type and project, on the current node.
 func (c *Cluster) StoragePoolNodeVolumeGetTypeIDByProject(project, volumeName 
string, volumeType int, poolID int64) (int64, error) {
-       return c.StoragePoolVolumeGetTypeID(project, volumeName, volumeType, 
poolID, c.nodeID)
+       return c.storagePoolVolumeGetTypeID(project, volumeName, volumeType, 
poolID, c.nodeID)
 }
 
 // XXX: this was extracted from lxd/storage_volume_utils.go, we find a way to

From d3f2a5d718363dfdf0ac72e8a6da6accea887e70 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 13:03:33 +0000
Subject: [PATCH 04/38] lxd/db: un-export StoragePoolVolumeGetType

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go             | 10 +++++-----
 lxd/db/storage_pools_export_test.go |  9 +++++++++
 2 files changed, 14 insertions(+), 5 deletions(-)
 create mode 100644 lxd/db/storage_pools_export_test.go

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 2dac1f632c..7a7ccc7afd 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -735,7 +735,7 @@ func (c *Cluster) storagePoolVolumesGet(project string, 
poolID, nodeID int64, vo
                        return nil, errors.Wrap(err, "failed to fetch volume 
types")
                }
                for _, volumeName := range volumeNames {
-                       _, volume, err := c.StoragePoolVolumeGetType(project, 
volumeName, volumeType, poolID, nodeID)
+                       _, volume, err := c.storagePoolVolumeGetType(project, 
volumeName, volumeType, poolID, nodeID)
                        if err != nil {
                                return nil, errors.Wrap(err, "failed to fetch 
volume type")
                        }
@@ -814,9 +814,9 @@ func (c *Cluster) StoragePoolNodeVolumesGetType(volumeType 
int, poolID int64) ([
        return c.storagePoolVolumesGetType("default", volumeType, poolID, 
c.nodeID)
 }
 
-// StoragePoolVolumeGetType returns a single storage volume attached to a
-// given storage pool of a given type, on the node with the given ID.
-func (c *Cluster) StoragePoolVolumeGetType(project string, volumeName string, 
volumeType int, poolID, nodeID int64) (int64, *api.StorageVolume, error) {
+// Return a single storage volume attached to a given storage pool of a given
+// type, on the node with the given ID.
+func (c *Cluster) storagePoolVolumeGetType(project string, volumeName string, 
volumeType int, poolID, nodeID int64) (int64, *api.StorageVolume, error) {
        // Custom volumes are "global", i.e. they are associated with the
        // default project.
        if volumeType == StoragePoolVolumeTypeCustom {
@@ -868,7 +868,7 @@ func (c *Cluster) StoragePoolNodeVolumeGetType(volumeName 
string, volumeType int
 // StoragePoolNodeVolumeGetTypeByProject gets a single storage volume attached 
to a
 // given storage pool of a given type, on the current node in the given 
project.
 func (c *Cluster) StoragePoolNodeVolumeGetTypeByProject(project, volumeName 
string, volumeType int, poolID int64) (int64, *api.StorageVolume, error) {
-       return c.StoragePoolVolumeGetType(project, volumeName, volumeType, 
poolID, c.nodeID)
+       return c.storagePoolVolumeGetType(project, volumeName, volumeType, 
poolID, c.nodeID)
 }
 
 // StoragePoolVolumeUpdateByProject updates the storage volume attached to a 
given storage pool.
diff --git a/lxd/db/storage_pools_export_test.go 
b/lxd/db/storage_pools_export_test.go
new file mode 100644
index 0000000000..6a83c10bc3
--- /dev/null
+++ b/lxd/db/storage_pools_export_test.go
@@ -0,0 +1,9 @@
+// +build linux,cgo,!agent
+
+package db
+
+import "github.com/lxc/lxd/shared/api"
+
+func (c *Cluster) StoragePoolVolumeGetType(project string, volumeName string, 
volumeType int, poolID, nodeID int64) (int64, *api.StorageVolume, error) {
+       return c.storagePoolVolumeGetType(project, volumeName, volumeType, 
poolID, nodeID)
+}

From 86cc9457d84726851b6ff0ed9bf9a62f4b660d6f Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 13:04:54 +0000
Subject: [PATCH 05/38] lxd/db: un-export StorageVolumeConfigGet

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   | 2 +-
 lxd/db/storage_volumes.go | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 7a7ccc7afd..ad48a44dee 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -833,7 +833,7 @@ func (c *Cluster) storagePoolVolumeGetType(project string, 
volumeName string, vo
                return -1, nil, err
        }
 
-       volumeConfig, err := c.StorageVolumeConfigGet(volumeID)
+       volumeConfig, err := c.storageVolumeConfigGet(volumeID)
        if err != nil {
                return -1, nil, err
        }
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 4ef0e08c1b..342bea8870 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -108,8 +108,8 @@ SELECT nodes.name FROM storage_volumes
        return name, nil
 }
 
-// StorageVolumeConfigGet gets the config of a storage volume.
-func (c *Cluster) StorageVolumeConfigGet(volumeID int64) (map[string]string, 
error) {
+// Get the config of a storage volume.
+func (c *Cluster) storageVolumeConfigGet(volumeID int64) (map[string]string, 
error) {
        var key, value string
        query := "SELECT key, value FROM storage_volumes_config WHERE 
storage_volume_id=?"
        inargs := []interface{}{volumeID}

From a1f9334fd881805f84bafca3d89fc389e53a2fe8 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 13:05:54 +0000
Subject: [PATCH 06/38] lxd/db: un-export StoragePoolVolumeTypeToName

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index ad48a44dee..ac31cbb950 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -843,7 +843,7 @@ func (c *Cluster) storagePoolVolumeGetType(project string, 
volumeName string, vo
                return -1, nil, err
        }
 
-       volumeTypeName, err := StoragePoolVolumeTypeToName(volumeType)
+       volumeTypeName, err := storagePoolVolumeTypeToName(volumeType)
        if err != nil {
                return -1, nil, err
        }
@@ -1084,9 +1084,8 @@ var StoragePoolNodeConfigKeys = []string{
        "lvm.vg_name",
 }
 
-// StoragePoolVolumeTypeToName converts a volume integer type code to its
-// human-readable name.
-func StoragePoolVolumeTypeToName(volumeType int) (string, error) {
+// Convert a volume integer type code to its human-readable name.
+func storagePoolVolumeTypeToName(volumeType int) (string, error) {
        switch volumeType {
        case StoragePoolVolumeTypeContainer:
                return StoragePoolVolumeTypeNameContainer, nil

From 2a043c44a07d8c5b950b790d16e5c91d44183a35 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 13:53:05 +0000
Subject: [PATCH 07/38] lxd/db: un-export StorageVolumeDescriptionUpdate

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   | 2 +-
 lxd/db/storage_volumes.go | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index ac31cbb950..138141df3e 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -890,7 +890,7 @@ func (c *Cluster) StoragePoolVolumeUpdateByProject(project, 
volumeName string, v
                                return err
                        }
 
-                       return StorageVolumeDescriptionUpdate(tx.tx, volumeID, 
volumeDescription)
+                       return storageVolumeDescriptionUpdate(tx.tx, volumeID, 
volumeDescription)
                })
                if err != nil {
                        return err
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 342bea8870..3ae17e0d4a 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -251,8 +251,8 @@ func (c *Cluster) StorageVolumeIsAvailable(pool, volume 
string) (bool, error) {
        return isAvailable, nil
 }
 
-// StorageVolumeDescriptionUpdate updates the description of a storage volume.
-func StorageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description 
string) error {
+// Updates the description of a storage volume.
+func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description 
string) error {
        _, err := tx.Exec("UPDATE storage_volumes SET description=? WHERE 
id=?", description, volumeID)
        return err
 }

From b6a033d79c6a95c38f59fda91963c4cf9e5f44c2 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:28:55 +0000
Subject: [PATCH 08/38] lxd/db: un-export StorageVolumeConfigAdd

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   | 4 ++--
 lxd/db/storage_volumes.go | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 138141df3e..ff4e5fa2ea 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -885,7 +885,7 @@ func (c *Cluster) StoragePoolVolumeUpdateByProject(project, 
volumeName string, v
                                return err
                        }
 
-                       err = StorageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig)
+                       err = storageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig)
                        if err != nil {
                                return err
                        }
@@ -1003,7 +1003,7 @@ INSERT INTO storage_volumes (storage_pool_id, node_id, 
type, snapshot, name, des
                                thisVolumeID = volumeID
                        }
 
-                       err = StorageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig)
+                       err = storageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig)
                        if err != nil {
                                tx.tx.Rollback()
                                return err
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 3ae17e0d4a..f009023ed0 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -257,8 +257,8 @@ func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID 
int64, description stri
        return err
 }
 
-// StorageVolumeConfigAdd adds a new storage volume config into database.
-func StorageVolumeConfigAdd(tx *sql.Tx, volumeID int64, volumeConfig 
map[string]string) error {
+// Add a new storage volume config into database.
+func storageVolumeConfigAdd(tx *sql.Tx, volumeID int64, volumeConfig 
map[string]string) error {
        str := "INSERT INTO storage_volumes_config (storage_volume_id, key, 
value) VALUES(?, ?, ?)"
        stmt, err := tx.Prepare(str)
        defer stmt.Close()

From 396135330b508ff2d947f27d891bdeb21fb1a7cb Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:33:42 +0000
Subject: [PATCH 09/38] lxd/db: un-export StorageVolumeConfigClear

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   | 2 +-
 lxd/db/storage_volumes.go | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index ff4e5fa2ea..5a30af09b0 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -880,7 +880,7 @@ func (c *Cluster) StoragePoolVolumeUpdateByProject(project, 
volumeName string, v
 
        err = c.Transaction(func(tx *ClusterTx) error {
                err = storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, 
project, volumeName, volumeType, poolID, func(volumeID int64) error {
-                       err = StorageVolumeConfigClear(tx.tx, volumeID)
+                       err = storageVolumeConfigClear(tx.tx, volumeID)
                        if err != nil {
                                return err
                        }
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index f009023ed0..df77a1835f 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -280,8 +280,8 @@ func storageVolumeConfigAdd(tx *sql.Tx, volumeID int64, 
volumeConfig map[string]
        return nil
 }
 
-// StorageVolumeConfigClear deletes storage volume config.
-func StorageVolumeConfigClear(tx *sql.Tx, volumeID int64) error {
+// Delete storage volume config.
+func storageVolumeConfigClear(tx *sql.Tx, volumeID int64) error {
        _, err := tx.Exec("DELETE FROM storage_volumes_config WHERE 
storage_volume_id=?", volumeID)
        if err != nil {
                return err

From 47b481e79b4c93fa836adbb96e5dbc6003a207a6 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 09:44:03 +0000
Subject: [PATCH 10/38] lxd/db/cluster: add new storage volume snapshots table

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/cluster/schema.go | 19 ++++++++++++++++++-
 lxd/db/cluster/update.go | 29 +++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index 02f2057d10..677f23a013 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -494,6 +494,23 @@ CREATE TABLE storage_volumes_config (
     UNIQUE (storage_volume_id, key),
     FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE 
CASCADE
 );
+CREATE TABLE storage_volumes_snapshots (
+    id INTEGER NOT NULL,
+    storage_volume_id INTEGER NOT NULL,
+    name TEXT NOT NULL,
+    description TEXT,
+    UNIQUE (id),
+    UNIQUE (storage_volume_id, name),
+    FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE 
CASCADE
+);
+CREATE TABLE storage_volumes_snapshots_config (
+    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+    storage_volume_snapshot_id INTEGER NOT NULL,
+    key TEXT NOT NULL,
+    value TEXT,
+    FOREIGN KEY (storage_volume_snapshot_id) REFERENCES 
storage_volumes_snapshots (id) ON DELETE CASCADE,
+    UNIQUE (storage_volume_snapshot_id, key)
+);
 
-INSERT INTO schema (version, updated_at) VALUES (24, strftime("%s"))
+INSERT INTO schema (version, updated_at) VALUES (25, strftime("%s"))
 `
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index 7b9763a238..ec584f5c52 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -60,6 +60,35 @@ var updates = map[int]schema.Update{
        22: updateFromV21,
        23: updateFromV22,
        24: updateFromV23,
+       25: updateFromV24,
+}
+
+// Create new storage snapshot tables and migrate data to them.
+func updateFromV24(tx *sql.Tx) error {
+       stmts := `
+CREATE TABLE storage_volumes_snapshots (
+    id INTEGER NOT NULL,
+    storage_volume_id INTEGER NOT NULL,
+    name TEXT NOT NULL,
+    description TEXT,
+    UNIQUE (id),
+    UNIQUE (storage_volume_id, name),
+    FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE 
CASCADE
+);
+CREATE TABLE storage_volumes_snapshots_config (
+    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+    storage_volume_snapshot_id INTEGER NOT NULL,
+    key TEXT NOT NULL,
+    value TEXT,
+    FOREIGN KEY (storage_volume_snapshot_id) REFERENCES 
storage_volumes_snapshots (id) ON DELETE CASCADE,
+    UNIQUE (storage_volume_snapshot_id, key)
+);
+`
+       _, err := tx.Exec(stmts)
+       if err != nil {
+               return errors.Wrap(err, "Failed to create storage snapshots 
tables")
+       }
+       return nil
 }
 
 // The lvm.vg_name config key is required for LVM to function.

From 6d6cf0d4099ddf0297efc1148ad4553b9e61f7fa Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 10:05:02 +0000
Subject: [PATCH 11/38] lxd/db/cluster: drop snapshot column from
 storage_volumes table

This also migrates the data of all non-snapshot storage volumes. Data migration
for snapshot storage volumes will come in a follow-up commit.

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/cluster/schema.go |  1 -
 lxd/db/cluster/update.go | 31 +++++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index 677f23a013..df7ab1e364 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -479,7 +479,6 @@ CREATE TABLE "storage_volumes" (
     node_id INTEGER NOT NULL,
     type INTEGER NOT NULL,
     description TEXT,
-    snapshot INTEGER NOT NULL DEFAULT 0,
     project_id INTEGER NOT NULL,
     UNIQUE (storage_pool_id, node_id, project_id, name, type),
     FOREIGN KEY (storage_pool_id) REFERENCES storage_pools (id) ON DELETE 
CASCADE,
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index ec584f5c52..3e6ca346a2 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -66,6 +66,37 @@ var updates = map[int]schema.Update{
 // Create new storage snapshot tables and migrate data to them.
 func updateFromV24(tx *sql.Tx) error {
        stmts := `
+ALTER TABLE storage_volumes RENAME TO old_storage_volumes;
+CREATE TABLE "storage_volumes" (
+    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+    name TEXT NOT NULL,
+    storage_pool_id INTEGER NOT NULL,
+    node_id INTEGER NOT NULL,
+    type INTEGER NOT NULL,
+    description TEXT,
+    project_id INTEGER NOT NULL,
+    UNIQUE (storage_pool_id, node_id, project_id, name, type),
+    FOREIGN KEY (storage_pool_id) REFERENCES storage_pools (id) ON DELETE 
CASCADE,
+    FOREIGN KEY (node_id) REFERENCES nodes (id) ON DELETE CASCADE,
+    FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
+);
+ALTER TABLE storage_volumes_config RENAME TO old_storage_volumes_config;
+CREATE TABLE storage_volumes_config (
+    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+    storage_volume_id INTEGER NOT NULL,
+    key TEXT NOT NULL,
+    value TEXT,
+    UNIQUE (storage_volume_id, key),
+    FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE 
CASCADE
+);
+INSERT INTO storage_volumes(id, name, storage_pool_id, node_id, type, 
description, project_id)
+   SELECT id, name, storage_pool_id, node_id, type, description, project_id 
FROM old_storage_volumes
+     WHERE snapshot=0;
+INSERT INTO storage_volumes_config
+   SELECT * FROM old_storage_volumes_config
+     WHERE storage_volume_id IN (SELECT id FROM storage_volumes);
+DROP TABLE old_storage_volumes;
+DROP TABLE old_storage_volumes_config;
 CREATE TABLE storage_volumes_snapshots (
     id INTEGER NOT NULL,
     storage_volume_id INTEGER NOT NULL,

From adc57351c41956d229b98dee717718e3be64125f Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 12:30:00 +0000
Subject: [PATCH 12/38] lxd/db/cluster: add storage_volumes_all view

This lists both regular volumes and shapshots, and is virtually equivalent to
the old storage_volumes table.

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/cluster/schema.go | 27 +++++++++++++++++++++++++++
 lxd/db/cluster/update.go | 25 +++++++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index df7ab1e364..903512c6a2 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -485,6 +485,33 @@ CREATE TABLE "storage_volumes" (
     FOREIGN KEY (node_id) REFERENCES nodes (id) ON DELETE CASCADE,
     FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
 );
+CREATE VIEW storage_volumes_all (
+         id,
+         name,
+         storage_pool_id,
+         node_id,
+         type,
+         description,
+         project_id) AS
+  SELECT id,
+         name,
+         storage_pool_id,
+         node_id,
+         type,
+         description,
+         project_id
+    FROM storage_volumes UNION
+  SELECT storage_volumes_snapshots.id,
+         printf('%s/%s',
+    storage_volumes.name,
+    storage_volumes_snapshots.name),
+         storage_volumes.storage_pool_id,
+         storage_volumes.node_id,
+         storage_volumes.type,
+         storage_volumes_snapshots.description,
+         storage_volumes.project_id
+    FROM storage_volumes
+    JOIN storage_volumes_snapshots ON storage_volumes.id = 
storage_volumes_snapshots.storage_volume_id;
 CREATE TABLE storage_volumes_config (
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     storage_volume_id INTEGER NOT NULL,
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index 3e6ca346a2..ab02b7cbc5 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -114,6 +114,31 @@ CREATE TABLE storage_volumes_snapshots_config (
     FOREIGN KEY (storage_volume_snapshot_id) REFERENCES 
storage_volumes_snapshots (id) ON DELETE CASCADE,
     UNIQUE (storage_volume_snapshot_id, key)
 );
+CREATE VIEW storage_volumes_all (
+         id,
+         name,
+         storage_pool_id,
+         node_id,
+         type,
+         description,
+         project_id) AS
+  SELECT id,
+         name,
+         storage_pool_id,
+         node_id,
+         type,
+         description,
+         project_id
+    FROM storage_volumes UNION
+  SELECT storage_volumes_snapshots.id,
+         printf('%s/%s', storage_volumes.name, storage_volumes_snapshots.name),
+         storage_volumes.storage_pool_id,
+         storage_volumes.node_id,
+         storage_volumes.type,
+         storage_volumes_snapshots.description,
+         storage_volumes.project_id
+    FROM storage_volumes
+    JOIN storage_volumes_snapshots ON storage_volumes.id = 
storage_volumes_snapshots.storage_volume_id;
 `
        _, err := tx.Exec(stmts)
        if err != nil {

From 8773c85517757edc32309d6cdf763efe7d139bba Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:02:15 +0000
Subject: [PATCH 13/38] lxd/db/schema: include triggers when generating SQL for
 fresh schemas

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/schema/query.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/db/schema/query.go b/lxd/db/schema/query.go
index 6672cdd140..f0e1eed8f0 100644
--- a/lxd/db/schema/query.go
+++ b/lxd/db/schema/query.go
@@ -50,7 +50,7 @@ SELECT version FROM schema ORDER BY version
 func selectTablesSQL(tx *sql.Tx) ([]string, error) {
        statement := `
 SELECT sql FROM sqlite_master WHERE
-  type IN ('table', 'index', 'view') AND
+  type IN ('table', 'index', 'view', 'trigger') AND
   name != 'schema' AND
   name NOT LIKE 'sqlite_%'
 ORDER BY name

From c2868fcd534be1f54db4a29e71f9bfc56be63208 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:03:47 +0000
Subject: [PATCH 14/38] lxd/db/cluster: add triggers to check that volume IDs
 don't overlap

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/cluster/schema.go | 14 ++++++++++++++
 lxd/db/cluster/update.go | 12 ++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index 903512c6a2..f0f5563101 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -512,6 +512,13 @@ CREATE VIEW storage_volumes_all (
          storage_volumes.project_id
     FROM storage_volumes
     JOIN storage_volumes_snapshots ON storage_volumes.id = 
storage_volumes_snapshots.storage_volume_id;
+CREATE TRIGGER storage_volumes_check_id
+  BEFORE INSERT ON storage_volumes
+  WHEN NEW.id IN (SELECT id FROM storage_volumes_snapshots)
+  BEGIN
+    SELECT RAISE(FAIL,
+    "invalid ID");
+  END;
 CREATE TABLE storage_volumes_config (
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     storage_volume_id INTEGER NOT NULL,
@@ -529,6 +536,13 @@ CREATE TABLE storage_volumes_snapshots (
     UNIQUE (storage_volume_id, name),
     FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE 
CASCADE
 );
+CREATE TRIGGER storage_volumes_snapshots_check_id
+  BEFORE INSERT ON storage_volumes_snapshots
+  WHEN NEW.id IN (SELECT id FROM storage_volumes)
+  BEGIN
+    SELECT RAISE(FAIL,
+    "invalid ID");
+  END;
 CREATE TABLE storage_volumes_snapshots_config (
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     storage_volume_snapshot_id INTEGER NOT NULL,
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index ab02b7cbc5..5febf383ef 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -106,6 +106,18 @@ CREATE TABLE storage_volumes_snapshots (
     UNIQUE (storage_volume_id, name),
     FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE 
CASCADE
 );
+CREATE TRIGGER storage_volumes_check_id
+  BEFORE INSERT ON storage_volumes
+  WHEN NEW.id IN (SELECT id FROM storage_volumes_snapshots)
+  BEGIN
+    SELECT RAISE(FAIL, "invalid ID");
+  END;
+CREATE TRIGGER storage_volumes_snapshots_check_id
+  BEFORE INSERT ON storage_volumes_snapshots
+  WHEN NEW.id IN (SELECT id FROM storage_volumes)
+  BEGIN
+    SELECT RAISE(FAIL, "invalid ID");
+  END;
 CREATE TABLE storage_volumes_snapshots_config (
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
     storage_volume_snapshot_id INTEGER NOT NULL,

From 22a6c9ae4312b5f4a2d316fddc767f16eab24b37 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 10:36:42 +0000
Subject: [PATCH 15/38] lxd/db: change StoragePoolVolumeSnapshotsGetType to
 query the snapshots table

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 5a30af09b0..07cfc90475 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -781,15 +781,21 @@ SELECT storage_volumes.name
 // Returns snapshots slice ordered by when they were created, oldest first.
 func (c *Cluster) StoragePoolVolumeSnapshotsGetType(volumeName string, 
volumeType int, poolID int64) ([]StorageVolumeArgs, error) {
        result := []StorageVolumeArgs{}
-       regexp := volumeName + shared.SnapshotDelimiter
-       length := len(regexp)
 
        // ORDER BY id is important here as the users of this function can 
expect that the results
        // will be returned in the order that the snapshots were created. This 
is specifically used
        // during migration to ensure that the storage engines can re-create 
snapshots using the
        // correct deltas.
-       query := "SELECT name, description FROM storage_volumes WHERE 
storage_pool_id=? AND node_id=? AND type=? AND snapshot=? AND 
SUBSTR(name,1,?)=? ORDER BY id"
-       inargs := []interface{}{poolID, c.nodeID, volumeType, true, length, 
regexp}
+       query := `
+SELECT storage_volumes_snapshots.name, storage_volumes_snapshots.description 
FROM storage_volumes_snapshots
+  JOIN storage_volumes ON storage_volumes_snapshots.storage_volume_id = 
storage_volumes.id
+  WHERE storage_volumes.storage_pool_id=?
+    AND storage_volumes.node_id=?
+    AND storage_volumes.type=?
+    AND storage_volumes.name=?
+  ORDER BY id
+`
+       inargs := []interface{}{poolID, c.nodeID, volumeType, volumeName}
        typeGuide := StorageVolumeArgs{} // StorageVolume struct used to guide 
the types expected.
        outfmt := []interface{}{typeGuide.Name, typeGuide.Description}
        dbResults, err := queryScan(c.db, query, inargs, outfmt)
@@ -799,7 +805,7 @@ func (c *Cluster) 
StoragePoolVolumeSnapshotsGetType(volumeName string, volumeTyp
 
        for _, r := range dbResults {
                row := StorageVolumeArgs{
-                       Name:        r[0].(string),
+                       Name:        volumeName + shared.SnapshotDelimiter + 
r[0].(string),
                        Description: r[1].(string),
                }
                result = append(result, row)

From 5d9bb60a59506288add74dc496e773eb082897e6 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 13:51:47 +0000
Subject: [PATCH 16/38] lxd/db: change StorageVolumeNextSnapshot to query the
 snapshot table

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_volumes.go | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index df77a1835f..80b85db918 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -9,7 +9,6 @@ import (
        "time"
 
        "github.com/lxc/lxd/lxd/db/query"
-       "github.com/lxc/lxd/shared"
        "github.com/pkg/errors"
 )
 
@@ -150,17 +149,23 @@ func (c *Cluster) StorageVolumeDescriptionGet(volumeID 
int64) (string, error) {
        return description.String, nil
 }
 
-// StorageVolumeNextSnapshot returns the index the next snapshot of the storage
+// StorageVolumeNextSnapshot returns the index of the next snapshot of the 
storage
 // volume with the given name should have.
 //
 // Note, the code below doesn't deal with snapshots of snapshots.
 // To do that, we'll need to weed out based on # slashes in names
 func (c *Cluster) StorageVolumeNextSnapshot(name string, typ int) int {
-       base := name + shared.SnapshotDelimiter + "snap"
+       base := "snap"
        length := len(base)
-       q := fmt.Sprintf("SELECT name FROM storage_volumes WHERE type=? AND 
snapshot=? AND SUBSTR(name,1,?)=?")
+       q := fmt.Sprintf(`
+SELECT storage_volumes_snapshots.name FROM storage_volumes_snapshots
+  JOIN storage_volumes ON 
storage_volumes_snapshots.storage_volume_id=storage_volumes.id
+ WHERE storage_volumes.type=?
+   AND storage_volumes.name=?
+   AND SUBSTR(storage_volumes_snapshots.name,1,?)=?
+`)
        var numstr string
-       inargs := []interface{}{typ, true, length, base}
+       inargs := []interface{}{typ, name, length, base}
        outfmt := []interface{}{numstr}
        results, err := queryScan(c.db, q, inargs, outfmt)
        if err != nil {
@@ -169,11 +174,7 @@ func (c *Cluster) StorageVolumeNextSnapshot(name string, 
typ int) int {
        max := 0
 
        for _, r := range results {
-               numstr = r[0].(string)
-               if len(numstr) <= length {
-                       continue
-               }
-               substr := numstr[length:]
+               substr := r[0].(string)
                var num int
                count, err := fmt.Sscanf(substr, "%d", &num)
                if err != nil || count != 1 {

From cd96100d804e3c68b6114bcd7db5727f7ac3e11c Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Fri, 14 Feb 2020 12:34:30 +0000
Subject: [PATCH 17/38] lxd/db: update StorageVolumeNodeAddresses to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_volumes.go | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 80b85db918..6662050c82 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -35,6 +35,8 @@ type StorageVolumeArgs struct {
 // StorageVolumeNodeAddresses returns the addresses of all nodes on which the
 // volume with the given name if defined.
 //
+// The volume name can be either a regular name or a volume snapshot name.
+//
 // The empty string is used in place of the address of the current node.
 func (c *ClusterTx) StorageVolumeNodeAddresses(poolID int64, project, name 
string, typ int) ([]string, error) {
        nodes := []struct {
@@ -52,9 +54,12 @@ func (c *ClusterTx) StorageVolumeNodeAddresses(poolID int64, 
project, name strin
        sql := `
 SELECT nodes.id, nodes.address
   FROM nodes
-  JOIN storage_volumes ON storage_volumes.node_id=nodes.id
-  JOIN projects ON projects.id = storage_volumes.project_id
- WHERE storage_volumes.storage_pool_id=? AND projects.name=? AND 
storage_volumes.name=? AND storage_volumes.type=?
+  JOIN storage_volumes_all ON storage_volumes_all.node_id=nodes.id
+  JOIN projects ON projects.id = storage_volumes_all.project_id
+ WHERE storage_volumes_all.storage_pool_id=?
+   AND projects.name=?
+   AND storage_volumes_all.name=?
+   AND storage_volumes_all.type=?
 `
        stmt, err := c.tx.Prepare(sql)
        if err != nil {

From 25c9ebaba9067eb4cc165629c22f1f85496591ac Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 12:31:29 +0000
Subject: [PATCH 18/38] lxd/db: update storagePoolVolumeGetTypeID to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 07cfc90475..4de793afe2 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -1028,12 +1028,12 @@ INSERT INTO storage_volumes (storage_pool_id, node_id, 
type, snapshot, name, des
 // volume type, on the given node.
 func (c *Cluster) storagePoolVolumeGetTypeID(project string, volumeName 
string, volumeType int, poolID, nodeID int64) (int64, error) {
        volumeID := int64(-1)
-       query := `SELECT storage_volumes.id
-FROM storage_volumes
-JOIN storage_pools ON storage_volumes.storage_pool_id = storage_pools.id
-JOIN projects ON storage_volumes.project_id = projects.id
-WHERE projects.name=? AND storage_volumes.storage_pool_id=? AND 
storage_volumes.node_id=?
-AND storage_volumes.name=? AND storage_volumes.type=?`
+       query := `SELECT storage_volumes_all.id
+FROM storage_volumes_all
+JOIN storage_pools ON storage_volumes_all.storage_pool_id = storage_pools.id
+JOIN projects ON storage_volumes_all.project_id = projects.id
+WHERE projects.name=? AND storage_volumes_all.storage_pool_id=? AND 
storage_volumes_all.node_id=?
+AND storage_volumes_all.name=? AND storage_volumes_all.type=?`
        inargs := []interface{}{project, poolID, nodeID, volumeName, volumeType}
        outargs := []interface{}{&volumeID}
 

From 21130a84e99b2d741a5b3398841dbdb68cbde41c Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 12:36:53 +0000
Subject: [PATCH 19/38] lxd/db: update storageVolumeNodeGet to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_volumes.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 6662050c82..d7e39c88b6 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -93,9 +93,9 @@ SELECT nodes.id, nodes.address
 func (c *Cluster) storageVolumeNodeGet(volumeID int64) (string, error) {
        name := ""
        query := `
-SELECT nodes.name FROM storage_volumes
-  JOIN nodes ON nodes.id=storage_volumes.node_id
-   WHERE storage_volumes.id=?
+SELECT nodes.name FROM storage_volumes_all
+  JOIN nodes ON nodes.id=storage_volumes_all.node_id
+   WHERE storage_volumes_all.id=?
 `
        inargs := []interface{}{volumeID}
        outargs := []interface{}{&name}

From b60deea15973b7ba2e24e4415487002237dbc165 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 12:37:01 +0000
Subject: [PATCH 20/38] lxd/db: update StorageVolumeDescriptionGet to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_volumes.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index d7e39c88b6..6779dfcbc9 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -139,7 +139,7 @@ func (c *Cluster) storageVolumeConfigGet(volumeID int64) 
(map[string]string, err
 // StorageVolumeDescriptionGet gets the description of a storage volume.
 func (c *Cluster) StorageVolumeDescriptionGet(volumeID int64) (string, error) {
        description := sql.NullString{}
-       query := "SELECT description FROM storage_volumes WHERE id=?"
+       query := "SELECT description FROM storage_volumes_all WHERE id=?"
        inargs := []interface{}{volumeID}
        outargs := []interface{}{&description}
 

From 5c5bff5959349f26813fc276b265cc138e402bb0 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:40:02 +0000
Subject: [PATCH 21/38] lxd/db: update storageVolumeIDsGet to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_volumes.go | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 6779dfcbc9..2a04d01190 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -300,10 +300,13 @@ func storageVolumeConfigClear(tx *sql.Tx, volumeID int64) 
error {
 // given pool, regardless of their node_id column.
 func storageVolumeIDsGet(tx *sql.Tx, project, volumeName string, volumeType 
int, poolID int64) ([]int64, error) {
        ids, err := query.SelectIntegers(tx, `
-SELECT storage_volumes.id
-  FROM storage_volumes
-  JOIN projects ON projects.id = storage_volumes.project_id
- WHERE projects.name=? AND storage_volumes.name=? AND storage_volumes.type=? 
AND storage_pool_id=?
+SELECT storage_volumes_all.id
+  FROM storage_volumes_all
+  JOIN projects ON projects.id = storage_volumes_all.project_id
+ WHERE projects.name=?
+   AND storage_volumes_all.name=?
+   AND storage_volumes_all.type=?
+   AND storage_volumes_all.storage_pool_id=?
 `, project, volumeName, volumeType, poolID)
        if err != nil {
                return nil, err

From 475cde01d9a93526c8552940fb26c12153f74039 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:50:59 +0000
Subject: [PATCH 22/38] lxd/db: update StoragePoolVolumesGetNames to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 4de793afe2..4fae34a6b9 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -669,7 +669,7 @@ func (c *Cluster) StoragePoolDelete(poolName string) 
(*api.StoragePool, error) {
 // a given storage pool.
 func (c *Cluster) StoragePoolVolumesGetNames(poolID int64) ([]string, error) {
        var volumeName string
-       query := "SELECT name FROM storage_volumes WHERE storage_pool_id=? AND 
node_id=?"
+       query := "SELECT name FROM storage_volumes_all WHERE storage_pool_id=? 
AND node_id=?"
        inargs := []interface{}{poolID, c.nodeID}
        outargs := []interface{}{volumeName}
 

From 69b907b878f0031c4a6d4bb806633e194f4a8f23 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:52:02 +0000
Subject: [PATCH 23/38] lxd/db: update StoragePoolVolumesGet to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 4fae34a6b9..02784cfd3b 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -696,9 +696,9 @@ func (c *Cluster) StoragePoolVolumesGet(project string, 
poolID int64, volumeType
                var err error
                nodeIDs, err = query.SelectIntegers(tx.tx, `
 SELECT DISTINCT node_id
-  FROM storage_volumes
-  JOIN projects ON projects.id = storage_volumes.project_id
- WHERE (projects.name=? OR storage_volumes.type=?) AND storage_pool_id=?
+  FROM storage_volumes_all
+  JOIN projects ON projects.id = storage_volumes_all.project_id
+ WHERE (projects.name=? OR storage_volumes_all.type=?) AND 
storage_volumes_all.storage_pool_id=?
 `, project, StoragePoolVolumeTypeCustom, poolID)
                return err
        })

From 3ed52bd61a82aacf64397d36ea2466755554b583 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:53:53 +0000
Subject: [PATCH 24/38] lxd/db: update storagePoolVolumesGetType to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 02784cfd3b..ea91896582 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -755,10 +755,13 @@ func (c *Cluster) storagePoolVolumesGet(project string, 
poolID, nodeID int64, vo
 func (c *Cluster) storagePoolVolumesGetType(project string, volumeType int, 
poolID, nodeID int64) ([]string, error) {
        var poolName string
        query := `
-SELECT storage_volumes.name
-  FROM storage_volumes
-  JOIN projects ON projects.id=storage_volumes.project_id
- WHERE (projects.name=? OR storage_volumes.type=?) AND storage_pool_id=? AND 
node_id=? AND type=?
+SELECT storage_volumes_all.name
+  FROM storage_volumes_all
+  JOIN projects ON projects.id=storage_volumes_all.project_id
+ WHERE (projects.name=? OR storage_volumes_all.type=?)
+   AND storage_volumes_all.storage_pool_id=?
+   AND storage_volumes_all.node_id=?
+   AND storage_volumes_all.type=?
 `
        inargs := []interface{}{project, StoragePoolVolumeTypeCustom, poolID, 
nodeID, volumeType}
        outargs := []interface{}{poolName}

From 992bbdf7fec809f34b987051559e6b400223faf9 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 15:19:06 +0000
Subject: [PATCH 25/38] lxd/db: update InstancePool to use storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/containers.go | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxd/db/containers.go b/lxd/db/containers.go
index 7f0864fce2..c8686db828 100644
--- a/lxd/db/containers.go
+++ b/lxd/db/containers.go
@@ -1096,10 +1096,13 @@ func (c *ClusterTx) InstancePool(project, instanceName 
string) (string, error) {
        poolName := ""
        query := `
 SELECT storage_pools.name FROM storage_pools
-  JOIN storage_volumes ON storage_pools.id=storage_volumes.storage_pool_id
-  JOIN instances ON instances.name=storage_volumes.name
+  JOIN storage_volumes_all ON 
storage_pools.id=storage_volumes_all.storage_pool_id
+  JOIN instances ON instances.name=storage_volumes_all.name
   JOIN projects ON projects.id=instances.project_id
- WHERE projects.name=? AND storage_volumes.node_id=? AND 
storage_volumes.name=? AND storage_volumes.type IN(?,?)
+ WHERE projects.name=?
+   AND storage_volumes_all.node_id=?
+   AND storage_volumes_all.name=?
+   AND storage_volumes_all.type IN(?,?)
 `
        inargs := []interface{}{project, c.nodeID, instanceName, 
StoragePoolVolumeTypeContainer, StoragePoolVolumeTypeVM}
        outargs := []interface{}{&poolName}

From 14daaee3ca71b3b502af33ec2a8ec6ca47d85084 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 15:20:12 +0000
Subject: [PATCH 26/38] lxd/db: update instancePoolSnapshot to use
 storage_volumes_all

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/containers.go | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxd/db/containers.go b/lxd/db/containers.go
index c8686db828..6523d2d407 100644
--- a/lxd/db/containers.go
+++ b/lxd/db/containers.go
@@ -1123,9 +1123,12 @@ func (c *ClusterTx) instancePoolSnapshot(project, 
fullName string) (string, erro
        poolName := ""
        query := `
 SELECT storage_pools.name FROM storage_pools
-  JOIN storage_volumes ON storage_pools.id=storage_volumes.storage_pool_id
-  JOIN projects ON projects.id=storage_volumes.project_id
- WHERE projects.name=? AND storage_volumes.node_id=? AND 
storage_volumes.name=? AND storage_volumes.type IN(?,?)
+  JOIN storage_volumes_all ON 
storage_pools.id=storage_volumes_all.storage_pool_id
+  JOIN projects ON projects.id=storage_volumes_all.project_id
+ WHERE projects.name=?
+   AND storage_volumes_all.node_id=?
+   AND storage_volumes_all.name=?
+   AND storage_volumes_all.type IN(?,?)
 `
        inargs := []interface{}{project, c.nodeID, fullName, 
StoragePoolVolumeTypeContainer, StoragePoolVolumeTypeVM}
        outargs := []interface{}{&poolName}

From f1d4aacd9b5a864794d30753d02bb2d774fa8cec Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:57:38 +0000
Subject: [PATCH 27/38] lxd/db: make StoragePoolVolumeDelete differentiate
 between regular volumes and snapshots

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index ea91896582..5c26a28e67 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -918,9 +918,17 @@ func (c *Cluster) StoragePoolVolumeDelete(project, 
volumeName string, volumeType
                return err
        }
 
+       isSnapshot := strings.Contains(volumeName, shared.SnapshotDelimiter)
+       var stmt string
+       if isSnapshot {
+               stmt = "DELETE FROM storage_volumes_snapshots WHERE id=?"
+       } else {
+               stmt = "DELETE FROM storage_volumes WHERE id=?"
+       }
+
        err = c.Transaction(func(tx *ClusterTx) error {
                err := storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, 
project, volumeName, volumeType, poolID, func(volumeID int64) error {
-                       _, err := tx.tx.Exec("DELETE FROM storage_volumes WHERE 
id=?", volumeID)
+                       _, err := tx.tx.Exec(stmt, volumeID)
                        return err
                })
                return err

From b3169414cec511adabf27599529f91dc1bced004 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:18:38 +0000
Subject: [PATCH 28/38] lxd/db: make storageVolumeConfigGet differentiate
 between regular volumes and snapshots

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   | 4 +++-
 lxd/db/storage_volumes.go | 9 +++++++--
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 5c26a28e67..7f34b578c5 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -832,6 +832,8 @@ func (c *Cluster) storagePoolVolumeGetType(project string, 
volumeName string, vo
                project = "default"
        }
 
+       isSnapshot := strings.Contains(volumeName, shared.SnapshotDelimiter)
+
        volumeID, err := c.storagePoolVolumeGetTypeID(project, volumeName, 
volumeType, poolID, nodeID)
        if err != nil {
                return -1, nil, err
@@ -842,7 +844,7 @@ func (c *Cluster) storagePoolVolumeGetType(project string, 
volumeName string, vo
                return -1, nil, err
        }
 
-       volumeConfig, err := c.storageVolumeConfigGet(volumeID)
+       volumeConfig, err := c.storageVolumeConfigGet(volumeID, isSnapshot)
        if err != nil {
                return -1, nil, err
        }
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 2a04d01190..e173bfae69 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -113,9 +113,14 @@ SELECT nodes.name FROM storage_volumes_all
 }
 
 // Get the config of a storage volume.
-func (c *Cluster) storageVolumeConfigGet(volumeID int64) (map[string]string, 
error) {
+func (c *Cluster) storageVolumeConfigGet(volumeID int64, isSnapshot bool) 
(map[string]string, error) {
        var key, value string
-       query := "SELECT key, value FROM storage_volumes_config WHERE 
storage_volume_id=?"
+       var query string
+       if isSnapshot {
+               query = "SELECT key, value FROM 
storage_volumes_snapshots_config WHERE storage_volume_snapshot_id=?"
+       } else {
+               query = "SELECT key, value FROM storage_volumes_config WHERE 
storage_volume_id=?"
+       }
        inargs := []interface{}{volumeID}
        outargs := []interface{}{key, value}
 

From 6e552278f06ec70884a44c3e88d5e835894915e9 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:26:53 +0000
Subject: [PATCH 29/38] lxd/db: make storageVolumeDescriptionUpdate
 differentiate between regular volumes and snapshots

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   |  4 +++-
 lxd/db/storage_volumes.go | 11 +++++++++--
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 7f34b578c5..54883f0655 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -889,6 +889,8 @@ func (c *Cluster) StoragePoolVolumeUpdateByProject(project, 
volumeName string, v
                return err
        }
 
+       isSnapshot := strings.Contains(volumeName, shared.SnapshotDelimiter)
+
        err = c.Transaction(func(tx *ClusterTx) error {
                err = storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, 
project, volumeName, volumeType, poolID, func(volumeID int64) error {
                        err = storageVolumeConfigClear(tx.tx, volumeID)
@@ -901,7 +903,7 @@ func (c *Cluster) StoragePoolVolumeUpdateByProject(project, 
volumeName string, v
                                return err
                        }
 
-                       return storageVolumeDescriptionUpdate(tx.tx, volumeID, 
volumeDescription)
+                       return storageVolumeDescriptionUpdate(tx.tx, volumeID, 
volumeDescription, isSnapshot)
                })
                if err != nil {
                        return err
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index e173bfae69..8b8565a688 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -263,8 +263,15 @@ func (c *Cluster) StorageVolumeIsAvailable(pool, volume 
string) (bool, error) {
 }
 
 // Updates the description of a storage volume.
-func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description 
string) error {
-       _, err := tx.Exec("UPDATE storage_volumes SET description=? WHERE 
id=?", description, volumeID)
+func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description 
string, isSnapshot bool) error {
+       var table string
+       if isSnapshot {
+               table = "storage_volumes_snapshots"
+       } else {
+               table = "storage_volumes"
+       }
+       stmt := fmt.Sprintf("UPDATE %s SET description=? WHERE id=?", table)
+       _, err := tx.Exec(stmt, description, volumeID)
        return err
 }
 

From ad4789df96988c345f51b181116f95f1e636e734 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:32:23 +0000
Subject: [PATCH 30/38] lxd/db: make storageVolumeConfigAdd differentiate
 between regular volumes and snapshots

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   | 4 ++--
 lxd/db/storage_volumes.go | 9 +++++++--
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 54883f0655..4d0ca4e6b2 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -898,7 +898,7 @@ func (c *Cluster) StoragePoolVolumeUpdateByProject(project, 
volumeName string, v
                                return err
                        }
 
-                       err = storageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig)
+                       err = storageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig, isSnapshot)
                        if err != nil {
                                return err
                        }
@@ -1024,7 +1024,7 @@ INSERT INTO storage_volumes (storage_pool_id, node_id, 
type, snapshot, name, des
                                thisVolumeID = volumeID
                        }
 
-                       err = storageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig)
+                       err = storageVolumeConfigAdd(tx.tx, volumeID, 
volumeConfig, snapshot)
                        if err != nil {
                                tx.tx.Rollback()
                                return err
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 8b8565a688..9016a31f37 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -276,8 +276,13 @@ func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID 
int64, description stri
 }
 
 // Add a new storage volume config into database.
-func storageVolumeConfigAdd(tx *sql.Tx, volumeID int64, volumeConfig 
map[string]string) error {
-       str := "INSERT INTO storage_volumes_config (storage_volume_id, key, 
value) VALUES(?, ?, ?)"
+func storageVolumeConfigAdd(tx *sql.Tx, volumeID int64, volumeConfig 
map[string]string, isSnapshot bool) error {
+       var str string
+       if isSnapshot {
+               str = "INSERT INTO storage_volumes_snapshots_config 
(storage_volume_snapshot_id, key, value) VALUES(?, ?, ?)"
+       } else {
+               str = "INSERT INTO storage_volumes_config (storage_volume_id, 
key, value) VALUES(?, ?, ?)"
+       }
        stmt, err := tx.Prepare(str)
        defer stmt.Close()
        if err != nil {

From 3c7636ef3716310e532cbaee24b590537963beb9 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:36:38 +0000
Subject: [PATCH 31/38] lxd/db: make storageVolumeConfigClear differentiate
 between regular volumes and snapshots

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go   |  2 +-
 lxd/db/storage_volumes.go | 10 ++++++++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 4d0ca4e6b2..734d0aa22a 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -893,7 +893,7 @@ func (c *Cluster) StoragePoolVolumeUpdateByProject(project, 
volumeName string, v
 
        err = c.Transaction(func(tx *ClusterTx) error {
                err = storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, 
project, volumeName, volumeType, poolID, func(volumeID int64) error {
-                       err = storageVolumeConfigClear(tx.tx, volumeID)
+                       err = storageVolumeConfigClear(tx.tx, volumeID, 
isSnapshot)
                        if err != nil {
                                return err
                        }
diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 9016a31f37..c143827334 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -304,8 +304,14 @@ func storageVolumeConfigAdd(tx *sql.Tx, volumeID int64, 
volumeConfig map[string]
 }
 
 // Delete storage volume config.
-func storageVolumeConfigClear(tx *sql.Tx, volumeID int64) error {
-       _, err := tx.Exec("DELETE FROM storage_volumes_config WHERE 
storage_volume_id=?", volumeID)
+func storageVolumeConfigClear(tx *sql.Tx, volumeID int64, isSnapshot bool) 
error {
+       var stmt string
+       if isSnapshot {
+               stmt = "DELETE FROM storage_volumes_snapshots_config WHERE 
storage_volume_snapshot_id=?"
+       } else {
+               stmt = "DELETE FROM storage_volumes_config WHERE 
storage_volume_id=?"
+       }
+       _, err := tx.Exec(stmt, volumeID)
        if err != nil {
                return err
        }

From 61675a451eac6076bb816b92637a5e69bad61964 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 15:00:38 +0000
Subject: [PATCH 32/38] lxd/db: make StoragePoolVolumeRename differentiate
 between regular volumes and snapshots

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index 734d0aa22a..f3c3dff375 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -948,9 +948,19 @@ func (c *Cluster) StoragePoolVolumeRename(project, 
oldVolumeName string, newVolu
                return err
        }
 
+       isSnapshot := strings.Contains(oldVolumeName, shared.SnapshotDelimiter)
+       var stmt string
+       if isSnapshot {
+               parts := strings.Split(newVolumeName, shared.SnapshotDelimiter)
+               newVolumeName = parts[1]
+               stmt = "UPDATE storage_volumes_snapshots SET name=? WHERE id=?"
+       } else {
+               stmt = "UPDATE storage_volumes SET name=? WHERE id=?"
+       }
+
        err = c.Transaction(func(tx *ClusterTx) error {
                err := storagePoolVolumeReplicateIfCeph(tx.tx, volumeID, 
project, oldVolumeName, volumeType, poolID, func(volumeID int64) error {
-                       _, err := tx.tx.Exec("UPDATE storage_volumes SET name=? 
WHERE id=? AND type=?", newVolumeName, volumeID, volumeType)
+                       _, err := tx.tx.Exec(stmt, newVolumeName, volumeID)
                        return err
                })
                return err

From d58515084168f4aee31c728997ee405ef8b491e4 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:42:50 +0000
Subject: [PATCH 33/38] lxd/db: consider snapshots in
 StorageVolumeMoveToLVMThinPoolNameKey

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_volumes.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index c143827334..314d2fef68 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -366,6 +366,10 @@ func (c *Cluster) StorageVolumeMoveToLVMThinPoolNameKey() 
error {
        if err != nil {
                return err
        }
+       err = exec(c.db, "DELETE FROM storage_volumes_snapshots_config WHERE 
key='lvm.thinpool_name';")
+       if err != nil {
+               return err
+       }
 
        return nil
 }

From dfd38d908468fa7fb478d0922bff35a1816357ad Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Tue, 18 Feb 2020 13:25:07 +0000
Subject: [PATCH 34/38] lxd/db: add ClusterTx.storagePoolVolumeGetTypeID()
 method

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 42 +++++++++++++++++++++++++++--------------
 1 file changed, 28 insertions(+), 14 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index f3c3dff375..c60a3fefce 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -1052,25 +1052,39 @@ INSERT INTO storage_volumes (storage_pool_id, node_id, 
type, snapshot, name, des
 // Return the ID of a storage volume on a given storage pool of a given storage
 // volume type, on the given node.
 func (c *Cluster) storagePoolVolumeGetTypeID(project string, volumeName 
string, volumeType int, poolID, nodeID int64) (int64, error) {
-       volumeID := int64(-1)
-       query := `SELECT storage_volumes_all.id
-FROM storage_volumes_all
-JOIN storage_pools ON storage_volumes_all.storage_pool_id = storage_pools.id
-JOIN projects ON storage_volumes_all.project_id = projects.id
-WHERE projects.name=? AND storage_volumes_all.storage_pool_id=? AND 
storage_volumes_all.node_id=?
-AND storage_volumes_all.name=? AND storage_volumes_all.type=?`
-       inargs := []interface{}{project, poolID, nodeID, volumeName, volumeType}
-       outargs := []interface{}{&volumeID}
+       var id int64
+       err := c.Transaction(func(tx *ClusterTx) error {
+               var err error
+               id, err = tx.storagePoolVolumeGetTypeID(project, volumeName, 
volumeType, poolID, nodeID)
+               return err
+       })
+       if err != nil {
+               return -1, err
+       }
+       return id, nil
+}
+
+func (c *ClusterTx) storagePoolVolumeGetTypeID(project string, volumeName 
string, volumeType int, poolID, nodeID int64) (int64, error) {
+       result, err := query.SelectIntegers(c.tx, `
+SELECT storage_volumes_all.id
+  FROM storage_volumes_all
+  JOIN storage_pools ON storage_volumes_all.storage_pool_id = storage_pools.id
+  JOIN projects ON storage_volumes_all.project_id = projects.id
+ WHERE projects.name=?
+   AND storage_volumes_all.storage_pool_id=?
+   AND storage_volumes_all.node_id=?
+   AND storage_volumes_all.name=?
+   AND storage_volumes_all.type=?`, project, poolID, nodeID, volumeName, 
volumeType)
 
-       err := dbQueryRowScan(c.db, query, inargs, outargs)
        if err != nil {
-               if err == sql.ErrNoRows {
-                       return -1, ErrNoSuchObject
-               }
                return -1, err
        }
 
-       return volumeID, nil
+       if len(result) == 0 {
+               return -1, ErrNoSuchObject
+       }
+
+       return int64(result[0]), nil
 }
 
 // StoragePoolNodeVolumeGetTypeID get the ID of a storage volume on a given

From 32df5a1c655aa26cd7fe1e700705291ed907abb6 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 15:11:29 +0000
Subject: [PATCH 35/38] lxd/db: make StoragePoolVolumeCreate differentiate
 between regular volumes and snapshots

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 37 ++++++++++++++++++++++++++++++++++---
 1 file changed, 34 insertions(+), 3 deletions(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index c60a3fefce..f30350ab55 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -1002,6 +1002,13 @@ func storagePoolVolumeReplicateIfCeph(tx *sql.Tx, 
volumeID int64, project, volum
 func (c *Cluster) StoragePoolVolumeCreate(project, volumeName, 
volumeDescription string, volumeType int, snapshot bool, poolID int64, 
volumeConfig map[string]string) (int64, error) {
        var thisVolumeID int64
 
+       var snapshotName string
+       if snapshot {
+               parts := strings.Split(volumeName, shared.SnapshotDelimiter)
+               volumeName = parts[0]
+               snapshotName = parts[1]
+       }
+
        err := c.Transaction(func(tx *ClusterTx) error {
                nodeIDs := []int{int(c.nodeID)}
                driver, err := storagePoolDriverGet(tx.tx, poolID)
@@ -1017,10 +1024,34 @@ func (c *Cluster) StoragePoolVolumeCreate(project, 
volumeName, volumeDescription
                }
 
                for _, nodeID := range nodeIDs {
-                       result, err := tx.tx.Exec(`
-INSERT INTO storage_volumes (storage_pool_id, node_id, type, snapshot, name, 
description, project_id) VALUES (?, ?, ?, ?, ?, ?, (SELECT id FROM projects 
WHERE name = ?))
+                       var result sql.Result
+                       // If we are creating a snapshot, figure out the volume
+                       // ID of the parent.
+                       if snapshot {
+                               parentID, err := tx.storagePoolVolumeGetTypeID(
+                                       project, volumeName, volumeType, 
poolID, int64(nodeID))
+                               if err != nil {
+                                       return errors.Wrap(err, "Find parent 
volume")
+                               }
+                               _, err = tx.tx.Exec(`
+UPDATE sqlite_sequence SET seq = seq + 1 WHERE name = 'storage_volumes'
+`)
+                               if err != nil {
+                                       return errors.Wrap(err, "Increment 
storage volumes sequence")
+                               }
+                               result, err = tx.tx.Exec(`
+INSERT INTO storage_volumes_snapshots (id, storage_volume_id, name, 
description)
+ VALUES ((SELECT seq FROM sqlite_sequence WHERE name = 'storage_volumes' LIMIT 
1), ?, ?, ?)
 `,
-                               poolID, nodeID, volumeType, snapshot, 
volumeName, volumeDescription, project)
+                                       parentID, snapshotName, 
volumeDescription)
+                       } else {
+
+                               result, err = tx.tx.Exec(`
+INSERT INTO storage_volumes (storage_pool_id, node_id, type, name, 
description, project_id)
+ VALUES (?, ?, ?, ?, ?, (SELECT id FROM projects WHERE name = ?))
+`,
+                                       poolID, nodeID, volumeType, volumeName, 
volumeDescription, project)
+                       }
                        if err != nil {
                                return err
                        }

From 05fb1147845c1c5c0aa6ea52f59f11fd48f1ddc5 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 15:17:04 +0000
Subject: [PATCH 36/38] lxd/db: no need to update snapshot names in
 ContainerNodeMove

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/containers.go | 23 +----------------------
 1 file changed, 1 insertion(+), 22 deletions(-)

diff --git a/lxd/db/containers.go b/lxd/db/containers.go
index 6523d2d407..6e855dfda6 100644
--- a/lxd/db/containers.go
+++ b/lxd/db/containers.go
@@ -467,10 +467,6 @@ func (c *ClusterTx) ContainerNodeMove(project, oldName, 
newName, newNode string)
        if err != nil {
                return errors.Wrap(err, "failed to get container's ID")
        }
-       snapshots, err := c.snapshotIDsAndNames(project, oldName)
-       if err != nil {
-               return errors.Wrap(err, "failed to get container's snapshots")
-       }
        node, err := c.NodeByName(newNode)
        if err != nil {
                return errors.Wrap(err, "failed to get new node's info")
@@ -493,7 +489,7 @@ func (c *ClusterTx) ContainerNodeMove(project, oldName, 
newName, newNode string)
                return nil
        }
 
-       // Update the container's and snapshots' storage volume name (since 
this is ceph,
+       // Update the instance's storage volume name (since this is ceph,
        // there's a clone of the volume for each node).
        count, err := c.NodesCount()
        if err != nil {
@@ -511,23 +507,6 @@ func (c *ClusterTx) ContainerNodeMove(project, oldName, 
newName, newNode string)
        if n != int64(count) {
                return fmt.Errorf("unexpected number of updated rows in volumes 
table: %d", n)
        }
-       for _, snapshotName := range snapshots {
-               oldSnapshotName := oldName + shared.SnapshotDelimiter + 
snapshotName
-               newSnapshotName := newName + shared.SnapshotDelimiter + 
snapshotName
-               stmt := "UPDATE storage_volumes SET name=? WHERE name=? AND 
storage_pool_id=? AND type=?"
-               result, err := c.tx.Exec(
-                       stmt, newSnapshotName, oldSnapshotName, poolID, 
StoragePoolVolumeTypeContainer)
-               if err != nil {
-                       return errors.Wrap(err, "failed to update snapshot 
volume")
-               }
-               n, err = result.RowsAffected()
-               if err != nil {
-                       return errors.Wrap(err, "failed to get rows affected by 
snapshot volume update")
-               }
-               if n != int64(count) {
-                       return fmt.Errorf("unexpected number of updated 
snapshots in volumes table: %d", n)
-               }
-       }
 
        return nil
 }

From 68907d21989d5e82a1db180f982d5f18abf1fdd6 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Mon, 17 Feb 2020 14:49:30 +0000
Subject: [PATCH 37/38] lxd/db: copy volume snapshots in
 StoragePoolNodeJoinCeph

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/db/storage_pools.go | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go
index f30350ab55..1995b6cdc0 100644
--- a/lxd/db/storage_pools.go
+++ b/lxd/db/storage_pools.go
@@ -161,6 +161,7 @@ SELECT id FROM storage_volumes WHERE storage_pool_id=? AND 
node_id=?
                return fmt.Errorf("not all ceph volumes were copied")
        }
        for i, otherVolumeID := range otherVolumeIDs {
+               volumeID := volumeIDs[i]
                config, err := query.SelectConfig(
                        c.tx, "storage_volumes_config", "storage_volume_id=?", 
otherVolumeID)
                if err != nil {
@@ -169,11 +170,46 @@ SELECT id FROM storage_volumes WHERE storage_pool_id=? 
AND node_id=?
                for key, value := range config {
                        _, err := c.tx.Exec(`
 INSERT INTO storage_volumes_config(storage_volume_id, key, value) VALUES(?, ?, 
?)
-`, volumeIDs[i], key, value)
+`, volumeID, key, value)
                        if err != nil {
                                return errors.Wrap(err, "failed to copy volume 
config")
                        }
                }
+
+               // Copy volume snapshots as well.
+               otherSnapshotIDs, err := query.SelectIntegers(c.tx,
+                       "SELECT id FROM storage_volumes_snapshots WHERE 
storage_volume_id = ?",
+                       otherVolumeID)
+
+               for _, otherSnapshotID := range otherSnapshotIDs {
+                       _, err := c.tx.Exec("UPDATE sqlite_sequence SET seq = 
seq + 1 WHERE name = 'storage_volumes")
+                       if err != nil {
+                               return errors.Wrap(err, "Increment storage 
volumes sequence")
+                       }
+
+                       result, err := c.tx.Exec(`
+INSERT INTO storage_volumes_snapshots (id, storage_volume_id, name, 
description)
+SELECT (SELECT seq FROM sqlite_sequence WHERE name = 'storage_volumes' LIMIT 
1), ?, name, description
+  FROM storage_volumes_snapshots WHERE id=?
+`, volumeID, otherSnapshotID)
+                       if err != nil {
+                               return errors.Wrap(err, "Copy volume snapshot")
+                       }
+                       snapshotID, err := result.LastInsertId()
+                       if err != nil {
+                               return err
+                       }
+
+                       _, err = c.tx.Exec(`
+INSERT INTO storage_volumes_snapshots_config (storage_volume_snapshot_id, key, 
value)
+SELECT ?, key, value
+  FROM storage_volumes_snapshots_config
+ WHERE storage_volume_snapshot_id=?
+`, snapshotID, otherSnapshotID)
+                       if err != nil {
+                               return errors.Wrap(err, "Copy volume snapshot 
config")
+                       }
+               }
        }
 
        return nil

From 1803e53a897a63c68ab30ccc27480512e08ac7a1 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanay...@canonical.com>
Date: Tue, 18 Feb 2020 16:07:25 +0000
Subject: [PATCH 38/38] lxd: no need to rename snapshot volumes when renaming a
 container

Signed-off-by: Free Ekanayaka <free.ekanay...@canonical.com>
---
 lxd/container_lxc.go | 22 ----------------------
 1 file changed, 22 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 361ff52d6e..c7ec0200e9 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -3681,28 +3681,6 @@ func (c *containerLXC) Rename(newName string) error {
 
                poolID, _, _ := c.storage.GetContainerPoolInfo()
 
-               if !c.IsSnapshot() {
-                       // Rename all the snapshot volumes.
-                       results, err := 
c.state.Cluster.ContainerGetSnapshots(c.project, oldName)
-                       if err != nil {
-                               logger.Error("Failed to get container 
snapshots", ctxMap)
-                               return err
-                       }
-
-                       for _, sname := range results {
-                               // Rename the snapshot volume.
-                               baseSnapName := filepath.Base(sname)
-                               newSnapshotName := newName + 
shared.SnapshotDelimiter + baseSnapName
-
-                               // Rename storage volume for the snapshot.
-                               err = 
c.state.Cluster.StoragePoolVolumeRename(c.project, sname, newSnapshotName, 
storagePoolVolumeTypeContainer, poolID)
-                               if err != nil {
-                                       logger.Error("Failed renaming storage 
volume", ctxMap)
-                                       return err
-                               }
-                       }
-               }
-
                // Rename storage volume for the container.
                err = c.state.Cluster.StoragePoolVolumeRename(c.project, 
oldName, newName, storagePoolVolumeTypeContainer, poolID)
                if err != nil {
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to