Revision fab9573b

b/lib/client/gnt_instance.py
212 212

  
213 213
  fmtoverride = dict.fromkeys(["tags", "disk.sizes", "nic.macs", "nic.ips",
214 214
                               "nic.modes", "nic.links", "nic.bridges",
215
                               "snodes"],
215
                               "snodes", "snodes.group", "snodes.group.uuid"],
216 216
                              (lambda value: ",".join(str(item)
217 217
                                                      for item in value),
218 218
                               False))
b/lib/cmdlib.py
4492 4492

  
4493 4493
  def ExpandNames(self, lu):
4494 4494
    lu.needed_locks = {}
4495
    lu.share_locks[locking.LEVEL_INSTANCE] = 1
4496
    lu.share_locks[locking.LEVEL_NODE] = 1
4495
    lu.share_locks = _ShareAll()
4497 4496

  
4498 4497
    if self.names:
4499 4498
      self.wanted = _GetWantedInstances(lu, self.names)
......
4504 4503
                       query.IQ_LIVE in self.requested_data)
4505 4504
    if self.do_locking:
4506 4505
      lu.needed_locks[locking.LEVEL_INSTANCE] = self.wanted
4506
      lu.needed_locks[locking.LEVEL_NODEGROUP] = []
4507 4507
      lu.needed_locks[locking.LEVEL_NODE] = []
4508 4508
      lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
4509 4509

  
4510
    self.do_grouplocks = (self.do_locking and
4511
                          query.IQ_NODES in self.requested_data)
4512

  
4510 4513
  def DeclareLocks(self, lu, level):
4511
    if level == locking.LEVEL_NODE and self.do_locking:
4512
      lu._LockInstancesNodes() # pylint: disable-msg=W0212
4514
    if self.do_locking:
4515
      if level == locking.LEVEL_NODEGROUP and self.do_grouplocks:
4516
        assert not lu.needed_locks[locking.LEVEL_NODEGROUP]
4517

  
4518
        # Lock all groups used by instances optimistically; this requires going
4519
        # via the node before it's locked, requiring verification later on
4520
        lu.needed_locks[locking.LEVEL_NODEGROUP] = \
4521
          set(group_uuid
4522
              for instance_name in
4523
                lu.glm.list_owned(locking.LEVEL_INSTANCE)
4524
              for group_uuid in
4525
                lu.cfg.GetInstanceNodeGroups(instance_name))
4526
      elif level == locking.LEVEL_NODE:
4527
        lu._LockInstancesNodes() # pylint: disable-msg=W0212
4528

  
4529
  @staticmethod
4530
  def _CheckGroupLocks(lu):
4531
    owned_instances = frozenset(lu.glm.list_owned(locking.LEVEL_INSTANCE))
4532
    owned_groups = frozenset(lu.glm.list_owned(locking.LEVEL_NODEGROUP))
4533

  
4534
    # Check if node groups for locked instances are still correct
4535
    for instance_name in owned_instances:
4536
      inst_groups = lu.cfg.GetInstanceNodeGroups(instance_name)
4537
      if not owned_groups.issuperset(inst_groups):
4538
        raise errors.OpPrereqError("Instance %s's node groups changed since"
4539
                                   " locks were acquired, current groups are"
4540
                                   " are '%s', owning groups '%s'; retry the"
4541
                                   " operation" %
4542
                                   (instance_name,
4543
                                    utils.CommaJoin(inst_groups),
4544
                                    utils.CommaJoin(owned_groups)),
4545
                                   errors.ECODE_STATE)
4513 4546

  
4514 4547
  def _GetQueryData(self, lu):
4515 4548
    """Computes the list of instances and their attributes.
4516 4549

  
4517 4550
    """
4551
    if self.do_grouplocks:
4552
      self._CheckGroupLocks(lu)
4553

  
4518 4554
    cluster = lu.cfg.GetClusterInfo()
4519 4555
    all_info = lu.cfg.GetAllInstancesInfo()
4520 4556

  
......
4577 4613
    else:
4578 4614
      consinfo = None
4579 4615

  
4616
    if query.IQ_NODES in self.requested_data:
4617
      node_names = set(itertools.chain(*map(operator.attrgetter("all_nodes"),
4618
                                            instance_list)))
4619
      nodes = dict((name, lu.cfg.GetNodeInfo(name)) for name in node_names)
4620
      groups = dict((uuid, lu.cfg.GetNodeGroup(uuid))
4621
                    for uuid in set(map(operator.attrgetter("group"),
4622
                                        nodes.values())))
4623
    else:
4624
      nodes = None
4625
      groups = None
4626

  
4580 4627
    return query.InstanceQueryData(instance_list, lu.cfg.GetClusterInfo(),
4581 4628
                                   disk_usage, offline_nodes, bad_nodes,
4582
                                   live_data, wrongnode_inst, consinfo)
4629
                                   live_data, wrongnode_inst, consinfo,
4630
                                   nodes, groups)
4583 4631

  
4584 4632

  
4585 4633
class LUQuery(NoHooksLU):
b/lib/query.py
83 83
(IQ_CONFIG,
84 84
 IQ_LIVE,
85 85
 IQ_DISKUSAGE,
86
 IQ_CONSOLE) = range(100, 104)
86
 IQ_CONSOLE,
87
 IQ_NODES) = range(100, 105)
