[lxc-devel] [lxd/master] Implement VM rename

stgraber on Github lxc-bot at linuxcontainers.org
Mon Jan 27 18:59:14 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/20200127/109ccbba/attachment.bin>
-------------- next part --------------
From a2e4eb739269695835fc1db817628a7e95a4b209 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 27 Jan 2020 20:50:01 +0200
Subject: [PATCH 1/6] lxd/instance: Add NetworkUpdateStatic
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/instance/instance_utils.go | 4 ++++
 lxd/networks.go                | 3 +++
 2 files changed, 7 insertions(+)

diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
index 4ad0300638..4fa6984ef6 100644
--- a/lxd/instance/instance_utils.go
+++ b/lxd/instance/instance_utils.go
@@ -48,6 +48,10 @@ var Create func(s *state.State, args db.InstanceArgs) (Instance, error)
 // network related functions into their own package at this time.
 var NetworkGetLeaseAddresses func(s *state.State, network string, hwaddr string) ([]api.InstanceStateNetworkAddress, error)
 
+// NetworkUpdateStatic is linked to main.networkUpdateStatic to limit scope of moving
+// network related functions into their own package at this time.
+var NetworkUpdateStatic func(s *state.State, network string) error
+
 // CompareSnapshots returns a list of snapshots to sync to the target and a list of
 // snapshots to remove from the target. A snapshot will be marked as "to sync" if it either doesn't
 // exist in the target or its creation date is different to the source. A snapshot will be marked
