This patch introduces the generic method that will perform the disk
template conversion of an instance, named '_ConvertInstanceTemplate'.
This method is not functional yet, since it is not accessible from the
'LUInstanceSetParams' LU. When all the appropriate checks will be made,
it will be exposed to the Logical Unit to support the disk template
conversion feature.
The procedure for converting the disk template works as follows:
- a completely new disk template is generated by calling the
'GenerateDiskTemplate' method. The new template matches the count and
the size, mode, and name parameters of the current instance's disks.
- the new block devices are created by calling the 'CreateDisks' method.
- the data from the old instance's disks are copied to the newly created
disks. In case a disk copy operation fails, the operation aborts and
the newly created disks are removed. The instance will remain intact.
- the original disks are detached from the instance and then are removed
from the cluster config by calling the 'RemoveInstanceDisk' for each
disk of the instance.
- the new disks are added to the cluster config and then attached to the
instance by calling the 'AddInstanceDisk' method for each new disk.
- the old block devices are removed from the node on which they reside
by calling the 'RemoveDisks' method.
Signed-off-by: Dimitris Bliablias <[email protected]>
---
lib/cmdlib/instance.py | 139
+++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 137 insertions(+), 2 deletions(-)
diff --git a/lib/cmdlib/instance.py b/lib/cmdlib/instance.py
index 7522ccf..a2132ee 100644
--- a/lib/cmdlib/instance.py
+++ b/lib/cmdlib/instance.py
@@ -3605,11 +3605,146 @@ class LUInstanceSetParams(LogicalUnit):
utils.CommaJoin(set(res_max + res_min))))
raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
+ def _ConvertInstanceTemplate(self, feedback_fn):
+ """Converts the disk template of an instance.
+
+ This function converts the disk template of an instance. It supports
+ conversions among all the available disk templates except conversions
+ between the LVM-based disk templates, that use their separate code
path.
+ Also, this method does not support conversions that include the
'diskless'
+ template and those targeting the 'blockdev' template.
+
+ @type feedback_fn: callable
+ @param feedback_fn: function used to send feedback back to the caller
+
+ @rtype: NoneType
+ @return: None
+ @raise errors.OpPrereqError: in case of failure
+
+ """
+ feedback_fn("Converting disk template from '%s' to '%s'" %
+ (self.instance.disk_template, self.op.disk_template))
+
+ assert not (self.op.disk_template in (constants.DT_BLOCK,
+ constants.DT_DISKLESS) or
+ self.instance.disk_template == constants.DT_DISKLESS), \
+ ("Unsupported disk template conversion from '%s' to '%s'" %
+ (self.instance.disk_template, self.op.disk_template))
+
+ pnode_uuid = self.instance.primary_node
+ snode_uuid = []
+ if self.op.remote_node_uuid:
+ snode_uuid = [self.op.remote_node_uuid]
+
+ old_disks = self.cfg.GetInstanceDisks(self.instance.uuid)
+
+ feedback_fn("Generating new '%s' disk template..." %
self.op.disk_template)
+ new_disks = GenerateDiskTemplate(self,
+ self.op.disk_template,
+ self.instance.uuid,
+ pnode_uuid,
+ snode_uuid,
+ self.disks_info,
+ None, # to be updated with the
file_path
+ None, # to be updated with the
file_driver
+ 0,
+ feedback_fn,
+ self.diskparams)
+
+ # Create the new block devices for the instance.
+ feedback_fn("Creating new empty disks of type '%s'..." %
+ self.op.disk_template)
+ try:
+ CreateDisks(self, self.instance,
disk_template=self.op.disk_template,
+ disks=new_disks)
+ except errors.OpExecError:
+ self.LogWarning("Device creation failed")
+ self.cfg.ReleaseDRBDMinors(self.instance.uuid)
+ raise
+
+ # Transfer the data from the old to the newly created disks of the
instance.
+ feedback_fn("Populating the new empty disks of type '%s'..." %
+ self.op.disk_template)
+ for idx, (old, new) in enumerate(zip(old_disks, new_disks)):
+ feedback_fn(" - copying data from disk %s (%s), size %s" %
+ (idx, self.instance.disk_template,
+ utils.FormatUnit(new.size, "h")))
+ if self.instance.disk_template == constants.DT_DRBD8:
+ old = old.children[0]
+ result = self.rpc.call_blockdev_convert(pnode_uuid, (old,
self.instance),
+ (new, self.instance))
+ msg = result.fail_msg
+ if msg:
+ # A disk failed to copy. Abort the conversion operation and
rollback
+ # the modifications to the previous state. The instance will
remain
+ # intact.
+ if self.op.disk_template == constants.DT_DRBD8:
+ new = new.children[0]
+ self.Log(" - ERROR: Could not copy disk '%s' to '%s'" %
+ (old.logical_id[1], new.logical_id[1]))
+ try:
+ self.LogInfo("Some disks failed to copy")
+ self.LogInfo("The instance will not be affected, aborting
operation")
+ self.LogInfo("Removing newly created disks of type '%s'..." %
+ self.op.disk_template)
+ RemoveDisks(self, self.instance,
disk_template=self.op.disk_template,
+ disks=new_disks)
+ self.LogInfo("Newly created disks removed successfully")
+ finally:
+ self.cfg.ReleaseDRBDMinors(self.instance.uuid)
+ result.Raise("Error while converting the instance's template")
+
+ # In case of DRBD disk, return its port to the pool
+ # NOTE: this must be done right before the call to cfg.Update!
+ if self.instance.disk_template == constants.DT_DRBD8:
+ for disk in old_disks:
+ tcp_port = disk.logical_id[2]
+ self.cfg.AddTcpUdpPort(tcp_port)
+
+ # Remove old disks from the instance.
+ feedback_fn("Detaching old disks (%s) from the instance and removing"
+ " them from cluster config" % self.instance.disk_template)
+ for old_disk in old_disks:
+ self.cfg.RemoveInstanceDisk(self.instance.uuid, old_disk.uuid)
+
+ # Update the instance structure.
+ self.instance = self.cfg.GetInstanceInfo(self.instance.uuid)
+ # The old disk_template will be needed to remove the old block
devices.
+ old_disk_template = self.instance.disk_template
+ self.instance.disk_template = self.op.disk_template
+ self.cfg.Update(self.instance, feedback_fn)