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

Reply via email to