Revision 218f4c3d

b/lib/cmdlib.py
10395 10395
    """Assign nodes to a new group.
10396 10396

  
10397 10397
    """
10398
    for node in self.op.nodes:
10399
      self.node_data[node].group = self.group_uuid
10400

  
10401
    # FIXME: Depends on side-effects of modifying the result of
10402
    # C{cfg.GetAllNodesInfo}
10398
    mods = [(node_name, self.group_uuid) for node_name in self.op.nodes]
10403 10399

  
10404
    self.cfg.Update(self.group, feedback_fn) # Saves all modified nodes.
10400
    self.cfg.AssignGroupNodes(mods)
10405 10401

  
10406 10402
  @staticmethod
10407 10403
  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
......
1517 1518
    else:
1518 1519
      nodegroup_obj.members.remove(node.name)
1519 1520

  
1521
  @locking.ssynchronized(_config_lock)
1522
  def AssignGroupNodes(self, mods):
1523
    """Changes the group of a number of nodes.
1524

  
1525
    @type mods: list of tuples; (node name, new group UUID)
1526
    @param modes: Node membership modifications
1527

  
1528
    """
1529
    groups = self._config_data.nodegroups
1530
    nodes = self._config_data.nodes
1531

  
1532
    resmod = []
1533

  
1534
    # Try to resolve names/UUIDs first
1535
    for (node_name, new_group_uuid) in mods:
1536
      try:
1537
        node = nodes[node_name]
1538
      except KeyError:
1539
        raise errors.ConfigurationError("Unable to find node '%s'" % node_name)
1540

  
1541
      if node.group == new_group_uuid:
1542
        # Node is being assigned to its current group
1543
        logging.debug("Node '%s' was assigned to its current group (%s)",
1544
                      node_name, node.group)
1545
        continue
1546

  
1547
      # Try to find current group of node
1548
      try:
1549
        old_group = groups[node.group]
1550
      except KeyError:
1551
        raise errors.ConfigurationError("Unable to find old group '%s'" %
1552
                                        node.group)
1553

  
1554
      # Try to find new group for node
1555
      try:
1556
        new_group = groups[new_group_uuid]
1557
      except KeyError:
1558
        raise errors.ConfigurationError("Unable to find new group '%s'" %
1559
                                        new_group_uuid)
1560

  
1561
      assert node.name in old_group.members, \
1562
        ("Inconsistent configuration: node '%s' not listed in members for its"
1563
         " old group '%s'" % (node.name, old_group.uuid))
1564
      assert node.name not in new_group.members, \
1565
        ("Inconsistent configuration: node '%s' already listed in members for"
1566
         " its new group '%s'" % (node.name, new_group.uuid))
1567

  
1568
      resmod.append((node, old_group, new_group))
1569

  
1570
    # Apply changes
1571
    for (node, old_group, new_group) in resmod:
1572
      assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
1573
        "Assigning to current group is not possible"
1574

  
1575
      node.group = new_group.uuid
1576

  
1577
      # Update members of involved groups
1578
      if node.name in old_group.members:
1579
        old_group.members.remove(node.name)
1580
      if node.name not in new_group.members:
1581
        new_group.members.append(node.name)
1582

  
1583
    # Update timestamps and serials (only once per node/group object)
1584
    now = time.time()
1585
    for obj in frozenset(itertools.chain(*resmod)): # pylint: disable-msg=W0142
1586
      obj.serial_no += 1
1587
      obj.mtime = now
1588

  
1589
    # Force ssconf update
1590
    self._config_data.cluster.serial_no += 1
1591

  
1592
    self._WriteConfig()
1593

  
1520 1594
  def _BumpSerialNo(self):
1521 1595
    """Bump up the serial number of the config.
1522 1596

  
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