self.requested_data = self.query.RequestedData()
self.names = self.query.RequestedNames()
+ # Sort only if no names were requested
+ self.sort_by_name = not self.names
+
self.do_locking = None
self.wanted = None
"""Collect data and execute query.
"""
- return query.GetQueryResponse(self.query, self._GetQueryData(lu))
+ return query.GetQueryResponse(self.query, self._GetQueryData(lu),
+ sort_by_name=self.sort_by_name)
def OldStyleQuery(self, lu):
"""Collect data and execute query.
"""
- return self.query.OldStyleQuery(self._GetQueryData(lu))
+ return self.query.OldStyleQuery(self._GetQueryData(lu),
+ sort_by_name=self.sort_by_name)
def _GetWantedNodes(lu, nodes):
# Special case for file storage
if storage_type == constants.ST_FILE:
# storage.FileStorage wants a list of storage directories
- return [[cfg.GetFileStorageDir()]]
+ return [[cfg.GetFileStorageDir(), cfg.GetSharedFileStorageDir()]]
return []
"volume_group_name": cluster.volume_group_name,
"drbd_usermode_helper": cluster.drbd_usermode_helper,
"file_storage_dir": cluster.file_storage_dir,
+ "shared_file_storage_dir": cluster.shared_file_storage_dir,
"maintain_node_health": cluster.maintain_node_health,
"ctime": cluster.ctime,
"mtime": cluster.mtime,
hostname = netutils.GetHostname(name=new_name)
self.LogInfo("Resolved given name '%s' to '%s'", new_name,
hostname.name)
+ if not utils.MatchNameComponent(self.op.new_name, [hostname.name]):
+ raise errors.OpPrereqError(("Resolved hostname '%s' does not look the"
+ " same as given hostname '%s'") %
+ (hostname.name, self.op.new_name),
+ errors.ECODE_INVAL)
new_name = self.op.new_name = hostname.name
if (self.op.ip_check and
netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
old_name = inst.name
rename_file_storage = False
- if (inst.disk_template == constants.DT_FILE and
+ if (inst.disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE) and
self.op.new_name != inst.name):
old_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
rename_file_storage = True
HTYPE = constants.HTYPE_INSTANCE
REQ_BGL = False
+ def CheckArguments(self):
+ _CheckIAllocatorOrNode(self, "iallocator", "target_node")
+
def ExpandNames(self):
self._ExpandAndLockInstance()
+ if self.op.target_node is not None:
+ self.op.target_node = _ExpandNodeName(self.cfg, self.op.target_node)
+
self.needed_locks[locking.LEVEL_NODE] = []
self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
self._migrater = TLMigrateInstance(self, self.op.instance_name,
- self.op.cleanup)
+ self.op.cleanup, self.op.iallocator,
+ self.op.target_node)
self.tasklets = [self._migrater]
def DeclareLocks(self, level):
if level == locking.LEVEL_NODE:
- self._LockInstancesNodes()
+ instance = self.context.cfg.GetInstanceInfo(self.op.instance_name)
+ if instance.disk_template in constants.DTS_EXT_MIRROR:
+ if self.op.target_node is None:
+ self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
+ else:
+ self.needed_locks[locking.LEVEL_NODE] = [instance.primary_node,
+ self.op.target_node]
+ del self.recalculate_locks[locking.LEVEL_NODE]
+ else:
+ self._LockInstancesNodes()
def BuildHooksEnv(self):
"""Build hooks env.
"""
instance = self._migrater.instance
source_node = instance.primary_node
- target_node = instance.secondary_nodes[0]
+ target_node = self._migrater.target_node
env = _BuildInstanceHookEnvByObject(self, instance)
env["MIGRATE_LIVE"] = self._migrater.live
env["MIGRATE_CLEANUP"] = self.op.cleanup
env.update({
"OLD_PRIMARY": source_node,
- "OLD_SECONDARY": target_node,
"NEW_PRIMARY": target_node,
- "NEW_SECONDARY": source_node,
})
+
+ if instance.disk_template in constants.DTS_NET_MIRROR:
+ env["OLD_SECONDARY"] = target_node
+ env["NEW_SECONDARY"] = source_node
+ else:
+ env["OLD_SECONDARY"] = env["NEW_SECONDARY"] = None
+
nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes)
nl_post = list(nl)
nl_post.append(source_node)
HTYPE = constants.HTYPE_NODE
REQ_BGL = False
+ def CheckArguments(self):
+ _CheckIAllocatorOrNode(self, "iallocator", "remote_node")
+
def ExpandNames(self):
self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
- self.needed_locks = {
- locking.LEVEL_NODE: [self.op.node_name],
- }
-
- self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
+ self.needed_locks = {}
# Create tasklets for migrating instances for all instances on this node
names = []
tasklets = []
+ self.lock_all_nodes = False
+
for inst in _GetNodePrimaryInstances(self.cfg, self.op.node_name):
logging.debug("Migrating instance %s", inst.name)
names.append(inst.name)
- tasklets.append(TLMigrateInstance(self, inst.name, False))
+ tasklets.append(TLMigrateInstance(self, inst.name, False,
+ self.op.iallocator, None))
+
+ if inst.disk_template in constants.DTS_EXT_MIRROR:
+ # We need to lock all nodes, as the iallocator will choose the
+ # destination nodes afterwards
+ self.lock_all_nodes = True
self.tasklets = tasklets
+ # Declare node locks
+ if self.lock_all_nodes:
+ self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
+ else:
+ self.needed_locks[locking.LEVEL_NODE] = [self.op.node_name]
+ self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
+
# Declare instance locks
self.needed_locks[locking.LEVEL_INSTANCE] = names
def DeclareLocks(self, level):
- if level == locking.LEVEL_NODE:
+ if level == locking.LEVEL_NODE and not self.lock_all_nodes:
self._LockInstancesNodes()
def BuildHooksEnv(self):
this variable is initalized only after CheckPrereq has run
"""
- def __init__(self, lu, instance_name, cleanup):
+ def __init__(self, lu, instance_name, cleanup,
+ iallocator=None, target_node=None):
"""Initializes this class.
"""
self.instance_name = instance_name
self.cleanup = cleanup
self.live = False # will be overridden later
+ self.iallocator = iallocator
+ self.target_node = target_node
def CheckPrereq(self):
"""Check prerequisites.
instance_name = _ExpandInstanceName(self.lu.cfg, self.instance_name)
instance = self.cfg.GetInstanceInfo(instance_name)
assert instance is not None
+ self.instance = instance
- if instance.disk_template != constants.DT_DRBD8:
- raise errors.OpPrereqError("Instance's disk layout is not"
- " drbd8, cannot migrate.", errors.ECODE_STATE)
+ if instance.disk_template not in constants.DTS_MIRRORED:
+ raise errors.OpPrereqError("Instance's disk layout '%s' does not allow"
+ " migrations" % instance.disk_template,
+ errors.ECODE_STATE)
- secondary_nodes = instance.secondary_nodes
- if not secondary_nodes:
- raise errors.ConfigurationError("No secondary node but using"
- " drbd8 disk template")
+ if instance.disk_template in constants.DTS_EXT_MIRROR:
+ if [self.iallocator, self.target_node].count(None) != 1:
+ raise errors.OpPrereqError("Do not specify both, iallocator and"
+ " target node", errors.ECODE_INVAL)
+
+ if self.iallocator:
+ self._RunAllocator()
+
+ # self.target_node is already populated, either directly or by the
+ # iallocator run
+ target_node = self.target_node
+
+ if len(self.lu.tasklets) == 1:
+ # It is safe to remove locks only when we're the only tasklet in the LU
+ nodes_keep = [instance.primary_node, self.target_node]
+ nodes_rel = [node for node in self.lu.acquired_locks[locking.LEVEL_NODE]
+ if node not in nodes_keep]
+ self.lu.context.glm.release(locking.LEVEL_NODE, nodes_rel)
+ self.lu.acquired_locks[locking.LEVEL_NODE] = nodes_keep
+
+ else:
+ secondary_nodes = instance.secondary_nodes
+ if not secondary_nodes:
+ raise errors.ConfigurationError("No secondary node but using"
+ " %s disk template" %
+ instance.disk_template)
+ target_node = secondary_nodes[0]
i_be = self.cfg.GetClusterInfo().FillBE(instance)
- target_node = secondary_nodes[0]
# check memory requirements on the secondary node
_CheckNodeFreeMemory(self.lu, target_node, "migrating instance %s" %
instance.name, i_be[constants.BE_MEMORY],
result.Raise("Can't migrate, please use failover",
prereq=True, ecode=errors.ECODE_STATE)
- self.instance = instance
+
+ def _RunAllocator(self):
+ """Run the allocator based on input opcode.
+
+ """
+ ial = IAllocator(self.cfg, self.rpc,
+ mode=constants.IALLOCATOR_MODE_RELOC,
+ name=self.instance_name,
+ # TODO See why hail breaks with a single node below
+ relocate_from=[self.instance.primary_node,
+ self.instance.primary_node],
+ )
+
+ ial.Run(self.iallocator)
+
+ if not ial.success:
+ raise errors.OpPrereqError("Can't compute nodes using"
+ " iallocator '%s': %s" %
+ (self.iallocator, ial.info),
+ errors.ECODE_NORES)
+ if len(ial.result) != ial.required_nodes:
+ raise errors.OpPrereqError("iallocator '%s' returned invalid number"
+ " of nodes (%s), required %s" %
+ (self.iallocator, len(ial.result),
+ ial.required_nodes), errors.ECODE_FAULT)
+ self.target_node = ial.result[0]
+ self.lu.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
+ self.instance_name, self.iallocator,
+ utils.CommaJoin(ial.result))
if self.lu.op.live is not None and self.lu.op.mode is not None:
raise errors.OpPrereqError("Only one of the 'live' and 'mode'"
self.lu.op.live = None
elif self.lu.op.mode is None:
# read the default value from the hypervisor
- i_hv = self.cfg.GetClusterInfo().FillHV(instance, skip_globals=False)
+ i_hv = self.cfg.GetClusterInfo().FillHV(self.instance, skip_globals=False)
self.lu.op.mode = i_hv[constants.HV_MIGRATION_MODE]
self.live = self.lu.op.mode == constants.HT_MIGRATION_LIVE
" primary node (%s)" % source_node)
demoted_node = target_node
- self._EnsureSecondary(demoted_node)
- try:
+ if instance.disk_template in constants.DTS_NET_MIRROR:
+ self._EnsureSecondary(demoted_node)
+ try:
+ self._WaitUntilSync()
+ except errors.OpExecError:
+ # we ignore here errors, since if the device is standalone, it
+ # won't be able to sync
+ pass
+ self._GoStandalone()
+ self._GoReconnect(False)
self._WaitUntilSync()
- except errors.OpExecError:
- # we ignore here errors, since if the device is standalone, it
- # won't be able to sync
- pass
- self._GoStandalone()
- self._GoReconnect(False)
- self._WaitUntilSync()
self.feedback_fn("* done")
"""
target_node = self.target_node
+ if self.instance.disk_template in constants.DTS_EXT_MIRROR:
+ return
+
try:
self._EnsureSecondary(target_node)
self._GoStandalone()
self.migration_info = migration_info = result.payload
- # Then switch the disks to master/master mode
- self._EnsureSecondary(target_node)
- self._GoStandalone()
- self._GoReconnect(True)
- self._WaitUntilSync()
+ if self.instance.disk_template not in constants.DTS_EXT_MIRROR:
+ # Then switch the disks to master/master mode
+ self._EnsureSecondary(target_node)
+ self._GoStandalone()
+ self._GoReconnect(True)
+ self._WaitUntilSync()
self.feedback_fn("* preparing %s to accept the instance" % target_node)
result = self.rpc.call_accept_instance(target_node,
raise errors.OpExecError("Could not finalize instance migration: %s" %
msg)
- self._EnsureSecondary(source_node)
- self._WaitUntilSync()
- self._GoStandalone()
- self._GoReconnect(False)
- self._WaitUntilSync()
+ if self.instance.disk_template not in constants.DTS_EXT_MIRROR:
+ self._EnsureSecondary(source_node)
+ self._WaitUntilSync()
+ self._GoStandalone()
+ self._GoReconnect(False)
+ self._WaitUntilSync()
self.feedback_fn("* done")
self.feedback_fn = feedback_fn
self.source_node = self.instance.primary_node
- self.target_node = self.instance.secondary_nodes[0]
+
+ # FIXME: if we implement migrate-to-any in DRBD, this needs fixing
+ if self.instance.disk_template in constants.DTS_NET_MIRROR:
+ self.target_node = self.instance.secondary_nodes[0]
+ # Otherwise self.target_node has been populated either
+ # directly, or through an iallocator.
+
self.all_nodes = [self.source_node, self.target_node]
self.nodes_ip = {
self.source_node: self.cfg.GetNodeInfo(self.source_node).secondary_ip,
disk_index)),
mode=disk["mode"])
disks.append(disk_dev)
+ elif template_name == constants.DT_SHARED_FILE:
+ if len(secondary_nodes) != 0:
+ raise errors.ProgrammerError("Wrong template configuration")
+
+ opcodes.RequireSharedFileStorage()
+
+ for idx, disk in enumerate(disk_info):
+ disk_index = idx + base_index
+ disk_dev = objects.Disk(dev_type=constants.LD_FILE, size=disk["size"],
+ iv_name="disk/%d" % disk_index,
+ logical_id=(file_driver,
+ "%s/disk%d" % (file_storage_dir,
+ disk_index)),
+ mode=disk["mode"])
+ disks.append(disk_dev)
+ elif template_name == constants.DT_BLOCK:
+ if len(secondary_nodes) != 0:
+ raise errors.ProgrammerError("Wrong template configuration")
+
+ for idx, disk in enumerate(disk_info):
+ disk_index = idx + base_index
+ disk_dev = objects.Disk(dev_type=constants.LD_BLOCKDEV, size=disk["size"],
+ logical_id=(constants.BLOCKDEV_DRIVER_MANUAL,
+ disk["adopt"]),
+ iv_name="disk/%d" % disk_index,
+ mode=disk["mode"])
+ disks.append(disk_dev)
+
else:
raise errors.ProgrammerError("Invalid disk template '%s'" % template_name)
return disks
"""
node = instance.primary_node
+
+ for device in instance.disks:
+ lu.cfg.SetDiskID(device, node)
+
logging.info("Pause sync of instance %s disks", instance.name)
result = lu.rpc.call_blockdev_pause_resume_sync(node, instance.disks, True)
try:
for idx, device in enumerate(instance.disks):
lu.LogInfo("* Wiping disk %d", idx)
- logging.info("Wiping disk %d for instance %s", idx, instance.name)
+ logging.info("Wiping disk %d for instance %s, node %s",
+ idx, instance.name, node)
# The wipe size is MIN_WIPE_CHUNK_PERCENT % of the instance disk but
# MAX_WIPE_CHUNK at max
pnode = target_node
all_nodes = [pnode]
- if instance.disk_template == constants.DT_FILE:
+ if instance.disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE):
file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1])
result = lu.rpc.call_file_storage_dir_create(pnode, file_storage_dir)
# 128 MB are added for drbd metadata for each disk
constants.DT_DRBD8: _compute(disks, 128),
constants.DT_FILE: {},
+ constants.DT_SHARED_FILE: {},
}
if disk_template not in req_size_dict:
# 128 MB are added for drbd metadata for each disk
constants.DT_DRBD8: sum(d["size"] + 128 for d in disks),
constants.DT_FILE: None,
+ constants.DT_SHARED_FILE: 0,
+ constants.DT_BLOCK: 0,
}
if disk_template not in req_size_dict:
if self.op.mode == constants.INSTANCE_IMPORT:
raise errors.OpPrereqError("Disk adoption not allowed for"
" instance import", errors.ECODE_INVAL)
+ else:
+ if self.op.disk_template in constants.DTS_MUST_ADOPT:
+ raise errors.OpPrereqError("Disk template %s requires disk adoption,"
+ " but no 'adopt' parameter given" %
+ self.op.disk_template,
+ errors.ECODE_INVAL)
self.adopt_disks = has_adopt
" in cluster" % mac,
errors.ECODE_NOTUNIQUE)
- # bridge verification
- bridge = nic.get("bridge", None)
- link = nic.get("link", None)
- if bridge and link:
- raise errors.OpPrereqError("Cannot pass 'bridge' and 'link'"
- " at the same time", errors.ECODE_INVAL)
- elif bridge and nic_mode == constants.NIC_MODE_ROUTED:
- raise errors.OpPrereqError("Cannot pass 'bridge' on a routed nic",
- errors.ECODE_INVAL)
- elif bridge:
- link = bridge
-
+ # Build nic parameters
+ link = nic.get(constants.INIC_LINK, None)
nicparams = {}
if nic_mode_req:
nicparams[constants.NIC_MODE] = nic_mode_req
req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks)
_CheckNodesFreeDiskPerVG(self, nodenames, req_sizes)
- else: # instead, we must check the adoption data
+ elif self.op.disk_template == constants.DT_PLAIN: # Check the adoption data
all_lvs = set([i["vg"] + "/" + i["adopt"] for i in self.disks])
if len(all_lvs) != len(self.disks):
raise errors.OpPrereqError("Duplicate volume names given for adoption",
for dsk in self.disks:
dsk["size"] = int(float(node_lvs[dsk["vg"] + "/" + dsk["adopt"]][0]))
+ elif self.op.disk_template == constants.DT_BLOCK:
+ # Normalize and de-duplicate device paths
+ all_disks = set([os.path.abspath(i["adopt"]) for i in self.disks])
+ if len(all_disks) != len(self.disks):
+ raise errors.OpPrereqError("Duplicate disk names given for adoption",
+ errors.ECODE_INVAL)
+ baddisks = [d for d in all_disks
+ if not d.startswith(constants.ADOPTABLE_BLOCKDEV_ROOT)]
+ if baddisks:
+ raise errors.OpPrereqError("Device node(s) %s lie outside %s and"
+ " cannot be adopted" %
+ (", ".join(baddisks),
+ constants.ADOPTABLE_BLOCKDEV_ROOT),
+ errors.ECODE_INVAL)
+
+ node_disks = self.rpc.call_bdev_sizes([pnode.name],
+ list(all_disks))[pnode.name]
+ node_disks.Raise("Cannot get block device information from node %s" %
+ pnode.name)
+ node_disks = node_disks.payload
+ delta = all_disks.difference(node_disks.keys())
+ if delta:
+ raise errors.OpPrereqError("Missing block device(s): %s" %
+ utils.CommaJoin(delta),
+ errors.ECODE_INVAL)
+ for dsk in self.disks:
+ dsk["size"] = int(float(node_disks[dsk["adopt"]]))
+
_CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams)
_CheckNodeHasOS(self, pnode.name, self.op.os_type, self.op.force_variant)
else:
network_port = None
- if constants.ENABLE_FILE_STORAGE:
+ if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
# this is needed because os.path.join does not accept None arguments
if self.op.file_storage_dir is None:
string_file_storage_dir = ""
string_file_storage_dir = self.op.file_storage_dir
# build the full file storage dir path
- file_storage_dir = utils.PathJoin(self.cfg.GetFileStorageDir(),
+ if self.op.disk_template == constants.DT_SHARED_FILE:
+ get_fsd_fn = self.cfg.GetSharedFileStorageDir
+ else:
+ get_fsd_fn = self.cfg.GetFileStorageDir
+
+ file_storage_dir = utils.PathJoin(get_fsd_fn(),
string_file_storage_dir, instance)
else:
file_storage_dir = ""
)
if self.adopt_disks:
- # rename LVs to the newly-generated names; we need to construct
- # 'fake' LV disks with the old data, plus the new unique_id
- tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
- rename_to = []
- for t_dsk, a_dsk in zip (tmp_disks, self.disks):
- rename_to.append(t_dsk.logical_id)
- t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"])
- self.cfg.SetDiskID(t_dsk, pnode_name)
- result = self.rpc.call_blockdev_rename(pnode_name,
- zip(tmp_disks, rename_to))
- result.Raise("Failed to rename adoped LVs")
+ if self.op.disk_template == constants.DT_PLAIN:
+ # rename LVs to the newly-generated names; we need to construct
+ # 'fake' LV disks with the old data, plus the new unique_id
+ tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
+ rename_to = []
+ for t_dsk, a_dsk in zip (tmp_disks, self.disks):
+ rename_to.append(t_dsk.logical_id)
+ t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"])
+ self.cfg.SetDiskID(t_dsk, pnode_name)
+ result = self.rpc.call_blockdev_rename(pnode_name,
+ zip(tmp_disks, rename_to))
+ result.Raise("Failed to rename adoped LVs")
else:
feedback_fn("* creating instance disks...")
try:
self.disk = instance.FindDisk(self.op.disk)
- if instance.disk_template != constants.DT_FILE:
- # TODO: check the free disk space for file, when that feature
- # will be supported
+ if instance.disk_template not in (constants.DT_FILE,
+ constants.DT_SHARED_FILE):
+ # TODO: check the free disk space for file, when that feature will be
+ # supported
_CheckNodesFreeDiskPerVG(self, nodenames,
self.disk.ComputeGrowth(self.op.amount))
result.append(("disk/%d" % device_idx, "remove"))
elif disk_op == constants.DDM_ADD:
# add a new disk
- if instance.disk_template == constants.DT_FILE:
+ if instance.disk_template in (constants.DT_FILE,
+ constants.DT_SHARED_FILE):
file_driver, file_path = instance.disks[0].logical_id
file_path = os.path.dirname(file_path)
else:
raise errors.ProgrammerError("Unknown instance '%s' passed to"
" IAllocator" % self.name)
- if instance.disk_template not in constants.DTS_NET_MIRROR:
+ if instance.disk_template not in constants.DTS_MIRRORED:
raise errors.OpPrereqError("Can't relocate non-mirrored instances",
errors.ECODE_INVAL)
- if len(instance.secondary_nodes) != 1:
+ if instance.disk_template in constants.DTS_NET_MIRROR and \
+ len(instance.secondary_nodes) != 1:
raise errors.OpPrereqError("Instance has not exactly one secondary node",
errors.ECODE_STATE)