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

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 91bda8ef1bd5a676818c8f7d3748e3ea18e06741 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 28 May 2020 16:17:38 +0200
Subject: [PATCH 01/14] lxd/db/cluster: Add content_type to storage_volumes

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/db/cluster/schema.go | 12 +++++++----
 lxd/db/cluster/update.go | 43 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index fbf24753fc..138f10b738 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -480,6 +480,7 @@ CREATE TABLE "storage_volumes" (
     type INTEGER NOT NULL,
     description TEXT,
     project_id INTEGER NOT NULL,
+    content_type INTEGER NOT NULL DEFAULT 0,
     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,
@@ -492,14 +493,16 @@ CREATE VIEW storage_volumes_all (
          node_id,
          type,
          description,
-         project_id) AS
+         project_id,
+         content_type) AS
   SELECT id,
          name,
          storage_pool_id,
          node_id,
          type,
          description,
-         project_id
+         project_id,
+         content_type
     FROM storage_volumes UNION
   SELECT storage_volumes_snapshots.id,
          printf('%s/%s',
@@ -509,7 +512,8 @@ CREATE VIEW storage_volumes_all (
          storage_volumes.node_id,
          storage_volumes.type,
          storage_volumes_snapshots.description,
-         storage_volumes.project_id
+         storage_volumes.project_id,
+         storage_volumes.content_type
     FROM storage_volumes
     JOIN storage_volumes_snapshots ON storage_volumes.id = 
storage_volumes_snapshots.storage_volume_id;
 CREATE TRIGGER storage_volumes_check_id
@@ -553,5 +557,5 @@ CREATE TABLE storage_volumes_snapshots_config (
     UNIQUE (storage_volume_snapshot_id, key)
 );
 
-INSERT INTO schema (version, updated_at) VALUES (28, strftime("%s"))
+INSERT INTO schema (version, updated_at) VALUES (29, strftime("%s"))
 `
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index fb2c5c73b7..739c4eee1a 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -65,6 +65,49 @@ var updates = map[int]schema.Update{
        26: updateFromV25,
        27: updateFromV26,
        28: updateFromV27,
+       29: updateFromV28,
+}
+
+// Add content type field to storage volumes
+func updateFromV28(tx *sql.Tx) error {
+       stmts := `ALTER TABLE storage_volumes ADD COLUMN content_type INTEGER 
NOT NULL DEFAULT 0;
+UPDATE storage_volumes SET content_type = 1 WHERE type = 3;
+DROP VIEW storage_volumes_all;
+CREATE VIEW storage_volumes_all (
+         id,
+         name,
+         storage_pool_id,
+         node_id,
+         type,
+         description,
+         project_id,
+         content_type) AS
+  SELECT id,
+         name,
+         storage_pool_id,
+         node_id,
+         type,
+         description,
+         project_id,
+         content_type
+    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,
+         storage_volumes.content_type
+    FROM storage_volumes
+    JOIN storage_volumes_snapshots ON storage_volumes.id = 
storage_volumes_snapshots.storage_volume_id;
+`
+       _, err := tx.Exec(stmts)
+       if err != nil {
+               return errors.Wrap(err, "Failed to add storage volume content 
type")
+       }
+
+       return nil
 }
 
 // Add expiry date to storage volume snapshots

From a8039b596c44a27a740d142ebb54ba4ded24849a Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 28 May 2020 18:45:25 +0200
Subject: [PATCH 02/14] shared/api: Add ContentType to StorageVolume

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 shared/api/storage_pool_volume.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/shared/api/storage_pool_volume.go 
b/shared/api/storage_pool_volume.go
index db916ff0fa..3aa881d9af 100644
--- a/shared/api/storage_pool_volume.go
+++ b/shared/api/storage_pool_volume.go
@@ -52,6 +52,9 @@ type StorageVolume struct {
 
        // API extension: clustering
        Location string `json:"location" yaml:"location"`
+
+       // API extension: custom_block_volumes
+       ContentType string `json:"content_type" yaml:"content_type"`
 }
 
 // StorageVolumePut represents the modifiable fields of a LXD storage volume.

From 8c11799f3e394eeee8cdc808e80538faf2501aee Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 28 May 2020 18:45:48 +0200
Subject: [PATCH 03/14] lxd/db: Add content type to storage volumes

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/db/storage_volumes.go | 51 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index af000ef4a6..04062554de 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -246,11 +246,21 @@ func (c *Cluster) storagePoolVolumeGetType(project 
string, volumeName string, vo
                return -1, nil, err
        }
 
+       volumeContentType, err := c.GetStorageVolumeContentType(volumeID)
+       if err != nil {
+               return -1, nil, err
+       }
+
        volumeTypeName, err := storagePoolVolumeTypeToName(volumeType)
        if err != nil {
                return -1, nil, err
        }
 
+       volumeContentTypeName, err := 
storagePoolVolumeContentTypeToName(volumeContentType)
+       if err != nil {
+               return -1, nil, err
+       }
+
        storageVolume := api.StorageVolume{
                Type: volumeTypeName,
        }
@@ -258,6 +268,7 @@ func (c *Cluster) storagePoolVolumeGetType(project string, 
volumeName string, vo
        storageVolume.Description = volumeDescription
        storageVolume.Config = volumeConfig
        storageVolume.Location = volumeNode
+       storageVolume.ContentType = volumeContentTypeName
 
        return volumeID, &storageVolume, nil
 }
@@ -504,6 +515,16 @@ const (
        StoragePoolVolumeTypeNameCustom    string = "custom"
 )
 
+const (
+       StoragePoolVolumeContentTypeFS = iota
+       StoragePoolVolumeContentTypeBlock
+)
+
+const (
+       StoragePoolVolumeContentTypeNameFS    string = "filesystem"
+       StoragePoolVolumeContentTypeNameBlock string = "block"
+)
+
 // StorageVolumeArgs is a value object holding all db-related details about a
 // storage volume.
 type StorageVolumeArgs struct {
@@ -657,6 +678,24 @@ func (c *Cluster) GetStorageVolumeDescription(volumeID 
int64) (string, error) {
        return description.String, nil
 }
 
+// GetStorageVolumeContentType gets the content type of a storage volume.
+func (c *Cluster) GetStorageVolumeContentType(volumeID int64) (int, error) {
+       var contentType int
+       query := "SELECT content_type FROM storage_volumes_all WHERE id=?"
+       inargs := []interface{}{volumeID}
+       outargs := []interface{}{&contentType}
+
+       err := dbQueryRowScan(c, query, inargs, outargs)
+       if err != nil {
+               if err == sql.ErrNoRows {
+                       return -1, ErrNoSuchObject
+               }
+               return -1, err
+       }
+
+       return contentType, nil
+}
+
 // GetNextStorageVolumeSnapshotIndex returns the index of the next snapshot of 
the storage
 // volume with the given name should have.
 //
@@ -887,3 +926,15 @@ func storagePoolVolumeTypeToName(volumeType int) (string, 
error) {
 
        return "", fmt.Errorf("Invalid storage volume type")
 }
+
+// Convert a volume integer content type code to its human-readable name.
+func storagePoolVolumeContentTypeToName(contentType int) (string, error) {
+       switch contentType {
+       case StoragePoolVolumeContentTypeFS:
+               return StoragePoolVolumeContentTypeNameFS, nil
+       case StoragePoolVolumeContentTypeBlock:
+               return StoragePoolVolumeContentTypeNameBlock, nil
+       }
+
+       return "", fmt.Errorf("Invalid storage volume content type")
+}

From 173c774c7444cc74bc678998c76258f4ce2e9a26 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 28 May 2020 18:46:10 +0200
Subject: [PATCH 04/14] lxc/storage_volume: Show content type when listing
 volumes

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxc/storage_volume.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxc/storage_volume.go b/lxc/storage_volume.go
index 3fde175de5..a1cbba5e65 100644
--- a/lxc/storage_volume.go
+++ b/lxc/storage_volume.go
@@ -1103,7 +1103,7 @@ func (c *cmdStorageVolumeList) Run(cmd *cobra.Command, 
args []string) error {
        data := [][]string{}
        for _, volume := range volumes {
                usedby := strconv.Itoa(len(volume.UsedBy))
-               entry := []string{volume.Type, volume.Name, volume.Description, 
usedby}
+               entry := []string{volume.Type, volume.Name, volume.Description, 
volume.ContentType, usedby}
                if shared.IsSnapshot(volume.Name) {
                        entry[0] = fmt.Sprintf("%s (snapshot)", volume.Type)
                }
@@ -1118,6 +1118,7 @@ func (c *cmdStorageVolumeList) Run(cmd *cobra.Command, 
args []string) error {
                i18n.G("TYPE"),
                i18n.G("NAME"),
                i18n.G("DESCRIPTION"),
+               i18n.G("CONTENT TYPE"),
                i18n.G("USED BY"),
        }
        if resource.server.IsClustered() {

From ad1ab17fa6caa1b68ddc7e11f79723258aafb5aa Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 28 May 2020 18:57:10 +0200
Subject: [PATCH 05/14] shared/version/api: Add API extension
 custom_block_volumes

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 shared/version/api.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/shared/version/api.go b/shared/version/api.go
index 532db6ab0c..d65bf57992 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -213,6 +213,7 @@ var APIExtensions = []string{
        "network_dns_search",
        "container_nic_routed_limits",
        "instance_nic_bridged_vlan",
+       "custom_block_volumes",
 }
 
 // APIExtensionsCount returns the number of available API extensions.

From 86b724c139007628f31396918ef9b621b3e3a4d4 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 28 May 2020 19:09:16 +0200
Subject: [PATCH 06/14] shared/api: Add ContentType to StorageVolumesPost

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 shared/api/storage_pool_volume.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/shared/api/storage_pool_volume.go 
b/shared/api/storage_pool_volume.go
index 3aa881d9af..85950c8461 100644
--- a/shared/api/storage_pool_volume.go
+++ b/shared/api/storage_pool_volume.go
@@ -11,6 +11,9 @@ type StorageVolumesPost struct {
 
        // API extension: storage_api_local_volume_handling
        Source StorageVolumeSource `json:"source" yaml:"source"`
+
+       // API extension: custom_block_volumes
+       ContentType string `json:"content_type" yaml:"content_type"`
 }
 
 // StorageVolumePost represents the fields required to rename a LXD storage 
pool volume

From 2321a93adb2c1846829bda481e56272bf0e28c1c Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Thu, 28 May 2020 19:10:02 +0200
Subject: [PATCH 07/14] lxc/storage_volume: Add -type flag to create

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxc/storage_volume.go | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxc/storage_volume.go b/lxc/storage_volume.go
index a1cbba5e65..a3c6c7d9db 100644
--- a/lxc/storage_volume.go
+++ b/lxc/storage_volume.go
@@ -451,9 +451,10 @@ func (c *cmdStorageVolumeCopy) Run(cmd *cobra.Command, 
args []string) error {
 
 // Create
 type cmdStorageVolumeCreate struct {
-       global        *cmdGlobal
-       storage       *cmdStorage
-       storageVolume *cmdStorageVolume
+       global          *cmdGlobal
+       storage         *cmdStorage
+       storageVolume   *cmdStorageVolume
+       flagContentType string
 }
 
 func (c *cmdStorageVolumeCreate) Command() *cobra.Command {
@@ -464,6 +465,7 @@ func (c *cmdStorageVolumeCreate) Command() *cobra.Command {
                `Create new custom storage volumes`))
 
        cmd.Flags().StringVar(&c.storage.flagTarget, "target", "", 
i18n.G("Cluster member name")+"``")
+       cmd.Flags().StringVar(&c.flagContentType, "type", "filesystem", 
i18n.G("Content type, block or filesystem")+"``")
        cmd.RunE = c.Run
 
        return cmd
@@ -497,6 +499,7 @@ func (c *cmdStorageVolumeCreate) Run(cmd *cobra.Command, 
args []string) error {
        vol := api.StorageVolumesPost{}
        vol.Name = volName
        vol.Type = volType
+       vol.ContentType = c.flagContentType
        vol.Config = map[string]string{}
 
        for i := 2; i < len(args); i++ {

From 7ccf57d235417fb10e01525a4f7ad641782c860f Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Sat, 30 May 2020 20:00:19 +0200
Subject: [PATCH 08/14] lxd/storage: Pass contentType to CreateCustomVolume

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/storage/backend_lxd.go    |  4 ++--
 lxd/storage/backend_mock.go   |  2 +-
 lxd/storage/pool_interface.go |  2 +-
 lxd/storage_volumes.go        | 15 ++++++++++++++-
 4 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 2a2cb7873d..f0cd88d2ea 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -2195,8 +2195,8 @@ func (b *lxdBackend) UpdateImage(fingerprint, newDesc 
string, newConfig map[stri
 }
 
 // CreateCustomVolume creates an empty custom volume.
-func (b *lxdBackend) CreateCustomVolume(projectName string, volName string, 
desc string, config map[string]string, op *operations.Operation) error {
-       logger := logging.AddContext(b.logger, log.Ctx{"project": projectName, 
"volName": volName, "desc": desc, "config": config})
+func (b *lxdBackend) CreateCustomVolume(projectName string, volName string, 
desc string, config map[string]string, contentType drivers.ContentType, op 
*operations.Operation) error {
+       logger := logging.AddContext(b.logger, log.Ctx{"project": projectName, 
"volName": volName, "desc": desc, "config": config, "contentType": contentType})
        logger.Debug("CreateCustomVolume started")
        defer logger.Debug("CreateCustomVolume finished")
 
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index b5dc13083d..8942aed692 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -179,7 +179,7 @@ func (b *mockBackend) UpdateImage(fingerprint, newDesc 
string, newConfig map[str
        return nil
 }
 
-func (b *mockBackend) CreateCustomVolume(projectName string, volName string, 
desc string, config map[string]string, op *operations.Operation) error {
+func (b *mockBackend) CreateCustomVolume(projectName string, volName string, 
desc string, config map[string]string, contentType drivers.ContentType, op 
*operations.Operation) error {
        return nil
 }
 
diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go
index bca6c57fd2..a1048621b7 100644
--- a/lxd/storage/pool_interface.go
+++ b/lxd/storage/pool_interface.go
@@ -67,7 +67,7 @@ type Pool interface {
        UpdateImage(fingerprint string, newDesc string, newConfig 
map[string]string, op *operations.Operation) error
 
        // Custom volumes.
-       CreateCustomVolume(projectName string, volName string, desc string, 
config map[string]string, op *operations.Operation) error
+       CreateCustomVolume(projectName string, volName string, desc string, 
config map[string]string, contentType drivers.ContentType, op 
*operations.Operation) error
        CreateCustomVolumeFromCopy(projectName string, volName, desc string, 
config map[string]string, srcPoolName, srcVolName string, srcVolOnly bool, op 
*operations.Operation) error
        UpdateCustomVolume(projectName string, volName string, newDesc string, 
newConfig map[string]string, op *operations.Operation) error
        RenameCustomVolume(projectName string, volName string, newVolName 
string, op *operations.Operation) error
diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go
index f4c194fe2c..8a47a80a4f 100644
--- a/lxd/storage_volumes.go
+++ b/lxd/storage_volumes.go
@@ -18,6 +18,7 @@ import (
        "github.com/lxc/lxd/lxd/response"
        "github.com/lxc/lxd/lxd/state"
        storagePools "github.com/lxc/lxd/lxd/storage"
+       "github.com/lxc/lxd/lxd/storage/drivers"
        "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
@@ -284,6 +285,10 @@ func storagePoolVolumesTypePost(d *Daemon, r 
*http.Request) response.Response {
                return response.BadRequest(fmt.Errorf("Storage volume names may 
not contain slashes"))
        }
 
+       if !shared.StringInSlice(req.ContentType, []string{"block", 
"filesystem"}) {
+               return response.BadRequest(fmt.Errorf("ContentType needs to be 
\"block\" or \"filesystem\""))
+       }
+
        req.Type = mux.Vars(r)["type"]
 
        // We currently only allow to create storage volumes of type 
storagePoolVolumeTypeCustom.
@@ -333,9 +338,17 @@ func doVolumeCreateOrCopy(d *Daemon, projectName, poolName 
string, req *api.Stor
                return response.SmartError(err)
        }
 
+       var contentType drivers.ContentType
+
+       if req.ContentType == "filesystem" {
+               contentType = drivers.ContentTypeFS
+       } else if req.ContentType == "block" {
+               contentType = drivers.ContentTypeBlock
+       }
+
        run = func(op *operations.Operation) error {
                if req.Source.Name == "" {
-                       return pool.CreateCustomVolume(projectName, req.Name, 
req.Description, req.Config, op)
+                       return pool.CreateCustomVolume(projectName, req.Name, 
req.Description, req.Config, contentType, op)
                }
 
                return pool.CreateCustomVolumeFromCopy(projectName, req.Name, 
req.Description, req.Config, req.Source.Pool, req.Source.Name, 
req.Source.VolumeOnly, op)

From 148a71943b1a73f01fd5ad27ee1feb8fcf059a5d Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Sat, 30 May 2020 21:42:48 +0200
Subject: [PATCH 09/14] lxd/migration: Add content type to VolumeTargetArgs

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/migration/migration_volumes.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/migration/migration_volumes.go 
b/lxd/migration/migration_volumes.go
index 261f7eaab0..57db9dbd3b 100644
--- a/lxd/migration/migration_volumes.go
+++ b/lxd/migration/migration_volumes.go
@@ -39,6 +39,7 @@ type VolumeTargetArgs struct {
        Refresh       bool
        Live          bool
        VolumeSize    int64
+       ContentType   string
 }
 
 // TypesToHeader converts one or more Types to a MigrationHeader. It uses the 
first type argument

From 6a2aa2a2177d0a9b8f8a30165b3fc53902f7f98b Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Sat, 30 May 2020 20:06:54 +0200
Subject: [PATCH 10/14] lxd/storage: Pass contentType to VolumeDBCreate

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/storage/backend_lxd.go | 24 +++++++++++++++++-------
 lxd/storage/utils.go       |  2 +-
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index f0cd88d2ea..e357f67bc5 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -2040,8 +2040,11 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op 
*operations.Operation) e
        // Derive content type from image type. Image types are not the same as 
instance types, so don't use
        // instance type constants for comparison.
        contentType := drivers.ContentTypeFS
+       dbContentType := db.StoragePoolVolumeContentTypeNameFS
+
        if image.Type == "virtual-machine" {
                contentType = drivers.ContentTypeBlock
+               dbContentType = db.StoragePoolVolumeContentTypeNameBlock
        }
 
        // Try and load any existing volume config on this storage pool so we 
can compare filesystems if needed.
@@ -2097,7 +2100,7 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op 
*operations.Operation) e
                }
        }
 
-       err = VolumeDBCreate(b.state, project.Default, b.name, fingerprint, "", 
db.StoragePoolVolumeTypeNameImage, false, volConfig, time.Time{})
+       err = VolumeDBCreate(b.state, project.Default, b.name, fingerprint, "", 
db.StoragePoolVolumeTypeNameImage, false, volConfig, time.Time{}, dbContentType)
        if err != nil {
                return err
        }
@@ -2211,7 +2214,7 @@ func (b *lxdBackend) CreateCustomVolume(projectName 
string, volName string, desc
        }
 
        // Create database entry for new storage volume.
-       err = VolumeDBCreate(b.state, projectName, b.name, volName, desc, 
db.StoragePoolVolumeTypeNameCustom, false, vol.Config(), time.Time{})
+       err = VolumeDBCreate(b.state, projectName, b.name, volName, desc, 
db.StoragePoolVolumeTypeNameCustom, false, vol.Config(), time.Time{}, 
string(contentType))
        if err != nil {
                return err
        }
@@ -2280,6 +2283,13 @@ func (b *lxdBackend) 
CreateCustomVolumeFromCopy(projectName string, volName stri
                desc = srcVolRow.Description
        }
 
+       // Get the source volume's content type.
+       contentType := drivers.ContentTypeFS
+
+       if srcVolRow.ContentType == "block" {
+               contentType = drivers.ContentTypeBlock
+       }
+
        // If we are copying snapshots, retrieve a list of snapshots from 
source volume.
        snapshotNames := []string{}
        if !srcVolOnly {
@@ -2323,7 +2333,7 @@ func (b *lxdBackend) 
CreateCustomVolumeFromCopy(projectName string, volName stri
                }
 
                // Create database entry for new storage volume.
-               err = VolumeDBCreate(b.state, projectName, b.name, volName, 
desc, db.StoragePoolVolumeTypeNameCustom, false, vol.Config(), time.Time{})
+               err = VolumeDBCreate(b.state, projectName, b.name, volName, 
desc, db.StoragePoolVolumeTypeNameCustom, false, vol.Config(), time.Time{}, 
string(contentType))
                if err != nil {
                        return err
                }
@@ -2335,7 +2345,7 @@ func (b *lxdBackend) 
CreateCustomVolumeFromCopy(projectName string, volName stri
                                newSnapshotName := 
drivers.GetSnapshotVolumeName(volName, snapName)
 
                                // Create database entry for new storage volume 
snapshot.
-                               err = VolumeDBCreate(b.state, projectName, 
b.name, newSnapshotName, desc, db.StoragePoolVolumeTypeNameCustom, true, 
vol.Config(), time.Time{})
+                               err = VolumeDBCreate(b.state, projectName, 
b.name, newSnapshotName, desc, db.StoragePoolVolumeTypeNameCustom, true, 
vol.Config(), time.Time{}, string(contentType))
                                if err != nil {
                                        return err
                                }
@@ -2471,7 +2481,7 @@ func (b *lxdBackend) 
CreateCustomVolumeFromMigration(projectName string, conn io
        }
 
        // Create database entry for new storage volume.
-       err = VolumeDBCreate(b.state, projectName, b.name, args.Name, 
args.Description, db.StoragePoolVolumeTypeNameCustom, false, vol.Config(), 
time.Time{})
+       err = VolumeDBCreate(b.state, projectName, b.name, args.Name, 
args.Description, db.StoragePoolVolumeTypeNameCustom, false, vol.Config(), 
time.Time{}, args.ContentType)
        if err != nil {
                return err
        }
@@ -2483,7 +2493,7 @@ func (b *lxdBackend) 
CreateCustomVolumeFromMigration(projectName string, conn io
                        newSnapshotName := 
drivers.GetSnapshotVolumeName(args.Name, snapName)
 
                        // Create database entry for new storage volume 
snapshot.
-                       err = VolumeDBCreate(b.state, projectName, b.name, 
newSnapshotName, args.Description, db.StoragePoolVolumeTypeNameCustom, true, 
vol.Config(), time.Time{})
+                       err = VolumeDBCreate(b.state, projectName, b.name, 
newSnapshotName, args.Description, db.StoragePoolVolumeTypeNameCustom, true, 
vol.Config(), time.Time{}, args.ContentType)
                        if err != nil {
                                return err
                        }
@@ -2850,7 +2860,7 @@ func (b *lxdBackend) 
CreateCustomVolumeSnapshot(projectName, volName string, new
        }
 
        // Create database entry for new storage volume snapshot.
-       err = VolumeDBCreate(b.state, projectName, b.name, fullSnapshotName, 
parentVol.Description, db.StoragePoolVolumeTypeNameCustom, true, 
parentVol.Config, newExpiryDate)
+       err = VolumeDBCreate(b.state, projectName, b.name, fullSnapshotName, 
parentVol.Description, db.StoragePoolVolumeTypeNameCustom, true, 
parentVol.Config, newExpiryDate, parentVol.ContentType)
        if err != nil {
                return err
        }
diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index 66b1aed6ec..9501aed2a4 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -137,7 +137,7 @@ func InstanceTypeToVolumeType(instType instancetype.Type) 
(drivers.VolumeType, e
 }
 
 // VolumeDBCreate creates a volume in the database.
-func VolumeDBCreate(s *state.State, project, poolName, volumeName, 
volumeDescription, volumeTypeName string, snapshot bool, volumeConfig 
map[string]string, expiryDate time.Time) error {
+func VolumeDBCreate(s *state.State, project, poolName, volumeName, 
volumeDescription, volumeTypeName string, snapshot bool, volumeConfig 
map[string]string, expiryDate time.Time, contentTypeName string) error {
        // Convert the volume type name to our internal integer representation.
        volDBType, err := VolumeTypeNameToType(volumeTypeName)
        if err != nil {

From 498732b75225682aea3eaf30a3a7a4f364384694 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Tue, 2 Jun 2020 10:18:00 +0200
Subject: [PATCH 11/14] lxd/db: Add contentType arg to CreateStoragePoolVolume

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/db/storage_volumes.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index 04062554de..c9669f1823 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -396,7 +396,7 @@ func storagePoolVolumeReplicateIfCeph(tx *sql.Tx, volumeID 
int64, project, volum
 
 // CreateStoragePoolVolume creates a new storage volume attached to a given
 // storage pool.
-func (c *Cluster) CreateStoragePoolVolume(project, volumeName, 
volumeDescription string, volumeType int, poolID int64, volumeConfig 
map[string]string) (int64, error) {
+func (c *Cluster) CreateStoragePoolVolume(project, volumeName, 
volumeDescription string, volumeType int, poolID int64, volumeConfig 
map[string]string, contentType int) (int64, error) {
        var thisVolumeID int64
 
        if shared.IsSnapshot(volumeName) {
@@ -421,10 +421,10 @@ func (c *Cluster) CreateStoragePoolVolume(project, 
volumeName, volumeDescription
                        var volumeID int64
 
                        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 = ?))
+INSERT INTO storage_volumes (storage_pool_id, node_id, type, name, 
description, project_id, content_type)
+ VALUES (?, ?, ?, ?, ?, (SELECT id FROM projects WHERE name = ?), ?)
 `,
-                               poolID, nodeID, volumeType, volumeName, 
volumeDescription, project)
+                               poolID, nodeID, volumeType, volumeName, 
volumeDescription, project, contentType)
                        if err != nil {
                                return err
                        }

