Package ganeti :: Package cmdlib :: Module group
[hide private]
[frames] | no frames]

Source Code for Module ganeti.cmdlib.group

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. 
   5  # 
   6  # This program is free software; you can redistribute it and/or modify 
   7  # it under the terms of the GNU General Public License as published by 
   8  # the Free Software Foundation; either version 2 of the License, or 
   9  # (at your option) any later version. 
  10  # 
  11  # This program is distributed in the hope that it will be useful, but 
  12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  14  # General Public License for more details. 
  15  # 
  16  # You should have received a copy of the GNU General Public License 
  17  # along with this program; if not, write to the Free Software 
  18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
  19  # 02110-1301, USA. 
  20   
  21   
  22  """Logical units dealing with node groups.""" 
  23   
  24  import itertools 
  25  import logging 
  26   
  27  from ganeti import constants 
  28  from ganeti import errors 
  29  from ganeti import locking 
  30  from ganeti import objects 
  31  from ganeti import qlang 
  32  from ganeti import query 
  33  from ganeti import utils 
  34  from ganeti.masterd import iallocator 
  35  from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase, \ 
  36    ResultWithJobs 
  37  from ganeti.cmdlib.common import MergeAndVerifyHvState, \ 
  38    MergeAndVerifyDiskState, GetWantedNodes, GetUpdatedParams, \ 
  39    CheckNodeGroupInstances, GetUpdatedIPolicy, \ 
  40    ComputeNewInstanceViolations, GetDefaultIAllocator, ShareAll, \ 
  41    CheckInstancesNodeGroups, LoadNodeEvacResult, MapInstanceLvsToNodes, \ 
  42    CheckIpolicyVsDiskTemplates 
  43   
  44  import ganeti.masterd.instance 
