Revision 64c7b383

b/lib/backend.py
520 520
      what[constants.NV_FILELIST])
521 521

  
522 522
  if constants.NV_NODELIST in what:
523
    result[constants.NV_NODELIST] = tmp = {}
524
    random.shuffle(what[constants.NV_NODELIST])
525
    for node in what[constants.NV_NODELIST]:
523
    (nodes, bynode) = what[constants.NV_NODELIST]
524

  
525
    # Add nodes from other groups (different for each node)
526
    try:
527
      nodes.extend(bynode[my_name])
528
    except KeyError:
529
      pass
530

  
531
    # Use a random order
532
    random.shuffle(nodes)
533

  
534
    # Try to contact all nodes
535
    val = {}
536
    for node in nodes:
526 537
      success, message = _GetSshRunner(cluster_name).VerifyNodeHostname(node)
527 538
      if not success:
528
        tmp[node] = message
539
        val[node] = message
540

  
541
    result[constants.NV_NODELIST] = val
529 542

  
530 543
  if constants.NV_NODENETTEST in what:
531 544
    result[constants.NV_NODENETTEST] = tmp = {}
b/lib/cmdlib.py
2542 2542

  
2543 2543
    return instdisk
2544 2544

  
2545
  @staticmethod
2546
  def _SshNodeSelector(group_uuid, all_nodes):
2547
    """Create endless iterators for all potential SSH check hosts.
2548

  
2549
    """
2550
    nodes = [node for node in all_nodes
2551
             if (node.group != group_uuid and
2552
                 not node.offline)]
2553
    keyfunc = operator.attrgetter("group")
2554

  
2555
    return map(itertools.cycle,
2556
               [sorted(map(operator.attrgetter("name"), names))
2557
                for _, names in itertools.groupby(sorted(nodes, key=keyfunc),
2558
                                                  keyfunc)])
2559

  
2560
  @classmethod
2561
  def _SelectSshCheckNodes(cls, group_nodes, group_uuid, all_nodes):
2562
    """Choose which nodes should talk to which other nodes.
2563

  
2564
    We will make nodes contact all nodes in their group, and one node from
2565
    every other group.
2566

  
2567
    @warning: This algorithm has a known issue if one node group is much
2568
      smaller than others (e.g. just one node). In such a case all other
2569
      nodes will talk to the single node.
2570

  
2571
    """
2572
    online_nodes = sorted(node.name for node in group_nodes if not node.offline)
2573
    sel = cls._SshNodeSelector(group_uuid, all_nodes)
2574

  
2575
    return (online_nodes,
2576
            dict((name, sorted([i.next() for i in sel]))
2577
                 for name in online_nodes))
2578

  
2545 2579
  def BuildHooksEnv(self):
2546 2580
    """Build hooks env.
2547 2581

  
......
2605 2639

  
2606 2640
    feedback_fn("* Gathering data (%d nodes)" % len(self.my_node_names))
2607 2641

  
2608
    # We will make nodes contact all nodes in their group, and one node from
2609
    # every other group.
2610
    # TODO: should it be a *random* node, different every time?
2611
    online_nodes = [node.name for node in node_data_list if not node.offline]
2612
    other_group_nodes = {}
2613

  
2614
    for name in sorted(self.all_node_info):
2615
      node = self.all_node_info[name]
2616
      if (node.group not in other_group_nodes
2617
          and node.group != self.group_uuid
2618
          and not node.offline):
2619
        other_group_nodes[node.group] = node.name
2620

  
2621 2642
    node_verify_param = {
2622 2643
      constants.NV_FILELIST:
2623 2644
        utils.UniqueSequence(filename
2624 2645
                             for files in filemap
2625 2646
                             for filename in files),
2626
      constants.NV_NODELIST: online_nodes + other_group_nodes.values(),
2647
      constants.NV_NODELIST:
2648
        self._SelectSshCheckNodes(node_data_list, self.group_uuid,
2649
                                  self.all_node_info.values()),
2627 2650
      constants.NV_HYPERVISOR: hypervisors,
2628 2651
      constants.NV_HVPARAMS:
2629 2652
        _GetAllHypervisorParameters(cluster, self.all_inst_info.values()),
b/test/ganeti.cmdlib_unittest.py
27 27
import time
28 28
import tempfile
29 29
import shutil
30
import operator
30 31

  
31 32
from ganeti import constants
32 33
from ganeti import mcpu
......
207 208
    self.assertEqual(set(["inst3c"]), set(prev))
208 209

  
209 210

  
211
class TestClusterVerifySsh(unittest.TestCase):
212
  def testMultipleGroups(self):
213
    fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
214
    mygroupnodes = [
215
      objects.Node(name="node20", group="my", offline=False),
216
      objects.Node(name="node21", group="my", offline=False),
217
      objects.Node(name="node22", group="my", offline=False),
218
      objects.Node(name="node23", group="my", offline=False),
219
      objects.Node(name="node24", group="my", offline=False),
220
      objects.Node(name="node25", group="my", offline=False),
221
      objects.Node(name="node26", group="my", offline=True),
222
      ]
223
    nodes = [
224
      objects.Node(name="node1", group="g1", offline=True),
225
      objects.Node(name="node2", group="g1", offline=False),
226
      objects.Node(name="node3", group="g1", offline=False),
227
      objects.Node(name="node4", group="g1", offline=True),
228
      objects.Node(name="node5", group="g1", offline=False),
229
      objects.Node(name="node10", group="xyz", offline=False),
230
      objects.Node(name="node11", group="xyz", offline=False),
231
      objects.Node(name="node40", group="alloff", offline=True),
232
      objects.Node(name="node41", group="alloff", offline=True),
233
      objects.Node(name="node50", group="aaa", offline=False),
234
      ] + mygroupnodes
235
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
236

  
237
    (online, perhost) = fn(mygroupnodes, "my", nodes)
238
    self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
239
    self.assertEqual(set(perhost.keys()), set(online))
240

  
241
    self.assertEqual(perhost, {
242
      "node20": ["node10", "node2", "node50"],
243
      "node21": ["node11", "node3", "node50"],
244
      "node22": ["node10", "node5", "node50"],
245
      "node23": ["node11", "node2", "node50"],
246
      "node24": ["node10", "node3", "node50"],
247
      "node25": ["node11", "node5", "node50"],
248
      })
249

  
250
  def testSingleGroup(self):
251
    fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
252
    nodes = [
253
      objects.Node(name="node1", group="default", offline=True),
254
      objects.Node(name="node2", group="default", offline=False),
255
      objects.Node(name="node3", group="default", offline=False),
256
      objects.Node(name="node4", group="default", offline=True),
257
      ]
258
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
259

  
260
    (online, perhost) = fn(nodes, "default", nodes)
261
    self.assertEqual(online, ["node2", "node3"])
262
    self.assertEqual(set(perhost.keys()), set(online))
263

  
264
    self.assertEqual(perhost, {
265
      "node2": [],
266
      "node3": [],
267
      })
268

  
269

  
210 270
if __name__ == "__main__":
211 271
  testutils.GanetiTestProgram()

Also available in: Unified diff