Package ganeti :: Package client :: Module gnt_cluster
[hide private]
[frames] | no frames]

Source Code for Module ganeti.client.gnt_cluster

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2010, 2011 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  """Cluster related commands""" 
 22   
 23  # pylint: disable-msg=W0401,W0613,W0614,C0103 
 24  # W0401: Wildcard import ganeti.cli 
 25  # W0613: Unused argument, since all functions follow the same API 
 26  # W0614: Unused import %s from wildcard import (since we need cli) 
 27  # C0103: Invalid name gnt-cluster 
 28   
 29  import os.path 
 30  import time 
 31  import OpenSSL 
 32   
 33  from ganeti.cli import * 
 34  from ganeti import opcodes 
 35  from ganeti import constants 
 36  from ganeti import errors 
 37  from ganeti import utils 
 38  from ganeti import bootstrap 
 39  from ganeti import ssh 
 40  from ganeti import objects 
 41  from ganeti import uidpool 
 42  from ganeti import compat 
43 44 45 @UsesRPC 46 -def InitCluster(opts, args):
47 """Initialize the cluster. 48 49 @param opts: the command line options selected by the user 50 @type args: list 51 @param args: should contain only one element, the desired 52 cluster name 53 @rtype: int 54 @return: the desired exit code 55 56 """ 57 if not opts.lvm_storage and opts.vg_name: 58 ToStderr("Options --no-lvm-storage and --vg-name conflict.") 59 return 1 60 61 vg_name = opts.vg_name 62 if opts.lvm_storage and not opts.vg_name: 63 vg_name = constants.DEFAULT_VG 64 65 if not opts.drbd_storage and opts.drbd_helper: 66 ToStderr("Options --no-drbd-storage and --drbd-usermode-helper conflict.") 67 return 1 68 69 drbd_helper = opts.drbd_helper 70 if opts.drbd_storage and not opts.drbd_helper: 71 drbd_helper = constants.DEFAULT_DRBD_HELPER 72 73 master_netdev = opts.master_netdev 74 if master_netdev is None: 75 master_netdev = constants.DEFAULT_BRIDGE 76 77 hvlist = opts.enabled_hypervisors 78 if hvlist is None: 79 hvlist = constants.DEFAULT_ENABLED_HYPERVISOR 80 hvlist = hvlist.split(",") 81 82 hvparams = dict(opts.hvparams) 83 beparams = opts.beparams 84 nicparams = opts.nicparams 85 86 # prepare beparams dict 87 beparams = objects.FillDict(constants.BEC_DEFAULTS, beparams) 88 utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES) 89 90 # prepare nicparams dict 91 nicparams = objects.FillDict(constants.NICC_DEFAULTS, nicparams) 92 utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES) 93 94 # prepare ndparams dict 95 if opts.ndparams is None: 96 ndparams = dict(constants.NDC_DEFAULTS) 97 else: 98 ndparams = objects.FillDict(constants.NDC_DEFAULTS, opts.ndparams) 99 utils.ForceDictType(ndparams, constants.NDS_PARAMETER_TYPES) 100 101 # prepare hvparams dict 102 for hv in constants.HYPER_TYPES: 103 if hv not in hvparams: 104 hvparams[hv] = {} 105 hvparams[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], hvparams[hv]) 106 utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES) 107 108 if opts.candidate_pool_size is None: 109 opts.candidate_pool_size = constants.MASTER_POOL_SIZE_DEFAULT 110 111 if opts.mac_prefix is None: 112 opts.mac_prefix = constants.DEFAULT_MAC_PREFIX 113 114 uid_pool = opts.uid_pool 115 if uid_pool is not None: 116 uid_pool = uidpool.ParseUidPool(uid_pool) 117 118 if opts.prealloc_wipe_disks is None: 119 opts.prealloc_wipe_disks = False 120 121 try: 122 primary_ip_version = int(opts.primary_ip_version) 123 except (ValueError, TypeError), err: 124 ToStderr("Invalid primary ip version value: %s" % str(err)) 125 return 1 126 127 bootstrap.InitCluster(cluster_name=args[0], 128 secondary_ip=opts.secondary_ip, 129 vg_name=vg_name, 130 mac_prefix=opts.mac_prefix, 131 master_netdev=master_netdev, 132 file_storage_dir=opts.file_storage_dir, 133 enabled_hypervisors=hvlist, 134 hvparams=hvparams, 135 beparams=beparams, 136 nicparams=nicparams, 137 ndparams=ndparams, 138 candidate_pool_size=opts.candidate_pool_size, 139 modify_etc_hosts=opts.modify_etc_hosts, 140 modify_ssh_setup=opts.modify_ssh_setup, 141 maintain_node_health=opts.maintain_node_health, 142 drbd_helper=drbd_helper, 143 uid_pool=uid_pool, 144 default_iallocator=opts.default_iallocator, 145 primary_ip_version=primary_ip_version, 146 prealloc_wipe_disks=opts.prealloc_wipe_disks, 147 ) 148 op = opcodes.OpClusterPostInit() 149 SubmitOpCode(op, opts=opts) 150 return 0
151
152 153 @UsesRPC 154 -def DestroyCluster(opts, args):
155 """Destroy the cluster. 156 157 @param opts: the command line options selected by the user 158 @type args: list 159 @param args: should be an empty list 160 @rtype: int 161 @return: the desired exit code 162 163 """ 164 if not opts.yes_do_it: 165 ToStderr("Destroying a cluster is irreversible. If you really want" 166 " destroy this cluster, supply the --yes-do-it option.") 167 return 1 168 169 op = opcodes.OpClusterDestroy() 170 master = SubmitOpCode(op, opts=opts) 171 # if we reached this, the opcode didn't fail; we can proceed to 172 # shutdown all the daemons 173 bootstrap.FinalizeClusterDestroy(master) 174 return 0
175
176 177 -def RenameCluster(opts, args):
178 """Rename the cluster. 179 180 @param opts: the command line options selected by the user 181 @type args: list 182 @param args: should contain only one element, the new cluster name 183 @rtype: int 184 @return: the desired exit code 185 186 """ 187 cl = GetClient() 188 189 (cluster_name, ) = cl.QueryConfigValues(["cluster_name"]) 190 191 new_name = args[0] 192 if not opts.force: 193 usertext = ("This will rename the cluster from '%s' to '%s'. If you are" 194 " connected over the network to the cluster name, the" 195 " operation is very dangerous as the IP address will be" 196 " removed from the node and the change may not go through." 197 " Continue?") % (cluster_name, new_name) 198 if not AskUser(usertext): 199 return 1 200 201 op = opcodes.OpClusterRename(name=new_name) 202 result = SubmitOpCode(op, opts=opts, cl=cl) 203 204 if result: 205 ToStdout("Cluster renamed from '%s' to '%s'", cluster_name, result) 206 207 return 0
208
209 210 -def RedistributeConfig(opts, args):
211 """Forces push of the cluster configuration. 212 213 @param opts: the command line options selected by the user 214 @type args: list 215 @param args: empty list 216 @rtype: int 217 @return: the desired exit code 218 219 """ 220 op = opcodes.OpClusterRedistConf() 221 SubmitOrSend(op, opts) 222 return 0
223
224 225 -def ShowClusterVersion(opts, args):
226 """Write version of ganeti software to the standard output. 227 228 @param opts: the command line options selected by the user 229 @type args: list 230 @param args: should be an empty list 231 @rtype: int 232 @return: the desired exit code 233 234 """ 235 cl = GetClient() 236 result = cl.QueryClusterInfo() 237 ToStdout("Software version: %s", result["software_version"]) 238 ToStdout("Internode protocol: %s", result["protocol_version"]) 239 ToStdout("Configuration format: %s", result["config_version"]) 240 ToStdout("OS api version: %s", result["os_api_version"]) 241 ToStdout("Export interface: %s", result["export_version"]) 242 return 0
243
244 245 -def ShowClusterMaster(opts, args):
246 """Write name of master node to the standard output. 247 248 @param opts: the command line options selected by the user 249 @type args: list 250 @param args: should be an empty list 251 @rtype: int 252 @return: the desired exit code 253 254 """ 255 master = bootstrap.GetMaster() 256 ToStdout(master) 257 return 0
258
259 260 -def _PrintGroupedParams(paramsdict, level=1, roman=False):
261 """Print Grouped parameters (be, nic, disk) by group. 262 263 @type paramsdict: dict of dicts 264 @param paramsdict: {group: {param: value, ...}, ...} 265 @type level: int 266 @param level: Level of indention 267 268 """ 269 indent = " " * level 270 for item, val in sorted(paramsdict.items()): 271 if isinstance(val, dict): 272 ToStdout("%s- %s:", indent, item) 273 _PrintGroupedParams(val, level=level + 1, roman=roman) 274 elif roman and isinstance(val, int): 275 ToStdout("%s %s: %s", indent, item, compat.TryToRoman(val)) 276 else: 277 ToStdout("%s %s: %s", indent, item, val)
278
279 280 -def ShowClusterConfig(opts, args):
281 """Shows cluster information. 282 283 @param opts: the command line options selected by the user 284 @type args: list 285 @param args: should be an empty list 286 @rtype: int 287 @return: the desired exit code 288 289 """ 290 cl = GetClient() 291 result = cl.QueryClusterInfo() 292 293 ToStdout("Cluster name: %s", result["name"]) 294 ToStdout("Cluster UUID: %s", result["uuid"]) 295 296 ToStdout("Creation time: %s", utils.FormatTime(result["ctime"])) 297 ToStdout("Modification time: %s", utils.FormatTime(result["mtime"])) 298 299 ToStdout("Master node: %s", result["master"]) 300 301 ToStdout("Architecture (this node): %s (%s)", 302 result["architecture"][0], result["architecture"][1]) 303 304 if result["tags"]: 305 tags = utils.CommaJoin(utils.NiceSort(result["tags"])) 306 else: 307 tags = "(none)" 308 309 ToStdout("Tags: %s", tags) 310 311 ToStdout("Default hypervisor: %s", result["default_hypervisor"]) 312 ToStdout("Enabled hypervisors: %s", 313 utils.CommaJoin(result["enabled_hypervisors"])) 314 315 ToStdout("Hypervisor parameters:") 316 _PrintGroupedParams(result["hvparams"]) 317 318 ToStdout("OS-specific hypervisor parameters:") 319 _PrintGroupedParams(result["os_hvp"]) 320 321 ToStdout("OS parameters:") 322 _PrintGroupedParams(result["osparams"]) 323 324 ToStdout("Hidden OSes: %s", utils.CommaJoin(result["hidden_os"])) 325 ToStdout("Blacklisted OSes: %s", utils.CommaJoin(result["blacklisted_os"])) 326 327 ToStdout("Cluster parameters:") 328 ToStdout(" - candidate pool size: %s", 329 compat.TryToRoman(result["candidate_pool_size"], 330 convert=opts.roman_integers)) 331 ToStdout(" - master netdev: %s", result["master_netdev"]) 332 ToStdout(" - lvm volume group: %s", result["volume_group_name"]) 333 if result["reserved_lvs"]: 334 reserved_lvs = utils.CommaJoin(result["reserved_lvs"]) 335 else: 336 reserved_lvs = "(none)" 337 ToStdout(" - lvm reserved volumes: %s", reserved_lvs) 338 ToStdout(" - drbd usermode helper: %s", result["drbd_usermode_helper"]) 339 ToStdout(" - file storage path: %s", result["file_storage_dir"]) 340 ToStdout(" - maintenance of node health: %s", 341 result["maintain_node_health"]) 342 ToStdout(" - uid pool: %s", 343 uidpool.FormatUidPool(result["uid_pool"], 344 roman=opts.roman_integers)) 345 ToStdout(" - default instance allocator: %s", result["default_iallocator"]) 346 ToStdout(" - primary ip version: %d", result["primary_ip_version"]) 347 ToStdout(" - preallocation wipe disks: %s", result["prealloc_wipe_disks"]) 348 ToStdout(" - OS search path: %s", utils.CommaJoin(constants.OS_SEARCH_PATH)) 349 350 ToStdout("Default node parameters:") 351 _PrintGroupedParams(result["ndparams"], roman=opts.roman_integers) 352 353 ToStdout("Default instance parameters:") 354 _PrintGroupedParams(result["beparams"], roman=opts.roman_integers) 355 356 ToStdout("Default nic parameters:") 357 _PrintGroupedParams(result["nicparams"], roman=opts.roman_integers) 358 359 return 0
360
361 362 -def ClusterCopyFile(opts, args):
363 """Copy a file from master to some nodes. 364 365 @param opts: the command line options selected by the user 366 @type args: list 367 @param args: should contain only one element, the path of 368 the file to be copied 369 @rtype: int 370 @return: the desired exit code 371 372 """ 373 filename = args[0] 374 if not os.path.exists(filename): 375 raise errors.OpPrereqError("No such filename '%s'" % filename, 376 errors.ECODE_INVAL) 377 378 cl = GetClient() 379 380 cluster_name = cl.QueryConfigValues(["cluster_name"])[0] 381 382 results = GetOnlineNodes(nodes=opts.nodes, cl=cl, filter_master=True, 383 secondary_ips=opts.use_replication_network) 384 385 srun = ssh.SshRunner(cluster_name=cluster_name) 386 for node in results: 387 if not srun.CopyFileToNode(node, filename): 388 ToStderr("Copy of file %s to node %s failed", filename, node) 389 390 return 0
391
392 393 -def RunClusterCommand(opts, args):
394 """Run a command on some nodes. 395 396 @param opts: the command line options selected by the user 397 @type args: list 398 @param args: should contain the command to be run and its arguments 399 @rtype: int 400 @return: the desired exit code 401 402 """ 403 cl = GetClient() 404 405 command = " ".join(args) 406 407 nodes = GetOnlineNodes(nodes=opts.nodes, cl=cl) 408 409 cluster_name, master_node = cl.QueryConfigValues(["cluster_name", 410 "master_node"]) 411 412 srun = ssh.SshRunner(cluster_name=cluster_name) 413 414 # Make sure master node is at list end 415 if master_node in nodes: 416 nodes.remove(master_node) 417 nodes.append(master_node) 418 419 for name in nodes: 420 result = srun.Run(name, "root", command) 421 ToStdout("------------------------------------------------") 422 ToStdout("node: %s", name) 423 ToStdout("%s", result.output) 424 ToStdout("return code = %s", result.exit_code) 425 426 return 0
427
428 429 -def VerifyCluster(opts, args):
430 """Verify integrity of cluster, performing various test on nodes. 431 432 @param opts: the command line options selected by the user 433 @type args: list 434 @param args: should be an empty list 435 @rtype: int 436 @return: the desired exit code 437 438 """ 439 skip_checks = [] 440 if opts.skip_nplusone_mem: 441 skip_checks.append(constants.VERIFY_NPLUSONE_MEM) 442 op = opcodes.OpClusterVerify(skip_checks=skip_checks, 443 verbose=opts.verbose, 444 error_codes=opts.error_codes, 445 debug_simulate_errors=opts.simulate_errors) 446 if SubmitOpCode(op, opts=opts): 447 return 0 448 else: 449 return 1
450
451 452 -def VerifyDisks(opts, args):
453 """Verify integrity of cluster disks. 454 455 @param opts: the command line options selected by the user 456 @type args: list 457 @param args: should be an empty list 458 @rtype: int 459 @return: the desired exit code 460 461 """ 462 cl = GetClient() 463 464 op = opcodes.OpClusterVerifyDisks() 465 result = SubmitOpCode(op, opts=opts, cl=cl) 466 if not isinstance(result, (list, tuple)) or len(result) != 3: 467 raise errors.ProgrammerError("Unknown result type for OpClusterVerifyDisks") 468 469 bad_nodes, instances, missing = result 470 471 retcode = constants.EXIT_SUCCESS 472 473 if bad_nodes: 474 for node, text in bad_nodes.items(): 475 ToStdout("Error gathering data on node %s: %s", 476 node, utils.SafeEncode(text[-400:])) 477 retcode |= 1 478 ToStdout("You need to fix these nodes first before fixing instances") 479 480 if instances: 481 for iname in instances: 482 if iname in missing: 483 continue 484 op = opcodes.OpInstanceActivateDisks(instance_name=iname) 485 try: 486 ToStdout("Activating disks for instance '%s'", iname) 487 SubmitOpCode(op, opts=opts, cl=cl) 488 except errors.GenericError, err: 489 nret, msg = FormatError(err) 490 retcode |= nret 491 ToStderr("Error activating disks for instance %s: %s", iname, msg) 492 493 if missing: 494 for iname, ival in missing.iteritems(): 495 all_missing = compat.all(x[0] in bad_nodes for x in ival) 496 if all_missing: 497 ToStdout("Instance %s cannot be verified as it lives on" 498 " broken nodes", iname) 499 else: 500 ToStdout("Instance %s has missing logical volumes:", iname) 501 ival.sort() 502 for node, vol in ival: 503 if node in bad_nodes: 504 ToStdout("\tbroken node %s /dev/%s", node, vol) 505 else: 506 ToStdout("\t%s /dev/%s", node, vol) 507 508 ToStdout("You need to run replace or recreate disks for all the above" 509 " instances, if this message persist after fixing nodes.") 510 retcode |= 1 511 512 return retcode
513
514 515 -def RepairDiskSizes(opts, args):
516 """Verify sizes of cluster disks. 517 518 @param opts: the command line options selected by the user 519 @type args: list 520 @param args: optional list of instances to restrict check to 521 @rtype: int 522 @return: the desired exit code 523 524 """ 525 op = opcodes.OpClusterRepairDiskSizes(instances=args) 526 SubmitOpCode(op, opts=opts)
527
528 529 @UsesRPC 530 -def MasterFailover(opts, args):
531 """Failover the master node. 532 533 This command, when run on a non-master node, will cause the current 534 master to cease being master, and the non-master to become new 535 master. 536 537 @param opts: the command line options selected by the user 538 @type args: list 539 @param args: should be an empty list 540 @rtype: int 541 @return: the desired exit code 542 543 """ 544 if opts.no_voting: 545 usertext = ("This will perform the failover even if most other nodes" 546 " are down, or if this node is outdated. This is dangerous" 547 " as it can lead to a non-consistent cluster. Check the" 548 " gnt-cluster(8) man page before proceeding. Continue?") 549 if not AskUser(usertext): 550 return 1 551 552 return bootstrap.MasterFailover(no_voting=opts.no_voting)
553
554 555 -def MasterPing(opts, args):
556 """Checks if the master is alive. 557 558 @param opts: the command line options selected by the user 559 @type args: list 560 @param args: should be an empty list 561 @rtype: int 562 @return: the desired exit code 563 564 """ 565 try: 566 cl = GetClient() 567 cl.QueryClusterInfo() 568 return 0 569 except Exception: # pylint: disable-msg=W0703 570 return 1
571
572 573 -def SearchTags(opts, args):
574 """Searches the tags on all the cluster. 575 576 @param opts: the command line options selected by the user 577 @type args: list 578 @param args: should contain only one element, the tag pattern 579 @rtype: int 580 @return: the desired exit code 581 582 """ 583 op = opcodes.OpTagsSearch(pattern=args[0]) 584 result = SubmitOpCode(op, opts=opts) 585 if not result: 586 return 1 587 result = list(result) 588 result.sort() 589 for path, tag in result: 590 ToStdout("%s %s", path, tag)
591
592 593 -def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, 594 new_confd_hmac_key, new_cds, cds_filename, 595 force):
596 """Renews cluster certificates, keys and secrets. 597 598 @type new_cluster_cert: bool 599 @param new_cluster_cert: Whether to generate a new cluster certificate 600 @type new_rapi_cert: bool 601 @param new_rapi_cert: Whether to generate a new RAPI certificate 602 @type rapi_cert_filename: string 603 @param rapi_cert_filename: Path to file containing new RAPI certificate 604 @type new_confd_hmac_key: bool 605 @param new_confd_hmac_key: Whether to generate a new HMAC key 606 @type new_cds: bool 607 @param new_cds: Whether to generate a new cluster domain secret 608 @type cds_filename: string 609 @param cds_filename: Path to file containing new cluster domain secret 610 @type force: bool 611 @param force: Whether to ask user for confirmation 612 613 """ 614 if new_rapi_cert and rapi_cert_filename: 615 ToStderr("Only one of the --new-rapi-certficate and --rapi-certificate" 616 " options can be specified at the same time.") 617 return 1 618 619 if new_cds and cds_filename: 620 ToStderr("Only one of the --new-cluster-domain-secret and" 621 " --cluster-domain-secret options can be specified at" 622 " the same time.") 623 return 1 624 625 if rapi_cert_filename: 626 # Read and verify new certificate 627 try: 628 rapi_cert_pem = utils.ReadFile(rapi_cert_filename) 629 630 OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, 631 rapi_cert_pem) 632 except Exception, err: # pylint: disable-msg=W0703 633 ToStderr("Can't load new RAPI certificate from %s: %s" % 634 (rapi_cert_filename, str(err))) 635 return 1 636 637 try: 638 OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, rapi_cert_pem) 639 except Exception, err: # pylint: disable-msg=W0703 640 ToStderr("Can't load new RAPI private key from %s: %s" % 641 (rapi_cert_filename, str(err))) 642 return 1 643 644 else: 645 rapi_cert_pem = None 646 647 if cds_filename: 648 try: 649 cds = utils.ReadFile(cds_filename) 650 except Exception, err: # pylint: disable-msg=W0703 651 ToStderr("Can't load new cluster domain secret from %s: %s" % 652 (cds_filename, str(err))) 653 return 1 654 else: 655 cds = None 656 657 if not force: 658 usertext = ("This requires all daemons on all nodes to be restarted and" 659 " may take some time. Continue?") 660 if not AskUser(usertext): 661 return 1 662 663 def _RenewCryptoInner(ctx): 664 ctx.feedback_fn("Updating certificates and keys") 665 bootstrap.GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, 666 new_confd_hmac_key, 667 new_cds, 668 rapi_cert_pem=rapi_cert_pem, 669 cds=cds) 670 671 files_to_copy = [] 672 673 if new_cluster_cert: 674 files_to_copy.append(constants.NODED_CERT_FILE) 675 676 if new_rapi_cert or rapi_cert_pem: 677 files_to_copy.append(constants.RAPI_CERT_FILE) 678 679 if new_confd_hmac_key: 680 files_to_copy.append(constants.CONFD_HMAC_KEY) 681 682 if new_cds or cds: 683 files_to_copy.append(constants.CLUSTER_DOMAIN_SECRET_FILE) 684 685 if files_to_copy: 686 for node_name in ctx.nonmaster_nodes: 687 ctx.feedback_fn("Copying %s to %s" % 688 (", ".join(files_to_copy), node_name)) 689 for file_name in files_to_copy: 690 ctx.ssh.CopyFileToNode(node_name, file_name)
691 692 RunWhileClusterStopped(ToStdout, _RenewCryptoInner) 693 694 ToStdout("All requested certificates and keys have been replaced." 695 " Running \"gnt-cluster verify\" now is recommended.") 696 697 return 0 698
699 700 -def RenewCrypto(opts, args):
701 """Renews cluster certificates, keys and secrets. 702 703 """ 704 return _RenewCrypto(opts.new_cluster_cert, 705 opts.new_rapi_cert, 706 opts.rapi_cert, 707 opts.new_confd_hmac_key, 708 opts.new_cluster_domain_secret, 709 opts.cluster_domain_secret, 710 opts.force)
711
712 713 -def SetClusterParams(opts, args):
714 """Modify the cluster. 715 716 @param opts: the command line options selected by the user 717 @type args: list 718 @param args: should be an empty list 719 @rtype: int 720 @return: the desired exit code 721 722 """ 723 if not (not opts.lvm_storage or opts.vg_name or 724 not opts.drbd_storage or opts.drbd_helper or 725 opts.enabled_hypervisors or opts.hvparams or 726 opts.beparams or opts.nicparams or opts.ndparams or 727 opts.candidate_pool_size is not None or 728 opts.uid_pool is not None or 729 opts.maintain_node_health is not None or 730 opts.add_uids is not None or 731 opts.remove_uids is not None or 732 opts.default_iallocator is not None or 733 opts.reserved_lvs is not None or 734 opts.master_netdev is not None or 735 opts.prealloc_wipe_disks is not None): 736 ToStderr("Please give at least one of the parameters.") 737 return 1 738 739 vg_name = opts.vg_name 740 if not opts.lvm_storage and opts.vg_name: 741 ToStderr("Options --no-lvm-storage and --vg-name conflict.") 742 return 1 743 744 if not opts.lvm_storage: 745 vg_name = "" 746 747 drbd_helper = opts.drbd_helper 748 if not opts.drbd_storage and opts.drbd_helper: 749 ToStderr("Options --no-drbd-storage and --drbd-usermode-helper conflict.") 750 return 1 751 752 if not opts.drbd_storage: 753 drbd_helper = "" 754 755 hvlist = opts.enabled_hypervisors 756 if hvlist is not None: 757 hvlist = hvlist.split(",") 758 759 # a list of (name, dict) we can pass directly to dict() (or []) 760 hvparams = dict(opts.hvparams) 761 for hv_params in hvparams.values(): 762 utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES) 763 764 beparams = opts.beparams 765 utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES) 766 767 nicparams = opts.nicparams 768 utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES) 769 770 ndparams = opts.ndparams 771 if ndparams is not None: 772 utils.ForceDictType(ndparams, constants.NDS_PARAMETER_TYPES) 773 774 mnh = opts.maintain_node_health 775 776 uid_pool = opts.uid_pool 777 if uid_pool is not None: 778 uid_pool = uidpool.ParseUidPool(uid_pool) 779 780 add_uids = opts.add_uids 781 if add_uids is not None: 782 add_uids = uidpool.ParseUidPool(add_uids) 783 784 remove_uids = opts.remove_uids 785 if remove_uids is not None: 786 remove_uids = uidpool.ParseUidPool(remove_uids) 787 788 if opts.reserved_lvs is not None: 789 if opts.reserved_lvs == "": 790 opts.reserved_lvs = [] 791 else: 792 opts.reserved_lvs = utils.UnescapeAndSplit(opts.reserved_lvs, sep=",") 793 794 op = opcodes.OpClusterSetParams(vg_name=vg_name, 795 drbd_helper=drbd_helper, 796 enabled_hypervisors=hvlist, 797 hvparams=hvparams, 798 os_hvp=None, 799 beparams=beparams, 800 nicparams=nicparams, 801 ndparams=ndparams, 802 candidate_pool_size=opts.candidate_pool_size, 803 maintain_node_health=mnh, 804 uid_pool=uid_pool, 805 add_uids=add_uids, 806 remove_uids=remove_uids, 807 default_iallocator=opts.default_iallocator, 808 prealloc_wipe_disks=opts.prealloc_wipe_disks, 809 master_netdev=opts.master_netdev, 810 reserved_lvs=opts.reserved_lvs) 811 SubmitOpCode(op, opts=opts) 812 return 0
813
814 815 -def QueueOps(opts, args):
816 """Queue operations. 817 818 @param opts: the command line options selected by the user 819 @type args: list 820 @param args: should contain only one element, the subcommand 821 @rtype: int 822 @return: the desired exit code 823 824 """ 825 command = args[0] 826 client = GetClient() 827 if command in ("drain", "undrain"): 828 drain_flag = command == "drain" 829 client.SetQueueDrainFlag(drain_flag) 830 elif command == "info": 831 result = client.QueryConfigValues(["drain_flag"]) 832 if result[0]: 833 val = "set" 834 else: 835 val = "unset" 836 ToStdout("The drain flag is %s" % val) 837 else: 838 raise errors.OpPrereqError("Command '%s' is not valid." % command, 839 errors.ECODE_INVAL) 840 841 return 0
842
843 844 -def _ShowWatcherPause(until):
845 if until is None or until < time.time(): 846 ToStdout("The watcher is not paused.") 847 else: 848 ToStdout("The watcher is paused until %s.", time.ctime(until))
849
850 851 -def WatcherOps(opts, args):
852 """Watcher operations. 853 854 @param opts: the command line options selected by the user 855 @type args: list 856 @param args: should contain only one element, the subcommand 857 @rtype: int 858 @return: the desired exit code 859 860 """ 861 command = args[0] 862 client = GetClient() 863 864 if command == "continue": 865 client.SetWatcherPause(None) 866 ToStdout("The watcher is no longer paused.") 867 868 elif command == "pause": 869 if len(args) < 2: 870 raise errors.OpPrereqError("Missing pause duration", errors.ECODE_INVAL) 871 872 result = client.SetWatcherPause(time.time() + ParseTimespec(args[1])) 873 _ShowWatcherPause(result) 874 875 elif command == "info": 876 result = client.QueryConfigValues(["watcher_pause"]) 877 _ShowWatcherPause(result[0]) 878 879 else: 880 raise errors.OpPrereqError("Command '%s' is not valid." % command, 881 errors.ECODE_INVAL) 882 883 return 0
884 885 886 commands = { 887 'init': ( 888 InitCluster, [ArgHost(min=1, max=1)], 889 [BACKEND_OPT, CP_SIZE_OPT, ENABLED_HV_OPT, GLOBAL_FILEDIR_OPT, 890 HVLIST_OPT, MAC_PREFIX_OPT, MASTER_NETDEV_OPT, NIC_PARAMS_OPT, 891 NOLVM_STORAGE_OPT, NOMODIFY_ETCHOSTS_OPT, NOMODIFY_SSH_SETUP_OPT, 892 SECONDARY_IP_OPT, VG_NAME_OPT, MAINTAIN_NODE_HEALTH_OPT, 893 UIDPOOL_OPT, DRBD_HELPER_OPT, NODRBD_STORAGE_OPT, 894 DEFAULT_IALLOCATOR_OPT, PRIMARY_IP_VERSION_OPT, PREALLOC_WIPE_DISKS_OPT, 895 NODE_PARAMS_OPT], 896 "[opts...] <cluster_name>", "Initialises a new cluster configuration"), 897 'destroy': ( 898 DestroyCluster, ARGS_NONE, [YES_DOIT_OPT], 899 "", "Destroy cluster"), 900 'rename': ( 901 RenameCluster, [ArgHost(min=1, max=1)], 902 [FORCE_OPT, DRY_RUN_OPT], 903 "<new_name>", 904 "Renames the cluster"), 905 'redist-conf': ( 906 RedistributeConfig, ARGS_NONE, [SUBMIT_OPT, DRY_RUN_OPT, PRIORITY_OPT], 907 "", "Forces a push of the configuration file and ssconf files" 908 " to the nodes in the cluster"), 909 'verify': ( 910 VerifyCluster, ARGS_NONE, 911 [VERBOSE_OPT, DEBUG_SIMERR_OPT, ERROR_CODES_OPT, NONPLUS1_OPT, 912 DRY_RUN_OPT, PRIORITY_OPT], 913 "", "Does a check on the cluster configuration"), 914 'verify-disks': ( 915 VerifyDisks, ARGS_NONE, [PRIORITY_OPT], 916 "", "Does a check on the cluster disk status"), 917 'repair-disk-sizes': ( 918 RepairDiskSizes, ARGS_MANY_INSTANCES, [DRY_RUN_OPT, PRIORITY_OPT], 919 "", "Updates mismatches in recorded disk sizes"), 920 'master-failover': ( 921 MasterFailover, ARGS_NONE, [NOVOTING_OPT], 922 "", "Makes the current node the master"), 923 'master-ping': ( 924 MasterPing, ARGS_NONE, [], 925 "", "Checks if the master is alive"), 926 'version': ( 927 ShowClusterVersion, ARGS_NONE, [], 928 "", "Shows the cluster version"), 929 'getmaster': ( 930 ShowClusterMaster, ARGS_NONE, [], 931 "", "Shows the cluster master"), 932 'copyfile': ( 933 ClusterCopyFile, [ArgFile(min=1, max=1)], 934 [NODE_LIST_OPT, USE_REPL_NET_OPT], 935 "[-n node...] <filename>", "Copies a file to all (or only some) nodes"), 936 'command': ( 937 RunClusterCommand, [ArgCommand(min=1)], 938 [NODE_LIST_OPT], 939 "[-n node...] <command>", "Runs a command on all (or only some) nodes"), 940 'info': ( 941 ShowClusterConfig, ARGS_NONE, [ROMAN_OPT], 942 "[--roman]", "Show cluster configuration"), 943 'list-tags': ( 944 ListTags, ARGS_NONE, [], "", "List the tags of the cluster"), 945 'add-tags': ( 946 AddTags, [ArgUnknown()], [TAG_SRC_OPT, PRIORITY_OPT], 947 "tag...", "Add tags to the cluster"), 948 'remove-tags': ( 949 RemoveTags, [ArgUnknown()], [TAG_SRC_OPT, PRIORITY_OPT], 950 "tag...", "Remove tags from the cluster"), 951 'search-tags': ( 952 SearchTags, [ArgUnknown(min=1, max=1)], [PRIORITY_OPT], "", 953 "Searches the tags on all objects on" 954 " the cluster for a given pattern (regex)"), 955 'queue': ( 956 QueueOps, 957 [ArgChoice(min=1, max=1, choices=["drain", "undrain", "info"])], 958 [], "drain|undrain|info", "Change queue properties"), 959 'watcher': ( 960 WatcherOps, 961 [ArgChoice(min=1, max=1, choices=["pause", "continue", "info"]), 962 ArgSuggest(min=0, max=1, choices=["30m", "1h", "4h"])], 963 [], 964 "{pause <timespec>|continue|info}", "Change watcher properties"), 965 'modify': ( 966 SetClusterParams, ARGS_NONE, 967 [BACKEND_OPT, CP_SIZE_OPT, ENABLED_HV_OPT, HVLIST_OPT, MASTER_NETDEV_OPT, 968 NIC_PARAMS_OPT, NOLVM_STORAGE_OPT, VG_NAME_OPT, MAINTAIN_NODE_HEALTH_OPT, 969 UIDPOOL_OPT, ADD_UIDS_OPT, REMOVE_UIDS_OPT, DRBD_HELPER_OPT, 970 NODRBD_STORAGE_OPT, DEFAULT_IALLOCATOR_OPT, RESERVED_LVS_OPT, 971 DRY_RUN_OPT, PRIORITY_OPT, PREALLOC_WIPE_DISKS_OPT, NODE_PARAMS_OPT], 972 "[opts...]", 973 "Alters the parameters of the cluster"), 974 "renew-crypto": ( 975 RenewCrypto, ARGS_NONE, 976 [NEW_CLUSTER_CERT_OPT, NEW_RAPI_CERT_OPT, RAPI_CERT_OPT, 977 NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT, 978 NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT], 979 "[opts...]", 980 "Renews cluster certificates, keys and secrets"), 981 } 982 983 984 #: dictionary with aliases for commands 985 aliases = { 986 'masterfailover': 'master-failover', 987 }
988 989 990 -def Main():
991 return GenericMain(commands, override={"tag_type": constants.TAG_CLUSTER}, 992 aliases=aliases)
993