Commit 8aa8f6b1 authored by Thomas Thrainer's avatar Thomas Thrainer
Browse files

cmdlib: Extract instance query related functionality



Split instance.py further by extracting instance querying related
logical units and functions to instance_query.py.
Signed-off-by: default avatarThomas Thrainer <thomasth@google.com>
Reviewed-by: default avatarBernardo Dal Seno <bdalseno@google.com>
parent 13f6af81
......@@ -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 \
......
......@@ -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, \
......
......@@ -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.
......
#
#
# 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: