Commit 3d835f7d authored by Aaron Karper's avatar Aaron Karper Committed by Hrvoje Ribicic

Make snapshotting for exports optional

This only uses snapshots if the instance is not down. The reboot (if
any) is delayed until after the export.
Signed-off-by: default avatarAaron Karper <>
Reviewed-by: default avatarHrvoje Ribicic <>

Cherry-picked-from: 01a9df2eSigned-off-by: default avatarHrvoje Ribicic <>
Reviewed-by: default avatarKlaus Aehlig <>
parent 793bad31
......@@ -4854,21 +4854,11 @@ def _GetImportExportIoCommand(instance, mode, ieio, ieargs):
elif ieio == constants.IEIO_RAW_DISK:
(disk, ) = ieargs
real_disk = _OpenRealBD(disk)
if mode == constants.IEM_IMPORT:
# we use nocreat to fail if the device is not already there or we pass a
# wrong path; we use notrunc to no attempt truncate on an LV device
suffix = utils.BuildShellCmd("| dd of=%s conv=nocreat,notrunc bs=%s",
str(constants.DD_BLOCK_SIZE)) # 1 MB
suffix = utils.BuildShellCmd("| %s", disk.Import())
elif mode == constants.IEM_EXPORT:
# the block size on the read dd is 1MiB to match our units
prefix = utils.BuildShellCmd("dd if=%s bs=%s count=%s |",
str(constants.DD_BLOCK_SIZE), # 1 MB
prefix = utils.BuildShellCmd("%s |", disk.Export())
exp_size = disk.size
elif ieio == constants.IEIO_SCRIPT:
......@@ -4937,6 +4927,11 @@ def StartImportExportDaemon(mode, opts, host, port, instance, component,
@param ieioargs: Input/output arguments
# Use Import/Export over socat.
# Export() gives a command that produces a flat stream.
# Import() gives a command that reads a flat stream to a disk template.
if mode == constants.IEM_IMPORT:
prefix = "import"
......@@ -278,13 +278,6 @@ class LUBackupExport(LogicalUnit):
raise errors.ProgrammerError("Unhandled export mode %r" %
# instance disk type verification
# TODO: Implement export support for file-based disks
for disk in self.cfg.GetInstanceDisks(self.instance.uuid):
if disk.dev_type in constants.DTS_FILEBASED:
raise errors.OpPrereqError("Export not supported for instances with"
" file-based disks", errors.ECODE_INVAL)
# Check prerequisites for zeroing
if self.op.zero_free_space:
# Check that user shutdown detection has been enabled
......@@ -423,6 +416,35 @@ class LUBackupExport(LogicalUnit):
feedback_fn("Zeroing completed!")
def StartInstance(self, feedback_fn, src_node_uuid):
"""Send the node instructions to start the instance.
@raise errors.OpExecError: If the instance didn't start up.
assert self.instance.disks_active
feedback_fn("Starting instance %s" %
result = self.rpc.call_instance_start(src_node_uuid,
(self.instance, None, None),
False, self.op.reason)
msg = result.fail_msg
if msg:
feedback_fn("Failed to start instance: %s" % msg)
ShutdownInstanceDisks(self, self.instance)
raise errors.OpExecError("Could not start instance: %s" % msg)
def InstanceDown(self):
"""Returns true iff the instance is shut down during transfer."""
return (self.instance.admin_state != constants.ADMINST_UP or
def DoReboot(self):
"""Returns true iff the instance needs to be started after transfer."""
return (self.op.shutdown and
self.instance.admin_state == constants.ADMINST_UP and
not self.op.remove_instance)
def Exec(self, feedback_fn):
"""Export an instance to an image in the cluster.
......@@ -454,28 +476,16 @@ class LUBackupExport(LogicalUnit):
self.instance = self.cfg.GetInstanceInfo(self.instance.uuid)
snapshot = not self.InstanceDown()
helper = masterd.instance.ExportInstanceHelper(self, feedback_fn,
self.instance, snapshot)
will_be_shut_down = (self.instance.admin_state != constants.ADMINST_UP or
if (not will_be_shut_down or self.op.mode == constants.EXPORT_MODE_LOCAL):
if snapshot:
if (self.op.shutdown and
self.instance.admin_state == constants.ADMINST_UP and
not self.op.remove_instance):
assert self.instance.disks_active
feedback_fn("Starting instance %s" %
result = self.rpc.call_instance_start(src_node_uuid,
(self.instance, None, None),
False, self.op.reason)
msg = result.fail_msg
if msg:
feedback_fn("Failed to start instance: %s" % msg)
ShutdownInstanceDisks(self, self.instance)
raise errors.OpExecError("Could not start instance: %s" % msg)
if self.DoReboot() and snapshot:
self.StartInstance(feedback_fn, src_node_uuid)
if self.op.mode == constants.EXPORT_MODE_LOCAL:
(fin_resu, dresults) = helper.LocalExport(self.dst_node,
......@@ -493,6 +503,9 @@ class LUBackupExport(LogicalUnit):
key_name, dest_ca_pem,
if self.DoReboot() and not snapshot:
self.StartInstance(feedback_fn, src_node_uuid)
......@@ -1149,20 +1149,23 @@ class _RemoteExportCb(ImportExportCbBase):
class ExportInstanceHelper(object):
def __init__(self, lu, feedback_fn, instance):
def __init__(self, lu, feedback_fn, instance, snapshot):
"""Initializes this class.
@param lu: Logical unit instance
@param feedback_fn: Feedback function
@type instance: L{objects.Instance}
@param instance: Instance object
@type snapshot: bool
@param instance: whether the export should use snapshotting
self._lu = lu
self._feedback_fn = feedback_fn
self._instance = instance
self._snapshot = snapshot
self._snap_disks = {}
self._disks_to_transfer = {}
self._removed_snaps = [False] * len(instance.disks)
def CreateSnapshots(self):
......@@ -1171,7 +1174,7 @@ class ExportInstanceHelper(object):
Currently support drbd, plain and ext disk templates.
assert not self._snap_disks
assert not self._disks_to_transfer
instance = self._instance
src_node = instance.primary_node
......@@ -1209,10 +1212,10 @@ class ExportInstanceHelper(object):
logical_id=disk_id, iv_name=disk.iv_name,
assert idx not in self._snap_disks
self._snap_disks[idx] = new_dev
assert idx not in self._disks_to_transfer
self._disks_to_transfer[idx] = new_dev
assert len(self._snap_disks) == len(instance.disks)
assert len(self._disks_to_transfer) == len(instance.disks)
assert len(self._removed_snaps) == len(instance.disks)
def _RemoveSnapshot(self, disk_index):
......@@ -1222,7 +1225,7 @@ class ExportInstanceHelper(object):
@param disk_index: Index of the snapshot to be removed
disk = self._snap_disks.get(disk_index)
disk = self._disks_to_transfer.get(disk_index)
if disk and not self._removed_snaps[disk_index]:
src_node = self._instance.primary_node
src_node_name = self._lu.cfg.GetNodeName(src_node)
......@@ -1251,11 +1254,15 @@ class ExportInstanceHelper(object):
instance = self._instance
src_node_uuid = instance.primary_node
assert len(self._snap_disks) == len(instance.disks)
if not self._snapshot:
disks = self._lu.cfg.GetInstanceDisks(instance.uuid)
self._disks_to_transfer = dict((i, disk) for i, disk in enumerate(disks))
assert len(self._disks_to_transfer) == len(instance.disks)
transfers = []
for idx, dev in self._snap_disks.items():
for idx, dev in self._disks_to_transfer.items():
if not dev:
......@@ -1290,7 +1297,7 @@ class ExportInstanceHelper(object):
self._feedback_fn("Finalizing export on %s" %
result = self._lu.rpc.call_finalize_export(dest_node.uuid, instance,
msg = result.fail_msg
fin_resu = not msg
if msg:
......@@ -703,9 +703,6 @@ class MoveSourceExecutor(object):"Retrieving instance information from source cluster")
instinfo = self._GetInstanceInfo(src_client, mrt.PollJob,
if instinfo["disk_template"] in constants.DTS_FILEBASED:
raise Error("Inter-cluster move of file-based instances is not"
" supported.")"Preparing export on source cluster")
expinfo = self._PrepareExport(src_client, mrt.PollJob,
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment