1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Cluster related commands"""
22
23
24
25
26
27
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
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
87 beparams = objects.FillDict(constants.BEC_DEFAULTS, beparams)
88 utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
89
90
91 nicparams = objects.FillDict(constants.NICC_DEFAULTS, nicparams)
92 utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
93
94
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
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
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
172
173 bootstrap.FinalizeClusterDestroy(master)
174 return 0
175
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
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
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
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
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
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
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
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
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
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
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
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
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
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:
570 return 1
571
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
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:
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:
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:
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
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
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
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
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
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
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
985 aliases = {
986 'masterfailover': 'master-failover',
987 }
993