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

Source Code for Module ganeti.client.gnt_node

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 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  """Node 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-node 
 28   
 29  from ganeti.cli import * 
 30  from ganeti import cli 
 31  from ganeti import bootstrap 
 32  from ganeti import opcodes 
 33  from ganeti import utils 
 34  from ganeti import constants 
 35  from ganeti import errors 
 36  from ganeti import netutils 
 37  from cStringIO import StringIO 
 38   
 39   
 40  #: default list of field for L{ListNodes} 
 41  _LIST_DEF_FIELDS = [ 
 42    "name", "dtotal", "dfree", 
 43    "mtotal", "mnode", "mfree", 
 44    "pinst_cnt", "sinst_cnt", 
 45    ] 
 46   
 47   
 48  #: Default field list for L{ListVolumes} 
 49  _LIST_VOL_DEF_FIELDS = ["node", "phys", "vg", "name", "size", "instance"] 
 50   
 51   
 52  #: default list of field for L{ListStorage} 
 53  _LIST_STOR_DEF_FIELDS = [ 
 54    constants.SF_NODE, 
 55    constants.SF_TYPE, 
 56    constants.SF_NAME, 
 57    constants.SF_SIZE, 
 58    constants.SF_USED, 
 59    constants.SF_FREE, 
 60    constants.SF_ALLOCATABLE, 
 61    ] 
 62   
 63   
 64  #: default list of power commands 
 65  _LIST_POWER_COMMANDS = ["on", "off", "cycle", "status"] 
 66   
 67   
 68  #: headers (and full field list) for L{ListStorage} 
 69  _LIST_STOR_HEADERS = { 
 70    constants.SF_NODE: "Node", 
 71    constants.SF_TYPE: "Type", 
 72    constants.SF_NAME: "Name", 
 73    constants.SF_SIZE: "Size", 
 74    constants.SF_USED: "Used", 
 75    constants.SF_FREE: "Free", 
 76    constants.SF_ALLOCATABLE: "Allocatable", 
 77    } 
 78   
 79   
 80  #: User-facing storage unit types 
 81  _USER_STORAGE_TYPE = { 
 82    constants.ST_FILE: "file", 
 83    constants.ST_LVM_PV: "lvm-pv", 
 84    constants.ST_LVM_VG: "lvm-vg", 
 85    } 
 86   
 87  _STORAGE_TYPE_OPT = \ 
 88    cli_option("-t", "--storage-type", 
 89               dest="user_storage_type", 
 90               choices=_USER_STORAGE_TYPE.keys(), 
 91               default=None, 
 92               metavar="STORAGE_TYPE", 
 93               help=("Storage type (%s)" % 
 94                     utils.CommaJoin(_USER_STORAGE_TYPE.keys()))) 
 95   
 96  _REPAIRABLE_STORAGE_TYPES = \ 
 97    [st for st, so in constants.VALID_STORAGE_OPERATIONS.iteritems() 
 98     if constants.SO_FIX_CONSISTENCY in so] 
 99   
100  _MODIFIABLE_STORAGE_TYPES = constants.MODIFIABLE_STORAGE_FIELDS.keys() 
101   
102   
103  NONODE_SETUP_OPT = cli_option("--no-node-setup", default=True, 
104                                action="store_false", dest="node_setup", 
105                                help=("Do not make initial SSH setup on remote" 
106                                      " node (needs to be done manually)")) 
107 108 109 -def ConvertStorageType(user_storage_type):
110 """Converts a user storage type to its internal name. 111 112 """ 113 try: 114 return _USER_STORAGE_TYPE[user_storage_type] 115 except KeyError: 116 raise errors.OpPrereqError("Unknown storage type: %s" % user_storage_type, 117 errors.ECODE_INVAL)
118
119 120 -def _RunSetupSSH(options, nodes):
121 """Wrapper around utils.RunCmd to call setup-ssh 122 123 @param options: The command line options 124 @param nodes: The nodes to setup 125 126 """ 127 cmd = [constants.SETUP_SSH] 128 129 # Pass --debug|--verbose to the external script if set on our invocation 130 # --debug overrides --verbose 131 if options.debug: 132 cmd.append("--debug") 133 elif options.verbose: 134 cmd.append("--verbose") 135 if not options.ssh_key_check: 136 cmd.append("--no-ssh-key-check") 137 if options.force_join: 138 cmd.append("--force-join") 139 140 cmd.extend(nodes) 141 142 result = utils.RunCmd(cmd, interactive=True) 143 144 if result.failed: 145 errmsg = ("Command '%s' failed with exit code %s; output %r" % 146 (result.cmd, result.exit_code, result.output)) 147 raise errors.OpExecError(errmsg)
148
149 150 @UsesRPC 151 -def AddNode(opts, args):
152 """Add a node to the cluster. 153 154 @param opts: the command line options selected by the user 155 @type args: list 156 @param args: should contain only one element, the new node name 157 @rtype: int 158 @return: the desired exit code 159 160 """ 161 cl = GetClient() 162 node = netutils.GetHostname(name=args[0]).name 163 readd = opts.readd 164 165 try: 166 output = cl.QueryNodes(names=[node], fields=['name', 'sip', 'master'], 167 use_locking=False) 168 node_exists, sip, is_master = output[0] 169 except (errors.OpPrereqError, errors.OpExecError): 170 node_exists = "" 171 sip = None 172 173 if readd: 174 if not node_exists: 175 ToStderr("Node %s not in the cluster" 176 " - please retry without '--readd'", node) 177 return 1 178 if is_master: 179 ToStderr("Node %s is the master, cannot readd", node) 180 return 1 181 else: 182 if node_exists: 183 ToStderr("Node %s already in the cluster (as %s)" 184 " - please retry with '--readd'", node, node_exists) 185 return 1 186 sip = opts.secondary_ip 187 188 # read the cluster name from the master 189 output = cl.QueryConfigValues(['cluster_name']) 190 cluster_name = output[0] 191 192 if not readd and opts.node_setup: 193 ToStderr("-- WARNING -- \n" 194 "Performing this operation is going to replace the ssh daemon" 195 " keypair\n" 196 "on the target machine (%s) with the ones of the" 197 " current one\n" 198 "and grant full intra-cluster ssh root access to/from it\n", node) 199 200 if opts.node_setup: 201 _RunSetupSSH(opts, [node]) 202 203 bootstrap.SetupNodeDaemon(cluster_name, node, opts.ssh_key_check) 204 205 op = opcodes.OpNodeAdd(node_name=args[0], secondary_ip=sip, 206 readd=opts.readd, group=opts.nodegroup, 207 vm_capable=opts.vm_capable, ndparams=opts.ndparams, 208 master_capable=opts.master_capable) 209 SubmitOpCode(op, opts=opts)
210
211 212 -def ListNodes(opts, args):
213 """List nodes and their properties. 214 215 @param opts: the command line options selected by the user 216 @type args: list 217 @param args: nodes to list, or empty for all 218 @rtype: int 219 @return: the desired exit code 220 221 """ 222 selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS) 223 224 fmtoverride = dict.fromkeys(["pinst_list", "sinst_list", "tags"], 225 (",".join, False)) 226 227 return GenericList(constants.QR_NODE, selected_fields, args, opts.units, 228 opts.separator, not opts.no_headers, 229 format_override=fmtoverride, verbose=opts.verbose)
230
231 232 -def ListNodeFields(opts, args):
233 """List node fields. 234 235 @param opts: the command line options selected by the user 236 @type args: list 237 @param args: fields to list, or empty for all 238 @rtype: int 239 @return: the desired exit code 240 241 """ 242 return GenericListFields(constants.QR_NODE, args, opts.separator, 243 not opts.no_headers)
244
245 246 -def EvacuateNode(opts, args):
247 """Relocate all secondary instance from a node. 248 249 @param opts: the command line options selected by the user 250 @type args: list 251 @param args: should be an empty list 252 @rtype: int 253 @return: the desired exit code 254 255 """ 256 cl = GetClient() 257 force = opts.force 258 259 dst_node = opts.dst_node 260 iallocator = opts.iallocator 261 262 op = opcodes.OpNodeEvacStrategy(nodes=args, 263 iallocator=iallocator, 264 remote_node=dst_node) 265 266 result = SubmitOpCode(op, cl=cl, opts=opts) 267 if not result: 268 # no instances to migrate 269 ToStderr("No secondary instances on node(s) %s, exiting.", 270 utils.CommaJoin(args)) 271 return constants.EXIT_SUCCESS 272 273 if not force and not AskUser("Relocate instance(s) %s from node(s) %s?" % 274 (",".join("'%s'" % name[0] for name in result), 275 utils.CommaJoin(args))): 276 return constants.EXIT_CONFIRMATION 277 278 jex = JobExecutor(cl=cl, opts=opts) 279 for row in result: 280 iname = row[0] 281 node = row[1] 282 ToStdout("Will relocate instance %s to node %s", iname, node) 283 op = opcodes.OpInstanceReplaceDisks(instance_name=iname, 284 remote_node=node, disks=[], 285 mode=constants.REPLACE_DISK_CHG, 286 early_release=opts.early_release) 287 jex.QueueJob(iname, op) 288 results = jex.GetResults() 289 bad_cnt = len([row for row in results if not row[0]]) 290 if bad_cnt == 0: 291 ToStdout("All %d instance(s) failed over successfully.", len(results)) 292 rcode = constants.EXIT_SUCCESS 293 else: 294 ToStdout("There were errors during the failover:\n" 295 "%d error(s) out of %d instance(s).", bad_cnt, len(results)) 296 rcode = constants.EXIT_FAILURE 297 return rcode
298
299 300 -def FailoverNode(opts, args):
301 """Failover all primary instance on a node. 302 303 @param opts: the command line options selected by the user 304 @type args: list 305 @param args: should be an empty list 306 @rtype: int 307 @return: the desired exit code 308 309 """ 310 cl = GetClient() 311 force = opts.force 312 selected_fields = ["name", "pinst_list"] 313 314 # these fields are static data anyway, so it doesn't matter, but 315 # locking=True should be safer 316 result = cl.QueryNodes(names=args, fields=selected_fields, 317 use_locking=False) 318 node, pinst = result[0] 319 320 if not pinst: 321 ToStderr("No primary instances on node %s, exiting.", node) 322 return 0 323 324 pinst = utils.NiceSort(pinst) 325 326 retcode = 0 327 328 if not force and not AskUser("Fail over instance(s) %s?" % 329 (",".join("'%s'" % name for name in pinst))): 330 return 2 331 332 jex = JobExecutor(cl=cl, opts=opts) 333 for iname in pinst: 334 op = opcodes.OpInstanceFailover(instance_name=iname, 335 ignore_consistency=opts.ignore_consistency) 336 jex.QueueJob(iname, op) 337 results = jex.GetResults() 338 bad_cnt = len([row for row in results if not row[0]]) 339 if bad_cnt == 0: 340 ToStdout("All %d instance(s) failed over successfully.", len(results)) 341 else: 342 ToStdout("There were errors during the failover:\n" 343 "%d error(s) out of %d instance(s).", bad_cnt, len(results)) 344 return retcode
345
346 347 -def MigrateNode(opts, args):
348 """Migrate all primary instance on a node. 349 350 """ 351 cl = GetClient() 352 force = opts.force 353 selected_fields = ["name", "pinst_list"] 354 355 result = cl.QueryNodes(names=args, fields=selected_fields, use_locking=False) 356 node, pinst = result[0] 357 358 if not pinst: 359 ToStdout("No primary instances on node %s, exiting." % node) 360 return 0 361 362 pinst = utils.NiceSort(pinst) 363 364 if not force and not AskUser("Migrate instance(s) %s?" % 365 (",".join("'%s'" % name for name in pinst))): 366 return 2 367 368 # this should be removed once --non-live is deprecated 369 if not opts.live and opts.migration_mode is not None: 370 raise errors.OpPrereqError("Only one of the --non-live and " 371 "--migration-mode options can be passed", 372 errors.ECODE_INVAL) 373 if not opts.live: # --non-live passed 374 mode = constants.HT_MIGRATION_NONLIVE 375 else: 376 mode = opts.migration_mode 377 op = opcodes.OpNodeMigrate(node_name=args[0], mode=mode) 378 SubmitOpCode(op, cl=cl, opts=opts)
379
380 381 -def ShowNodeConfig(opts, args):
382 """Show node information. 383 384 @param opts: the command line options selected by the user 385 @type args: list 386 @param args: should either be an empty list, in which case 387 we show information about all nodes, or should contain 388 a list of nodes to be queried for information 389 @rtype: int 390 @return: the desired exit code 391 392 """ 393 cl = GetClient() 394 result = cl.QueryNodes(fields=["name", "pip", "sip", 395 "pinst_list", "sinst_list", 396 "master_candidate", "drained", "offline", 397 "master_capable", "vm_capable", "powered", 398 "ndparams", "custom_ndparams"], 399 names=args, use_locking=False) 400 401 for (name, primary_ip, secondary_ip, pinst, sinst, is_mc, drained, offline, 402 master_capable, vm_capable, powered, ndparams, 403 ndparams_custom) in result: 404 ToStdout("Node name: %s", name) 405 ToStdout(" primary ip: %s", primary_ip) 406 ToStdout(" secondary ip: %s", secondary_ip) 407 ToStdout(" master candidate: %s", is_mc) 408 ToStdout(" drained: %s", drained) 409 ToStdout(" offline: %s", offline) 410 if powered is not None: 411 ToStdout(" powered: %s", powered) 412 ToStdout(" master_capable: %s", master_capable) 413 ToStdout(" vm_capable: %s", vm_capable) 414 if vm_capable: 415 if pinst: 416 ToStdout(" primary for instances:") 417 for iname in utils.NiceSort(pinst): 418 ToStdout(" - %s", iname) 419 else: 420 ToStdout(" primary for no instances") 421 if sinst: 422 ToStdout(" secondary for instances:") 423 for iname in utils.NiceSort(sinst): 424 ToStdout(" - %s", iname) 425 else: 426 ToStdout(" secondary for no instances") 427 ToStdout(" node parameters:") 428 buf = StringIO() 429 FormatParameterDict(buf, ndparams_custom, ndparams, level=2) 430 ToStdout(buf.getvalue().rstrip("\n")) 431 432 return 0
433
434 435 -def RemoveNode(opts, args):
436 """Remove a node from the cluster. 437 438 @param opts: the command line options selected by the user 439 @type args: list 440 @param args: should contain only one element, the name of 441 the node to be removed 442 @rtype: int 443 @return: the desired exit code 444 445 """ 446 op = opcodes.OpNodeRemove(node_name=args[0]) 447 SubmitOpCode(op, opts=opts) 448 return 0
449
450 451 -def PowercycleNode(opts, args):
452 """Remove a node from the cluster. 453 454 @param opts: the command line options selected by the user 455 @type args: list 456 @param args: should contain only one element, the name of 457 the node to be removed 458 @rtype: int 459 @return: the desired exit code 460 461 """ 462 node = args[0] 463 if (not opts.confirm and 464 not AskUser("Are you sure you want to hard powercycle node %s?" % node)): 465 return 2 466 467 op = opcodes.OpNodePowercycle(node_name=node, force=opts.force) 468 result = SubmitOpCode(op, opts=opts) 469 if result: 470 ToStderr(result) 471 return 0
472
473 474 -def PowerNode(opts, args):
475 """Change/ask power state of a node. 476 477 @param opts: the command line options selected by the user 478 @type args: list 479 @param args: should contain only one element, the name of 480 the node to be removed 481 @rtype: int 482 @return: the desired exit code 483 484 """ 485 command = args[0] 486 node = args[1] 487 488 if command not in _LIST_POWER_COMMANDS: 489 ToStderr("power subcommand %s not supported." % command) 490 return constants.EXIT_FAILURE 491 492 oob_command = "power-%s" % command 493 494 opcodelist = [] 495 if oob_command == constants.OOB_POWER_OFF: 496 opcodelist.append(opcodes.OpNodeSetParams(node_name=node, offline=True, 497 auto_promote=opts.auto_promote)) 498 499 opcodelist.append(opcodes.OpOobCommand(node_names=[node], 500 command=oob_command)) 501 502 cli.SetGenericOpcodeOpts(opcodelist, opts) 503 504 job_id = cli.SendJob(opcodelist) 505 506 # We just want the OOB Opcode status 507 # If it fails PollJob gives us the error message in it 508 result = cli.PollJob(job_id)[-1] 509 510 if result: 511 (_, data_tuple) = result[0] 512 if data_tuple[0] != constants.RS_NORMAL: 513 if data_tuple[0] == constants.RS_UNAVAIL: 514 result = "OOB is not supported" 515 else: 516 result = "RPC failed, look out for warning in the output" 517 ToStderr(result) 518 return constants.EXIT_FAILURE 519 else: 520 if oob_command == constants.OOB_POWER_STATUS: 521 text = "The machine is %spowered" 522 if data_tuple[1][constants.OOB_POWER_STATUS_POWERED]: 523 result = text % "" 524 else: 525 result = text % "not " 526 ToStdout(result) 527 528 return constants.EXIT_SUCCESS
529
530 531 -def ListVolumes(opts, args):
532 """List logical volumes on node(s). 533 534 @param opts: the command line options selected by the user 535 @type args: list 536 @param args: should either be an empty list, in which case 537 we list data for all nodes, or contain a list of nodes 538 to display data only for those 539 @rtype: int 540 @return: the desired exit code 541 542 """ 543 selected_fields = ParseFields(opts.output, _LIST_VOL_DEF_FIELDS) 544 545 op = opcodes.OpNodeQueryvols(nodes=args, output_fields=selected_fields) 546 output = SubmitOpCode(op, opts=opts) 547 548 if not opts.no_headers: 549 headers = {"node": "Node", "phys": "PhysDev", 550 "vg": "VG", "name": "Name", 551 "size": "Size", "instance": "Instance"} 552 else: 553 headers = None 554 555 unitfields = ["size"] 556 557 numfields = ["size"] 558 559 data = GenerateTable(separator=opts.separator, headers=headers, 560 fields=selected_fields, unitfields=unitfields, 561 numfields=numfields, data=output, units=opts.units) 562 563 for line in data: 564 ToStdout(line) 565 566 return 0
567
568 569 -def ListStorage(opts, args):
570 """List physical volumes on node(s). 571 572 @param opts: the command line options selected by the user 573 @type args: list 574 @param args: should either be an empty list, in which case 575 we list data for all nodes, or contain a list of nodes 576 to display data only for those 577 @rtype: int 578 @return: the desired exit code 579 580 """ 581 # TODO: Default to ST_FILE if LVM is disabled on the cluster 582 if opts.user_storage_type is None: 583 opts.user_storage_type = constants.ST_LVM_PV 584 585 storage_type = ConvertStorageType(opts.user_storage_type) 586 587 selected_fields = ParseFields(opts.output, _LIST_STOR_DEF_FIELDS) 588 589 op = opcodes.OpNodeQueryStorage(nodes=args, 590 storage_type=storage_type, 591 output_fields=selected_fields) 592 output = SubmitOpCode(op, opts=opts) 593 594 if not opts.no_headers: 595 headers = { 596 constants.SF_NODE: "Node", 597 constants.SF_TYPE: "Type", 598 constants.SF_NAME: "Name", 599 constants.SF_SIZE: "Size", 600 constants.SF_USED: "Used", 601 constants.SF_FREE: "Free", 602 constants.SF_ALLOCATABLE: "Allocatable", 603 } 604 else: 605 headers = None 606 607 unitfields = [constants.SF_SIZE, constants.SF_USED, constants.SF_FREE] 608 numfields = [constants.SF_SIZE, constants.SF_USED, constants.SF_FREE] 609 610 # change raw values to nicer strings 611 for row in output: 612 for idx, field in enumerate(selected_fields): 613 val = row[idx] 614 if field == constants.SF_ALLOCATABLE: 615 if val: 616 val = "Y" 617 else: 618 val = "N" 619 row[idx] = str(val) 620 621 data = GenerateTable(separator=opts.separator, headers=headers, 622 fields=selected_fields, unitfields=unitfields, 623 numfields=numfields, data=output, units=opts.units) 624 625 for line in data: 626 ToStdout(line) 627 628 return 0
629
630 631 -def ModifyStorage(opts, args):
632 """Modify storage volume on a node. 633 634 @param opts: the command line options selected by the user 635 @type args: list 636 @param args: should contain 3 items: node name, storage type and volume name 637 @rtype: int 638 @return: the desired exit code 639 640 """ 641 (node_name, user_storage_type, volume_name) = args 642 643 storage_type = ConvertStorageType(user_storage_type) 644 645 changes = {} 646 647 if opts.allocatable is not None: 648 changes[constants.SF_ALLOCATABLE] = opts.allocatable 649 650 if changes: 651 op = opcodes.OpNodeModifyStorage(node_name=node_name, 652 storage_type=storage_type, 653 name=volume_name, 654 changes=changes) 655 SubmitOpCode(op, opts=opts) 656 else: 657 ToStderr("No changes to perform, exiting.")
658
659 660 -def RepairStorage(opts, args):
661 """Repairs a storage volume on a node. 662 663 @param opts: the command line options selected by the user 664 @type args: list 665 @param args: should contain 3 items: node name, storage type and volume name 666 @rtype: int 667 @return: the desired exit code 668 669 """ 670 (node_name, user_storage_type, volume_name) = args 671 672 storage_type = ConvertStorageType(user_storage_type) 673 674 op = opcodes.OpRepairNodeStorage(node_name=node_name, 675 storage_type=storage_type, 676 name=volume_name, 677 ignore_consistency=opts.ignore_consistency) 678 SubmitOpCode(op, opts=opts)
679
680 681 -def SetNodeParams(opts, args):
682 """Modifies a node. 683 684 @param opts: the command line options selected by the user 685 @type args: list 686 @param args: should contain only one element, the node name 687 @rtype: int 688 @return: the desired exit code 689 690 """ 691 all_changes = [opts.master_candidate, opts.drained, opts.offline, 692 opts.master_capable, opts.vm_capable, opts.secondary_ip, 693 opts.ndparams] 694 if all_changes.count(None) == len(all_changes): 695 ToStderr("Please give at least one of the parameters.") 696 return 1 697 698 op = opcodes.OpNodeSetParams(node_name=args[0], 699 master_candidate=opts.master_candidate, 700 offline=opts.offline, 701 drained=opts.drained, 702 master_capable=opts.master_capable, 703 vm_capable=opts.vm_capable, 704 secondary_ip=opts.secondary_ip, 705 force=opts.force, 706 ndparams=opts.ndparams, 707 auto_promote=opts.auto_promote, 708 powered=opts.node_powered) 709 710 # even if here we process the result, we allow submit only 711 result = SubmitOrSend(op, opts) 712 713 if result: 714 ToStdout("Modified node %s", args[0]) 715 for param, data in result: 716 ToStdout(" - %-5s -> %s", param, data) 717 return 0
718 719 720 commands = { 721 'add': ( 722 AddNode, [ArgHost(min=1, max=1)], 723 [SECONDARY_IP_OPT, READD_OPT, NOSSH_KEYCHECK_OPT, NODE_FORCE_JOIN_OPT, 724 NONODE_SETUP_OPT, VERBOSE_OPT, NODEGROUP_OPT, PRIORITY_OPT, 725 CAPAB_MASTER_OPT, CAPAB_VM_OPT, NODE_PARAMS_OPT], 726 "[-s ip] [--readd] [--no-ssh-key-check] [--force-join]" 727 " [--no-node-setup] [--verbose]" 728 " <node_name>", 729 "Add a node to the cluster"), 730 'evacuate': ( 731 EvacuateNode, [ArgNode(min=1)], 732 [FORCE_OPT, IALLOCATOR_OPT, NEW_SECONDARY_OPT, EARLY_RELEASE_OPT, 733 PRIORITY_OPT], 734 "[-f] {-I <iallocator> | -n <dst>} <node>", 735 "Relocate the secondary instances from a node" 736 " to other nodes (only for instances with drbd disk template)"), 737 'failover': ( 738 FailoverNode, ARGS_ONE_NODE, [FORCE_OPT, IGNORE_CONSIST_OPT, PRIORITY_OPT], 739 "[-f] <node>", 740 "Stops the primary instances on a node and start them on their" 741 " secondary node (only for instances with drbd disk template)"), 742 'migrate': ( 743 MigrateNode, ARGS_ONE_NODE, 744 [FORCE_OPT, NONLIVE_OPT, MIGRATION_MODE_OPT, PRIORITY_OPT], 745 "[-f] <node>", 746 "Migrate all the primary instance on a node away from it" 747 " (only for instances of type drbd)"), 748 'info': ( 749 ShowNodeConfig, ARGS_MANY_NODES, [], 750 "[<node_name>...]", "Show information about the node(s)"), 751 'list': ( 752 ListNodes, ARGS_MANY_NODES, 753 [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, VERBOSE_OPT], 754 "[nodes...]", 755 "Lists the nodes in the cluster. The available fields can be shown using" 756 " the \"list-fields\" command (see the man page for details)." 757 " The default field list is (in order): %s." % 758 utils.CommaJoin(_LIST_DEF_FIELDS)), 759 "list-fields": ( 760 ListNodeFields, [ArgUnknown()], 761 [NOHDR_OPT, SEP_OPT], 762 "[fields...]", 763 "Lists all available fields for nodes"), 764 'modify': ( 765 SetNodeParams, ARGS_ONE_NODE, 766 [FORCE_OPT, SUBMIT_OPT, MC_OPT, DRAINED_OPT, OFFLINE_OPT, 767 CAPAB_MASTER_OPT, CAPAB_VM_OPT, SECONDARY_IP_OPT, 768 AUTO_PROMOTE_OPT, DRY_RUN_OPT, PRIORITY_OPT, NODE_PARAMS_OPT, 769 NODE_POWERED_OPT], 770 "<node_name>", "Alters the parameters of a node"), 771 'powercycle': ( 772 PowercycleNode, ARGS_ONE_NODE, 773 [FORCE_OPT, CONFIRM_OPT, DRY_RUN_OPT, PRIORITY_OPT], 774 "<node_name>", "Tries to forcefully powercycle a node"), 775 'power': ( 776 PowerNode, 777 [ArgChoice(min=1, max=1, choices=_LIST_POWER_COMMANDS), 778 ArgNode(min=1, max=1)], 779 [SUBMIT_OPT, AUTO_PROMOTE_OPT, PRIORITY_OPT], 780 "on|off|cycle|status <node>", 781 "Change power state of node by calling out-of-band helper."), 782 'remove': ( 783 RemoveNode, ARGS_ONE_NODE, [DRY_RUN_OPT, PRIORITY_OPT], 784 "<node_name>", "Removes a node from the cluster"), 785 'volumes': ( 786 ListVolumes, [ArgNode()], 787 [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, PRIORITY_OPT], 788 "[<node_name>...]", "List logical volumes on node(s)"), 789 'list-storage': ( 790 ListStorage, ARGS_MANY_NODES, 791 [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, _STORAGE_TYPE_OPT, 792 PRIORITY_OPT], 793 "[<node_name>...]", "List physical volumes on node(s). The available" 794 " fields are (see the man page for details): %s." % 795 (utils.CommaJoin(_LIST_STOR_HEADERS))), 796 'modify-storage': ( 797 ModifyStorage, 798 [ArgNode(min=1, max=1), 799 ArgChoice(min=1, max=1, choices=_MODIFIABLE_STORAGE_TYPES), 800 ArgFile(min=1, max=1)], 801 [ALLOCATABLE_OPT, DRY_RUN_OPT, PRIORITY_OPT], 802 "<node_name> <storage_type> <name>", "Modify storage volume on a node"), 803 'repair-storage': ( 804 RepairStorage, 805 [ArgNode(min=1, max=1), 806 ArgChoice(min=1, max=1, choices=_REPAIRABLE_STORAGE_TYPES), 807 ArgFile(min=1, max=1)], 808 [IGNORE_CONSIST_OPT, DRY_RUN_OPT, PRIORITY_OPT], 809 "<node_name> <storage_type> <name>", 810 "Repairs a storage volume on a node"), 811 'list-tags': ( 812 ListTags, ARGS_ONE_NODE, [], 813 "<node_name>", "List the tags of the given node"), 814 'add-tags': ( 815 AddTags, [ArgNode(min=1, max=1), ArgUnknown()], [TAG_SRC_OPT, PRIORITY_OPT], 816 "<node_name> tag...", "Add tags to the given node"), 817 'remove-tags': ( 818 RemoveTags, [ArgNode(min=1, max=1), ArgUnknown()], 819 [TAG_SRC_OPT, PRIORITY_OPT], 820 "<node_name> tag...", "Remove tags from the given node"), 821 }
822 823 824 -def Main():
825 return GenericMain(commands, override={"tag_type": constants.TAG_NODE})
826