# Return expanded names
return self.wanted
- @classmethod
- def FieldsQuery(cls, fields):
- """Returns list of available fields.
-
- @return: List of L{objects.QueryFieldDefinition}
-
- """
- return query.QueryFields(cls.FIELDS, fields)
-
def ExpandNames(self, lu):
"""Expand names for this query.
return params_copy
+def _RunPostHook(lu, node_name):
+ """Runs the post-hook for an opcode on a single node.
+
+ """
+ hm = lu.proc.hmclass(lu.rpc.call_hooks_runner, lu)
+ try:
+ hm.RunPhase(constants.HOOKS_PHASE_POST, nodes=[node_name])
+ except:
+ # pylint: disable-msg=W0702
+ lu.LogWarning("Errors occurred running hooks on %s" % node_name)
+
+
def _CheckOutputFields(static, dynamic, selected):
"""Checks whether all selected fields are valid.
master = self.cfg.GetMasterNode()
# Run post hooks on master node before it's removed
- hm = self.proc.hmclass(self.rpc.call_hooks_runner, self)
- try:
- hm.RunPhase(constants.HOOKS_PHASE_POST, [master])
- except:
- # pylint: disable-msg=W0702
- self.LogWarning("Errors occurred running hooks on %s" % master)
+ _RunPostHook(self, master)
result = self.rpc.call_node_stop_master(master, False)
result.Raise("Could not disable the master role")
utils.CommaJoin(inst_config.secondary_nodes),
code=self.ETYPE_WARNING)
- if inst_config.disk_template in constants.DTS_NET_MIRROR:
+ if inst_config.disk_template in constants.DTS_INT_MIRROR:
pnode = inst_config.primary_node
instance_nodes = utils.NiceSort(inst_config.all_nodes)
instance_groups = {}
self.nodes = []
self.master_node = self.cfg.GetMasterNode()
+ assert self.op.power_delay >= 0.0
+
if self.op.node_names:
if self.op.command in self._SKIP_MASTER:
if self.master_node in self.op.node_names:
master_node = self.master_node
ret = []
- for node in self.nodes:
+ for idx, node in enumerate(self.nodes):
node_entry = [(constants.RS_NORMAL, node.name)]
ret.append(node_entry)
node_entry.append((constants.RS_NORMAL, result.payload))
+ if (self.op.command == constants.OOB_POWER_ON and
+ idx < len(self.nodes) - 1):
+ time.sleep(self.op.power_delay)
+
return ret
def _CheckPayload(self, result):
raise errors.OpExecError("Check of out-of-band payload failed due to %s" %
utils.CommaJoin(errs))
+class _OsQuery(_QueryBase):
+ FIELDS = query.OS_FIELDS
-
-class LUOsDiagnose(NoHooksLU):
- """Logical unit for OS diagnose/query.
-
- """
- REQ_BGL = False
- _HID = "hidden"
- _BLK = "blacklisted"
- _VLD = "valid"
- _FIELDS_STATIC = utils.FieldSet()
- _FIELDS_DYNAMIC = utils.FieldSet("name", _VLD, "node_status", "variants",
- "parameters", "api_versions", _HID, _BLK)
-
- def CheckArguments(self):
- if self.op.names:
- raise errors.OpPrereqError("Selective OS query not supported",
- errors.ECODE_INVAL)
-
- _CheckOutputFields(static=self._FIELDS_STATIC,
- dynamic=self._FIELDS_DYNAMIC,
- selected=self.op.output_fields)
-
- def ExpandNames(self):
- # Lock all nodes, in shared mode
+ def ExpandNames(self, lu):
+ # Lock all nodes in shared mode
# Temporary removal of locks, should be reverted later
# TODO: reintroduce locks when they are lighter-weight
- self.needed_locks = {}
+ lu.needed_locks = {}
#self.share_locks[locking.LEVEL_NODE] = 1
#self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
+ # The following variables interact with _QueryBase._GetNames
+ if self.names:
+ self.wanted = self.names
+ else:
+ self.wanted = locking.ALL_SET
+
+ self.do_locking = self.use_locking
+
+ def DeclareLocks(self, lu, level):
+ pass
+
@staticmethod
def _DiagnoseByOS(rlist):
"""Remaps a per-node return list into an a per-os per-node dictionary
variants, params, api_versions))
return all_os
- def Exec(self, feedback_fn):
- """Compute the list of OSes.
+ def _GetQueryData(self, lu):
+ """Computes the list of nodes and their attributes.
"""
+ # Locking is not used
+ assert not (lu.acquired_locks or self.do_locking or self.use_locking)
+
valid_nodes = [node.name
- for node in self.cfg.GetAllNodesInfo().values()
+ for node in lu.cfg.GetAllNodesInfo().values()
if not node.offline and node.vm_capable]
- node_data = self.rpc.call_os_diagnose(valid_nodes)
- pol = self._DiagnoseByOS(node_data)
- output = []
- cluster = self.cfg.GetClusterInfo()
+ pol = self._DiagnoseByOS(lu.rpc.call_os_diagnose(valid_nodes))
+ cluster = lu.cfg.GetClusterInfo()
+
+ data = {}
+
+ for (os_name, os_data) in pol.items():
+ info = query.OsInfo(name=os_name, valid=True, node_status=os_data,
+ hidden=(os_name in cluster.hidden_os),
+ blacklisted=(os_name in cluster.blacklisted_os))
+
+ variants = set()
+ parameters = set()
+ api_versions = set()
- for os_name in utils.NiceSort(pol.keys()):
- os_data = pol[os_name]
- row = []
- valid = True
- (variants, params, api_versions) = null_state = (set(), set(), set())
for idx, osl in enumerate(os_data.values()):
- valid = bool(valid and osl and osl[0][1])
- if not valid:
- (variants, params, api_versions) = null_state
+ info.valid = bool(info.valid and osl and osl[0][1])
+ if not info.valid:
break
- node_variants, node_params, node_api = osl[0][3:6]
- if idx == 0: # first entry
- variants = set(node_variants)
- params = set(node_params)
- api_versions = set(node_api)
- else: # keep consistency
+
+ (node_variants, node_params, node_api) = osl[0][3:6]
+ if idx == 0:
+ # First entry
+ variants.update(node_variants)
+ parameters.update(node_params)
+ api_versions.update(node_api)
+ else:
+ # Filter out inconsistent values
variants.intersection_update(node_variants)
- params.intersection_update(node_params)
+ parameters.intersection_update(node_params)
api_versions.intersection_update(node_api)
- is_hid = os_name in cluster.hidden_os
- is_blk = os_name in cluster.blacklisted_os
- if ((self._HID not in self.op.output_fields and is_hid) or
- (self._BLK not in self.op.output_fields and is_blk) or
- (self._VLD not in self.op.output_fields and not valid)):
- continue
+ info.variants = list(variants)
+ info.parameters = list(parameters)
+ info.api_versions = list(api_versions)
- for field in self.op.output_fields:
- if field == "name":
- val = os_name
- elif field == self._VLD:
- val = valid
- elif field == "node_status":
- # this is just a copy of the dict
- val = {}
- for node_name, nos_list in os_data.items():
- val[node_name] = nos_list
- elif field == "variants":
- val = utils.NiceSort(list(variants))
- elif field == "parameters":
- val = list(params)
- elif field == "api_versions":
- val = list(api_versions)
- elif field == self._HID:
- val = is_hid
- elif field == self._BLK:
- val = is_blk
- else:
- raise errors.ParameterError(field)
- row.append(val)
- output.append(row)
+ data[os_name] = info
- return output
+ # Prepare data in requested order
+ return [data[name] for name in self._GetNames(lu, pol.keys(), None)
+ if name in data]
+
+
+class LUOsDiagnose(NoHooksLU):
+ """Logical unit for OS diagnose/query.
+
+ """
+ REQ_BGL = False
+
+ @staticmethod
+ def _BuildFilter(fields, names):
+ """Builds a filter for querying OSes.
+
+ """
+ name_filter = qlang.MakeSimpleFilter("name", names)
+
+ # Legacy behaviour: Hide hidden, blacklisted or invalid OSes if the
+ # respective field is not requested
+ status_filter = [[qlang.OP_NOT, [qlang.OP_TRUE, fname]]
+ for fname in ["hidden", "blacklisted"]
+ if fname not in fields]
+ if "valid" not in fields:
+ status_filter.append([qlang.OP_TRUE, "valid"])
+
+ if status_filter:
+ status_filter.insert(0, qlang.OP_AND)
+ else:
+ status_filter = None
+
+ if name_filter and status_filter:
+ return [qlang.OP_AND, name_filter, status_filter]
+ elif name_filter:
+ return name_filter
+ else:
+ return status_filter
+
+ def CheckArguments(self):
+ self.oq = _OsQuery(self._BuildFilter(self.op.output_fields, self.op.names),
+ self.op.output_fields, False)
+
+ def ExpandNames(self):
+ self.oq.ExpandNames(self)
+
+ def Exec(self, feedback_fn):
+ return self.oq.OldStyleQuery(self)
class LUNodeRemove(LogicalUnit):
self.context.RemoveNode(node.name)
# Run post hooks on the node before it's removed
- hm = self.proc.hmclass(self.rpc.call_hooks_runner, self)
- try:
- hm.RunPhase(constants.HOOKS_PHASE_POST, [node.name])
- except:
- # pylint: disable-msg=W0702
- self.LogWarning("Errors occurred running hooks on %s" % node.name)
+ _RunPostHook(self, node.name)
result = self.rpc.call_node_leave_cluster(node.name, modify_ssh_setup)
msg = result.fail_msg
self.needed_locks = {}
def Exec(self, feedback_fn):
- return self.qcls.FieldsQuery(self.op.fields)
+ return query.QueryFields(self.qcls.FIELDS, self.op.fields)
class LUNodeModifyStorage(NoHooksLU):
"MASTER_CAPABLE": str(self.op.master_capable),
"VM_CAPABLE": str(self.op.vm_capable),
}
- nodes_0 = self.cfg.GetNodeList()
- nodes_1 = nodes_0 + [self.op.node_name, ]
- return env, nodes_0, nodes_1
+
+ # Exclude added node
+ pre_nodes = list(set(self.cfg.GetNodeList()) - set([self.op.node_name]))
+ post_nodes = pre_nodes + [self.op.node_name, ]
+
+ return (env, pre_nodes, post_nodes)
def CheckPrereq(self):
"""Check prerequisites.
if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
for instance_name in self.acquired_locks[locking.LEVEL_INSTANCE]:
instance = self.context.cfg.GetInstanceInfo(instance_name)
- i_mirrored = instance.disk_template in constants.DTS_NET_MIRROR
+ i_mirrored = instance.disk_template in constants.DTS_INT_MIRROR
if i_mirrored and self.op.node_name in instance.all_nodes:
instances_keep.append(instance_name)
self.affected_instances.append(instance)
"""
self.iallocator = getattr(self.op, "iallocator", None)
self.target_node = getattr(self.op, "target_node", None)
- _CheckIAllocatorOrNode(self, "iallocator", "target_node")
def ExpandNames(self):
self._ExpandAndLockInstance()
"NEW_PRIMARY": self.op.target_node,
}
- if instance.disk_template in constants.DTS_NET_MIRROR:
+ if instance.disk_template in constants.DTS_INT_MIRROR:
env["OLD_SECONDARY"] = instance.secondary_nodes[0]
env["NEW_SECONDARY"] = source_node
else:
errors.ECODE_STATE)
if instance.disk_template in constants.DTS_EXT_MIRROR:
+ _CheckIAllocatorOrNode(self, "iallocator", "target_node")
if self.op.iallocator:
self._RunAllocator()
# Release all unnecessary node locks
instance.disk_template)
target_node = secondary_nodes[0]
+ if self.op.iallocator or (self.op.target_node and
+ self.op.target_node != target_node):
+ raise errors.OpPrereqError("Instances with disk template %s cannot"
+ " be failed over to arbitrary nodes"
+ " (neither an iallocator nor a target"
+ " node can be passed)" %
+ instance.disk_template, errors.ECODE_INVAL)
_CheckNodeOnline(self, target_node)
_CheckNodeNotDrained(self, target_node)
HTYPE = constants.HTYPE_INSTANCE
REQ_BGL = False
- def CheckArguments(self):
- _CheckIAllocatorOrNode(self, "iallocator", "target_node")
-
def ExpandNames(self):
self._ExpandAndLockInstance()
"NEW_PRIMARY": target_node,
})
- if instance.disk_template in constants.DTS_NET_MIRROR:
+ if instance.disk_template in constants.DTS_INT_MIRROR:
env["OLD_SECONDARY"] = target_node
env["NEW_SECONDARY"] = source_node
else:
errors.ECODE_STATE)
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)
+ _CheckIAllocatorOrNode(self.lu, "iallocator", "target_node")
if self.iallocator:
self._RunAllocator()
" %s disk template" %
instance.disk_template)
target_node = secondary_nodes[0]
+ if self.lu.op.iallocator or (self.lu.op.target_node and
+ self.lu.op.target_node != target_node):
+ raise errors.OpPrereqError("Instances with disk template %s cannot"
+ " be migrated over to arbitrary nodes"
+ " (neither an iallocator nor a target"
+ " node can be passed)" %
+ instance.disk_template, errors.ECODE_INVAL)
i_be = self.cfg.GetClusterInfo().FillBE(instance)
" primary node (%s)" % source_node)
demoted_node = target_node
- if instance.disk_template in constants.DTS_NET_MIRROR:
+ if instance.disk_template in constants.DTS_INT_MIRROR:
self._EnsureSecondary(demoted_node)
try:
self._WaitUntilSync()
self.source_node = self.instance.primary_node
# FIXME: if we implement migrate-to-any in DRBD, this needs fixing
- if self.instance.disk_template in constants.DTS_NET_MIRROR:
+ if self.instance.disk_template in constants.DTS_INT_MIRROR:
self.target_node = self.instance.secondary_nodes[0]
# Otherwise self.target_node has been populated either
# directly, or through an iallocator.
_CheckIAllocatorOrNode(self, "iallocator", "pnode")
if self.op.pnode is not None:
- if self.op.disk_template in constants.DTS_NET_MIRROR:
+ if self.op.disk_template in constants.DTS_INT_MIRROR:
if self.op.snode is None:
raise errors.OpPrereqError("The networked disk templates need"
" a mirror node", errors.ECODE_INVAL)
self.secondaries = []
# mirror node verification
- if self.op.disk_template in constants.DTS_NET_MIRROR:
+ if self.op.disk_template in constants.DTS_INT_MIRROR:
if self.op.snode == pnode.name:
raise errors.OpPrereqError("The secondary node cannot be the"
" primary node.", errors.ECODE_INVAL)
if self.op.wait_for_sync:
disk_abort = not _WaitForSync(self, iobj)
- elif iobj.disk_template in constants.DTS_NET_MIRROR:
+ elif iobj.disk_template in constants.DTS_INT_MIRROR:
# make sure the disks are not degraded (still sync-ing is ok)
time.sleep(15)
feedback_fn("* checking mirrors status")
return _FindFaultyInstanceDisks(self.cfg, self.rpc, self.instance,
node_name, True)
+ def _CheckDisksActivated(self, instance):
+ """Checks if the instance disks are activated.
+
+ @param instance: The instance to check disks
+ @return: True if they are activated, False otherwise
+
+ """
+ nodes = instance.all_nodes
+
+ for idx, dev in enumerate(instance.disks):
+ for node in nodes:
+ self.lu.LogInfo("Checking disk/%d on %s", idx, node)
+ self.cfg.SetDiskID(dev, node)
+
+ result = self.rpc.call_blockdev_find(node, dev)
+
+ if result.offline:
+ continue
+ elif result.fail_msg or not result.payload:
+ return False
+
+ return True
+
+
def CheckPrereq(self):
"""Check prerequisites.
errors.ECODE_INVAL)
if self.mode == constants.REPLACE_DISK_AUTO:
+ if not self._CheckDisksActivated(instance):
+ raise errors.OpPrereqError("Please run activate-disks on instance %s"
+ " first" % self.instance_name,
+ errors.ECODE_STATE)
faulty_primary = self._FindFaultyDisks(instance.primary_node)
faulty_secondary = self._FindFaultyDisks(secondary_node)
errors.ECODE_INVAL)
if (self.op.disk_template and
- self.op.disk_template in constants.DTS_NET_MIRROR and
+ self.op.disk_template in constants.DTS_INT_MIRROR and
self.op.remote_node is None):
raise errors.OpPrereqError("Changing the disk template to a mirrored"
" one requires specifying a secondary node",
self.op.disk_template),
errors.ECODE_INVAL)
_CheckInstanceDown(self, instance, "cannot change disk template")
- if self.op.disk_template in constants.DTS_NET_MIRROR:
+ if self.op.disk_template in constants.DTS_INT_MIRROR:
if self.op.remote_node == pnode:
raise errors.OpPrereqError("Given new secondary node %s is the same"
" as the primary node of the instance" %
In particular, it returns information about newly split instances, and
instances that were already split, and remain so after the change.
- Only instances whose disk template is listed in constants.DTS_NET_MIRROR are
+ Only instances whose disk template is listed in constants.DTS_INT_MIRROR are
considered.
@type changes: list of (node_name, new_group_uuid) pairs.
return [instance.primary_node] + list(instance.secondary_nodes)
for inst in instance_data.values():
- if inst.disk_template not in constants.DTS_NET_MIRROR:
+ if inst.disk_template not in constants.DTS_INT_MIRROR:
continue
instance_nodes = InstanceNodes(inst)
"""
disk_space = _ComputeDiskSize(self.disk_template, self.disks)
- if self.disk_template in constants.DTS_NET_MIRROR:
+ if self.disk_template in constants.DTS_INT_MIRROR:
self.required_nodes = 2
else:
self.required_nodes = 1
raise errors.OpPrereqError("Can't relocate non-mirrored instances",
errors.ECODE_INVAL)
- if instance.disk_template in constants.DTS_NET_MIRROR and \
+ if instance.disk_template in constants.DTS_INT_MIRROR and \
len(instance.secondary_nodes) != 1:
raise errors.OpPrereqError("Instance has not exactly one secondary node",
errors.ECODE_STATE)
constants.QR_INSTANCE: _InstanceQuery,
constants.QR_NODE: _NodeQuery,
constants.QR_GROUP: _GroupQuery,
+ constants.QR_OS: _OsQuery,
}
+assert set(_QUERY_IMPL.keys()) == constants.QR_VIA_OP
+
def _GetQueryImplementation(name):
"""Returns the implemtnation for a query type.
- @param name: Query type, must be one of L{constants.QR_OP_QUERY}
+ @param name: Query type, must be one of L{constants.QR_VIA_OP}
"""
try: