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