45 46 47 -class LUGroupAdd(LogicalUnit):
48 """Logical unit for creating node groups. 49 50 """ 51 HPATH = "group-add" 52 HTYPE = constants.HTYPE_GROUP 53 REQ_BGL = False 54
55 - def ExpandNames(self):
56 # We need the new group's UUID here so that we can create and acquire the 57 # corresponding lock. Later, in Exec(), we'll indicate to cfg.AddNodeGroup 58 # that it should not check whether the UUID exists in the configuration. 59 self.group_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId()) 60 self.needed_locks = {} 61 self.add_locks[locking.LEVEL_NODEGROUP] = self.group_uuid
62
63 - def _CheckIpolicy(self):
64 """Checks the group's ipolicy for consistency and validity. 65 66 """ 67 if self.op.ipolicy: 68 cluster = self.cfg.GetClusterInfo() 69 full_ipolicy = cluster.SimpleFillIPolicy(self.op.ipolicy) 70 try: 71 objects.InstancePolicy.CheckParameterSyntax(full_ipolicy, False) 72 except errors.ConfigurationError, err: 73 raise errors.OpPrereqError("Invalid instance policy: %s" % err, 74 errors.ECODE_INVAL) 75 CheckIpolicyVsDiskTemplates(full_ipolicy, 76 cluster.enabled_disk_templates)
77
78 - def CheckPrereq(self):
79 """Check prerequisites. 80 81 This checks that the given group name is not an existing node group 82 already. 83 84 """ 85 try: 86 existing_uuid = self.cfg.LookupNodeGroup(self.op.group_name) 87 except errors.OpPrereqError: 88 pass 89 else: 90 raise errors.OpPrereqError("Desired group name '%s' already exists as a" 91 " node group (UUID: %s)" % 92 (self.op.group_name, existing_uuid), 93 errors.ECODE_EXISTS) 94 95 if self.op.ndparams: 96 utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES) 97 98 if self.op.hv_state: 99 self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state, None) 100 else: 101 self.new_hv_state = None 102 103 if self.op.disk_state: 104 self.new_disk_state = MergeAndVerifyDiskState(self.op.disk_state, None) 105 else: 106 self.new_disk_state = None 107 108 if self.op.diskparams: 109 for templ in constants.DISK_TEMPLATES: 110 if templ in self.op.diskparams: 111 utils.ForceDictType(self.op.diskparams[templ], 112 constants.DISK_DT_TYPES) 113 self.new_diskparams = self.op.diskparams 114 try: 115 utils.VerifyDictOptions(self.new_diskparams, constants.DISK_DT_DEFAULTS) 116 except errors.OpPrereqError, err: 117 raise errors.OpPrereqError("While verify diskparams options: %s" % err, 118 errors.ECODE_INVAL) 119 else: 120 self.new_diskparams = {} 121 122 self._CheckIpolicy()
123
124 - def BuildHooksEnv(self):
125 """Build hooks env. 126 127 """ 128 return { 129 "GROUP_NAME": self.op.group_name, 130 }
131
132 - def BuildHooksNodes(self):
133 """Build hooks nodes. 134 135 """ 136 mn = self.cfg.GetMasterNode() 137 return ([mn], [mn])
138
139 - def Exec(self, feedback_fn):
140 """Add the node group to the cluster. 141 142 """ 143 group_obj = objects.NodeGroup(name=self.op.group_name, members=[], 144 uuid=self.group_uuid, 145 alloc_policy=self.op.alloc_policy, 146 ndparams=self.op.ndparams, 147 diskparams=self.new_diskparams, 148 ipolicy=self.op.ipolicy, 149 hv_state_static=self.new_hv_state, 150 disk_state_static=self.new_disk_state) 151 152 self.cfg.AddNodeGroup(group_obj, self.proc.GetECId(), check_uuid=False) 153 del self.remove_locks[locking.LEVEL_NODEGROUP]
154
155 156 -class LUGroupAssignNodes(NoHooksLU):
157 """Logical unit for assigning nodes to groups. 158 159 """ 160 REQ_BGL = False 161
162 - def ExpandNames(self):
163 # These raise errors.OpPrereqError on their own: 164 self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name) 165 (self.op.node_uuids, self.op.nodes) = GetWantedNodes(self, self.op.nodes) 166 167 # We want to lock all the affected nodes and groups. We have readily 168 # available the list of nodes, and the *destination* group. To gather the 169 # list of "source" groups, we need to fetch node information later on. 170 self.needed_locks = { 171 locking.LEVEL_NODEGROUP: set([self.group_uuid]), 172 locking.LEVEL_NODE: self.op.node_uuids, 173 }
174
175 - def DeclareLocks(self, level):
176 if level == locking.LEVEL_NODEGROUP: 177 assert len(self.needed_locks[locking.LEVEL_NODEGROUP]) == 1 178 179 # Try to get all affected nodes' groups without having the group or node 180 # lock yet. Needs verification later in the code flow. 181 groups = self.cfg.GetNodeGroupsFromNodes(self.op.node_uuids) 182 183 self.needed_locks[locking.LEVEL_NODEGROUP].update(groups)
184
185 - def CheckPrereq(self):
186 """Check prerequisites. 187 188 """ 189 assert self.needed_locks[locking.LEVEL_NODEGROUP] 190 assert (frozenset(self.owned_locks(locking.LEVEL_NODE)) == 191 frozenset(self.op.node_uuids)) 192 193 expected_locks = (set([self.group_uuid]) | 194 self.cfg.GetNodeGroupsFromNodes(self.op.node_uuids)) 195 actual_locks = self.owned_locks(locking.LEVEL_NODEGROUP) 196 if actual_locks != expected_locks: 197 raise errors.OpExecError("Nodes changed groups since locks were acquired," 198 " current groups are '%s', used to be '%s'" % 199 (utils.CommaJoin(expected_locks), 200 utils.CommaJoin(actual_locks))) 201 202 self.node_data = self.cfg.GetAllNodesInfo() 203 self.group = self.cfg.GetNodeGroup(self.group_uuid) 204 instance_data = self.cfg.GetAllInstancesInfo() 205 206 if self.group is None: 207 raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" % 208 (self.op.group_name, self.group_uuid)) 209 210 (new_splits, previous_splits) = \ 211 self.CheckAssignmentForSplitInstances([(uuid, self.group_uuid) 212 for uuid in self.op.node_uuids], 213 self.node_data, instance_data) 214 215 if new_splits: 216 fmt_new_splits = utils.CommaJoin(utils.NiceSort( 217 self.cfg.GetInstanceNames(new_splits))) 218 219 if not self.op.force: 220 raise errors.OpExecError("The following instances get split by this" 221 " change and --force was not given: %s" % 222 fmt_new_splits) 223 else: 224 self.LogWarning("This operation will split the following instances: %s", 225 fmt_new_splits) 226 227 if previous_splits: 228 self.LogWarning("In addition, these already-split instances continue" 229 " to be split across groups: %s", 230 utils.CommaJoin(utils.NiceSort( 231 self.cfg.GetInstanceNames(previous_splits))))
232
233 - def Exec(self, feedback_fn):
234 """Assign nodes to a new group. 235 236 """ 237 mods = [(node_uuid, self.group_uuid) for node_uuid in self.op.node_uuids] 238 239 self.cfg.AssignGroupNodes(mods)
240 241 @staticmethod
242 - def CheckAssignmentForSplitInstances(changes, node_data, instance_data):
243 """Check for split instances after a node assignment. 244 245 This method considers a series of node assignments as an atomic operation, 246 and returns information about split instances after applying the set of 247 changes. 248 249 In particular, it returns information about newly split instances, and 250 instances that were already split, and remain so after the change. 251 252 Only instances whose disk template is listed in constants.DTS_INT_MIRROR are 253 considered. 254 255 @type changes: list of (node_uuid, new_group_uuid) pairs. 256 @param changes: list of node assignments to consider. 257 @param node_data: a dict with data for all nodes 258 @param instance_data: a dict with all instances to consider 259 @rtype: a two-tuple 260 @return: a list of instances that were previously okay and result split as a 261 consequence of this change, and a list of instances that were previously 262 split and this change does not fix. 263 264 """ 265 changed_nodes = dict((uuid, group) for uuid, group in changes 266 if node_data[uuid].group != group) 267 268 all_split_instances = set() 269 previously_split_instances = set() 270 271 for inst in instance_data.values(): 272 if inst.disk_template not in constants.DTS_INT_MIRROR: 273 continue 274 275 if len(set(node_data[node_uuid].group 276 for node_uuid in inst.all_nodes)) > 1: 277 previously_split_instances.add(inst.uuid) 278 279 if len(set(changed_nodes.get(node_uuid, node_data[node_uuid].group) 280 for node_uuid in inst.all_nodes)) > 1: 281 all_split_instances.add(inst.uuid) 282 283 return (list(all_split_instances - previously_split_instances), 284 list(previously_split_instances & all_split_instances))
285
286 287 -class GroupQuery(QueryBase):
288 FIELDS = query.GROUP_FIELDS 289
290 - def ExpandNames(self, lu):
291 lu.needed_locks = {} 292 293 self._all_groups = lu.cfg.GetAllNodeGroupsInfo() 294 self._cluster = lu.cfg.GetClusterInfo() 295 name_to_uuid = dict((g.name, g.uuid) for g in self._all_groups.values()) 296 297 if not self.names: 298 self.wanted = [name_to_uuid[name] 299 for name in utils.NiceSort(name_to_uuid.keys())] 300 else: 301 # Accept names to be either names or UUIDs. 302 missing = [] 303 self.wanted = [] 304 all_uuid = frozenset(self._all_groups.keys()) 305 306 for name in self.names: 307 if name in all_uuid: 308 self.wanted.append(name) 309 elif name in name_to_uuid: 310 self.wanted.append(name_to_uuid[name]) 311 else: 312 missing.append(name) 313 314 if missing: 315 raise errors.OpPrereqError("Some groups do not exist: %s" % 316 utils.CommaJoin(missing), 317 errors.ECODE_NOENT)
318
319 - def DeclareLocks(self, lu, level):
320 pass
321
322 - def _GetQueryData(self, lu):
323 """Computes the list of node groups and their attributes. 324 325 """ 326 do_nodes = query.GQ_NODE in self.requested_data 327 do_instances = query.GQ_INST in self.requested_data 328 329 group_to_nodes = None 330 group_to_instances = None 331 332 # For GQ_NODE, we need to map group->[nodes], and group->[instances] for 333 # GQ_INST. The former is attainable with just GetAllNodesInfo(), but for the 334 # latter GetAllInstancesInfo() is not enough, for we have to go through 335 # instance->node. Hence, we will need to process nodes even if we only need 336 # instance information. 337 if do_nodes or do_instances: 338 all_nodes = lu.cfg.GetAllNodesInfo() 339 group_to_nodes = dict((uuid, []) for uuid in self.wanted) 340 node_to_group = {} 341 342 for node in all_nodes.values(): 343 if node.group in group_to_nodes: 344 group_to_nodes[node.group].append(node.uuid) 345 node_to_group[node.uuid] = node.group 346 347 if do_instances: 348 all_instances = lu.cfg.GetAllInstancesInfo() 349 group_to_instances = dict((uuid, []) for uuid in self.wanted) 350 351 for instance in all_instances.values(): 352 node = instance.primary_node 353 if node in node_to_group: 354 group_to_instances[node_to_group[node]].append(instance.uuid) 355 356 if not do_nodes: 357 # Do not pass on node information if it was not requested. 358 group_to_nodes = None 359 360 return query.GroupQueryData(self._cluster, 361 [self._all_groups[uuid] 362 for uuid in self.wanted], 363 group_to_nodes, group_to_instances, 364 query.GQ_DISKPARAMS in self.requested_data)
365
366 367 -class LUGroupQuery(NoHooksLU):
368 """Logical unit for querying node groups. 369 370 """ 371 REQ_BGL = False 372
373 - def CheckArguments(self):
374 self.gq = GroupQuery(qlang.MakeSimpleFilter("name", self.op.names), 375 self.op.output_fields, False)
376
377 - def ExpandNames(self):
378 self.gq.ExpandNames(self)
379
380 - def DeclareLocks(self, level):
381 self.gq.DeclareLocks(self, level)
382
383 - def Exec(self, feedback_fn):
384 return self.gq.OldStyleQuery(self)
385
386 387 -class LUGroupSetParams(LogicalUnit):
388 """Modifies the parameters of a node group. 389 390 """ 391 HPATH = "group-modify" 392 HTYPE = constants.HTYPE_GROUP 393 REQ_BGL = False 394
395 - def CheckArguments(self):
396 all_changes = [ 397 self.op.ndparams, 398 self.op.diskparams, 399 self.op.alloc_policy, 400 self.op.hv_state, 401 self.op.disk_state, 402 self.op.ipolicy, 403 ] 404 405 if all_changes.count(None) == len(all_changes): 406 raise errors.OpPrereqError("Please pass at least one modification", 407 errors.ECODE_INVAL)
408
409 - def ExpandNames(self):
410 # This raises errors.OpPrereqError on its own: 411 self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name) 412 413 self.needed_locks = { 414 locking.LEVEL_INSTANCE: [], 415 locking.LEVEL_NODEGROUP: [self.group_uuid], 416 } 417 418 self.share_locks[locking.LEVEL_INSTANCE] = 1
419
420 - def DeclareLocks(self, level):
421 if level == locking.LEVEL_INSTANCE: 422 assert not self.needed_locks[locking.LEVEL_INSTANCE] 423 424 # Lock instances optimistically, needs verification once group lock has 425 # been acquired 426 self.needed_locks[locking.LEVEL_INSTANCE] = \ 427 self.cfg.GetInstanceNames( 428 self.cfg.GetNodeGroupInstances(self.group_uuid))
429 430 @staticmethod
431 - def _UpdateAndVerifyDiskParams(old, new):
432 """Updates and verifies disk parameters. 433 434 """ 435 new_params = GetUpdatedParams(old, new) 436 utils.ForceDictType(new_params, constants.DISK_DT_TYPES) 437 return new_params
438
439 - def _CheckIpolicy(self, cluster, owned_instance_names):
440 """Sanity checks for the ipolicy. 441 442 @type cluster: C{objects.Cluster} 443 @param cluster: the cluster's configuration 444 @type owned_instance_names: list of string 445 @param owned_instance_names: list of instances 446 447 """ 448 if self.op.ipolicy: 449 self.new_ipolicy = GetUpdatedIPolicy(self.group.ipolicy, 450 self.op.ipolicy, 451 group_policy=True) 452 453 new_ipolicy = cluster.SimpleFillIPolicy(self.new_ipolicy) 454 CheckIpolicyVsDiskTemplates(new_ipolicy, 455 cluster.enabled_disk_templates) 456 instances = \ 457 dict(self.cfg.GetMultiInstanceInfoByName(owned_instance_names)) 458 gmi = ganeti.masterd.instance 459 violations = \ 460 ComputeNewInstanceViolations(gmi.CalculateGroupIPolicy(cluster, 461 self.group), 462 new_ipolicy, instances.values(), 463 self.cfg) 464 465 if violations: 466 self.LogWarning("After the ipolicy change the following instances" 467 " violate them: %s", 468 utils.CommaJoin(violations))
469
470 - def CheckPrereq(self):
471 """Check prerequisites. 472 473 """ 474 owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) 475 476 # Check if locked instances are still correct 477 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names) 478 479 self.group = self.cfg.GetNodeGroup(self.group_uuid) 480 cluster = self.cfg.GetClusterInfo() 481 482 if self.group is None: 483 raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" % 484 (self.op.group_name, self.group_uuid)) 485 486 if self.op.ndparams: 487 new_ndparams = GetUpdatedParams(self.group.ndparams, self.op.ndparams) 488 utils.ForceDictType(new_ndparams, constants.NDS_PARAMETER_TYPES) 489 self.new_ndparams = new_ndparams 490 491 if self.op.diskparams: 492 diskparams = self.group.diskparams 493 uavdp = self._UpdateAndVerifyDiskParams 494 # For each disktemplate subdict update and verify the values 495 new_diskparams = dict((dt, 496 uavdp(diskparams.get(dt, {}), 497 self.op.diskparams[dt])) 498 for dt in constants.DISK_TEMPLATES 499 if dt in self.op.diskparams) 500 # As we've all subdicts of diskparams ready, lets merge the actual 501 # dict with all updated subdicts 502 self.new_diskparams = objects.FillDict(diskparams, new_diskparams) 503 try: 504 utils.VerifyDictOptions(self.new_diskparams, constants.DISK_DT_DEFAULTS) 505 except errors.OpPrereqError, err: 506 raise errors.OpPrereqError("While verify diskparams options: %s" % err, 507 errors.ECODE_INVAL) 508 509 if self.op.hv_state: 510 self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state, 511 self.group.hv_state_static) 512 513 if self.op.disk_state: 514 self.new_disk_state = \ 515 MergeAndVerifyDiskState(self.op.disk_state, 516 self.group.disk_state_static) 517 518 self._CheckIpolicy(cluster, owned_instance_names)
519
520 - def BuildHooksEnv(self):
521 """Build hooks env. 522 523 """ 524 return { 525 "GROUP_NAME": self.op.group_name, 526 "NEW_ALLOC_POLICY": self.op.alloc_policy, 527 }
528
529 - def BuildHooksNodes(self):
530 """Build hooks nodes. 531 532 """ 533 mn = self.cfg.GetMasterNode() 534 return ([mn], [mn])
535
536 - def Exec(self, feedback_fn):
537 """Modifies the node group. 538 539 """ 540 result = [] 541 542 if self.op.ndparams: 543 self.group.ndparams = self.new_ndparams 544 result.append(("ndparams", str(self.group.ndparams))) 545 546 if self.op.diskparams: 547 self.group.diskparams = self.new_diskparams 548 result.append(("diskparams", str(self.group.diskparams))) 549 550 if self.op.alloc_policy: 551 self.group.alloc_policy = self.op.alloc_policy 552 553 if self.op.hv_state: 554 self.group.hv_state_static = self.new_hv_state 555 556 if self.op.disk_state: 557 self.group.disk_state_static = self.new_disk_state 558 559 if self.op.ipolicy: 560 self.group.ipolicy = self.new_ipolicy 561 562 self.cfg.Update(self.group, feedback_fn) 563 return result
564
565 566 -class LUGroupRemove(LogicalUnit):
567 HPATH = "group-remove" 568 HTYPE = constants.HTYPE_GROUP 569 REQ_BGL = False 570
571 - def ExpandNames(self):
572 # This will raises errors.OpPrereqError on its own: 573 self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name) 574 self.needed_locks = { 575 locking.LEVEL_NODEGROUP: [self.group_uuid], 576 }
577
578 - def CheckPrereq(self):
579 """Check prerequisites. 580 581 This checks that the given group name exists as a node group, that is 582 empty (i.e., contains no nodes), and that is not the last group of the 583 cluster. 584 585 """ 586 # Verify that the group is empty. 587 group_nodes = [node.uuid 588 for node in self.cfg.GetAllNodesInfo().values() 589 if node.group == self.group_uuid] 590 591 if group_nodes: 592 raise errors.OpPrereqError("Group '%s' not empty, has the following" 593 " nodes: %s" % 594 (self.op.group_name, 595 utils.CommaJoin(utils.NiceSort(group_nodes))), 596 errors.ECODE_STATE) 597 598 # Verify the cluster would not be left group-less. 599 if len(self.cfg.GetNodeGroupList()) == 1: 600 raise errors.OpPrereqError("Group '%s' is the only group, cannot be" 601 " removed" % self.op.group_name, 602 errors.ECODE_STATE)
603
604 - def BuildHooksEnv(self):
605 """Build hooks env. 606 607 """ 608 return { 609 "GROUP_NAME": self.op.group_name, 610 }
611
612 - def BuildHooksNodes(self):
613 """Build hooks nodes. 614 615 """ 616 mn = self.cfg.GetMasterNode() 617 return ([mn], [mn])
618
619 - def Exec(self, feedback_fn):
620 """Remove the node group. 621 622 """ 623 try: 624 self.cfg.RemoveNodeGroup(self.group_uuid) 625 except errors.ConfigurationError: 626 raise errors.OpExecError("Group '%s' with UUID %s disappeared" % 627 (self.op.group_name, self.group_uuid)) 628 629 self.remove_locks[locking.LEVEL_NODEGROUP] = self.group_uuid
630
631 632 -class LUGroupRename(LogicalUnit):
633 HPATH = "group-rename" 634 HTYPE = constants.HTYPE_GROUP 635 REQ_BGL = False 636
637 - def ExpandNames(self):
638 # This raises errors.OpPrereqError on its own: 639 self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name) 640 641 self.needed_locks = { 642 locking.LEVEL_NODEGROUP: [self.group_uuid], 643 }
644
645 - def CheckPrereq(self):
646 """Check prerequisites. 647 648 Ensures requested new name is not yet used. 649 650 """ 651 try: 652 new_name_uuid = self.cfg.LookupNodeGroup(self.op.new_name) 653 except errors.OpPrereqError: 654 pass 655 else: 656 raise errors.OpPrereqError("Desired new name '%s' clashes with existing" 657 " node group (UUID: %s)" % 658 (self.op.new_name, new_name_uuid), 659 errors.ECODE_EXISTS)
660
661 - def BuildHooksEnv(self):
662 """Build hooks env. 663 664 """ 665 return { 666 "OLD_NAME": self.op.group_name, 667 "NEW_NAME": self.op.new_name, 668 }
669
670 - def BuildHooksNodes(self):
671 """Build hooks nodes. 672 673 """ 674 mn = self.cfg.GetMasterNode() 675 676 all_nodes = self.cfg.GetAllNodesInfo() 677 all_nodes.pop(mn, None) 678 679 run_nodes = [mn] 680 run_nodes.extend(node.uuid for node in all_nodes.values() 681 if node.group == self.group_uuid) 682 683 return (run_nodes, run_nodes)
684
685 - def Exec(self, feedback_fn):
686 """Rename the node group. 687 688 """ 689 group = self.cfg.GetNodeGroup(self.group_uuid) 690 691 if group is None: 692 raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" % 693 (self.op.group_name, self.group_uuid)) 694 695 group.name = self.op.new_name 696 self.cfg.Update(group, feedback_fn) 697 698 return self.op.new_name
699
700 701 -class LUGroupEvacuate(LogicalUnit):
702 HPATH = "group-evacuate" 703 HTYPE = constants.HTYPE_GROUP 704 REQ_BGL = False 705
706 - def ExpandNames(self):
707 # This raises errors.OpPrereqError on its own: 708 self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name) 709 710 if self.op.target_groups: 711 self.req_target_uuids = map(self.cfg.LookupNodeGroup, 712 self.op.target_groups) 713 else: 714 self.req_target_uuids = [] 715 716 if self.group_uuid in self.req_target_uuids: 717 raise errors.OpPrereqError("Group to be evacuated (%s) can not be used" 718 " as a target group (targets are %s)" % 719 (self.group_uuid, 720 utils.CommaJoin(self.req_target_uuids)), 721 errors.ECODE_INVAL) 722 723 self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator) 724 725 self.share_locks = ShareAll() 726 self.needed_locks = { 727 locking.LEVEL_INSTANCE: [], 728 locking.LEVEL_NODEGROUP: [], 729 locking.LEVEL_NODE: [], 730 }
731
732 - def DeclareLocks(self, level):
733 if level == locking.LEVEL_INSTANCE: 734 assert not self.needed_locks[locking.LEVEL_INSTANCE] 735 736 # Lock instances optimistically, needs verification once node and group 737 # locks have been acquired 738 self.needed_locks[locking.LEVEL_INSTANCE] = \ 739 self.cfg.GetInstanceNames( 740 self.cfg.GetNodeGroupInstances(self.group_uuid)) 741 742 elif level == locking.LEVEL_NODEGROUP: 743 assert not self.needed_locks[locking.LEVEL_NODEGROUP] 744 745 if self.req_target_uuids: 746 lock_groups = set([self.group_uuid] + self.req_target_uuids) 747 748 # Lock all groups used by instances optimistically; this requires going 749 # via the node before it's locked, requiring verification later on 750 lock_groups.update(group_uuid 751 for instance_name in 752 self.owned_locks(locking.LEVEL_INSTANCE) 753 for group_uuid in 754 self.cfg.GetInstanceNodeGroups( 755 self.cfg.GetInstanceInfoByName(instance_name) 756 .uuid)) 757 else: 758 # No target groups, need to lock all of them 759 lock_groups = locking.ALL_SET 760 761 self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups 762 763 elif level == locking.LEVEL_NODE: 764 # This will only lock the nodes in the group to be evacuated which 765 # contain actual instances 766 self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND 767 self._LockInstancesNodes() 768 769 # Lock all nodes in group to be evacuated and target groups 770 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) 771 assert self.group_uuid in owned_groups 772 member_node_uuids = [node_uuid 773 for group in owned_groups 774 for node_uuid in 775 self.cfg.GetNodeGroup(group).members] 776 self.needed_locks[locking.LEVEL_NODE].extend(member_node_uuids)
777
778 - def CheckPrereq(self):
779 owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) 780 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) 781 owned_node_uuids = frozenset(self.owned_locks(locking.LEVEL_NODE)) 782 783 assert owned_groups.issuperset(self.req_target_uuids) 784 assert self.group_uuid in owned_groups 785 786 # Check if locked instances are still correct 787 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names) 788 789 # Get instance information 790 self.instances = \ 791 dict(self.cfg.GetMultiInstanceInfoByName(owned_instance_names)) 792 793 # Check if node groups for locked instances are still correct 794 CheckInstancesNodeGroups(self.cfg, self.instances, 795 owned_groups, owned_node_uuids, self.group_uuid) 796 797 if self.req_target_uuids: 798 # User requested specific target groups 799 self.target_uuids = self.req_target_uuids 800 else: 801 # All groups except the one to be evacuated are potential targets 802 self.target_uuids = [group_uuid for group_uuid in owned_groups 803 if group_uuid != self.group_uuid] 804 805 if not self.target_uuids: 806 raise errors.OpPrereqError("There are no possible target groups", 807 errors.ECODE_INVAL)
808
809 - def BuildHooksEnv(self):
810 """Build hooks env. 811 812 """ 813 return { 814 "GROUP_NAME": self.op.group_name, 815 "TARGET_GROUPS": " ".join(self.target_uuids), 816 }
817
818 - def BuildHooksNodes(self):
819 """Build hooks nodes. 820 821 """ 822 mn = self.cfg.GetMasterNode() 823 824 assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP) 825 826 run_nodes = [mn] + self.cfg.GetNodeGroup(self.group_uuid).members 827 828 return (run_nodes, run_nodes)
829
830 - def Exec(self, feedback_fn):
831 inst_names = list(self.owned_locks(locking.LEVEL_INSTANCE)) 832 833 assert self.group_uuid not in self.target_uuids 834 835 req = iallocator.IAReqGroupChange(instances=inst_names, 836 target_groups=self.target_uuids) 837 ial = iallocator.IAllocator(self.cfg, self.rpc, req) 838 839 ial.Run(self.op.iallocator) 840 841 if not ial.success: 842 raise errors.OpPrereqError("Can't compute group evacuation using" 843 " iallocator '%s': %s" % 844 (self.op.iallocator, ial.info), 845 errors.ECODE_NORES) 846 847 jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False) 848 849 self.LogInfo("Iallocator returned %s job(s) for evacuating node group %s", 850 len(jobs), self.op.group_name) 851 852 return ResultWithJobs(jobs)
853
854 855 -class LUGroupVerifyDisks(NoHooksLU):
856 """Verifies the status of all disks in a node group. 857 858 """ 859 REQ_BGL = False 860
861 - def ExpandNames(self):
862 # Raises errors.OpPrereqError on its own if group can't be found 863 self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name) 864 865 self.share_locks = ShareAll() 866 self.needed_locks = { 867 locking.LEVEL_INSTANCE: [], 868 locking.LEVEL_NODEGROUP: [], 869 locking.LEVEL_NODE: [], 870 871 # This opcode is acquires all node locks in a group. LUClusterVerifyDisks 872 # starts one instance of this opcode for every group, which means all 873 # nodes will be locked for a short amount of time, so it's better to 874 # acquire the node allocation lock as well. 875 locking.LEVEL_NODE_ALLOC: locking.ALL_SET, 876 }
877
878 - def DeclareLocks(self, level):
879 if level == locking.LEVEL_INSTANCE: 880 assert not self.needed_locks[locking.LEVEL_INSTANCE] 881 882 # Lock instances optimistically, needs verification once node and group 883 # locks have been acquired 884 self.needed_locks[locking.LEVEL_INSTANCE] = \ 885 self.cfg.GetInstanceNames( 886 self.cfg.GetNodeGroupInstances(self.group_uuid)) 887 888 elif level == locking.LEVEL_NODEGROUP: 889 assert not self.needed_locks[locking.LEVEL_NODEGROUP] 890 891 self.needed_locks[locking.LEVEL_NODEGROUP] = \ 892 set([self.group_uuid] + 893 # Lock all groups used by instances optimistically; this requires 894 # going via the node before it's locked, requiring verification 895 # later on 896 [group_uuid 897 for instance_name in self.owned_locks(locking.LEVEL_INSTANCE) 898 for group_uuid in 899 self.cfg.GetInstanceNodeGroups( 900 self.cfg.GetInstanceInfoByName(instance_name).uuid)]) 901 902 elif level == locking.LEVEL_NODE: 903 # This will only lock the nodes in the group to be verified which contain 904 # actual instances 905 self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND 906 self._LockInstancesNodes() 907 908 # Lock all nodes in group to be verified 909 assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP) 910 member_node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members 911 self.needed_locks[locking.LEVEL_NODE].extend(member_node_uuids)
912
913 - def CheckPrereq(self):
914 owned_inst_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE)) 915 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) 916 owned_node_uuids = frozenset(self.owned_locks(locking.LEVEL_NODE)) 917 918 assert self.group_uuid in owned_groups 919 920 # Check if locked instances are still correct 921 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_inst_names) 922 923 # Get instance information 924 self.instances = dict(self.cfg.GetMultiInstanceInfoByName(owned_inst_names)) 925 926 # Check if node groups for locked instances are still correct 927 CheckInstancesNodeGroups(self.cfg, self.instances, 928 owned_groups, owned_node_uuids, self.group_uuid)
929
930 - def _VerifyInstanceLvs(self, node_errors, offline_disk_instance_names, 931 missing_disks):
932 node_lv_to_inst = MapInstanceLvsToNodes( 933 [inst for inst in self.instances.values() if inst.disks_active]) 934 if node_lv_to_inst: 935 node_uuids = utils.NiceSort(set(self.owned_locks(locking.LEVEL_NODE)) & 936 set(self.cfg.GetVmCapableNodeList())) 937 938 node_lvs = self.rpc.call_lv_list(node_uuids, []) 939 940 for (node_uuid, node_res) in node_lvs.items(): 941 if node_res.offline: 942 continue 943 944 msg = node_res.fail_msg 945 if msg: 946 logging.warning("Error enumerating LVs on node %s: %s", 947 self.cfg.GetNodeName(node_uuid), msg) 948 node_errors[node_uuid] = msg 949 continue 950 951 for lv_name, (_, _, lv_online) in node_res.payload.items(): 952 inst = node_lv_to_inst.pop((node_uuid, lv_name), None) 953 if not lv_online and inst is not None: 954 offline_disk_instance_names.add(inst.name) 955 956 # any leftover items in nv_dict are missing LVs, let's arrange the data 957 # better 958 for key, inst in node_lv_to_inst.iteritems(): 959 missing_disks.setdefault(inst.name, []).append(list(key))
960
961 - def _VerifyDrbdStates(self, node_errors, offline_disk_instance_names):
962 node_to_inst = {} 963 for inst in self.instances.values(): 964 if not inst.disks_active or inst.disk_template != constants.DT_DRBD8: 965 continue 966 967 for node_uuid in itertools.chain([inst.primary_node], 968 inst.secondary_nodes): 969 node_to_inst.setdefault(node_uuid, []).append(inst) 970 971 nodes_ip = dict((uuid, node.secondary_ip) for (uuid, node) 972 in self.cfg.GetMultiNodeInfo(node_to_inst.keys())) 973 for (node_uuid, insts) in node_to_inst.items(): 974 node_disks = [(inst.disks, inst) for inst in insts] 975 node_res = self.rpc.call_drbd_needs_activation(node_uuid, nodes_ip, 976 node_disks) 977 msg = node_res.fail_msg 978 if msg: 979 logging.warning("Error getting DRBD status on node %s: %s", 980 self.cfg.GetNodeName(node_uuid), msg) 981 node_errors[node_uuid] = msg 982 continue 983 984 faulty_disk_uuids = set(node_res.payload) 985 for inst in self.instances.values(): 986 inst_disk_uuids = set([disk.uuid for disk in inst.disks]) 987 if inst_disk_uuids.intersection(faulty_disk_uuids): 988 offline_disk_instance_names.add(inst.name)
989
990 - def Exec(self, feedback_fn):
991 """Verify integrity of cluster disks. 992 993 @rtype: tuple of three items 994 @return: a tuple of (dict of node-to-node_error, list of instances 995 which need activate-disks, dict of instance: (node, volume) for 996 missing volumes 997 998 """ 999 node_errors = {} 1000 offline_disk_instance_names = set() 1001 missing_disks = {} 1002 1003 self._VerifyInstanceLvs(node_errors, offline_disk_instance_names, 1004 missing_disks) 1005 self._VerifyDrbdStates(node_errors, offline_disk_instance_names) 1006 1007 return (node_errors, list(offline_disk_instance_names), missing_disks)
1008