From dafbf9c11a0fcffe2cd2a406427e24d19c161091 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Tue, 2 Jun 2020 10:18:29 +0200
Subject: [PATCH 12/14] *: Pass content type to CreateStoragePoolVolume

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/instance/drivers/driver_lxc.go  |  2 +-
 lxd/instance/drivers/driver_qemu.go |  2 +-
 lxd/patches.go                      | 16 ++++++++--------
 lxd/storage/utils.go                | 18 +++++++++++++++++-
 4 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/lxd/instance/drivers/driver_lxc.go 
b/lxd/instance/drivers/driver_lxc.go
index bbf0394b0c..c6bcc946bf 100644
--- a/lxd/instance/drivers/driver_lxc.go
+++ b/lxd/instance/drivers/driver_lxc.go
@@ -237,7 +237,7 @@ func lxcCreate(s *state.State, args db.InstanceArgs) 
(instance.Instance, error)
        if c.IsSnapshot() {
                _, err = s.Cluster.CreateStorageVolumeSnapshot(args.Project, 
args.Name, "", db.StoragePoolVolumeTypeContainer, poolID, volumeConfig, 
time.Time{})
        } else {
-               _, err = s.Cluster.CreateStoragePoolVolume(args.Project, 
args.Name, "", db.StoragePoolVolumeTypeContainer, poolID, volumeConfig)
+               _, err = s.Cluster.CreateStoragePoolVolume(args.Project, 
args.Name, "", db.StoragePoolVolumeTypeContainer, poolID, volumeConfig, 
db.StoragePoolVolumeContentTypeFS)
        }
        if err != nil {
                c.Delete()
diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index f798c7239c..d9270d04fa 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -250,7 +250,7 @@ func qemuCreate(s *state.State, args db.InstanceArgs) 
(instance.Instance, error)
                _, err = s.Cluster.CreateStorageVolumeSnapshot(args.Project, 
args.Name, "", db.StoragePoolVolumeTypeVM, poolID, volumeConfig, time.Time{})
 
        } else {
-               _, err = s.Cluster.CreateStoragePoolVolume(args.Project, 
args.Name, "", db.StoragePoolVolumeTypeVM, poolID, volumeConfig)
+               _, err = s.Cluster.CreateStoragePoolVolume(args.Project, 
args.Name, "", db.StoragePoolVolumeTypeVM, poolID, volumeConfig, 
db.StoragePoolVolumeContentTypeBlock)
        }
        if err != nil {
                return nil, err
diff --git a/lxd/patches.go b/lxd/patches.go
index 6b2293cd2f..ecb445c3ba 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -570,7 +570,7 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, 
defaultPoolName string,
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for container \"%s\"", ct)
                                return err
