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