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