@@ -739,7 +739,7 @@ func upgradeFromStorageTypeBtrfs(name string, d *Daemon, 
defaultPoolName string,
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for image \"%s\"", img)
                                return err
@@ -860,7 +860,7 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, 
defaultPoolName string, d
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for container \"%s\"", ct)
                                return err
@@ -1007,7 +1007,7 @@ func upgradeFromStorageTypeDir(name string, d *Daemon, 
defaultPoolName string, d
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for image \"%s\"", img)
                                return err
@@ -1169,7 +1169,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, 
defaultPoolName string, d
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for container \"%s\"", ct)
                                return err
@@ -1513,7 +1513,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, 
defaultPoolName string, d
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for image \"%s\"", img)
                                return err
@@ -1705,7 +1705,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, 
defaultPoolName string, d
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
ct, "", db.StoragePoolVolumeTypeContainer, poolID, containerPoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for container \"%s\"", ct)
                                return err
@@ -1847,7 +1847,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, 
defaultPoolName string, d
                        }
                } else if err == db.ErrNoSuchObject {
                        // Insert storage volumes for containers into the 
database.
-                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig)
+                       _, err := d.cluster.CreateStoragePoolVolume("default", 
img, "", db.StoragePoolVolumeTypeImage, poolID, imagePoolVolumeConfig, 
db.StoragePoolVolumeContentTypeFS)
                        if err != nil {
                                logger.Errorf("Could not insert a storage 
volume for image \"%s\"", img)
                                return err
diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index 9501aed2a4..d4eecd78e0 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -136,6 +136,17 @@ func InstanceTypeToVolumeType(instType instancetype.Type) 
(drivers.VolumeType, e
        return "", fmt.Errorf("Invalid instance type")
 }
 
+func VolumeContentTypeNameToContentType(contentTypeName string) (int, error) {
+       switch contentTypeName {
+       case db.StoragePoolVolumeContentTypeNameFS:
+               return db.StoragePoolVolumeContentTypeFS, nil
+       case db.StoragePoolVolumeContentTypeNameBlock:
+               return db.StoragePoolVolumeContentTypeBlock, nil
+       }
+
+       return -1, fmt.Errorf("Invalid storage volume content type name")
+}
+
 // VolumeDBCreate creates a volume in the database.
 func VolumeDBCreate(s *state.State, project, poolName, volumeName, 
volumeDescription, volumeTypeName string, snapshot bool, volumeConfig 
map[string]string, expiryDate time.Time, contentTypeName string) error {
        // Convert the volume type name to our internal integer representation.
@@ -144,6 +155,11 @@ func VolumeDBCreate(s *state.State, project, poolName, 
volumeName, volumeDescrip
                return err
        }
 
+       volDBContentType, err := 
VolumeContentTypeNameToContentType(contentTypeName)
+       if err != nil {
+               return err
+       }
+
        // Load storage pool the volume will be attached to.
        poolID, poolStruct, err := s.Cluster.GetStoragePool(poolName)
        if err != nil {
@@ -181,7 +197,7 @@ func VolumeDBCreate(s *state.State, project, poolName, 
volumeName, volumeDescrip
        if snapshot {
                _, err = s.Cluster.CreateStorageVolumeSnapshot(project, 
volumeName, volumeDescription, volDBType, poolID, volumeConfig, expiryDate)
        } else {
-               _, err = s.Cluster.CreateStoragePoolVolume(project, volumeName, 
volumeDescription, volDBType, poolID, volumeConfig)
+               _, err = s.Cluster.CreateStoragePoolVolume(project, volumeName, 
volumeDescription, volDBType, poolID, volumeConfig, volDBContentType)
        }
        if err != nil {
                return fmt.Errorf("Error inserting %q of type %q into database 
%q", poolName, volumeTypeName, err)

From 36e03b6a483519a97d098b9a5f177e6d6199704a Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Tue, 2 Jun 2020 13:17:46 +0200
Subject: [PATCH 13/14] lxd/db: Add ContentType to StorageVolumeArgs

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/db/storage_volumes.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lxd/db/storage_volumes.go b/lxd/db/storage_volumes.go
index c9669f1823..ada9134542 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -549,6 +549,8 @@ type StorageVolumeArgs struct {
        // At least on of ProjectID or ProjectName must be set.
        ProjectID   int64
        ProjectName string
+
+       ContentType string
 }
 
 // GetStorageVolumeNodeAddresses returns the addresses of all nodes on which 
the

From f41c516a4a1f8b90dfcb7f18fe7fe8cbaaf1f000 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.h...@canonical.com>
Date: Tue, 2 Jun 2020 13:22:30 +0200
Subject: [PATCH 14/14] lxd/*: Pass correct content type

Signed-off-by: Thomas Hipp <thomas.h...@canonical.com>
---
 lxd/migrate_storage_volumes.go |  1 +
 lxd/storage/backend_lxd.go     | 14 +++++++-------
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/lxd/migrate_storage_volumes.go b/lxd/migrate_storage_volumes.go
index e8e5be36f8..b6886d503c 100644
--- a/lxd/migrate_storage_volumes.go
+++ b/lxd/migrate_storage_volumes.go
@@ -274,6 +274,7 @@ func (c *migrationSink) DoStorage(state *state.State, 
projectName string, poolNa
                        Description:   req.Description,
                        MigrationType: respTypes[0],
                        TrackProgress: true,
+                       ContentType:   string(contentType),
                }
 
                // A zero length Snapshots slice indicates volume only 
migration in
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index e357f67bc5..6027598f34 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -2207,7 +2207,7 @@ func (b *lxdBackend) CreateCustomVolume(projectName 
string, volName string, desc
        volStorageName := project.StorageVolume(projectName, volName)
 
        // Validate config.
-       vol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, 
volStorageName, config)
+       vol := b.newVolume(drivers.VolumeTypeCustom, contentType, 
volStorageName, config)
        err := b.driver.ValidateVolume(vol, false)
        if err != nil {
                return err
@@ -2320,11 +2320,11 @@ func (b *lxdBackend) 
CreateCustomVolumeFromCopy(projectName string, volName stri
 
                // Get the volume name on storage.
                volStorageName := project.StorageVolume(projectName, volName)
-               vol := b.newVolume(drivers.VolumeTypeCustom, 
drivers.ContentTypeFS, volStorageName, config)
+               vol := b.newVolume(drivers.VolumeTypeCustom, contentType, 
volStorageName, config)
 
                // Get the src volume name on storage.
                srcVolStorageName := project.StorageVolume(projectName, 
srcVolName)
-               srcVol := b.newVolume(drivers.VolumeTypeCustom, 
drivers.ContentTypeFS, srcVolStorageName, srcVolRow.Config)
+               srcVol := b.newVolume(drivers.VolumeTypeCustom, contentType, 
srcVolStorageName, srcVolRow.Config)
 
                // Check the supplied config and remove any fields not relevant 
for pool type.
                err := b.driver.ValidateVolume(vol, true)
@@ -2368,9 +2368,9 @@ func (b *lxdBackend) 
CreateCustomVolumeFromCopy(projectName string, volName stri
        logger.Debug("CreateCustomVolumeFromCopy cross-pool mode detected")
 
        // Negotiate the migration type to use.
-       offeredTypes := srcPool.MigrationTypes(drivers.ContentTypeFS, false)
+       offeredTypes := srcPool.MigrationTypes(contentType, false)
        offerHeader := migration.TypesToHeader(offeredTypes...)
-       migrationTypes, err := migration.MatchTypes(offerHeader, 
FallbackMigrationType(drivers.ContentTypeFS), 
b.MigrationTypes(drivers.ContentTypeFS, false))
+       migrationTypes, err := migration.MatchTypes(offerHeader, 
FallbackMigrationType(contentType), b.MigrationTypes(contentType, false))
        if err != nil {
                return fmt.Errorf("Failed to negotiate copy migration type: 
%v", err)
        }
@@ -2405,7 +2405,7 @@ func (b *lxdBackend) 
CreateCustomVolumeFromCopy(projectName string, volName stri
                        Snapshots:     snapshotNames,
                        MigrationType: migrationTypes[0],
                        TrackProgress: false, // Do not use a progress tracker 
on receiver.
-
+                       ContentType:   string(contentType),
                }, op)
 
                if err != nil {
@@ -2474,7 +2474,7 @@ func (b *lxdBackend) 
CreateCustomVolumeFromMigration(projectName string, conn io
        volStorageName := project.StorageVolume(projectName, args.Name)
 
        // Check the supplied config and remove any fields not relevant for 
destination pool type.
-       vol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, 
volStorageName, args.Config)
+       vol := b.newVolume(drivers.VolumeTypeCustom, 
drivers.ContentType(args.ContentType), volStorageName, args.Config)
        err := b.driver.ValidateVolume(vol, true)
        if err != nil {
                return err
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to