87 88

  
88 89
(LQ_MODE,
89 90
 LQ_OWNER,
......
1223 1224

  
1224 1225
  """
1225 1226
  def __init__(self, instances, cluster, disk_usage, offline_nodes, bad_nodes,
1226
               live_data, wrongnode_inst, console):
1227
               live_data, wrongnode_inst, console, nodes, groups):
1227 1228
    """Initializes this class.
1228 1229

  
1229 1230
    @param instances: List of instance objects
......
1240 1241
    @param wrongnode_inst: Set of instances running on wrong node(s)
1241 1242
    @type console: dict; instance name as key
1242 1243
    @param console: Per-instance console information
1244
    @type nodes: dict; node name as key
1245
    @param nodes: Node objects
1243 1246

  
1244 1247
    """
1245 1248
    assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
......
1255 1258
    self.live_data = live_data
1256 1259
    self.wrongnode_inst = wrongnode_inst
1257 1260
    self.console = console
1261
    self.nodes = nodes
1262
    self.groups = groups
1258 1263

  
1259 1264
    # Used for individual rows
1260 1265
    self.inst_hvparams = None
......
1701 1706
  }
1702 1707

  
1703 1708

  
1709
def _GetInstNodeGroup(ctx, default, node_name):
1710
  """Gets group UUID of an instance node.
1711

  
1712
  @type ctx: L{InstanceQueryData}
1713
  @param default: Default value
1714
  @type node_name: string
1715
  @param node_name: Node name
1716

  
1717
  """
1718
  try:
1719
    node = ctx.nodes[node_name]
1720
  except KeyError:
1721
    return default
1722
  else:
1723
    return node.group
1724

  
1725

  
1726
def _GetInstNodeGroupName(ctx, default, node_name):
1727
  """Gets group name of an instance node.
1728

  
1729
  @type ctx: L{InstanceQueryData}
1730
  @param default: Default value
1731
  @type node_name: string
1732
  @param node_name: Node name
1733

  
1734
  """
1735
  try:
1736
    node = ctx.nodes[node_name]
1737
  except KeyError:
1738
    return default
1739

  
1740
  try:
1741
    group = ctx.groups[node.group]
1742
  except KeyError:
1743
    return default
1744

  
1745
  return group.name
1746

  
1747

  
1704 1748
def _BuildInstanceFields():
1705 1749
  """Builds list of fields for instance queries.
1706 1750

  
......
1708 1752
  fields = [
1709 1753
    (_MakeField("pnode", "Primary_node", QFT_TEXT, "Primary node"),
1710 1754
     IQ_CONFIG, QFF_HOSTNAME, _GetItemAttr("primary_node")),
1755
    (_MakeField("pnode.group", "PrimaryNodeGroup", QFT_TEXT,
1756
                "Primary node's group"),
1757
     IQ_NODES, 0,
1758
     lambda ctx, inst: _GetInstNodeGroupName(ctx, _FS_UNAVAIL,
1759
                                             inst.primary_node)),
1760
    (_MakeField("pnode.group.uuid", "PrimaryNodeGroupUUID", QFT_TEXT,
1761
                "Primary node's group UUID"),
1762
     IQ_NODES, 0,
1763
     lambda ctx, inst: _GetInstNodeGroup(ctx, _FS_UNAVAIL, inst.primary_node)),
1711 1764
    # TODO: Allow filtering by secondary node as hostname
1712 1765
    (_MakeField("snodes", "Secondary_Nodes", QFT_OTHER,
1713 1766
                "Secondary nodes; usually this will just be one node"),
1714 1767
     IQ_CONFIG, 0, lambda ctx, inst: list(inst.secondary_nodes)),
1768
    (_MakeField("snodes.group", "SecondaryNodesGroups", QFT_OTHER,
1769
                "Node groups of secondary nodes"),
1770
     IQ_NODES, 0,
1771
     lambda ctx, inst: map(compat.partial(_GetInstNodeGroupName, ctx, None),
1772
                           inst.secondary_nodes)),
1773
    (_MakeField("snodes.group.uuid", "SecondaryNodesGroupsUUID", QFT_OTHER,
1774
                "Node group UUIDs of secondary nodes"),
1775
     IQ_NODES, 0,
1776
     lambda ctx, inst: map(compat.partial(_GetInstNodeGroup, ctx, None),
1777
                           inst.secondary_nodes)),
1715 1778
    (_MakeField("admin_state", "Autostart", QFT_BOOL,
1716 1779
                "Desired state of instance (if set, the instance should be"
1717 1780
                " up)"),
b/test/ganeti.query_unittest.py
581 581
      ]
582 582

  
583 583
    iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
584
                                  set(), {})
584
                                  set(), {}, None, None)
585 585
    self.assertEqual(q.Query(iqd),
586 586
      [[(constants.RS_NORMAL, "inst1"),
587 587
        (constants.RS_NORMAL, 128),
......
610 610
    q = self._Create(selected)
611 611
    self.assertEqual(q.RequestedData(),
612 612
                     set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
613
                          query.IQ_CONSOLE]))
613
                          query.IQ_CONSOLE, query.IQ_NODES]))
614 614

  
615 615
    cluster = objects.Cluster(cluster_name="testcluster",
616 616
      hvparams=constants.HVC_DEFAULTS,
......
767 767

  
768 768
    iqd = query.InstanceQueryData(instances, cluster, disk_usage,
769 769
                                  offline_nodes, bad_nodes, live_data,
770
                                  wrongnode_inst, consinfo)
770
                                  wrongnode_inst, consinfo, {}, {})
771 771
    result = q.Query(iqd)
772 772
    self.assertEqual(len(result), len(instances))
773 773
    self.assert_(compat.all(len(row) == len(selected)

Also available in: Unified diff