Revision 54c31fd3

b/lib/cmdlib.py
11992 11992
    """Assign nodes to a new group.
11993 11993

  
11994 11994
    """
11995
    for node in self.op.nodes:
11996
      self.node_data[node].group = self.group_uuid
11995
    mods = [(node_name, self.group_uuid) for node_name in self.op.nodes]
11997 11996

  
11998
    # FIXME: Depends on side-effects of modifying the result of
11999
    # C{cfg.GetAllNodesInfo}
12000

  
12001
    self.cfg.Update(self.group, feedback_fn) # Saves all modified nodes.
11997
    self.cfg.AssignGroupNodes(mods)
12002 11998

  
12003 11999
  @staticmethod
12004 12000
  def CheckAssignmentForSplitInstances(changes, node_data, instance_data):
b/lib/config.py
38 38
import random
39 39
import logging
40 40
import time
41
import itertools
41 42

  
42 43
from ganeti import errors
43 44
from ganeti import locking
......
1594 1595
    else:
1595 1596
      nodegroup_obj.members.remove(node.name)
1596 1597

  
1598
  @locking.ssynchronized(_config_lock)
1599
  def AssignGroupNodes(self, mods):
1600
    """Changes the group of a number of nodes.
1601

  
1602
    @type mods: list of tuples; (node name, new group UUID)
1603
    @param modes: Node membership modifications
1604

  
1605
    """
1606
    groups = self._config_data.nodegroups
1607
    nodes = self._config_data.nodes
1608

  
1609
    resmod = []
1610

  
1611
    # Try to resolve names/UUIDs first
1612
    for (node_name, new_group_uuid) in mods:
1613
      try:
1614
        node = nodes[node_name]
1615
      except KeyError:
1616
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1617

  
1618
      if node.group == new_group_uuid:
1619
        # Node is being assigned to its current group
1620
        logging.debug("Node '%s' was assigned to its current group (%s)",
1621
                      node_name, node.group)
1622
        continue
1623

  
1624
      # Try to find current group of node
1625
      try:
1626
        old_group = groups[node.group]
1627
      except KeyError:
1628
        raise errors.ConfigurationError("Unable to find old group '%s'" %
1629
                                        node.group)
1630

  
1631
      # Try to find new group for node
1632
      try:
1633
        new_group = groups[new_group_uuid]
1634
      except KeyError:
1635
        raise errors.ConfigurationError("Unable to find new group '%s'" %
1636
                                        new_group_uuid)
1637

  
1638
      assert node.name in old_group.members, \
1639
        ("Inconsistent configuration: node '%s' not listed in members for its"
1640
         " old group '%s'" % (node.name, old_group.uuid))
1641
      assert node.name not in new_group.members, \
1642
        ("Inconsistent configuration: node '%s' already listed in members for"
1643
         " its new group '%s'" % (node.name, new_group.uuid))
1644

  
1645
      resmod.append((node, old_group, new_group))
1646

  
1647
    # Apply changes
1648
    for (node, old_group, new_group) in resmod:
1649
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
1650
        "Assigning to current group is not possible"
1651

  
1652
      node.group = new_group.uuid
1653

  
1654
      # Update members of involved groups
1655
      if node.name in old_group.members:
1656
        old_group.members.remove(node.name)
1657
      if node.name not in new_group.members:
1658
        new_group.members.append(node.name)
1659

  
1660
    # Update timestamps and serials (only once per node/group object)
1661
    now = time.time()
1662
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable-msg=W0142
1663
      obj.serial_no += 1
1664
      obj.mtime = now
1665

  
1666
    # Force ssconf update
1667
    self._config_data.cluster.serial_no += 1
1668

  
1669
    self._WriteConfig()
1670

  
1597 1671
  def _BumpSerialNo(self):
