[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