[lxc-devel] [lxd/master] Handle storage volumes with remote storage
monstermunchkin on Github
lxc-bot at linuxcontainers.org
Fri Jul 31 17:11:23 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/20200731/227e2e87/attachment.bin>
-------------- next part --------------
From 2bb4536597033b57628b3138b35a8155466dfd40 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Thu, 30 Jul 2020 07:58:04 +0200
Subject: [PATCH 1/2] lxd/db/cluster: Update tables to allow null value for
node ID
This changes the affected tables to allow the null value for the node
ID.
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
lxd/db/cluster/schema.go | 6 +-
lxd/db/cluster/update.go | 239 +++++++++++++++++++++++++++++++++++++++
2 files changed, 242 insertions(+), 3 deletions(-)
diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index 701847d41a..fc414fa26d 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -84,7 +84,7 @@ CREATE TABLE images_source (
);
CREATE TABLE "instances" (
id INTEGER primary key AUTOINCREMENT NOT NULL,
- node_id INTEGER NOT NULL,
+ node_id INTEGER,
name TEXT NOT NULL,
architecture INTEGER NOT NULL,
type INTEGER NOT NULL,
@@ -491,7 +491,7 @@ 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,
+ node_id INTEGER,
type INTEGER NOT NULL,
description TEXT,
project_id INTEGER NOT NULL,
@@ -572,5 +572,5 @@ CREATE TABLE storage_volumes_snapshots_config (
UNIQUE (storage_volume_snapshot_id, key)
);
-INSERT INTO schema (version, updated_at) VALUES (33, strftime("%s"))
+INSERT INTO schema (version, updated_at) VALUES (34, strftime("%s"))
`
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index c6fd84cbfb..e9d459ca7c 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -70,6 +70,245 @@ var updates = map[int]schema.Update{
31: updateFromV30,
32: updateFromV31,
33: updateFromV32,
+ 34: updateFromV33,
+}
+
+// Remove multiple entries of the same volume when using remote storage.
+// Also, allow node ID to be null for the instances and storage_volumes tables, and set it to null
+// for instances and storage volumes using remote storage.
+func updateFromV33(tx *sql.Tx) error {
+ stmts := `
+SELECT storage_volumes.id, storage_volumes.name
+FROM storage_volumes
+JOIN storage_pools ON storage_pools.id=storage_volumes.storage_pool_id
+WHERE storage_pools.driver IN ("ceph", "cephfs")
+ORDER BY storage_volumes.name
+`
+
+ // Get the total number of storage volume rows.
+ count, err := query.Count(tx, "storage_volumes", "")
+ if err != nil {
+ return errors.Wrap(err, "Failed to get storage volumes count")
+ }
+
+ volumes := make([]struct {
+ ID int
+ Name string
+ }, count)
+ dest := func(i int) []interface{} {
+ return []interface{}{
+ &volumes[i].ID,
+ &volumes[i].Name,
+ }
+ }
+
+ stmt, err := tx.Prepare(stmts)
+ if err != nil {
+ return errors.Wrap(err, "Failed to prepary storage volume query")
+ }
+
+ err = query.SelectObjects(stmt, dest)
+ if err != nil {
+ return errors.Wrap(err, "Failed to fetch storage volumes")
+ }
+
+ // Remove multiple entries of the same volume when using remote storage
+ for i := 1; i < count; i++ {
+ if volumes[i-1].Name == volumes[i].Name {
+ _, err = tx.Exec(`DELETE FROM storage_volumes WHERE id=?`, volumes[i-1].ID)
+ if err != nil {
+ return errors.Wrap(err, "Failed to delete row from storage_volumes")
+ }
+ }
+ }
+
+ stmts = `
+CREATE TABLE instances_new (
+ id INTEGER primary key AUTOINCREMENT NOT NULL,
+ node_id INTEGER,
+ name TEXT NOT NULL,
+ architecture INTEGER NOT NULL,
+ type INTEGER NOT NULL,
+ ephemeral INTEGER NOT NULL DEFAULT 0,
+ creation_date DATETIME NOT NULL DEFAULT 0,
+ stateful INTEGER NOT NULL DEFAULT 0,
+ last_use_date DATETIME,
+ description TEXT,
+ project_id INTEGER NOT NULL,
+ expiry_date DATETIME,
+ UNIQUE (project_id, name),
+ FOREIGN KEY (node_id) REFERENCES nodes (id) ON DELETE CASCADE,
+ FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
+);
+
+CREATE TABLE storage_volumes_new (
+ id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ name TEXT NOT NULL,
+ storage_pool_id INTEGER NOT NULL,
+ node_id INTEGER,
+ 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,
+ FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE
+);`
+
+ // Create new tables where node ID can be null.
+ _, err = tx.Exec(stmts)
+ if err != nil {
+ return err
+ }
+
+ // Get the total number of instance rows in the instances table.
+ count, err = query.Count(tx, "instances", "")
+ if err != nil {
+ return errors.Wrap(err, "Failed to get instances count")
+ }
+
+ // Fetch all instances rows in the instances table.
+ instances := make([]struct {
+ ID int
+ NodeID int
+ Name string
+ Architecture int
+ Type int
+ Ephemeral int
+ CreationDate time.Time
+ Stateful int
+ LastUseDate time.Time
+ Description string
+ ProjectID int
+ ExpiryDate time.Time
+ }, count)
+ dest = func(i int) []interface{} {
+ return []interface{}{
+ &instances[i].ID,
+ &instances[i].NodeID,
+ &instances[i].Name,
+ &instances[i].Architecture,
+ &instances[i].Type,
+ &instances[i].Ephemeral,
+ &instances[i].CreationDate,
+ &instances[i].Stateful,
+ &instances[i].LastUseDate,
+ &instances[i].Description,
+ &instances[i].ProjectID,
+ &instances[i].ExpiryDate,
+ }
+ }
+
+ stmt, err = tx.Prepare(`SELECT * FROM instances;`)
+ if err != nil {
+ return errors.Wrap(err, "Failed to prepare instance query")
+ }
+
+ err = query.SelectObjects(stmt, dest)
+ if err != nil {
+ return errors.Wrap(err, "Failed to fetch instances")
+ }
+
+ for _, instance := range instances {
+ _, err = tx.Exec(`INSERT INTO instances_new (id, node_id, name, architecture, type, ephemeral, creation_date, stateful, last_use_date, description, project_id, expiry_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`, instance.ID, instance.NodeID, instance.Name, instance.Architecture, instance.Type, instance.Ephemeral, instance.CreationDate, instance.Stateful, instance.LastUseDate, instance.Description, instance.ProjectID, instance.ExpiryDate)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Copy rows from storage_volumes to storage_volumes_new
+ count, err = query.Count(tx, "storage_volumes", "")
+ if err != nil {
+ return errors.Wrap(err, "Failed to get storage_volumes count")
+ }
+
+ storageVolumes := make([]struct {
+ ID int
+ Name string
+ StoragePoolID int
+ NodeID string
+ Type int
+ Description string
+ ProjectID int
+ ContentType int
+ }, count)
+
+ dest = func(i int) []interface{} {
+ return []interface{}{
+ &storageVolumes[i].ID,
+ &storageVolumes[i].Name,
+ &storageVolumes[i].StoragePoolID,
+ &storageVolumes[i].NodeID,
+ &storageVolumes[i].Type,
+ &storageVolumes[i].Description,
+ &storageVolumes[i].ProjectID,
+ &storageVolumes[i].ContentType,
+ }
+ }
+
+ stmt, err = tx.Prepare(`SELECT * FROM storage_volumes;`)
+ if err != nil {
+ return errors.Wrap(err, "Failed to prepare storage volumes query")
+ }
+
+ err = query.SelectObjects(stmt, dest)
+ if err != nil {
+ return errors.Wrap(err, "Failed to fetch storage volumes")
+ }
+
+ for _, storageVolume := range storageVolumes {
+ _, err = tx.Exec(`INSERT INTO storage_volumes_new (id, name, storage_pool_id, node_id, type, description, project_id, content_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?);`, storageVolume.ID, storageVolume.Name, storageVolume.StoragePoolID, storageVolume.NodeID, storageVolume.Type, storageVolume.Description, storageVolume.ProjectID, storageVolume.ContentType)
+ if err != nil {
+ return err
+ }
+ }
+
+ _, err = tx.Exec(`
+PRAGMA foreign_keys = OFF;
+PRAGMA legacy_alter_table = ON;
+
+DROP TABLE instances;
+ALTER TABLE instances_new RENAME TO instances;
+DROP TABLE storage_volumes;
+ALTER TABLE storage_volumes_new RENAME TO storage_volumes;
+
+UPDATE instances
+SET node_id=null
+WHERE id IN (
+ SELECT instances.id from instances
+ JOIN storage_volumes ON instances.name=storage_volumes.name AND instances.type=storage_volumes.type AND instances.project_id=storage_volumes.project_id
+ JOIN storage_pools ON storage_volumes.storage_pool_id=storage_pools.id
+ WHERE storage_pools.driver IN ("ceph", "cephfs")
+);
+
+UPDATE storage_volumes
+SET node_id=null
+WHERE storage_volumes.node_id IN (
+ SELECT node_id FROM storage_volumes
+ JOIN storage_pools ON storage_volumes.storage_pool_id=storage_pools.id
+ WHERE storage_pools.driver IN ("ceph", "cephfs")
+);
+
+PRAGMA foreign_keys = ON;
+PRAGMA legacy_alter_table = OFF;
+
+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 INDEX instances_node_id_idx ON instances (node_id);
+CREATE INDEX instances_project_id_and_name_idx ON instances (project_id, name);
+CREATE INDEX instances_project_id_and_node_id_and_name_idx ON instances (project_id, node_id, name);
+CREATE INDEX instances_project_id_and_node_id_idx ON instances (project_id, node_id);
+CREATE INDEX instances_project_id_idx ON instances (project_id);`)
+ if err != nil {
+ return err
+ }
+
+ return nil
}
// Add type field to networks.
From 7d528803d07a15ebccecda42f0bf569132bc9605 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 31 Jul 2020 12:09:39 +0200
Subject: [PATCH 2/2] lxd/db: Fix volume listing
Don't fail when a volume cannot be found on a certain node, but check
the other nodes as well.
Signed-off-by: Thomas Hipp <thomas.hipp 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 85a045c249..145f0d7202 100644
--- a/lxd/db/storage_volumes.go
+++ b/lxd/db/storage_volumes.go
@@ -106,7 +106,7 @@ SELECT DISTINCT node_id
for _, nodeID := range nodeIDs {
nodeVolumes, err := c.storagePoolVolumesGet(project, poolID, int64(nodeID), volumeTypes)
if err != nil {
- return nil, err
+ continue
}
volumes = append(volumes, nodeVolumes...)
}
More information about the lxc-devel
mailing list