cmdlib: Extract instance query related functionality
authorThomas Thrainer <thomasth@google.com>
Tue, 14 May 2013 12:02:29 +0000 (14:02 +0200)
committerThomas Thrainer <thomasth@google.com>
Fri, 17 May 2013 09:32:15 +0000 (11:32 +0200)
Split instance.py further by extracting instance querying related
logical units and functions to instance_query.py.

Signed-off-by: Thomas Thrainer <thomasth@google.com>
Reviewed-by: Bernardo Dal Seno <bdalseno@google.com>

Makefile.am
lib/cmdlib/__init__.py
lib/cmdlib/instance.py
lib/cmdlib/instance_query.py [new file with mode: 0644]
lib/cmdlib/query.py

index 4c8b3ac..67e2d18 100644 (file)
@@ -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 \
index d4c290e..3f67039 100644 (file)
@@ -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, \
index 3c0b136..5f43d93 100644 (file)
@@ -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 (file)
index 0000000..e664fc0
--- /dev/null
@@ -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
index 4680fc7..57c7f29 100644 (file)
@@ -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