diff --git a/lxd/networks.go b/lxd/networks.go
index 8b2458870e..d05fb81214 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -38,6 +38,9 @@ import (
 func init() {
 	// Link networkGetLeaseAddresses into instance package.
 	instance.NetworkGetLeaseAddresses = networkGetLeaseAddresses
+
+	// Link networkUpdateStatic into instance package.
+	instance.NetworkUpdateStatic = networkUpdateStatic
 }
 
 // Lock to prevent concurent networks creation

From cbd75974b0bfab32e4b4e5f6b3970807e9e4bc6b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 27 Jan 2020 20:50:37 +0200
Subject: [PATCH 2/6] Add maasRename to VM
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/instance/drivers/driver_qemu.go | 35 +++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go
index ef0f5dc99b..e8658aee27 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -3432,6 +3432,41 @@ func (vm *qemu) maasInterfaces(devices map[string]map[string]string) ([]maas.Con
 	return interfaces, nil
 }
 
+func (vm *qemu) maasRename(newName string) error {
+	maasURL, err := cluster.ConfigGetString(vm.state.Cluster, "maas.api.url")
+	if err != nil {
+		return err
+	}
+
+	if maasURL == "" {
+		return nil
+	}
+
+	interfaces, err := vm.maasInterfaces(vm.expandedDevices.CloneNative())
+	if err != nil {
+		return err
+	}
+
+	if len(interfaces) == 0 {
+		return nil
+	}
+
+	if vm.state.MAAS == nil {
+		return fmt.Errorf("Can't perform the operation because MAAS is currently unavailable")
+	}
+
+	exists, err := vm.state.MAAS.DefinedContainer(project.Prefix(vm.project, vm.name))
+	if err != nil {
+		return err
+	}
+
+	if !exists {
+		return vm.maasUpdate(nil)
+	}
+
+	return vm.state.MAAS.RenameContainer(project.Prefix(vm.project, vm.name), project.Prefix(vm.project, newName))
+}
+
 func (vm *qemu) maasDelete() error {
 	maasURL, err := cluster.ConfigGetString(vm.state.Cluster, "maas.api.url")
 	if err != nil {

From 32f264dcca562cec8329edbf067ffd44eddce958 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 27 Jan 2020 20:50:55 +0200
Subject: [PATCH 3/6] lxd/storage/generic: Don't fail rename on missing path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/storage/drivers/generic_vfs.go | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/lxd/storage/drivers/generic_vfs.go b/lxd/storage/drivers/generic_vfs.go
index 64aea3f035..1c3cdabf2c 100644
--- a/lxd/storage/drivers/generic_vfs.go
+++ b/lxd/storage/drivers/generic_vfs.go
@@ -51,26 +51,28 @@ func genericVFSRenameVolume(d Driver, vol Volume, newVolName string, op *operati
 	srcVolumePath := GetVolumeMountPath(d.Name(), vol.volType, vol.name)
 	dstVolumePath := GetVolumeMountPath(d.Name(), vol.volType, newVolName)
 
-	err := os.Rename(srcVolumePath, dstVolumePath)
-	if err != nil {
-		return errors.Wrapf(err, "Failed to rename '%s' to '%s'", srcVolumePath, dstVolumePath)
-	}
-
 	revertRename := true
-	defer func() {
-		if !revertRename {
-			return
+	if shared.PathExists(srcVolumePath) {
+		err := os.Rename(srcVolumePath, dstVolumePath)
+		if err != nil {
+			return errors.Wrapf(err, "Failed to rename '%s' to '%s'", srcVolumePath, dstVolumePath)
 		}
 
-		os.Rename(dstVolumePath, srcVolumePath)
-	}()
+		defer func() {
+			if !revertRename {
+				return
+			}
+
+			os.Rename(dstVolumePath, srcVolumePath)
+		}()
+	}
 
 	// And if present, the snapshots too.
 	srcSnapshotDir := GetVolumeSnapshotDir(d.Name(), vol.volType, vol.name)
 	dstSnapshotDir := GetVolumeSnapshotDir(d.Name(), vol.volType, newVolName)
 
 	if shared.PathExists(srcSnapshotDir) {
-		err = os.Rename(srcSnapshotDir, dstSnapshotDir)
+		err := os.Rename(srcSnapshotDir, dstSnapshotDir)
 		if err != nil {
 			return errors.Wrapf(err, "Failed to rename '%s' to '%s'", srcSnapshotDir, dstSnapshotDir)
 		}

From 8227dc155bc05948fa85ee6a2057ee11ed264739 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 27 Jan 2020 20:51:22 +0200
Subject: [PATCH 4/6] lxd/storage/zfs: Fix block mounts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/storage/drivers/driver_zfs_volumes.go | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go
index 0ab3bd9771..7d26064e3c 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -849,7 +849,18 @@ func (d *zfs) MountVolume(vol Volume, op *operations.Operation) (bool, error) {
 
 	// For block devices, we make them appear.
 	if vol.contentType == ContentTypeBlock {
-		err := d.setDatasetProperties(d.dataset(vol, false), "volmode=dev")
+		current, err := d.getDatasetProperty(d.dataset(vol, false), "volmode")
+		if err != nil {
+			return false, err
+		}
+
+		// Check if already active.
+		if current == "dev" {
+			return false, nil
+		}
+
+		// Activate.
+		err = d.setDatasetProperties(d.dataset(vol, false), "volmode=dev")
 		if err != nil {
 			return false, err
 		}
@@ -857,7 +868,7 @@ func (d *zfs) MountVolume(vol Volume, op *operations.Operation) (bool, error) {
 		// Wait half a second to give udev a chance to kick in.
 		time.Sleep(500 * time.Millisecond)
 
-		return false, nil
+		return true, nil
 	}
 
 	// Check if not already mounted.

From e4bf6710b6e0bf3b687425a2cb92250b728a1488 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 27 Jan 2020 20:51:27 +0200
Subject: [PATCH 5/6] lxd/storage/zfs: Fix renames
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/storage/drivers/driver_zfs_volumes.go | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go
index 7d26064e3c..c8c710c80e 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -949,9 +949,11 @@ func (d *zfs) RenameVolume(vol Volume, newVolName string, op *operations.Operati
 	})
 
 	// Update the mountpoints.
-	err = d.setDatasetProperties(d.dataset(newVol, false), fmt.Sprintf("mountpoint=%s", newVol.MountPath()))
-	if err != nil {
-		return err
+	if vol.contentType == ContentTypeFS {
+		err = d.setDatasetProperties(d.dataset(newVol, false), fmt.Sprintf("mountpoint=%s", newVol.MountPath()))
+		if err != nil {
+			return err
+		}
 	}
 
 	// For VM images, create a filesystem volume too.

From 46d4b25b60c7ec1074361ba97e155eb3653e7fcf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 27 Jan 2020 20:51:36 +0200
Subject: [PATCH 6/6] lxd/vm: Implement Rename
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/instance/drivers/driver_qemu.go | 136 +++++++++++++++++++++++++++-
 1 file changed, 135 insertions(+), 1 deletion(-)

diff --git a/lxd/instance/drivers/driver_qemu.go b/lxd/instance/drivers/driver_qemu.go
index e8658aee27..af48094b85 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -1776,7 +1776,141 @@ func (vm *qemu) Backups() ([]backup.Backup, error) {
 
 // Rename the instance.
 func (vm *qemu) Rename(newName string) error {
-	return fmt.Errorf("Rename Not implemented")
+	oldName := vm.Name()
+	ctxMap := log.Ctx{
+		"project":   vm.project,
+		"name":      vm.name,
+		"created":   vm.creationDate,
+		"ephemeral": vm.ephemeral,
+		"used":      vm.lastUsedDate,
+		"newname":   newName}
+
+	logger.Info("Renaming instance", ctxMap)
+
+	// Sanity checks.
+	if !vm.IsSnapshot() && !shared.ValidHostname(newName) {
+		return fmt.Errorf("Invalid instance name")
+	}
+
+	if vm.IsRunning() {
+		return fmt.Errorf("Renaming of running instance not allowed")
+	}
+
+	// Clean things up.
+	vm.cleanup()
+
+	// Check if we can load new storage layer for pool driver type.
+	pool, err := storagePools.GetPoolByInstance(vm.state, vm)
+	if err != nil {
+		return errors.Wrap(err, "Load instance storage pool")
+	}
+
+	if vm.IsSnapshot() {
+		_, newSnapName, _ := shared.InstanceGetParentAndSnapshotName(newName)
+		err = pool.RenameInstanceSnapshot(vm, newSnapName, nil)
+		if err != nil {
+			return errors.Wrap(err, "Rename instance snapshot")
+		}
+	} else {
+		err = pool.RenameInstance(vm, newName, nil)
+		if err != nil {
+			return errors.Wrap(err, "Rename instance")
+		}
+	}
+
+	if !vm.IsSnapshot() {
+		// Rename all the instance snapshot database entries.
+		results, err := vm.state.Cluster.ContainerGetSnapshots(vm.project, oldName)
+		if err != nil {
+			logger.Error("Failed to get instance snapshots", ctxMap)
+			return err
+		}
+
+		for _, sname := range results {
+			// Rename the snapshot.
+			oldSnapName := strings.SplitN(sname, shared.SnapshotDelimiter, 2)[1]
+			baseSnapName := filepath.Base(sname)
+			err := vm.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
+				return tx.InstanceSnapshotRename(vm.project, oldName, oldSnapName, baseSnapName)
+			})
+			if err != nil {
+				logger.Error("Failed renaming snapshot", ctxMap)
+				return err
+			}
+		}
+	}
+
+	// Rename the instance database entry.
+	err = vm.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
+		if vm.IsSnapshot() {
+			oldParts := strings.SplitN(oldName, shared.SnapshotDelimiter, 2)
+			newParts := strings.SplitN(newName, shared.SnapshotDelimiter, 2)
+			return tx.InstanceSnapshotRename(vm.project, oldParts[0], oldParts[1], newParts[1])
+		}
+
+		return tx.InstanceRename(vm.project, oldName, newName)
+	})
+	if err != nil {
+		logger.Error("Failed renaming instance", ctxMap)
+		return err
+	}
+
+	// Rename the logging path.
+	os.RemoveAll(shared.LogPath(newName))
+	if shared.PathExists(vm.LogPath()) {
+		err := os.Rename(vm.LogPath(), shared.LogPath(newName))
+		if err != nil {
+			logger.Error("Failed renaming instance", ctxMap)
+			return err
+		}
+	}
+
+	// Rename the MAAS entry.
+	if !vm.IsSnapshot() {
+		err = vm.maasRename(newName)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Rename the backups.
+	backups, err := vm.Backups()
+	if err != nil {
+		return err
+	}
+
+	for _, backup := range backups {
+		backupName := strings.Split(backup.Name(), "/")[1]
+		newName := fmt.Sprintf("%s/%s", newName, backupName)
+
+		err = backup.Rename(newName)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Set the new name in the struct.
+	vm.name = newName
+
+	// Update lease files.
+	instance.NetworkUpdateStatic(vm.state, "")
+
+	logger.Info("Renamed instance", ctxMap)
+
+	if vm.IsSnapshot() {
+		vm.state.Events.SendLifecycle(vm.project, "virtual-machine-snapshot-renamed",
+			fmt.Sprintf("/1.0/virtual-machines/%s", oldName), map[string]interface{}{
+				"new_name":      newName,
+				"snapshot_name": oldName,
+			})
+	} else {
+		vm.state.Events.SendLifecycle(vm.project, "virtual-machine-renamed",
+			fmt.Sprintf("/1.0/virtual-machines/%s", oldName), map[string]interface{}{
+				"new_name": newName,
+			})
+	}
+
+	return nil
 }
 
 // Update the instance config.


More information about the lxc-devel mailing list