The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7714
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 2bb4536597033b57628b3138b35a8155466dfd40 Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@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.h...@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.h...@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.h...@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...) }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel