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 <akarper@google.com>
Reviewed-by: default avatarHrvoje Ribicic <riba@google.com>

Cherry-picked-from: 01a9df2eSigned-off-by: default avatarHrvoje Ribicic <riba@google.com>
Reviewed-by: default avatarKlaus Aehlig <aehlig@google.com>
parent 793bad31
...@@ -4854,21 +4854,11 @@ def _GetImportExportIoCommand(instance, mode, ieio, ieargs): ...@@ -4854,21 +4854,11 @@ def _GetImportExportIoCommand(instance, mode, ieio, ieargs):
elif ieio == constants.IEIO_RAW_DISK: elif ieio == constants.IEIO_RAW_DISK:
(disk, ) = ieargs (disk, ) = ieargs
real_disk = _OpenRealBD(disk)
if mode == constants.IEM_IMPORT: if mode == constants.IEM_IMPORT:
# we use nocreat to fail if the device is not already there or we pass a suffix = utils.BuildShellCmd("| %s", disk.Import())
# wrong path; we use notrunc to no attempt truncate on an LV device
suffix = utils.BuildShellCmd("| dd of=%s conv=nocreat,notrunc bs=%s",
real_disk.dev_path,
str(constants.DD_BLOCK_SIZE)) # 1 MB
elif mode == constants.IEM_EXPORT: elif mode == constants.IEM_EXPORT:
# the block size on the read dd is 1MiB to match our units prefix = utils.BuildShellCmd("%s |", disk.Export())
prefix = utils.BuildShellCmd("dd if=%s bs=%s count=%s |",
real_disk.dev_path,
str(constants.DD_BLOCK_SIZE), # 1 MB
str(disk.size))
exp_size = disk.size exp_size = disk.size
elif ieio == constants.IEIO_SCRIPT: elif ieio == constants.IEIO_SCRIPT:
...@@ -4937,6 +4927,11 @@ def StartImportExportDaemon(mode, opts, host, port, instance, component, ...@@ -4937,6 +4927,11 @@ def StartImportExportDaemon(mode, opts, host, port, instance, component,
@param ieioargs: Input/output arguments @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: if mode == constants.IEM_IMPORT:
prefix = "import" prefix = "import"
......
...@@ -278,13 +278,6 @@ class LUBackupExport(LogicalUnit): ...@@ -278,13 +278,6 @@ class LUBackupExport(LogicalUnit):
raise errors.ProgrammerError("Unhandled export mode %r" % raise errors.ProgrammerError("Unhandled export mode %r" %
self.op.mode) self.op.mode)
# 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 # Check prerequisites for zeroing
if self.op.zero_free_space: if self.op.zero_free_space:
# Check that user shutdown detection has been enabled # Check that user shutdown detection has been enabled
...@@ -423,6 +416,35 @@ class LUBackupExport(LogicalUnit): ...@@ -423,6 +416,35 @@ class LUBackupExport(LogicalUnit):
feedback_fn("Zeroing completed!") 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" % self.instance.name)
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
self.op.shutdown)
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): def Exec(self, feedback_fn):
"""Export an instance to an image in the cluster. """Export an instance to an image in the cluster.
...@@ -454,28 +476,16 @@ class LUBackupExport(LogicalUnit): ...@@ -454,28 +476,16 @@ class LUBackupExport(LogicalUnit):
self.instance = self.cfg.GetInstanceInfo(self.instance.uuid) self.instance = self.cfg.GetInstanceInfo(self.instance.uuid)
try: try:
snapshot = not self.InstanceDown()
helper = masterd.instance.ExportInstanceHelper(self, feedback_fn, helper = masterd.instance.ExportInstanceHelper(self, feedback_fn,
self.instance) self.instance, snapshot)
will_be_shut_down = (self.instance.admin_state != constants.ADMINST_UP or if snapshot:
self.op.shutdown)
if (not will_be_shut_down or self.op.mode == constants.EXPORT_MODE_LOCAL):
helper.CreateSnapshots() helper.CreateSnapshots()
try:
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" % self.instance.name)
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)
try:
if self.DoReboot() and snapshot:
self.StartInstance(feedback_fn, src_node_uuid)
if self.op.mode == constants.EXPORT_MODE_LOCAL: if self.op.mode == constants.EXPORT_MODE_LOCAL:
(fin_resu, dresults) = helper.LocalExport(self.dst_node, (fin_resu, dresults) = helper.LocalExport(self.dst_node,
self.op.compress) self.op.compress)
...@@ -493,6 +503,9 @@ class LUBackupExport(LogicalUnit): ...@@ -493,6 +503,9 @@ class LUBackupExport(LogicalUnit):
key_name, dest_ca_pem, key_name, dest_ca_pem,
self.op.compress, self.op.compress,
timeouts) timeouts)
if self.DoReboot() and not snapshot:
self.StartInstance(feedback_fn, src_node_uuid)
finally: finally:
helper.Cleanup() helper.Cleanup()
......
...@@ -1149,20 +1149,23 @@ class _RemoteExportCb(ImportExportCbBase): ...@@ -1149,20 +1149,23 @@ class _RemoteExportCb(ImportExportCbBase):
class ExportInstanceHelper(object): class ExportInstanceHelper(object):
def __init__(self, lu, feedback_fn, instance): def __init__(self, lu, feedback_fn, instance, snapshot):
"""Initializes this class. """Initializes this class.
@param lu: Logical unit instance @param lu: Logical unit instance
@param feedback_fn: Feedback function @param feedback_fn: Feedback function
@type instance: L{objects.Instance} @type instance: L{objects.Instance}
@param instance: Instance object @param instance: Instance object
@type snapshot: bool
@param instance: whether the export should use snapshotting
""" """
self._lu = lu self._lu = lu
self._feedback_fn = feedback_fn self._feedback_fn = feedback_fn
self._instance = instance self._instance = instance
self._snapshot = snapshot
self._snap_disks = {} self._disks_to_transfer = {}
self._removed_snaps = [False] * len(instance.disks) self._removed_snaps = [False] * len(instance.disks)
def CreateSnapshots(self): def CreateSnapshots(self):
...@@ -1171,7 +1174,7 @@ class ExportInstanceHelper(object): ...@@ -1171,7 +1174,7 @@ class ExportInstanceHelper(object):
Currently support drbd, plain and ext disk templates. Currently support drbd, plain and ext disk templates.
""" """
assert not self._snap_disks assert not self._disks_to_transfer
instance = self._instance instance = self._instance
src_node = instance.primary_node src_node = instance.primary_node
...@@ -1209,10 +1212,10 @@ class ExportInstanceHelper(object): ...@@ -1209,10 +1212,10 @@ class ExportInstanceHelper(object):
logical_id=disk_id, iv_name=disk.iv_name, logical_id=disk_id, iv_name=disk.iv_name,
params=disk_params) params=disk_params)
assert idx not in self._snap_disks assert idx not in self._disks_to_transfer
self._snap_disks[idx] = new_dev 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) assert len(self._removed_snaps) == len(instance.disks)
def _RemoveSnapshot(self, disk_index): def _RemoveSnapshot(self, disk_index):
...@@ -1222,7 +1225,7 @@ class ExportInstanceHelper(object): ...@@ -1222,7 +1225,7 @@ class ExportInstanceHelper(object):
@param disk_index: Index of the snapshot to be removed @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]: if disk and not self._removed_snaps[disk_index]:
src_node = self._instance.primary_node src_node = self._instance.primary_node
src_node_name = self._lu.cfg.GetNodeName(src_node) src_node_name = self._lu.cfg.GetNodeName(src_node)
...@@ -1251,11 +1254,15 @@ class ExportInstanceHelper(object): ...@@ -1251,11 +1254,15 @@ class ExportInstanceHelper(object):
instance = self._instance instance = self._instance
src_node_uuid = instance.primary_node 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 = [] transfers = []
for idx, dev in self._snap_disks.items(): for idx, dev in self._disks_to_transfer.items():
if not dev: if not dev:
transfers.append(None) transfers.append(None)
continue continue
...@@ -1290,7 +1297,7 @@ class ExportInstanceHelper(object): ...@@ -1290,7 +1297,7 @@ class ExportInstanceHelper(object):
self._feedback_fn("Finalizing export on %s" % dest_node.name) self._feedback_fn("Finalizing export on %s" % dest_node.name)
result = self._lu.rpc.call_finalize_export(dest_node.uuid, instance, result = self._lu.rpc.call_finalize_export(dest_node.uuid, instance,
self._snap_disks) self._disks_to_transfer.values())
msg = result.fail_msg msg = result.fail_msg
fin_resu = not msg fin_resu = not msg
if msg: if msg:
......
...@@ -703,9 +703,6 @@ class MoveSourceExecutor(object): ...@@ -703,9 +703,6 @@ class MoveSourceExecutor(object):
logging.info("Retrieving instance information from source cluster") logging.info("Retrieving instance information from source cluster")
instinfo = self._GetInstanceInfo(src_client, mrt.PollJob, instinfo = self._GetInstanceInfo(src_client, mrt.PollJob,
mrt.move.src_instance_name) mrt.move.src_instance_name)
if instinfo["disk_template"] in constants.DTS_FILEBASED:
raise Error("Inter-cluster move of file-based instances is not"
" supported.")
logging.info("Preparing export on source cluster") logging.info("Preparing export on source cluster")
expinfo = self._PrepareExport(src_client, mrt.PollJob, 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