[lxc-devel] [lxd/master] Storage volumes snapshots table

freeekanayaka on Github lxc-bot at linuxcontainers.org
Tue Feb 18 16:10:26 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200218/49db3bd0/attachment-0001.bin>
-------------- next part --------------
From 8d34e3adcbbbcc507f3b93330b9edc5145438d7a Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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.ekanayaka at 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 {


More information about the lxc-devel mailing list