1598 1672
    """Bump up the serial number of the config.
1599 1673

  
b/test/ganeti.config_unittest.py
1 1
#!/usr/bin/python
2 2
#
3 3

  
4
# Copyright (C) 2006, 2007, 2010 Google Inc.
4
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
28 28
import tempfile
29 29
import os.path
30 30
import socket
31
import operator
32
import itertools
31 33

  
32 34
from ganeti import bootstrap
33 35
from ganeti import config
......
36 38
from ganeti import objects
37 39
from ganeti import utils
38 40
from ganeti import netutils
41
from ganeti import compat
39 42

  
40 43
from ganeti.config import TemporaryReservationManager
41 44

  
......
239 242
    cfg.AddNodeGroup(group, "my-job", check_uuid=False) # Does not raise.
240 243
    self.assertEqual(uuid, group.uuid)
241 244

  
245
  def testAssignGroupNodes(self):
246
    me = netutils.Hostname()
247
    cfg = self._get_object()
248

  
249
    # Create two groups
250
    grp1 = objects.NodeGroup(name="grp1", members=[],
251
                             uuid="2f2fadf7-2a70-4a23-9ab5-2568c252032c")
252
    grp1_serial = 1
253
    cfg.AddNodeGroup(grp1, "job")
254

  
255
    grp2 = objects.NodeGroup(name="grp2", members=[],
256
                             uuid="798d0de3-680f-4a0e-b29a-0f54f693b3f1")
257
    grp2_serial = 1
258
    cfg.AddNodeGroup(grp2, "job")
259
    self.assertEqual(set(map(operator.attrgetter("name"),
260
                             cfg.GetAllNodeGroupsInfo().values())),
261
                     set(["grp1", "grp2", constants.INITIAL_NODE_GROUP_NAME]))
262

  
263
    # No-op
264
    cluster_serial = cfg.GetClusterInfo().serial_no
265
    cfg.AssignGroupNodes([])
266
    cluster_serial += 1
267

  
268
    # Create two nodes
269
    node1 = objects.Node(name="node1", group=grp1.uuid, ndparams={})
270
    node1_serial = 1
271
    node2 = objects.Node(name="node2", group=grp2.uuid, ndparams={})
272
    node2_serial = 1
273
    cfg.AddNode(node1, "job")
274
    cfg.AddNode(node2, "job")
275
    cluster_serial += 2
276
    self.assertEqual(set(cfg.GetNodeList()), set(["node1", "node2", me.name]))
277

  
278
    def _VerifySerials():
279
      self.assertEqual(cfg.GetClusterInfo().serial_no, cluster_serial)
280
      self.assertEqual(node1.serial_no, node1_serial)
281
      self.assertEqual(node2.serial_no, node2_serial)
282
      self.assertEqual(grp1.serial_no, grp1_serial)
283
      self.assertEqual(grp2.serial_no, grp2_serial)
284

  
285
    _VerifySerials()
286

  
287
    self.assertEqual(set(grp1.members), set(["node1"]))
288
    self.assertEqual(set(grp2.members), set(["node2"]))
289

  
290
    # Check invalid nodes and groups
291
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
292
      ("unknown.node.example.com", grp2.uuid),
293
      ])
294
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
295
      (node1.name, "unknown-uuid"),
296
      ])
297

  
298
    self.assertEqual(node1.group, grp1.uuid)
299
    self.assertEqual(node2.group, grp2.uuid)
300
    self.assertEqual(set(grp1.members), set(["node1"]))
301
    self.assertEqual(set(grp2.members), set(["node2"]))
302

  
303
    # Another no-op
304
    cfg.AssignGroupNodes([])
305
    cluster_serial += 1
306
    _VerifySerials()
307

  
308
    # Assign to the same group (should be a no-op)
309
    self.assertEqual(node2.group, grp2.uuid)
310
    cfg.AssignGroupNodes([
311
      (node2.name, grp2.uuid),
312
      ])
313
    cluster_serial += 1
314
    self.assertEqual(node2.group, grp2.uuid)
315
    _VerifySerials()
316
    self.assertEqual(set(grp1.members), set(["node1"]))
317
    self.assertEqual(set(grp2.members), set(["node2"]))
318

  
319
    # Assign node 2 to group 1
320
    self.assertEqual(node2.group, grp2.uuid)
321
    cfg.AssignGroupNodes([
322
      (node2.name, grp1.uuid),
323
      ])
324
    cluster_serial += 1
325
    node2_serial += 1
326
    grp1_serial += 1
327
    grp2_serial += 1
328
    self.assertEqual(node2.group, grp1.uuid)
329
    _VerifySerials()
330
    self.assertEqual(set(grp1.members), set(["node1", "node2"]))
331
    self.assertFalse(grp2.members)
332

  
333
    # And assign both nodes to group 2
334
    self.assertEqual(node1.group, grp1.uuid)
335
    self.assertEqual(node2.group, grp1.uuid)
336
    self.assertNotEqual(grp1.uuid, grp2.uuid)
337
    cfg.AssignGroupNodes([
338
      (node1.name, grp2.uuid),
339
      (node2.name, grp2.uuid),
340
      ])
341
    cluster_serial += 1
342
    node1_serial += 1
343
    node2_serial += 1
344
    grp1_serial += 1
345
    grp2_serial += 1
346
    self.assertEqual(node1.group, grp2.uuid)
347
    self.assertEqual(node2.group, grp2.uuid)
348
    _VerifySerials()
349
    self.assertFalse(grp1.members)
350
    self.assertEqual(set(grp2.members), set(["node1", "node2"]))
351

  
352
    # Destructive tests
353
    orig_group = node2.group
354
    try:
355
      other_uuid = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
356
      assert compat.all(node.group != other_uuid
357
                        for node in cfg.GetAllNodesInfo().values())
358
      node2.group = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
359
      self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
360
        ("node2", grp2.uuid),
361
        ])
362
      _VerifySerials()
363
    finally:
364
      node2.group = orig_group
365

  
242 366

  
243 367
class TestTRM(unittest.TestCase):
244 368
  EC_ID = 1

Also available in: Unified diff