The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6334
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) === This PR comes from https://github.com/lxc/lxd/pull/6285, but to make that PR smaller I am breaking it into parts. This PR changes the return type of `StoragePoolVolumeSnapshotsGetType` to return a slice of `StorageVolumeArgs` structs so that getting a list of snapshots with their descriptions only requires a single query, rather than n+1 for queries for the number of snapshots a volume has. Also it explicitly defines the return order of snapshots as this is important for migration, rather than implicitly depend on sqlite returning in creation order.
From 1c404d9fb75f87ee8dd6a5520cd04bd9186c385b Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 14 Oct 2019 12:27:34 +0100 Subject: [PATCH 1/3] lxd/db/storage/pools: StoragePoolVolumeSnapshotsGetType returns StorageVolumeArgs slice Updates StoragePoolVolumeSnapshotsGetType to return a slice of StorageVolumeArgs with Name and Description populated. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/storage_pools.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go index c0ed91eed2..75d20fc250 100644 --- a/lxd/db/storage_pools.go +++ b/lxd/db/storage_pools.go @@ -776,22 +776,26 @@ SELECT storage_volumes.name // StoragePoolVolumeSnapshotsGetType get all snapshots of a storage volume // attached to a given storage pool of a given volume type, on the given node. -func (c *Cluster) StoragePoolVolumeSnapshotsGetType(volumeName string, volumeType int, poolID int64) ([]string, error) { - result := []string{} +func (c *Cluster) StoragePoolVolumeSnapshotsGetType(volumeName string, volumeType int, poolID int64) ([]StorageVolumeArgs, error) { + result := []StorageVolumeArgs{} regexp := volumeName + shared.SnapshotDelimiter length := len(regexp) - query := "SELECT name FROM storage_volumes WHERE storage_pool_id=? AND node_id=? AND type=? AND snapshot=? AND SUBSTR(name,1,?)=?" + query := "SELECT name, description FROM storage_volumes WHERE storage_pool_id=? AND node_id=? AND type=? AND snapshot=? AND SUBSTR(name,1,?)=?" inargs := []interface{}{poolID, c.nodeID, volumeType, true, length, regexp} - outfmt := []interface{}{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) if err != nil { return result, err } for _, r := range dbResults { - result = append(result, r[0].(string)) + row := StorageVolumeArgs{ + Name: r[0].(string), + Description: r[1].(string), + } + result = append(result, row) } return result, nil From 8f7d9c3e2d0fbaf48e9e1a477df06c5e24bb07a6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 22 Oct 2019 11:51:24 +0100 Subject: [PATCH 2/3] lxd/db/storage/pools: Makes StoragePoolVolumeSnapshotsGetType return in volume ID order Adds explicit order by id to SQL statement and comment to add clarity around the expectations of this function's return values. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/storage_pools.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lxd/db/storage_pools.go b/lxd/db/storage_pools.go index 75d20fc250..ade8291d1c 100644 --- a/lxd/db/storage_pools.go +++ b/lxd/db/storage_pools.go @@ -776,12 +776,17 @@ SELECT storage_volumes.name // StoragePoolVolumeSnapshotsGetType get all snapshots of a storage volume // attached to a given storage pool of a given volume type, on the given node. +// Returns snapshots slice ordered by volume ID (i.e effectively the order they were created). func (c *Cluster) StoragePoolVolumeSnapshotsGetType(volumeName string, volumeType int, poolID int64) ([]StorageVolumeArgs, error) { result := []StorageVolumeArgs{} regexp := volumeName + shared.SnapshotDelimiter length := len(regexp) - 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 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} typeGuide := StorageVolumeArgs{} // StorageVolume struct used to guide the types expected. outfmt := []interface{}{typeGuide.Name, typeGuide.Description} From 057314a219b8e10eea5250b423a5368a3c3eee5d Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 22 Oct 2019 12:12:33 +0100 Subject: [PATCH 3/3] lxd: Updates use of StoragePoolVolumeSnapshotsGetType return type change Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/migrate_storage_volumes.go | 6 +++--- lxd/storage_btrfs.go | 6 +++--- lxd/storage_ceph.go | 2 +- lxd/storage_ceph_utils.go | 4 ++-- lxd/storage_cephfs.go | 4 ++-- lxd/storage_dir.go | 4 ++-- lxd/storage_lvm.go | 2 +- lxd/storage_migration.go | 6 +++--- lxd/storage_volumes.go | 4 ++-- lxd/storage_volumes_snapshot.go | 4 ++-- lxd/storage_volumes_utils.go | 4 ++-- lxd/storage_zfs.go | 4 ++-- 12 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lxd/migrate_storage_volumes.go b/lxd/migrate_storage_volumes.go index 1c492dedb2..57b8428525 100644 --- a/lxd/migrate_storage_volumes.go +++ b/lxd/migrate_storage_volumes.go @@ -62,14 +62,14 @@ func (s *migrationSourceWs) DoStorage(migrateOp *operations.Operation) error { if err == nil { poolID, err := state.Cluster.StoragePoolGetID(pool.Name) if err == nil { - for _, name := range snaps { - _, snapVolume, err := state.Cluster.StoragePoolNodeVolumeGetType(name, storagePoolVolumeTypeCustom, poolID) + for _, snap := range snaps { + _, snapVolume, err := state.Cluster.StoragePoolNodeVolumeGetType(snap.Name, storagePoolVolumeTypeCustom, poolID) if err != nil { continue } snapshots = append(snapshots, volumeSnapshotToProtobuf(snapVolume)) - _, snapName, _ := shared.ContainerGetParentAndSnapshotName(name) + _, snapName, _ := shared.ContainerGetParentAndSnapshotName(snap.Name) snapshotNames = append(snapshotNames, snapName) } } diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go index 8690f2a8ae..22196a68a7 100644 --- a/lxd/storage_btrfs.go +++ b/lxd/storage_btrfs.go @@ -780,7 +780,7 @@ func (s *storageBtrfs) StoragePoolVolumeRename(newName string) error { } for _, vol := range volumes { - _, snapshotName, _ := shared.ContainerGetParentAndSnapshotName(vol) + _, snapshotName, _ := shared.ContainerGetParentAndSnapshotName(vol.Name) oldVolumeName := fmt.Sprintf("%s%s%s", s.volume.Name, shared.SnapshotDelimiter, snapshotName) newVolumeName := fmt.Sprintf("%s%s%s", newName, shared.SnapshotDelimiter, snapshotName) @@ -2903,7 +2903,7 @@ func (s *storageBtrfs) doCrossPoolVolumeCopy(sourcePool string, sourceName strin } for _, snap := range snapshots { - srcSnapshotMntPoint := driver.GetStoragePoolVolumeSnapshotMountPoint(sourcePool, snap) + srcSnapshotMntPoint := driver.GetStoragePoolVolumeSnapshotMountPoint(sourcePool, snap.Name) _, err = rsync.LocalCopy(srcSnapshotMntPoint, destVolumeMntPoint, bwlimit, true) if err != nil { @@ -2912,7 +2912,7 @@ func (s *storageBtrfs) doCrossPoolVolumeCopy(sourcePool string, sourceName strin } // create snapshot - _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap) + _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap.Name) err = s.doVolumeSnapshotCreate(s.pool.Name, s.volume.Name, fmt.Sprintf("%s/%s", s.volume.Name, snapOnlyName)) if err != nil { diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go index b6c4d5e29d..948505f12d 100644 --- a/lxd/storage_ceph.go +++ b/lxd/storage_ceph.go @@ -428,7 +428,7 @@ func (s *storageCeph) StoragePoolVolumeDelete() error { } for _, snap := range snapshots { - err := s.doPoolVolumeSnapshotDelete(snap) + err := s.doPoolVolumeSnapshotDelete(snap.Name) if err != nil { return err } diff --git a/lxd/storage_ceph_utils.go b/lxd/storage_ceph_utils.go index 2b3d9bed36..10b197346f 100644 --- a/lxd/storage_ceph_utils.go +++ b/lxd/storage_ceph_utils.go @@ -1949,8 +1949,8 @@ func (s *storageCeph) doCrossPoolVolumeCopy(source *api.StorageVolumeSource) err if !source.VolumeOnly { for _, snap := range snapshots { - _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap) - srcSnapshotMntPoint := driver.GetStoragePoolVolumeSnapshotMountPoint(source.Pool, snap) + _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap.Name) + srcSnapshotMntPoint := driver.GetStoragePoolVolumeSnapshotMountPoint(source.Pool, snap.Name) _, err = rsync.LocalCopy(srcSnapshotMntPoint, dstVolumeMntPoint, bwlimit, true) if err != nil { diff --git a/lxd/storage_cephfs.go b/lxd/storage_cephfs.go index 120a6100b1..e60150b22c 100644 --- a/lxd/storage_cephfs.go +++ b/lxd/storage_cephfs.go @@ -805,8 +805,8 @@ func (s *storageCephFs) StoragePoolVolumeCopy(source *api.StorageVolumeSource) e } for _, snap := range snapshots { - _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap) - err = s.copyVolume(source.Pool, snap, fmt.Sprintf("%s/%s", s.volume.Name, snapOnlyName)) + _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap.Name) + err = s.copyVolume(source.Pool, snap.Name, fmt.Sprintf("%s/%s", s.volume.Name, snapOnlyName)) if err != nil { return err } diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go index da290132c5..b32e6b2c2f 100644 --- a/lxd/storage_dir.go +++ b/lxd/storage_dir.go @@ -1403,8 +1403,8 @@ func (s *storageDir) StoragePoolVolumeCopy(source *api.StorageVolumeSource) erro } for _, snap := range snapshots { - _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap) - err = s.copyVolumeSnapshot(source.Pool, snap, fmt.Sprintf("%s/%s", s.volume.Name, snapOnlyName)) + _, snapOnlyName, _ := shared.ContainerGetParentAndSnapshotName(snap.Name) + err = s.copyVolumeSnapshot(source.Pool, snap.Name, fmt.Sprintf("%s/%s", s.volume.Name, snapOnlyName)) if err != nil { return err } diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go index a3d408f6d2..0bd5337229 100644 --- a/lxd/storage_lvm.go +++ b/lxd/storage_lvm.go @@ -2231,7 +2231,7 @@ func (s *storageLvm) StoragePoolVolumeCopy(source *api.StorageVolumeSource) erro } for _, snap := range snapshots { - err = s.copyVolumeSnapshot(source.Pool, snap) + err = s.copyVolumeSnapshot(source.Pool, snap.Name) if err != nil { return err } diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go index 7bb005266d..6ae562c309 100644 --- a/lxd/storage_migration.go +++ b/lxd/storage_migration.go @@ -68,10 +68,10 @@ func (s rsyncStorageSourceDriver) SendStorageVolume(conn *websocket.Conn, op *op } for _, snap := range snapshots { - wrapper := StorageProgressReader(op, "fs_progress", snap) - path := driver.GetStoragePoolVolumeSnapshotMountPoint(pool.Name, snap) + wrapper := StorageProgressReader(op, "fs_progress", snap.Name) + path := driver.GetStoragePoolVolumeSnapshotMountPoint(pool.Name, snap.Name) path = shared.AddSlash(path) - logger.Debugf("Starting to send storage volume snapshot %s on storage pool %s from %s", snap, pool.Name, path) + logger.Debugf("Starting to send storage volume snapshot %s on storage pool %s from %s", snap.Name, pool.Name, path) err = rsync.Send(volume.Name, path, conn, wrapper, s.rsyncFeatures, bwlimit, state.OS.ExecPath) if err != nil { diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go index b3ab72ec23..7cf8c954da 100644 --- a/lxd/storage_volumes.go +++ b/lxd/storage_volumes.go @@ -1016,7 +1016,7 @@ func storagePoolVolumeTypeDelete(d *Daemon, r *http.Request, volumeTypeName stri switch volumeType { case storagePoolVolumeTypeCustom: - var snapshots []string + var snapshots []db.StorageVolumeArgs // Delete storage volume snapshots snapshots, err = d.cluster.StoragePoolVolumeSnapshotsGetType(volumeName, volumeType, poolID) @@ -1025,7 +1025,7 @@ func storagePoolVolumeTypeDelete(d *Daemon, r *http.Request, volumeTypeName stri } for _, snapshot := range snapshots { - s, err := storagePoolVolumeInit(d.State(), project, poolName, snapshot, volumeType) + s, err := storagePoolVolumeInit(d.State(), project, poolName, snapshot.Name, volumeType) if err != nil { return response.NotFound(err) } diff --git a/lxd/storage_volumes_snapshot.go b/lxd/storage_volumes_snapshot.go index bd77b92bb6..b44e86cdd0 100644 --- a/lxd/storage_volumes_snapshot.go +++ b/lxd/storage_volumes_snapshot.go @@ -201,7 +201,7 @@ func storagePoolVolumeSnapshotsTypeGet(d *Daemon, r *http.Request) response.Resp resultString := []string{} resultMap := []*api.StorageVolumeSnapshot{} for _, volume := range volumes { - _, snapshotName, _ := shared.ContainerGetParentAndSnapshotName(volume) + _, snapshotName, _ := shared.ContainerGetParentAndSnapshotName(volume.Name) if !recursion { apiEndpoint, err := storagePoolVolumeTypeToAPIEndpoint(volumeType) @@ -210,7 +210,7 @@ func storagePoolVolumeSnapshotsTypeGet(d *Daemon, r *http.Request) response.Resp } resultString = append(resultString, fmt.Sprintf("/%s/storage-pools/%s/volumes/%s/%s/snapshots/%s", version.APIVersion, poolName, apiEndpoint, volumeName, snapshotName)) } else { - _, vol, err := d.cluster.StoragePoolNodeVolumeGetType(volume, volumeType, poolID) + _, vol, err := d.cluster.StoragePoolNodeVolumeGetType(volume.Name, volumeType, poolID) if err != nil { continue } diff --git a/lxd/storage_volumes_utils.go b/lxd/storage_volumes_utils.go index aa02763399..ac9fecd908 100644 --- a/lxd/storage_volumes_utils.go +++ b/lxd/storage_volumes_utils.go @@ -647,7 +647,7 @@ func storagePoolVolumeCreateInternal(state *state.State, poolName string, vol *a } for _, snap := range snapshots { - _, snapName, _ := shared.ContainerGetParentAndSnapshotName(snap) + _, snapName, _ := shared.ContainerGetParentAndSnapshotName(snap.Name) _, err := storagePoolVolumeSnapshotCopyInternal(state, poolName, vol, snapName) if err != nil { return err @@ -731,7 +731,7 @@ func storagePoolVolumeSnapshotDBCreateInternal(state *state.State, dbArgs *db.St } // storagePoolVolumeSnapshotsGet returns a list of snapshots of the form <volume>/<snapshot-name>. -func storagePoolVolumeSnapshotsGet(s *state.State, pool string, volume string, volType int) ([]string, error) { +func storagePoolVolumeSnapshotsGet(s *state.State, pool string, volume string, volType int) ([]db.StorageVolumeArgs, error) { poolID, err := s.Cluster.StoragePoolGetID(pool) if err != nil { return nil, err diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go index a124173704..0ddead6d96 100644 --- a/lxd/storage_zfs.go +++ b/lxd/storage_zfs.go @@ -669,7 +669,7 @@ func (s *storageZfs) StoragePoolVolumeUpdate(writable *api.StorageVolumePut, cha return err } - if volumes[len(volumes)-1] != fmt.Sprintf("%s/%s", s.volume.Name, writable.Restore) { + if volumes[len(volumes)-1].Name != fmt.Sprintf("%s/%s", s.volume.Name, writable.Restore) { return fmt.Errorf("ZFS can only restore from the latest snapshot. Delete newer snapshots or copy the snapshot into a new volume instead") } @@ -2834,7 +2834,7 @@ func (s *storageZfs) doCrossPoolStorageVolumeCopy(source *api.StorageVolumeSourc if !source.VolumeOnly { for _, snap := range snapshots { - srcMountPoint := driver.GetStoragePoolVolumeSnapshotMountPoint(source.Pool, snap) + srcMountPoint := driver.GetStoragePoolVolumeSnapshotMountPoint(source.Pool, snap.Name) _, err = rsync.LocalCopy(srcMountPoint, dstMountPoint, bwlimit, true) if err != nil {
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel