Commit e29e9550 authored by Iustin Pop's avatar Iustin Pop
Browse files

Implement conversion from plain to drbd



This patch adds a new mode to instance modify, the changing of the disk
template. For now only plain to drbd conversion is supported, and the
new secondary node must be specified manually (no iallocator support).

The procedure for conversion works as follows:

- a completely new disk template is created, matching the count, size
  and mode of the instance's current disks
- we create manually (not via _CreateDisks) all the missing volumes
- we rename on the primary the LVs to the new name
- we create manually the DRBD devices

Failures during the creation of volumes will leave orphan volumes.
Failure during the rename might leave some disks renamed and some not,
leading to an inconsistent instance.

Once the disks are renamed, we update the instance information and wait
for resync. Any failures of the DRBD sync must be manually handled (like
a normal failure, e.g. by running replace-disks, etc.).
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent 31624382
......@@ -7733,8 +7733,12 @@ class LUSetInstanceParams(LogicalUnit):
self.op.beparams = {}
if not hasattr(self.op, 'hvparams'):
self.op.hvparams = {}
if not hasattr(self.op, "disk_template"):
self.op.disk_template = None
if not hasattr(self.op, "remote_node"):
self.op.remote_node = None
self.op.force = getattr(self.op, "force", False)
if not (self.op.nics or self.op.disks or
if not (self.op.nics or self.op.disks or self.op.disk_template or
self.op.hvparams or self.op.beparams):
raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
......@@ -7781,6 +7785,19 @@ class LUSetInstanceParams(LogicalUnit):
raise errors.OpPrereqError("Only one disk add or remove operation"
" supported at a time", errors.ECODE_INVAL)
if self.op.disks and self.op.disk_template is not None:
raise errors.OpPrereqError("Disk template conversion and other disk"
" changes not supported at the same time",
errors.ECODE_INVAL)
if self.op.disk_template:
_CheckDiskTemplate(self.op.disk_template)
if (self.op.disk_template in constants.DTS_NET_MIRROR and
self.op.remote_node is None):
raise errors.OpPrereqError("Changing the disk template to a mirrored"
" one requires specifying a secondary node",
errors.ECODE_INVAL)
# NIC validation
nic_addremove = 0
for nic_op, nic_dict in self.op.nics:
......@@ -7843,6 +7860,9 @@ class LUSetInstanceParams(LogicalUnit):
def DeclareLocks(self, level):
if level == locking.LEVEL_NODE:
self._LockInstancesNodes()
if self.op.disk_template and self.op.remote_node:
self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node)
def BuildHooksEnv(self):
"""Build hooks env.
......@@ -7892,6 +7912,8 @@ class LUSetInstanceParams(LogicalUnit):
del args['nics'][-1]
env = _BuildInstanceHookEnvByObject(self, self.instance, override=args)
if self.op.disk_template:
env["NEW_DISK_TEMPLATE"] = self.op.disk_template
nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
return env, nl, nl
......@@ -7945,6 +7967,25 @@ class LUSetInstanceParams(LogicalUnit):
pnode = instance.primary_node
nodelist = list(instance.all_nodes)
if self.op.disk_template:
if instance.disk_template == self.op.disk_template:
raise errors.OpPrereqError("Instance already has disk template %s" %
instance.disk_template, errors.ECODE_INVAL)
if (instance.disk_template,
self.op.disk_template) not in self._DISK_CONVERSIONS:
raise errors.OpPrereqError("Unsupported disk template conversion from"
" %s to %s" % (instance.disk_template,
self.op.disk_template),
errors.ECODE_INVAL)
if self.op.disk_template in constants.DTS_NET_MIRROR:
_CheckNodeOnline(self, self.op.remote_node)
_CheckNodeNotDrained(self, self.op.remote_node)
disks = [{"size": d.size} for d in instance.disks]
required = _ComputeDiskSize(self.op.disk_template, disks)
_CheckNodesFreeDisk(self, [self.op.remote_node], required)
_CheckInstanceDown(self, instance, "cannot change disk template")
# hvparams processing
if self.op.hvparams:
i_hvdict, hv_new = self._GetUpdatedParams(
......@@ -8128,6 +8169,55 @@ class LUSetInstanceParams(LogicalUnit):
return
def _ConvertPlainToDrbd(self, feedback_fn):
"""Converts an instance from plain to drbd.
"""
feedback_fn("Converting template to drbd")
instance = self.instance
pnode = instance.primary_node
snode = self.op.remote_node
# create a fake disk info for _GenerateDiskTemplate
disk_info = [{"size": d.size, "mode": d.mode} for d in instance.disks]
new_disks = _GenerateDiskTemplate(self, self.op.disk_template,
instance.name, pnode, [snode],
disk_info, None, None, 0)
info = _GetInstanceInfoText(instance)
feedback_fn("Creating aditional volumes...")
# first, create the missing data and meta devices
for disk in new_disks:
# unfortunately this is... not too nice
_CreateSingleBlockDev(self, pnode, instance, disk.children[1],
info, True)
for child in disk.children:
_CreateSingleBlockDev(self, snode, instance, child, info, True)
# at this stage, all new LVs have been created, we can rename the
# old ones
feedback_fn("Renaming original volumes...")
rename_list = [(o, n.children[0].logical_id)
for (o, n) in zip(instance.disks, new_disks)]
result = self.rpc.call_blockdev_rename(pnode, rename_list)
result.Raise("Failed to rename original LVs")
feedback_fn("Initializing DRBD devices...")
# all child devices are in place, we can now create the DRBD devices
for disk in new_disks:
for node in [pnode, snode]:
f_create = node == pnode
_CreateSingleBlockDev(self, node, instance, disk, info, f_create)
# at this point, the instance has been modified
instance.disk_template = constants.DT_DRBD8
instance.disks = new_disks
self.cfg.Update(instance, feedback_fn)
# disks are created, waiting for sync
disk_abort = not _WaitForSync(self, instance)
if disk_abort:
raise errors.OpExecError("There are some degraded disks for"
" this instance, please cleanup manually")
def Exec(self, feedback_fn):
"""Modifies an instance.
......@@ -8192,6 +8282,20 @@ class LUSetInstanceParams(LogicalUnit):
# change a given disk
instance.disks[disk_op].mode = disk_dict['mode']
result.append(("disk.mode/%d" % disk_op, disk_dict['mode']))
if self.op.disk_template:
r_shut = _ShutdownInstanceDisks(self, instance)
if not r_shut:
raise errors.OpExecError("Cannot shutdow instance disks, unable to"
" proceed with disk template conversion")
mode = (instance.disk_template, self.op.disk_template)
try:
self._DISK_CONVERSIONS[mode](self, feedback_fn)
except:
self.cfg.ReleaseDRBDMinors(instance.name)
raise
result.append(("disk_template", self.op.disk_template))
# NIC changes
for nic_op, nic_dict in self.op.nics:
if nic_op == constants.DDM_REMOVE:
......@@ -8236,6 +8340,9 @@ class LUSetInstanceParams(LogicalUnit):
return result
_DISK_CONVERSIONS = {
(constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
}
class LUQueryExports(NoHooksLU):
"""Query the exports list
......
......@@ -620,7 +620,8 @@ class OpSetInstanceParams(OpCode):
__slots__ = [
"instance_name",
"hvparams", "beparams", "force",
"nics", "disks",
"nics", "disks", "disk_template",
"remote_node",
]
......
......@@ -1207,7 +1207,7 @@ def SetInstanceParams(opts, args):
@return: the desired exit code
"""
if not (opts.nics or opts.disks or
if not (opts.nics or opts.disks or opts.disk_template or
opts.hvparams or opts.beparams):
ToStderr("Please give at least one of the parameters.")
return 1
......@@ -1247,9 +1247,18 @@ def SetInstanceParams(opts, args):
errors.ECODE_INVAL)
disk_dict['size'] = utils.ParseUnit(disk_dict['size'])
if (opts.disk_template and
opts.disk_template in constants.DTS_NET_MIRROR and
not opts.node):
ToStderr("Changing the disk template to a mirrored one requires"
" specifying a secondary node")
return 1
op = opcodes.OpSetInstanceParams(instance_name=args[0],
nics=opts.nics,
disks=opts.disks,
disk_template=opts.disk_template,
remote_node=opts.node,
hvparams=opts.hvparams,
beparams=opts.beparams,
force=opts.force)
......@@ -1261,7 +1270,7 @@ def SetInstanceParams(opts, args):
ToStdout("Modified instance %s", args[0])
for param, data in result:
ToStdout(" - %-5s -> %s", param, data)
ToStdout("Please don't forget that these parameters take effect"
ToStdout("Please don't forget that most parameters take effect"
" only at the next start of the instance.")
return 0
......@@ -1407,7 +1416,8 @@ commands = {
"Replaces all disks for the instance"),
'modify': (
SetInstanceParams, ARGS_ONE_INSTANCE,
[BACKEND_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, NET_OPT, SUBMIT_OPT],
[BACKEND_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, NET_OPT, SUBMIT_OPT,
DISK_TEMPLATE_OPT, SINGLE_NODE_OPT],
"<instance>", "Alters the parameters of an instance"),
'shutdown': (
GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
......
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