diff --git a/Makefile.am b/Makefile.am index 4c8b3acc31374576ecaa474e28544b93bed1c2d5..67e2d18e430647ed38a1cf0d6668f40843942a03 100644 --- a/Makefile.am +++ b/Makefile.am @@ -318,6 +318,7 @@ cmdlib_PYTHON = \ lib/cmdlib/instance_storage.py \ lib/cmdlib/instance_migration.py \ lib/cmdlib/instance_operation.py \ + lib/cmdlib/instance_query.py \ lib/cmdlib/instance_utils.py \ lib/cmdlib/backup.py \ lib/cmdlib/query.py \ diff --git a/lib/cmdlib/__init__.py b/lib/cmdlib/__init__.py index d4c290eabe414da1a3617e7c98c2a80614330b5d..3f67039b30db0ccccb2d42584ad92d06daa4f08c 100644 --- a/lib/cmdlib/__init__.py +++ b/lib/cmdlib/__init__.py @@ -72,8 +72,6 @@ from ganeti.cmdlib.instance import \ LUInstanceRename, \ LUInstanceRemove, \ LUInstanceMove, \ - LUInstanceQuery, \ - LUInstanceQueryData, \ LUInstanceMultiAlloc, \ LUInstanceSetParams, \ LUInstanceChangeGroup @@ -92,6 +90,9 @@ from ganeti.cmdlib.instance_operation import \ LUInstanceReinstall, \ LUInstanceReboot, \ LUInstanceConsole +from ganeti.cmdlib.instance_query import \ + LUInstanceQuery, \ + LUInstanceQueryData from ganeti.cmdlib.backup import \ LUBackupQuery, \ LUBackupPrepare, \ diff --git a/lib/cmdlib/instance.py b/lib/cmdlib/instance.py index 3c0b136a24a2c69e01d0554c701c906d352817ea..5f43d936a0f40df61ba907076d4c88f090526cb6 100644 --- a/lib/cmdlib/instance.py +++ b/lib/cmdlib/instance.py @@ -23,9 +23,7 @@ import OpenSSL import copy -import itertools import logging -import operator import os from ganeti import compat @@ -40,29 +38,24 @@ from ganeti import netutils from ganeti import objects from ganeti import opcodes from ganeti import pathutils -from ganeti import qlang from ganeti import rpc from ganeti import utils -from ganeti import query -from ganeti.cmdlib.base import NoHooksLU, LogicalUnit, _QueryBase, \ - ResultWithJobs +from ganeti.cmdlib.base import NoHooksLU, LogicalUnit, ResultWithJobs from ganeti.cmdlib.common import INSTANCE_DOWN, \ INSTANCE_NOT_RUNNING, CAN_CHANGE_INSTANCE_OFFLINE, _CheckNodeOnline, \ _ShareAll, _GetDefaultIAllocator, _CheckInstanceNodeGroups, \ _LoadNodeEvacResult, _CheckIAllocatorOrNode, _CheckParamsNotGlobal, \ _IsExclusiveStorageEnabledNode, _CheckHVParams, _CheckOSParams, \ - _GetWantedInstances, _CheckInstancesNodeGroups, _AnnotateDiskParams, \ - _GetUpdatedParams, _ExpandInstanceName, _ComputeIPolicySpecViolation, \ - _CheckInstanceState, _ExpandNodeName + _AnnotateDiskParams, _GetUpdatedParams, _ExpandInstanceName, \ + _ComputeIPolicySpecViolation, _CheckInstanceState, _ExpandNodeName from ganeti.cmdlib.instance_storage import _CreateDisks, \ _CheckNodesFreeDiskPerVG, _WipeDisks, _WaitForSync, \ _IsExclusiveStorageEnabledNodeName, _CreateSingleBlockDev, _ComputeDisks, \ _CheckRADOSFreeSpace, _ComputeDiskSizePerVG, _GenerateDiskTemplate, \ _CreateBlockDev, _StartInstanceDisks, _ShutdownInstanceDisks, \ _AssembleInstanceDisks -from ganeti.cmdlib.instance_operation import _GetInstanceConsole from ganeti.cmdlib.instance_utils import _BuildInstanceHookEnvByObject, \ _GetClusterDomainSecret, _BuildInstanceHookEnv, _NICListToTuple, \ _NICToTuple, _CheckNodeNotDrained, _RemoveInstance, _CopyLockList, \ @@ -1809,400 +1802,6 @@ class LUInstanceMove(LogicalUnit): (instance.name, target_node, msg)) -class _InstanceQuery(_QueryBase): - FIELDS = query.INSTANCE_FIELDS - - def ExpandNames(self, lu): - lu.needed_locks = {} - lu.share_locks = _ShareAll() - - if self.names: - self.wanted = _GetWantedInstances(lu, self.names) - else: - self.wanted = locking.ALL_SET - - self.do_locking = (self.use_locking and - query.IQ_LIVE in self.requested_data) - if self.do_locking: - lu.needed_locks[locking.LEVEL_INSTANCE] = self.wanted - lu.needed_locks[locking.LEVEL_NODEGROUP] = [] - lu.needed_locks[locking.LEVEL_NODE] = [] - lu.needed_locks[locking.LEVEL_NETWORK] = [] - lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE - - self.do_grouplocks = (self.do_locking and - query.IQ_NODES in self.requested_data) - - def DeclareLocks(self, lu, level): - if self.do_locking: - if level == locking.LEVEL_NODEGROUP and self.do_grouplocks: - assert not lu.needed_locks[locking.LEVEL_NODEGROUP] - - # Lock all groups used by instances optimistically; this requires going - # via the node before it's locked, requiring verification later on - lu.needed_locks[locking.LEVEL_NODEGROUP] = \ - set(group_uuid - for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE) - for group_uuid in lu.cfg.GetInstanceNodeGroups(instance_name)) - elif level == locking.LEVEL_NODE: - lu._LockInstancesNodes() # pylint: disable=W0212 - - elif level == locking.LEVEL_NETWORK: - lu.needed_locks[locking.LEVEL_NETWORK] = \ - frozenset(net_uuid - for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE) - for net_uuid in lu.cfg.GetInstanceNetworks(instance_name)) - - @staticmethod - def _CheckGroupLocks(lu): - owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE)) - owned_groups = frozenset(lu.owned_locks(locking.LEVEL_NODEGROUP)) - - # Check if node groups for locked instances are still correct - for instance_name in owned_instances: - _CheckInstanceNodeGroups(lu.cfg, instance_name, owned_groups) - - def _GetQueryData(self, lu): - """Computes the list of instances and their attributes. - - """ - if self.do_grouplocks: - self._CheckGroupLocks(lu) - - cluster = lu.cfg.GetClusterInfo() - all_info = lu.cfg.GetAllInstancesInfo() - - instance_names = self._GetNames(lu, all_info.keys(), locking.LEVEL_INSTANCE) - - instance_list = [all_info[name] for name in instance_names] - nodes = frozenset(itertools.chain(*(inst.all_nodes - for inst in instance_list))) - hv_list = list(set([inst.hypervisor for inst in instance_list])) - bad_nodes = [] - offline_nodes = [] - wrongnode_inst = set() - - # Gather data as requested - if self.requested_data & set([query.IQ_LIVE, query.IQ_CONSOLE]): - live_data = {} - node_data = lu.rpc.call_all_instances_info(nodes, hv_list) - for name in nodes: - result = node_data[name] - if result.offline: - # offline nodes will be in both lists - assert result.fail_msg - offline_nodes.append(name) - if result.fail_msg: - bad_nodes.append(name) - elif result.payload: - for inst in result.payload: - if inst in all_info: - if all_info[inst].primary_node == name: - live_data.update(result.payload) - else: - wrongnode_inst.add(inst) - else: - # orphan instance; we don't list it here as we don't - # handle this case yet in the output of instance listing - logging.warning("Orphan instance '%s' found on node %s", - inst, name) - # else no instance is alive - else: - live_data = {} - - if query.IQ_DISKUSAGE in self.requested_data: - gmi = ganeti.masterd.instance - disk_usage = dict((inst.name, - gmi.ComputeDiskSize(inst.disk_template, - [{constants.IDISK_SIZE: disk.size} - for disk in inst.disks])) - for inst in instance_list) - else: - disk_usage = None - - if query.IQ_CONSOLE in self.requested_data: - consinfo = {} - for inst in instance_list: - if inst.name in live_data: - # Instance is running - consinfo[inst.name] = _GetInstanceConsole(cluster, inst) - else: - consinfo[inst.name] = None - assert set(consinfo.keys()) == set(instance_names) - else: - consinfo = None - - if query.IQ_NODES in self.requested_data: - node_names = set(itertools.chain(*map(operator.attrgetter("all_nodes"), - instance_list))) - nodes = dict(lu.cfg.GetMultiNodeInfo(node_names)) - groups = dict((uuid, lu.cfg.GetNodeGroup(uuid)) - for uuid in set(map(operator.attrgetter("group"), - nodes.values()))) - else: - nodes = None - groups = None - - if query.IQ_NETWORKS in self.requested_data: - net_uuids = itertools.chain(*(lu.cfg.GetInstanceNetworks(i.name) - for i in instance_list)) - networks = dict((uuid, lu.cfg.GetNetwork(uuid)) for uuid in net_uuids) - else: - networks = None - - return query.InstanceQueryData(instance_list, lu.cfg.GetClusterInfo(), - disk_usage, offline_nodes, bad_nodes, - live_data, wrongnode_inst, consinfo, - nodes, groups, networks) - - -class LUInstanceQuery(NoHooksLU): - """Logical unit for querying instances. - - """ - # pylint: disable=W0142 - REQ_BGL = False - - def CheckArguments(self): - self.iq = _InstanceQuery(qlang.MakeSimpleFilter("name", self.op.names), - self.op.output_fields, self.op.use_locking) - - def ExpandNames(self): - self.iq.ExpandNames(self) - - def DeclareLocks(self, level): - self.iq.DeclareLocks(self, level) - - def Exec(self, feedback_fn): - return self.iq.OldStyleQuery(self) - - -class LUInstanceQueryData(NoHooksLU): - """Query runtime instance data. - - """ - REQ_BGL = False - - def ExpandNames(self): - self.needed_locks = {} - - # Use locking if requested or when non-static information is wanted - if not (self.op.static or self.op.use_locking): - self.LogWarning("Non-static data requested, locks need to be acquired") - self.op.use_locking = True - - if self.op.instances or not self.op.use_locking: - # Expand instance names right here - self.wanted_names = _GetWantedInstances(self, self.op.instances) - else: - # Will use acquired locks - self.wanted_names = None - - if self.op.use_locking: - self.share_locks = _ShareAll() - - if self.wanted_names is None: - self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET - else: - self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names - - self.needed_locks[locking.LEVEL_NODEGROUP] = [] - self.needed_locks[locking.LEVEL_NODE] = [] - self.needed_locks[locking.LEVEL_NETWORK] = [] - self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE - - def DeclareLocks(self, level): - if self.op.use_locking: - owned_instances = self.owned_locks(locking.LEVEL_INSTANCE) - if level == locking.LEVEL_NODEGROUP: - - # Lock all groups used by instances optimistically; this requires going - # via the node before it's locked, requiring verification later on - self.needed_locks[locking.LEVEL_NODEGROUP] = \ - frozenset(group_uuid - for instance_name in owned_instances - for group_uuid in - self.cfg.GetInstanceNodeGroups(instance_name)) - - elif level == locking.LEVEL_NODE: - self._LockInstancesNodes() - - elif level == locking.LEVEL_NETWORK: - self.needed_locks[locking.LEVEL_NETWORK] = \ - frozenset(net_uuid - for instance_name in owned_instances - for net_uuid in - self.cfg.GetInstanceNetworks(instance_name)) - - def CheckPrereq(self): - """Check prerequisites. - - This only checks the optional instance list against the existing names. - - """ - owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) - owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) - owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE)) - owned_networks = frozenset(self.owned_locks(locking.LEVEL_NETWORK)) - - if self.wanted_names is None: - assert self.op.use_locking, "Locking was not used" - self.wanted_names = owned_instances - - instances = dict(self.cfg.GetMultiInstanceInfo(self.wanted_names)) - - if self.op.use_locking: - _CheckInstancesNodeGroups(self.cfg, instances, owned_groups, owned_nodes, - None) - else: - assert not (owned_instances or owned_groups or - owned_nodes or owned_networks) - - self.wanted_instances = instances.values() - - def _ComputeBlockdevStatus(self, node, instance, dev): - """Returns the status of a block device - - """ - if self.op.static or not node: - return None - - self.cfg.SetDiskID(dev, node) - - result = self.rpc.call_blockdev_find(node, dev) - if result.offline: - return None - - result.Raise("Can't compute disk status for %s" % instance.name) - - status = result.payload - if status is None: - return None - - return (status.dev_path, status.major, status.minor, - status.sync_percent, status.estimated_time, - status.is_degraded, status.ldisk_status) - - def _ComputeDiskStatus(self, instance, snode, dev): - """Compute block device status. - - """ - (anno_dev,) = _AnnotateDiskParams(instance, [dev], self.cfg) - - return self._ComputeDiskStatusInner(instance, snode, anno_dev) - - def _ComputeDiskStatusInner(self, instance, snode, dev): - """Compute block device status. - - @attention: The device has to be annotated already. - - """ - if dev.dev_type in constants.LDS_DRBD: - # we change the snode then (otherwise we use the one passed in) - if dev.logical_id[0] == instance.primary_node: - snode = dev.logical_id[1] - else: - snode = dev.logical_id[0] - - dev_pstatus = self._ComputeBlockdevStatus(instance.primary_node, - instance, dev) - dev_sstatus = self._ComputeBlockdevStatus(snode, instance, dev) - - if dev.children: - dev_children = map(compat.partial(self._ComputeDiskStatusInner, - instance, snode), - dev.children) - else: - dev_children = [] - - return { - "iv_name": dev.iv_name, - "dev_type": dev.dev_type, - "logical_id": dev.logical_id, - "physical_id": dev.physical_id, - "pstatus": dev_pstatus, - "sstatus": dev_sstatus, - "children": dev_children, - "mode": dev.mode, - "size": dev.size, - "name": dev.name, - "uuid": dev.uuid, - } - - def Exec(self, feedback_fn): - """Gather and return data""" - result = {} - - cluster = self.cfg.GetClusterInfo() - - node_names = itertools.chain(*(i.all_nodes for i in self.wanted_instances)) - nodes = dict(self.cfg.GetMultiNodeInfo(node_names)) - - groups = dict(self.cfg.GetMultiNodeGroupInfo(node.group - for node in nodes.values())) - - group2name_fn = lambda uuid: groups[uuid].name - for instance in self.wanted_instances: - pnode = nodes[instance.primary_node] - - if self.op.static or pnode.offline: - remote_state = None - if pnode.offline: - self.LogWarning("Primary node %s is marked offline, returning static" - " information only for instance %s" % - (pnode.name, instance.name)) - else: - remote_info = self.rpc.call_instance_info(instance.primary_node, - instance.name, - instance.hypervisor) - remote_info.Raise("Error checking node %s" % instance.primary_node) - remote_info = remote_info.payload - if remote_info and "state" in remote_info: - remote_state = "up" - else: - if instance.admin_state == constants.ADMINST_UP: - remote_state = "down" - else: - remote_state = instance.admin_state - - disks = map(compat.partial(self._ComputeDiskStatus, instance, None), - instance.disks) - - snodes_group_uuids = [nodes[snode_name].group - for snode_name in instance.secondary_nodes] - - result[instance.name] = { - "name": instance.name, - "config_state": instance.admin_state, - "run_state": remote_state, - "pnode": instance.primary_node, - "pnode_group_uuid": pnode.group, - "pnode_group_name": group2name_fn(pnode.group), - "snodes": instance.secondary_nodes, - "snodes_group_uuids": snodes_group_uuids, - "snodes_group_names": map(group2name_fn, snodes_group_uuids), - "os": instance.os, - # this happens to be the same format used for hooks - "nics": _NICListToTuple(self, instance.nics), - "disk_template": instance.disk_template, - "disks": disks, - "hypervisor": instance.hypervisor, - "network_port": instance.network_port, - "hv_instance": instance.hvparams, - "hv_actual": cluster.FillHV(instance, skip_globals=True), - "be_instance": instance.beparams, - "be_actual": cluster.FillBE(instance), - "os_instance": instance.osparams, - "os_actual": cluster.SimpleFillOS(instance.os, instance.osparams), - "serial_no": instance.serial_no, - "mtime": instance.mtime, - "ctime": instance.ctime, - "uuid": instance.uuid, - } - - return result - - class LUInstanceMultiAlloc(NoHooksLU): """Allocates multiple instances at the same time. diff --git a/lib/cmdlib/instance_query.py b/lib/cmdlib/instance_query.py new file mode 100644 index 0000000000000000000000000000000000000000..e664fc02127329fdad1d877920dde85b66d3bf6b --- /dev/null +++ b/lib/cmdlib/instance_query.py @@ -0,0 +1,433 @@ +# +# + +# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + + +"""Logical units for querying instances.""" + +import itertools +import logging +import operator + +from ganeti import compat +from ganeti import constants +from ganeti import locking +from ganeti import qlang +from ganeti import query +from ganeti.cmdlib.base import _QueryBase, NoHooksLU +from ganeti.cmdlib.common import _ShareAll, _GetWantedInstances, \ + _CheckInstanceNodeGroups, _CheckInstancesNodeGroups, _AnnotateDiskParams +from ganeti.cmdlib.instance_operation import _GetInstanceConsole +from ganeti.cmdlib.instance_utils import _NICListToTuple + +import ganeti.masterd.instance + + +class _InstanceQuery(_QueryBase): + FIELDS = query.INSTANCE_FIELDS + + def ExpandNames(self, lu): + lu.needed_locks = {} + lu.share_locks = _ShareAll() + + if self.names: + self.wanted = _GetWantedInstances(lu, self.names) + else: + self.wanted = locking.ALL_SET + + self.do_locking = (self.use_locking and + query.IQ_LIVE in self.requested_data) + if self.do_locking: + lu.needed_locks[locking.LEVEL_INSTANCE] = self.wanted + lu.needed_locks[locking.LEVEL_NODEGROUP] = [] + lu.needed_locks[locking.LEVEL_NODE] = [] + lu.needed_locks[locking.LEVEL_NETWORK] = [] + lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE + + self.do_grouplocks = (self.do_locking and + query.IQ_NODES in self.requested_data) + + def DeclareLocks(self, lu, level): + if self.do_locking: + if level == locking.LEVEL_NODEGROUP and self.do_grouplocks: + assert not lu.needed_locks[locking.LEVEL_NODEGROUP] + + # Lock all groups used by instances optimistically; this requires going + # via the node before it's locked, requiring verification later on + lu.needed_locks[locking.LEVEL_NODEGROUP] = \ + set(group_uuid + for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE) + for group_uuid in lu.cfg.GetInstanceNodeGroups(instance_name)) + elif level == locking.LEVEL_NODE: + lu._LockInstancesNodes() # pylint: disable=W0212 + + elif level == locking.LEVEL_NETWORK: + lu.needed_locks[locking.LEVEL_NETWORK] = \ + frozenset(net_uuid + for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE) + for net_uuid in lu.cfg.GetInstanceNetworks(instance_name)) + + @staticmethod + def _CheckGroupLocks(lu): + owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE)) + owned_groups = frozenset(lu.owned_locks(locking.LEVEL_NODEGROUP)) + + # Check if node groups for locked instances are still correct + for instance_name in owned_instances: + _CheckInstanceNodeGroups(lu.cfg, instance_name, owned_groups) + + def _GetQueryData(self, lu): + """Computes the list of instances and their attributes. + + """ + if self.do_grouplocks: + self._CheckGroupLocks(lu) + + cluster = lu.cfg.GetClusterInfo() + all_info = lu.cfg.GetAllInstancesInfo() + + instance_names = self._GetNames(lu, all_info.keys(), locking.LEVEL_INSTANCE) + + instance_list = [all_info[name] for name in instance_names] + nodes = frozenset(itertools.chain(*(inst.all_nodes + for inst in instance_list))) + hv_list = list(set([inst.hypervisor for inst in instance_list])) + bad_nodes = [] + offline_nodes = [] + wrongnode_inst = set() + + # Gather data as requested + if self.requested_data & set([query.IQ_LIVE, query.IQ_CONSOLE]): + live_data = {} + node_data = lu.rpc.call_all_instances_info(nodes, hv_list) + for name in nodes: + result = node_data[name] + if result.offline: + # offline nodes will be in both lists + assert result.fail_msg + offline_nodes.append(name) + if result.fail_msg: + bad_nodes.append(name) + elif result.payload: + for inst in result.payload: + if inst in all_info: + if all_info[inst].primary_node == name: + live_data.update(result.payload) + else: + wrongnode_inst.add(inst) + else: + # orphan instance; we don't list it here as we don't + # handle this case yet in the output of instance listing + logging.warning("Orphan instance '%s' found on node %s", + inst, name) + # else no instance is alive + else: + live_data = {} + + if query.IQ_DISKUSAGE in self.requested_data: + gmi = ganeti.masterd.instance + disk_usage = dict((inst.name, + gmi.ComputeDiskSize(inst.disk_template, + [{constants.IDISK_SIZE: disk.size} + for disk in inst.disks])) + for inst in instance_list) + else: + disk_usage = None + + if query.IQ_CONSOLE in self.requested_data: + consinfo = {} + for inst in instance_list: + if inst.name in live_data: + # Instance is running + consinfo[inst.name] = _GetInstanceConsole(cluster, inst) + else: + consinfo[inst.name] = None + assert set(consinfo.keys()) == set(instance_names) + else: + consinfo = None + + if query.IQ_NODES in self.requested_data: + node_names = set(itertools.chain(*map(operator.attrgetter("all_nodes"), + instance_list))) + nodes = dict(lu.cfg.GetMultiNodeInfo(node_names)) + groups = dict((uuid, lu.cfg.GetNodeGroup(uuid)) + for uuid in set(map(operator.attrgetter("group"), + nodes.values()))) + else: + nodes = None + groups = None + + if query.IQ_NETWORKS in self.requested_data: + net_uuids = itertools.chain(*(lu.cfg.GetInstanceNetworks(i.name) + for i in instance_list)) + networks = dict((uuid, lu.cfg.GetNetwork(uuid)) for uuid in net_uuids) + else: + networks = None + + return query.InstanceQueryData(instance_list, lu.cfg.GetClusterInfo(), + disk_usage, offline_nodes, bad_nodes, + live_data, wrongnode_inst, consinfo, + nodes, groups, networks) + + +class LUInstanceQuery(NoHooksLU): + """Logical unit for querying instances. + + """ + # pylint: disable=W0142 + REQ_BGL = False + + def CheckArguments(self): + self.iq = _InstanceQuery(qlang.MakeSimpleFilter("name", self.op.names), + self.op.output_fields, self.op.use_locking) + + def ExpandNames(self): + self.iq.ExpandNames(self) + + def DeclareLocks(self, level): + self.iq.DeclareLocks(self, level) + + def Exec(self, feedback_fn): + return self.iq.OldStyleQuery(self) + + +class LUInstanceQueryData(NoHooksLU): + """Query runtime instance data. + + """ + REQ_BGL = False + + def ExpandNames(self): + self.needed_locks = {} + + # Use locking if requested or when non-static information is wanted + if not (self.op.static or self.op.use_locking): + self.LogWarning("Non-static data requested, locks need to be acquired") + self.op.use_locking = True + + if self.op.instances or not self.op.use_locking: + # Expand instance names right here + self.wanted_names = _GetWantedInstances(self, self.op.instances) + else: + # Will use acquired locks + self.wanted_names = None + + if self.op.use_locking: + self.share_locks = _ShareAll() + + if self.wanted_names is None: + self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET + else: + self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names + + self.needed_locks[locking.LEVEL_NODEGROUP] = [] + self.needed_locks[locking.LEVEL_NODE] = [] + self.needed_locks[locking.LEVEL_NETWORK] = [] + self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE + + def DeclareLocks(self, level): + if self.op.use_locking: + owned_instances = self.owned_locks(locking.LEVEL_INSTANCE) + if level == locking.LEVEL_NODEGROUP: + + # Lock all groups used by instances optimistically; this requires going + # via the node before it's locked, requiring verification later on + self.needed_locks[locking.LEVEL_NODEGROUP] = \ + frozenset(group_uuid + for instance_name in owned_instances + for group_uuid in + self.cfg.GetInstanceNodeGroups(instance_name)) + + elif level == locking.LEVEL_NODE: + self._LockInstancesNodes() + + elif level == locking.LEVEL_NETWORK: + self.needed_locks[locking.LEVEL_NETWORK] = \ + frozenset(net_uuid + for instance_name in owned_instances + for net_uuid in + self.cfg.GetInstanceNetworks(instance_name)) + + def CheckPrereq(self): + """Check prerequisites. + + This only checks the optional instance list against the existing names. + + """ + owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) + owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) + owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE)) + owned_networks = frozenset(self.owned_locks(locking.LEVEL_NETWORK)) + + if self.wanted_names is None: + assert self.op.use_locking, "Locking was not used" + self.wanted_names = owned_instances + + instances = dict(self.cfg.GetMultiInstanceInfo(self.wanted_names)) + + if self.op.use_locking: + _CheckInstancesNodeGroups(self.cfg, instances, owned_groups, owned_nodes, + None) + else: + assert not (owned_instances or owned_groups or + owned_nodes or owned_networks) + + self.wanted_instances = instances.values() + + def _ComputeBlockdevStatus(self, node, instance, dev): + """Returns the status of a block device + + """ + if self.op.static or not node: + return None + + self.cfg.SetDiskID(dev, node) + + result = self.rpc.call_blockdev_find(node, dev) + if result.offline: + return None + + result.Raise("Can't compute disk status for %s" % instance.name) + + status = result.payload + if status is None: + return None + + return (status.dev_path, status.major, status.minor, + status.sync_percent, status.estimated_time, + status.is_degraded, status.ldisk_status) + + def _ComputeDiskStatus(self, instance, snode, dev): + """Compute block device status. + + """ + (anno_dev,) = _AnnotateDiskParams(instance, [dev], self.cfg) + + return self._ComputeDiskStatusInner(instance, snode, anno_dev) + + def _ComputeDiskStatusInner(self, instance, snode, dev): + """Compute block device status. + + @attention: The device has to be annotated already. + + """ + if dev.dev_type in constants.LDS_DRBD: + # we change the snode then (otherwise we use the one passed in) + if dev.logical_id[0] == instance.primary_node: + snode = dev.logical_id[1] + else: + snode = dev.logical_id[0] + + dev_pstatus = self._ComputeBlockdevStatus(instance.primary_node, + instance, dev) + dev_sstatus = self._ComputeBlockdevStatus(snode, instance, dev) + + if dev.children: + dev_children = map(compat.partial(self._ComputeDiskStatusInner, + instance, snode), + dev.children) + else: + dev_children = [] + + return { + "iv_name": dev.iv_name, + "dev_type": dev.dev_type, + "logical_id": dev.logical_id, + "physical_id": dev.physical_id, + "pstatus": dev_pstatus, + "sstatus": dev_sstatus, + "children": dev_children, + "mode": dev.mode, + "size": dev.size, + "name": dev.name, + "uuid": dev.uuid, + } + + def Exec(self, feedback_fn): + """Gather and return data""" + result = {} + + cluster = self.cfg.GetClusterInfo() + + node_names = itertools.chain(*(i.all_nodes for i in self.wanted_instances)) + nodes = dict(self.cfg.GetMultiNodeInfo(node_names)) + + groups = dict(self.cfg.GetMultiNodeGroupInfo(node.group + for node in nodes.values())) + + group2name_fn = lambda uuid: groups[uuid].name + for instance in self.wanted_instances: + pnode = nodes[instance.primary_node] + + if self.op.static or pnode.offline: + remote_state = None + if pnode.offline: + self.LogWarning("Primary node %s is marked offline, returning static" + " information only for instance %s" % + (pnode.name, instance.name)) + else: + remote_info = self.rpc.call_instance_info(instance.primary_node, + instance.name, + instance.hypervisor) + remote_info.Raise("Error checking node %s" % instance.primary_node) + remote_info = remote_info.payload + if remote_info and "state" in remote_info: + remote_state = "up" + else: + if instance.admin_state == constants.ADMINST_UP: + remote_state = "down" + else: + remote_state = instance.admin_state + + disks = map(compat.partial(self._ComputeDiskStatus, instance, None), + instance.disks) + + snodes_group_uuids = [nodes[snode_name].group + for snode_name in instance.secondary_nodes] + + result[instance.name] = { + "name": instance.name, + "config_state": instance.admin_state, + "run_state": remote_state, + "pnode": instance.primary_node, + "pnode_group_uuid": pnode.group, + "pnode_group_name": group2name_fn(pnode.group), + "snodes": instance.secondary_nodes, + "snodes_group_uuids": snodes_group_uuids, + "snodes_group_names": map(group2name_fn, snodes_group_uuids), + "os": instance.os, + # this happens to be the same format used for hooks + "nics": _NICListToTuple(self, instance.nics), + "disk_template": instance.disk_template, + "disks": disks, + "hypervisor": instance.hypervisor, + "network_port": instance.network_port, + "hv_instance": instance.hvparams, + "hv_actual": cluster.FillHV(instance, skip_globals=True), + "be_instance": instance.beparams, + "be_actual": cluster.FillBE(instance), + "os_instance": instance.osparams, + "os_actual": cluster.SimpleFillOS(instance.os, instance.osparams), + "serial_no": instance.serial_no, + "mtime": instance.mtime, + "ctime": instance.ctime, + "uuid": instance.uuid, + } + + return result diff --git a/lib/cmdlib/query.py b/lib/cmdlib/query.py index 4680fc776ef220bbbef4d07ef5b0b8db254b7dd4..57c7f2981d1ef05e99896d66fa4fe6d96314b9be 100644 --- a/lib/cmdlib/query.py +++ b/lib/cmdlib/query.py @@ -28,7 +28,7 @@ from ganeti.cmdlib.backup import _ExportQuery from ganeti.cmdlib.base import NoHooksLU from ganeti.cmdlib.cluster import _ClusterQuery from ganeti.cmdlib.group import _GroupQuery -from ganeti.cmdlib.instance import _InstanceQuery +from ganeti.cmdlib.instance_query import _InstanceQuery from ganeti.cmdlib.misc import _ExtStorageQuery from ganeti.cmdlib.network import _NetworkQuery from ganeti.cmdlib.node import _NodeQuery