1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Module dealing with command line parsing"""
23
24
25 import sys
26 import textwrap
27 import os.path
28 import time
29 import logging
30 import errno
31 import itertools
32 import shlex
33 from cStringIO import StringIO
34
35 from ganeti import utils
36 from ganeti import errors
37 from ganeti import constants
38 from ganeti import opcodes
39 from ganeti import luxi
40 from ganeti import ssconf
41 from ganeti import rpc
42 from ganeti import ssh
43 from ganeti import compat
44 from ganeti import netutils
45 from ganeti import qlang
46 from ganeti import objects
47
48 from optparse import (OptionParser, TitledHelpFormatter,
49 Option, OptionValueError)
50
51
52 __all__ = [
53
54 "ABSOLUTE_OPT",
55 "ADD_UIDS_OPT",
56 "ALLOCATABLE_OPT",
57 "ALLOC_POLICY_OPT",
58 "ALL_OPT",
59 "ALLOW_FAILOVER_OPT",
60 "AUTO_PROMOTE_OPT",
61 "AUTO_REPLACE_OPT",
62 "BACKEND_OPT",
63 "BLK_OS_OPT",
64 "CAPAB_MASTER_OPT",
65 "CAPAB_VM_OPT",
66 "CLEANUP_OPT",
67 "CLUSTER_DOMAIN_SECRET_OPT",
68 "CONFIRM_OPT",
69 "CP_SIZE_OPT",
70 "DEBUG_OPT",
71 "DEBUG_SIMERR_OPT",
72 "DISKIDX_OPT",
73 "DISK_OPT",
74 "DISK_PARAMS_OPT",
75 "DISK_TEMPLATE_OPT",
76 "DRAINED_OPT",
77 "DRY_RUN_OPT",
78 "DRBD_HELPER_OPT",
79 "DST_NODE_OPT",
80 "EARLY_RELEASE_OPT",
81 "ENABLED_HV_OPT",
82 "ERROR_CODES_OPT",
83 "FIELDS_OPT",
84 "FILESTORE_DIR_OPT",
85 "FILESTORE_DRIVER_OPT",
86 "FORCE_FILTER_OPT",
87 "FORCE_OPT",
88 "FORCE_VARIANT_OPT",
89 "GLOBAL_FILEDIR_OPT",
90 "HID_OS_OPT",
91 "GLOBAL_SHARED_FILEDIR_OPT",
92 "HVLIST_OPT",
93 "HVOPTS_OPT",
94 "HYPERVISOR_OPT",
95 "IALLOCATOR_OPT",
96 "DEFAULT_IALLOCATOR_OPT",
97 "IDENTIFY_DEFAULTS_OPT",
98 "IGNORE_CONSIST_OPT",
99 "IGNORE_ERRORS_OPT",
100 "IGNORE_FAILURES_OPT",
101 "IGNORE_OFFLINE_OPT",
102 "IGNORE_REMOVE_FAILURES_OPT",
103 "IGNORE_SECONDARIES_OPT",
104 "IGNORE_SIZE_OPT",
105 "INTERVAL_OPT",
106 "MAC_PREFIX_OPT",
107 "MAINTAIN_NODE_HEALTH_OPT",
108 "MASTER_NETDEV_OPT",
109 "MASTER_NETMASK_OPT",
110 "MC_OPT",
111 "MIGRATION_MODE_OPT",
112 "NET_OPT",
113 "NEW_CLUSTER_CERT_OPT",
114 "NEW_CLUSTER_DOMAIN_SECRET_OPT",
115 "NEW_CONFD_HMAC_KEY_OPT",
116 "NEW_RAPI_CERT_OPT",
117 "NEW_SECONDARY_OPT",
118 "NEW_SPICE_CERT_OPT",
119 "NIC_PARAMS_OPT",
120 "NODE_FORCE_JOIN_OPT",
121 "NODE_LIST_OPT",
122 "NODE_PLACEMENT_OPT",
123 "NODEGROUP_OPT",
124 "NODE_PARAMS_OPT",
125 "NODE_POWERED_OPT",
126 "NODRBD_STORAGE_OPT",
127 "NOHDR_OPT",
128 "NOIPCHECK_OPT",
129 "NO_INSTALL_OPT",
130 "NONAMECHECK_OPT",
131 "NOLVM_STORAGE_OPT",
132 "NOMODIFY_ETCHOSTS_OPT",
133 "NOMODIFY_SSH_SETUP_OPT",
134 "NONICS_OPT",
135 "NONLIVE_OPT",
136 "NONPLUS1_OPT",
137 "NORUNTIME_CHGS_OPT",
138 "NOSHUTDOWN_OPT",
139 "NOSTART_OPT",
140 "NOSSH_KEYCHECK_OPT",
141 "NOVOTING_OPT",
142 "NO_REMEMBER_OPT",
143 "NWSYNC_OPT",
144 "OFFLINE_INST_OPT",
145 "ONLINE_INST_OPT",
146 "ON_PRIMARY_OPT",
147 "ON_SECONDARY_OPT",
148 "OFFLINE_OPT",
149 "OSPARAMS_OPT",
150 "OS_OPT",
151 "OS_SIZE_OPT",
152 "OOB_TIMEOUT_OPT",
153 "POWER_DELAY_OPT",
154 "PREALLOC_WIPE_DISKS_OPT",
155 "PRIMARY_IP_VERSION_OPT",
156 "PRIMARY_ONLY_OPT",
157 "PRIORITY_OPT",
158 "RAPI_CERT_OPT",
159 "READD_OPT",
160 "REBOOT_TYPE_OPT",
161 "REMOVE_INSTANCE_OPT",
162 "REMOVE_UIDS_OPT",
163 "RESERVED_LVS_OPT",
164 "RUNTIME_MEM_OPT",
165 "ROMAN_OPT",
166 "SECONDARY_IP_OPT",
167 "SECONDARY_ONLY_OPT",
168 "SELECT_OS_OPT",
169 "SEP_OPT",
170 "SHOWCMD_OPT",
171 "SHUTDOWN_TIMEOUT_OPT",
172 "SINGLE_NODE_OPT",
173 "SPECS_CPU_COUNT_OPT",
174 "SPECS_DISK_COUNT_OPT",
175 "SPECS_DISK_SIZE_OPT",
176 "SPECS_MEM_SIZE_OPT",
177 "SPECS_NIC_COUNT_OPT",
178 "IPOLICY_DISK_TEMPLATES",
179 "IPOLICY_VCPU_RATIO",
180 "SPICE_CACERT_OPT",
181 "SPICE_CERT_OPT",
182 "SRC_DIR_OPT",
183 "SRC_NODE_OPT",
184 "SUBMIT_OPT",
185 "STARTUP_PAUSED_OPT",
186 "STATIC_OPT",
187 "SYNC_OPT",
188 "TAG_ADD_OPT",
189 "TAG_SRC_OPT",
190 "TIMEOUT_OPT",
191 "TO_GROUP_OPT",
192 "UIDPOOL_OPT",
193 "USEUNITS_OPT",
194 "USE_EXTERNAL_MIP_SCRIPT",
195 "USE_REPL_NET_OPT",
196 "VERBOSE_OPT",
197 "VG_NAME_OPT",
198 "YES_DOIT_OPT",
199 "DISK_STATE_OPT",
200 "HV_STATE_OPT",
201 "IGNORE_IPOLICY_OPT",
202 "INSTANCE_POLICY_OPTS",
203
204 "ConfirmOperation",
205 "CreateIPolicyFromOpts",
206 "GenericMain",
207 "GenericInstanceCreate",
208 "GenericList",
209 "GenericListFields",
210 "GetClient",
211 "GetOnlineNodes",
212 "JobExecutor",
213 "JobSubmittedException",
214 "ParseTimespec",
215 "RunWhileClusterStopped",
216 "SubmitOpCode",
217 "SubmitOrSend",
218 "UsesRPC",
219
220 "ToStderr", "ToStdout",
221 "FormatError",
222 "FormatQueryResult",
223 "FormatParameterDict",
224 "GenerateTable",
225 "AskUser",
226 "FormatTimestamp",
227 "FormatLogMessage",
228
229 "ListTags",
230 "AddTags",
231 "RemoveTags",
232
233 "ARGS_MANY_INSTANCES",
234 "ARGS_MANY_NODES",
235 "ARGS_MANY_GROUPS",
236 "ARGS_NONE",
237 "ARGS_ONE_INSTANCE",
238 "ARGS_ONE_NODE",
239 "ARGS_ONE_GROUP",
240 "ARGS_ONE_OS",
241 "ArgChoice",
242 "ArgCommand",
243 "ArgFile",
244 "ArgGroup",
245 "ArgHost",
246 "ArgInstance",
247 "ArgJobId",
248 "ArgNode",
249 "ArgOs",
250 "ArgSuggest",
251 "ArgUnknown",
252 "OPT_COMPL_INST_ADD_NODES",
253 "OPT_COMPL_MANY_NODES",
254 "OPT_COMPL_ONE_IALLOCATOR",
255 "OPT_COMPL_ONE_INSTANCE",
256 "OPT_COMPL_ONE_NODE",
257 "OPT_COMPL_ONE_NODEGROUP",
258 "OPT_COMPL_ONE_OS",
259 "cli_option",
260 "SplitNodeOption",
261 "CalculateOSNames",
262 "ParseFields",
263 "COMMON_CREATE_OPTS",
264 ]
265
266 NO_PREFIX = "no_"
267 UN_PREFIX = "-"
268
269
270 _PRIORITY_NAMES = [
271 ("low", constants.OP_PRIO_LOW),
272 ("normal", constants.OP_PRIO_NORMAL),
273 ("high", constants.OP_PRIO_HIGH),
274 ]
275
276
277
278
279 _PRIONAME_TO_VALUE = dict(_PRIORITY_NAMES)
280
281
282 (QR_NORMAL,
283 QR_UNKNOWN,
284 QR_INCOMPLETE) = range(3)
285
286
287 _CHOOSE_BATCH = 25
288
289
290
291 TISPECS_GROUP_TYPES = {
292 constants.ISPECS_MIN: constants.VTYPE_INT,
293 constants.ISPECS_MAX: constants.VTYPE_INT,
294 }
295
296 TISPECS_CLUSTER_TYPES = {
297 constants.ISPECS_MIN: constants.VTYPE_INT,
298 constants.ISPECS_MAX: constants.VTYPE_INT,
299 constants.ISPECS_STD: constants.VTYPE_INT,
300 }
305 self.min = min
306 self.max = max
307
309 return ("<%s min=%s max=%s>" %
310 (self.__class__.__name__, self.min, self.max))
311
314 """Suggesting argument.
315
316 Value can be any of the ones passed to the constructor.
317
318 """
319
320 - def __init__(self, min=0, max=None, choices=None):
323
325 return ("<%s min=%s max=%s choices=%r>" %
326 (self.__class__.__name__, self.min, self.max, self.choices))
327
330 """Choice argument.
331
332 Value can be any of the ones passed to the constructor. Like L{ArgSuggest},
333 but value must be one of the choices.
334
335 """
336
339 """Unknown argument to program (e.g. determined at runtime).
340
341 """
342
345 """Instances argument.
346
347 """
348
351 """Node argument.
352
353 """
354
357 """Node group argument.
358
359 """
360
363 """Job ID argument.
364
365 """
366
369 """File path argument.
370
371 """
372
375 """Command argument.
376
377 """
378
381 """Host argument.
382
383 """
384
385
386 -class ArgOs(_Argument):
387 """OS argument.
388
389 """
390
391
392 ARGS_NONE = []
393 ARGS_MANY_INSTANCES = [ArgInstance()]
394 ARGS_MANY_NODES = [ArgNode()]
395 ARGS_MANY_GROUPS = [ArgGroup()]
396 ARGS_ONE_INSTANCE = [ArgInstance(min=1, max=1)]
397 ARGS_ONE_NODE = [ArgNode(min=1, max=1)]
398
399 ARGS_ONE_GROUP = [ArgGroup(min=1, max=1)]
400 ARGS_ONE_OS = [ArgOs(min=1, max=1)]
424
453
471
488
505
508 """OptParsers custom converter for units.
509
510 """
511 try:
512 return utils.ParseUnit(value)
513 except errors.UnitParseError, err:
514 raise OptionValueError("option %s: %s" % (opt, err))
515
518 """Convert a KeyVal string into a dict.
519
520 This function will convert a key=val[,...] string into a dict. Empty
521 values will be converted specially: keys which have the prefix 'no_'
522 will have the value=False and the prefix stripped, the others will
523 have value=True.
524
525 @type opt: string
526 @param opt: a string holding the option name for which we process the
527 data, used in building error messages
528 @type data: string
529 @param data: a string of the format key=val,key=val,...
530 @rtype: dict
531 @return: {key=val, key=val}
532 @raises errors.ParameterError: if there are duplicate keys
533
534 """
535 kv_dict = {}
536 if data:
537 for elem in utils.UnescapeAndSplit(data, sep=","):
538 if "=" in elem:
539 key, val = elem.split("=", 1)
540 else:
541 if elem.startswith(NO_PREFIX):
542 key, val = elem[len(NO_PREFIX):], False
543 elif elem.startswith(UN_PREFIX):
544 key, val = elem[len(UN_PREFIX):], None
545 else:
546 key, val = elem, True
547 if key in kv_dict:
548 raise errors.ParameterError("Duplicate key '%s' in option %s" %
549 (key, opt))
550 kv_dict[key] = val
551 return kv_dict
552
555 """Custom parser for ident:key=val,key=val options.
556
557 This will store the parsed values as a tuple (ident, {key: val}). As such,
558 multiple uses of this option via action=append is possible.
559
560 """
561 if ":" not in value:
562 ident, rest = value, ""
563 else:
564 ident, rest = value.split(":", 1)
565
566 if ident.startswith(NO_PREFIX):
567 if rest:
568 msg = "Cannot pass options when removing parameter groups: %s" % value
569 raise errors.ParameterError(msg)
570 retval = (ident[len(NO_PREFIX):], False)
571 elif (ident.startswith(UN_PREFIX) and
572 (len(ident) <= len(UN_PREFIX) or
573 not ident[len(UN_PREFIX)][0].isdigit())):
574 if rest:
575 msg = "Cannot pass options when removing parameter groups: %s" % value
576 raise errors.ParameterError(msg)
577 retval = (ident[len(UN_PREFIX):], None)
578 else:
579 kv_dict = _SplitKeyVal(opt, rest)
580 retval = (ident, kv_dict)
581 return retval
582
585 """Custom parser class for key=val,key=val options.
586
587 This will store the parsed values as a dict {key: val}.
588
589 """
590 return _SplitKeyVal(opt, value)
591
594 """Custom parser for yes/no options.
595
596 This will store the parsed value as either True or False.
597
598 """
599 value = value.lower()
600 if value == constants.VALUE_FALSE or value == "no":
601 return False
602 elif value == constants.VALUE_TRUE or value == "yes":
603 return True
604 else:
605 raise errors.ParameterError("Invalid boolean value '%s'" % value)
606
609 """Custom parser for comma-separated lists.
610
611 """
612
613
614 if not value:
615 return []
616 else:
617 return utils.UnescapeAndSplit(value)
618
621 """Custom parser for float numbers which might be also defaults.
622
623 """
624 value = value.lower()
625
626 if value == constants.VALUE_DEFAULT:
627 return value
628 else:
629 return float(value)
630
631
632
633
634 (OPT_COMPL_MANY_NODES,
635 OPT_COMPL_ONE_NODE,
636 OPT_COMPL_ONE_INSTANCE,
637 OPT_COMPL_ONE_OS,
638 OPT_COMPL_ONE_IALLOCATOR,
639 OPT_COMPL_INST_ADD_NODES,
640 OPT_COMPL_ONE_NODEGROUP) = range(100, 107)
641
642 OPT_COMPL_ALL = frozenset([
643 OPT_COMPL_MANY_NODES,
644 OPT_COMPL_ONE_NODE,
645 OPT_COMPL_ONE_INSTANCE,
646 OPT_COMPL_ONE_OS,
647 OPT_COMPL_ONE_IALLOCATOR,
648 OPT_COMPL_INST_ADD_NODES,
649 OPT_COMPL_ONE_NODEGROUP,
650 ])
675
676
677
678 cli_option = CliOption
679
680
681 _YORNO = "yes|no"
682
683 DEBUG_OPT = cli_option("-d", "--debug", default=0, action="count",
684 help="Increase debugging level")
685
686 NOHDR_OPT = cli_option("--no-headers", default=False,
687 action="store_true", dest="no_headers",
688 help="Don't display column headers")
689
690 SEP_OPT = cli_option("--separator", default=None,
691 action="store", dest="separator",
692 help=("Separator between output fields"
693 " (defaults to one space)"))
694
695 USEUNITS_OPT = cli_option("--units", default=None,
696 dest="units", choices=("h", "m", "g", "t"),
697 help="Specify units for output (one of h/m/g/t)")
698
699 FIELDS_OPT = cli_option("-o", "--output", dest="output", action="store",
700 type="string", metavar="FIELDS",
701 help="Comma separated list of output fields")
702
703 FORCE_OPT = cli_option("-f", "--force", dest="force", action="store_true",
704 default=False, help="Force the operation")
705
706 CONFIRM_OPT = cli_option("--yes", dest="confirm", action="store_true",
707 default=False, help="Do not require confirmation")
708
709 IGNORE_OFFLINE_OPT = cli_option("--ignore-offline", dest="ignore_offline",
710 action="store_true", default=False,
711 help=("Ignore offline nodes and do as much"
712 " as possible"))
713
714 TAG_ADD_OPT = cli_option("--tags", dest="tags",
715 default=None, help="Comma-separated list of instance"
716 " tags")
717
718 TAG_SRC_OPT = cli_option("--from", dest="tags_source",
719 default=None, help="File with tag names")
720
721 SUBMIT_OPT = cli_option("--submit", dest="submit_only",
722 default=False, action="store_true",
723 help=("Submit the job and return the job ID, but"
724 " don't wait for the job to finish"))
725
726 SYNC_OPT = cli_option("--sync", dest="do_locking",
727 default=False, action="store_true",
728 help=("Grab locks while doing the queries"
729 " in order to ensure more consistent results"))
730
731 DRY_RUN_OPT = cli_option("--dry-run", default=False,
732 action="store_true",
733 help=("Do not execute the operation, just run the"
734 " check steps and verify it it could be"
735 " executed"))
736
737 VERBOSE_OPT = cli_option("-v", "--verbose", default=False,
738 action="store_true",
739 help="Increase the verbosity of the operation")
740
741 DEBUG_SIMERR_OPT = cli_option("--debug-simulate-errors", default=False,
742 action="store_true", dest="simulate_errors",
743 help="Debugging option that makes the operation"
744 " treat most runtime checks as failed")
745
746 NWSYNC_OPT = cli_option("--no-wait-for-sync", dest="wait_for_sync",
747 default=True, action="store_false",
748 help="Don't wait for sync (DANGEROUS!)")
749
750 ONLINE_INST_OPT = cli_option("--online", dest="online_inst",
751 action="store_true", default=False,
752 help="Enable offline instance")
753
754 OFFLINE_INST_OPT = cli_option("--offline", dest="offline_inst",
755 action="store_true", default=False,
756 help="Disable down instance")
757
758 DISK_TEMPLATE_OPT = cli_option("-t", "--disk-template", dest="disk_template",
759 help=("Custom disk setup (%s)" %
760 utils.CommaJoin(constants.DISK_TEMPLATES)),
761 default=None, metavar="TEMPL",
762 choices=list(constants.DISK_TEMPLATES))
763
764 NONICS_OPT = cli_option("--no-nics", default=False, action="store_true",
765 help="Do not create any network cards for"
766 " the instance")
767
768 FILESTORE_DIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
769 help="Relative path under default cluster-wide"
770 " file storage dir to store file-based disks",
771 default=None, metavar="<DIR>")
772
773 FILESTORE_DRIVER_OPT = cli_option("--file-driver", dest="file_driver",
774 help="Driver to use for image files",
775 default="loop", metavar="<DRIVER>",
776 choices=list(constants.FILE_DRIVER))
777
778 IALLOCATOR_OPT = cli_option("-I", "--iallocator", metavar="<NAME>",
779 help="Select nodes for the instance automatically"
780 " using the <NAME> iallocator plugin",
781 default=None, type="string",
782 completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
783
784 DEFAULT_IALLOCATOR_OPT = cli_option("-I", "--default-iallocator",
785 metavar="<NAME>",
786 help="Set the default instance allocator plugin",
787 default=None, type="string",
788 completion_suggest=OPT_COMPL_ONE_IALLOCATOR)
789
790 OS_OPT = cli_option("-o", "--os-type", dest="os", help="What OS to run",
791 metavar="<os>",
792 completion_suggest=OPT_COMPL_ONE_OS)
793
794 OSPARAMS_OPT = cli_option("-O", "--os-parameters", dest="osparams",
795 type="keyval", default={},
796 help="OS parameters")
797
798 FORCE_VARIANT_OPT = cli_option("--force-variant", dest="force_variant",
799 action="store_true", default=False,
800 help="Force an unknown variant")
801
802 NO_INSTALL_OPT = cli_option("--no-install", dest="no_install",
803 action="store_true", default=False,
804 help="Do not install the OS (will"
805 " enable no-start)")
806
807 NORUNTIME_CHGS_OPT = cli_option("--no-runtime-changes",
808 dest="allow_runtime_chgs",
809 default=True, action="store_false",
810 help="Don't allow runtime changes")
811
812 BACKEND_OPT = cli_option("-B", "--backend-parameters", dest="beparams",
813 type="keyval", default={},
814 help="Backend parameters")
815
816 HVOPTS_OPT = cli_option("-H", "--hypervisor-parameters", type="keyval",
817 default={}, dest="hvparams",
818 help="Hypervisor parameters")
819
820 DISK_PARAMS_OPT = cli_option("-D", "--disk-parameters", dest="diskparams",
821 help="Disk template parameters, in the format"
822 " template:option=value,option=value,...",
823 type="identkeyval", action="append", default=[])
824
825 SPECS_MEM_SIZE_OPT = cli_option("--specs-mem-size", dest="ispecs_mem_size",
826 type="keyval", default={},
827 help="Memory size specs: list of key=value,"
828 " where key is one of min, max, std"
829 " (in MB or using a unit)")
830
831 SPECS_CPU_COUNT_OPT = cli_option("--specs-cpu-count", dest="ispecs_cpu_count",
832 type="keyval", default={},
833 help="CPU count specs: list of key=value,"
834 " where key is one of min, max, std")
835
836 SPECS_DISK_COUNT_OPT = cli_option("--specs-disk-count",
837 dest="ispecs_disk_count",
838 type="keyval", default={},
839 help="Disk count specs: list of key=value,"
840 " where key is one of min, max, std")
841
842 SPECS_DISK_SIZE_OPT = cli_option("--specs-disk-size", dest="ispecs_disk_size",
843 type="keyval", default={},
844 help="Disk size specs: list of key=value,"
845 " where key is one of min, max, std"
846 " (in MB or using a unit)")
847
848 SPECS_NIC_COUNT_OPT = cli_option("--specs-nic-count", dest="ispecs_nic_count",
849 type="keyval", default={},
850 help="NIC count specs: list of key=value,"
851 " where key is one of min, max, std")
852
853 IPOLICY_DISK_TEMPLATES = cli_option("--ipolicy-disk-templates",
854 dest="ipolicy_disk_templates",
855 type="list", default=None,
856 help="Comma-separated list of"
857 " enabled disk templates")
858
859 IPOLICY_VCPU_RATIO = cli_option("--ipolicy-vcpu-ratio",
860 dest="ipolicy_vcpu_ratio",
861 type="maybefloat", default=None,
862 help="The maximum allowed vcpu-to-cpu ratio")
863
864 IPOLICY_SPINDLE_RATIO = cli_option("--ipolicy-spindle-ratio",
865 dest="ipolicy_spindle_ratio",
866 type="maybefloat", default=None,
867 help=("The maximum allowed instances to"
868 " spindle ratio"))
869
870 HYPERVISOR_OPT = cli_option("-H", "--hypervisor-parameters", dest="hypervisor",
871 help="Hypervisor and hypervisor options, in the"
872 " format hypervisor:option=value,option=value,...",
873 default=None, type="identkeyval")
874
875 HVLIST_OPT = cli_option("-H", "--hypervisor-parameters", dest="hvparams",
876 help="Hypervisor and hypervisor options, in the"
877 " format hypervisor:option=value,option=value,...",
878 default=[], action="append", type="identkeyval")
879
880 NOIPCHECK_OPT = cli_option("--no-ip-check", dest="ip_check", default=True,
881 action="store_false",
882 help="Don't check that the instance's IP"
883 " is alive")
884
885 NONAMECHECK_OPT = cli_option("--no-name-check", dest="name_check",
886 default=True, action="store_false",
887 help="Don't check that the instance's name"
888 " is resolvable")
889
890 NET_OPT = cli_option("--net",
891 help="NIC parameters", default=[],
892 dest="nics", action="append", type="identkeyval")
893
894 DISK_OPT = cli_option("--disk", help="Disk parameters", default=[],
895 dest="disks", action="append", type="identkeyval")
896
897 DISKIDX_OPT = cli_option("--disks", dest="disks", default=None,
898 help="Comma-separated list of disks"
899 " indices to act on (e.g. 0,2) (optional,"
900 " defaults to all disks)")
901
902 OS_SIZE_OPT = cli_option("-s", "--os-size", dest="sd_size",
903 help="Enforces a single-disk configuration using the"
904 " given disk size, in MiB unless a suffix is used",
905 default=None, type="unit", metavar="<size>")
906
907 IGNORE_CONSIST_OPT = cli_option("--ignore-consistency",
908 dest="ignore_consistency",
909 action="store_true", default=False,
910 help="Ignore the consistency of the disks on"
911 " the secondary")
912
913 ALLOW_FAILOVER_OPT = cli_option("--allow-failover",
914 dest="allow_failover",
915 action="store_true", default=False,
916 help="If migration is not possible fallback to"
917 " failover")
918
919 NONLIVE_OPT = cli_option("--non-live", dest="live",
920 default=True, action="store_false",
921 help="Do a non-live migration (this usually means"
922 " freeze the instance, save the state, transfer and"
923 " only then resume running on the secondary node)")
924
925 MIGRATION_MODE_OPT = cli_option("--migration-mode", dest="migration_mode",
926 default=None,
927 choices=list(constants.HT_MIGRATION_MODES),
928 help="Override default migration mode (choose"
929 " either live or non-live")
930
931 NODE_PLACEMENT_OPT = cli_option("-n", "--node", dest="node",
932 help="Target node and optional secondary node",
933 metavar="<pnode>[:<snode>]",
934 completion_suggest=OPT_COMPL_INST_ADD_NODES)
935
936 NODE_LIST_OPT = cli_option("-n", "--node", dest="nodes", default=[],
937 action="append", metavar="<node>",
938 help="Use only this node (can be used multiple"
939 " times, if not given defaults to all nodes)",
940 completion_suggest=OPT_COMPL_ONE_NODE)
941
942 NODEGROUP_OPT_NAME = "--node-group"
943 NODEGROUP_OPT = cli_option("-g", NODEGROUP_OPT_NAME,
944 dest="nodegroup",
945 help="Node group (name or uuid)",
946 metavar="<nodegroup>",
947 default=None, type="string",
948 completion_suggest=OPT_COMPL_ONE_NODEGROUP)
949
950 SINGLE_NODE_OPT = cli_option("-n", "--node", dest="node", help="Target node",
951 metavar="<node>",
952 completion_suggest=OPT_COMPL_ONE_NODE)
953
954 NOSTART_OPT = cli_option("--no-start", dest="start", default=True,
955 action="store_false",
956 help="Don't start the instance after creation")
957
958 SHOWCMD_OPT = cli_option("--show-cmd", dest="show_command",
959 action="store_true", default=False,
960 help="Show command instead of executing it")
961
962 CLEANUP_OPT = cli_option("--cleanup", dest="cleanup",
963 default=False, action="store_true",
964 help="Instead of performing the migration, try to"
965 " recover from a failed cleanup. This is safe"
966 " to run even if the instance is healthy, but it"
967 " will create extra replication traffic and "
968 " disrupt briefly the replication (like during the"
969 " migration")
970
971 STATIC_OPT = cli_option("-s", "--static", dest="static",
972 action="store_true", default=False,
973 help="Only show configuration data, not runtime data")
974
975 ALL_OPT = cli_option("--all", dest="show_all",
976 default=False, action="store_true",
977 help="Show info on all instances on the cluster."
978 " This can take a long time to run, use wisely")
979
980 SELECT_OS_OPT = cli_option("--select-os", dest="select_os",
981 action="store_true", default=False,
982 help="Interactive OS reinstall, lists available"
983 " OS templates for selection")
984
985 IGNORE_FAILURES_OPT = cli_option("--ignore-failures", dest="ignore_failures",
986 action="store_true", default=False,
987 help="Remove the instance from the cluster"
988 " configuration even if there are failures"
989 " during the removal process")
990
991 IGNORE_REMOVE_FAILURES_OPT = cli_option("--ignore-remove-failures",
992 dest="ignore_remove_failures",
993 action="store_true", default=False,
994 help="Remove the instance from the"
995 " cluster configuration even if there"
996 " are failures during the removal"
997 " process")
998
999 REMOVE_INSTANCE_OPT = cli_option("--remove-instance", dest="remove_instance",
1000 action="store_true", default=False,
1001 help="Remove the instance from the cluster")
1002
1003 DST_NODE_OPT = cli_option("-n", "--target-node", dest="dst_node",
1004 help="Specifies the new node for the instance",
1005 metavar="NODE", default=None,
1006 completion_suggest=OPT_COMPL_ONE_NODE)
1007
1008 NEW_SECONDARY_OPT = cli_option("-n", "--new-secondary", dest="dst_node",
1009 help="Specifies the new secondary node",
1010 metavar="NODE", default=None,
1011 completion_suggest=OPT_COMPL_ONE_NODE)
1012
1013 ON_PRIMARY_OPT = cli_option("-p", "--on-primary", dest="on_primary",
1014 default=False, action="store_true",
1015 help="Replace the disk(s) on the primary"
1016 " node (applies only to internally mirrored"
1017 " disk templates, e.g. %s)" %
1018 utils.CommaJoin(constants.DTS_INT_MIRROR))
1019
1020 ON_SECONDARY_OPT = cli_option("-s", "--on-secondary", dest="on_secondary",
1021 default=False, action="store_true",
1022 help="Replace the disk(s) on the secondary"
1023 " node (applies only to internally mirrored"
1024 " disk templates, e.g. %s)" %
1025 utils.CommaJoin(constants.DTS_INT_MIRROR))
1026
1027 AUTO_PROMOTE_OPT = cli_option("--auto-promote", dest="auto_promote",
1028 default=False, action="store_true",
1029 help="Lock all nodes and auto-promote as needed"
1030 " to MC status")
1031
1032 AUTO_REPLACE_OPT = cli_option("-a", "--auto", dest="auto",
1033 default=False, action="store_true",
1034 help="Automatically replace faulty disks"
1035 " (applies only to internally mirrored"
1036 " disk templates, e.g. %s)" %
1037 utils.CommaJoin(constants.DTS_INT_MIRROR))
1038
1039 IGNORE_SIZE_OPT = cli_option("--ignore-size", dest="ignore_size",
1040 default=False, action="store_true",
1041 help="Ignore current recorded size"
1042 " (useful for forcing activation when"
1043 " the recorded size is wrong)")
1044
1045 SRC_NODE_OPT = cli_option("--src-node", dest="src_node", help="Source node",
1046 metavar="<node>",
1047 completion_suggest=OPT_COMPL_ONE_NODE)
1048
1049 SRC_DIR_OPT = cli_option("--src-dir", dest="src_dir", help="Source directory",
1050 metavar="<dir>")
1051
1052 SECONDARY_IP_OPT = cli_option("-s", "--secondary-ip", dest="secondary_ip",
1053 help="Specify the secondary ip for the node",
1054 metavar="ADDRESS", default=None)
1055
1056 READD_OPT = cli_option("--readd", dest="readd",
1057 default=False, action="store_true",
1058 help="Readd old node after replacing it")
1059
1060 NOSSH_KEYCHECK_OPT = cli_option("--no-ssh-key-check", dest="ssh_key_check",
1061 default=True, action="store_false",
1062 help="Disable SSH key fingerprint checking")
1063
1064 NODE_FORCE_JOIN_OPT = cli_option("--force-join", dest="force_join",
1065 default=False, action="store_true",
1066 help="Force the joining of a node")
1067
1068 MC_OPT = cli_option("-C", "--master-candidate", dest="master_candidate",
1069 type="bool", default=None, metavar=_YORNO,
1070 help="Set the master_candidate flag on the node")
1071
1072 OFFLINE_OPT = cli_option("-O", "--offline", dest="offline", metavar=_YORNO,
1073 type="bool", default=None,
1074 help=("Set the offline flag on the node"
1075 " (cluster does not communicate with offline"
1076 " nodes)"))
1077
1078 DRAINED_OPT = cli_option("-D", "--drained", dest="drained", metavar=_YORNO,
1079 type="bool", default=None,
1080 help=("Set the drained flag on the node"
1081 " (excluded from allocation operations)"))
1082
1083 CAPAB_MASTER_OPT = cli_option("--master-capable", dest="master_capable",
1084 type="bool", default=None, metavar=_YORNO,
1085 help="Set the master_capable flag on the node")
1086
1087 CAPAB_VM_OPT = cli_option("--vm-capable", dest="vm_capable",
1088 type="bool", default=None, metavar=_YORNO,
1089 help="Set the vm_capable flag on the node")
1090
1091 ALLOCATABLE_OPT = cli_option("--allocatable", dest="allocatable",
1092 type="bool", default=None, metavar=_YORNO,
1093 help="Set the allocatable flag on a volume")
1094
1095 NOLVM_STORAGE_OPT = cli_option("--no-lvm-storage", dest="lvm_storage",
1096 help="Disable support for lvm based instances"
1097 " (cluster-wide)",
1098 action="store_false", default=True)
1099
1100 ENABLED_HV_OPT = cli_option("--enabled-hypervisors",
1101 dest="enabled_hypervisors",
1102 help="Comma-separated list of hypervisors",
1103 type="string", default=None)
1104
1105 NIC_PARAMS_OPT = cli_option("-N", "--nic-parameters", dest="nicparams",
1106 type="keyval", default={},
1107 help="NIC parameters")
1108
1109 CP_SIZE_OPT = cli_option("-C", "--candidate-pool-size", default=None,
1110 dest="candidate_pool_size", type="int",
1111 help="Set the candidate pool size")
1112
1113 VG_NAME_OPT = cli_option("--vg-name", dest="vg_name",
1114 help=("Enables LVM and specifies the volume group"
1115 " name (cluster-wide) for disk allocation"
1116 " [%s]" % constants.DEFAULT_VG),
1117 metavar="VG", default=None)
1118
1119 YES_DOIT_OPT = cli_option("--yes-do-it", "--ya-rly", dest="yes_do_it",
1120 help="Destroy cluster", action="store_true")
1121
1122 NOVOTING_OPT = cli_option("--no-voting", dest="no_voting",
1123 help="Skip node agreement check (dangerous)",
1124 action="store_true", default=False)
1125
1126 MAC_PREFIX_OPT = cli_option("-m", "--mac-prefix", dest="mac_prefix",
1127 help="Specify the mac prefix for the instance IP"
1128 " addresses, in the format XX:XX:XX",
1129 metavar="PREFIX",
1130 default=None)
1131
1132 MASTER_NETDEV_OPT = cli_option("--master-netdev", dest="master_netdev",
1133 help="Specify the node interface (cluster-wide)"
1134 " on which the master IP address will be added"
1135 " (cluster init default: %s)" %
1136 constants.DEFAULT_BRIDGE,
1137 metavar="NETDEV",
1138 default=None)
1139
1140 MASTER_NETMASK_OPT = cli_option("--master-netmask", dest="master_netmask",
1141 help="Specify the netmask of the master IP",
1142 metavar="NETMASK",
1143 default=None)
1144
1145 USE_EXTERNAL_MIP_SCRIPT = cli_option("--use-external-mip-script",
1146 dest="use_external_mip_script",
1147 help="Specify whether to run a user-provided"
1148 " script for the master IP address turnup and"
1149 " turndown operations",
1150 type="bool", metavar=_YORNO, default=None)
1151
1152 GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
1153 help="Specify the default directory (cluster-"
1154 "wide) for storing the file-based disks [%s]" %
1155 constants.DEFAULT_FILE_STORAGE_DIR,
1156 metavar="DIR",
1157 default=constants.DEFAULT_FILE_STORAGE_DIR)
1158
1159 GLOBAL_SHARED_FILEDIR_OPT = cli_option("--shared-file-storage-dir",
1160 dest="shared_file_storage_dir",
1161 help="Specify the default directory (cluster-"
1162 "wide) for storing the shared file-based"
1163 " disks [%s]" %
1164 constants.DEFAULT_SHARED_FILE_STORAGE_DIR,
1165 metavar="SHAREDDIR",
1166 default=constants.DEFAULT_SHARED_FILE_STORAGE_DIR)
1167
1168 NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts",
1169 help="Don't modify /etc/hosts",
1170 action="store_false", default=True)
1171
1172 NOMODIFY_SSH_SETUP_OPT = cli_option("--no-ssh-init", dest="modify_ssh_setup",
1173 help="Don't initialize SSH keys",
1174 action="store_false", default=True)
1175
1176 ERROR_CODES_OPT = cli_option("--error-codes", dest="error_codes",
1177 help="Enable parseable error messages",
1178 action="store_true", default=False)
1179
1180 NONPLUS1_OPT = cli_option("--no-nplus1-mem", dest="skip_nplusone_mem",
1181 help="Skip N+1 memory redundancy tests",
1182 action="store_true", default=False)
1183
1184 REBOOT_TYPE_OPT = cli_option("-t", "--type", dest="reboot_type",
1185 help="Type of reboot: soft/hard/full",
1186 default=constants.INSTANCE_REBOOT_HARD,
1187 metavar="<REBOOT>",
1188 choices=list(constants.REBOOT_TYPES))
1189
1190 IGNORE_SECONDARIES_OPT = cli_option("--ignore-secondaries",
1191 dest="ignore_secondaries",
1192 default=False, action="store_true",
1193 help="Ignore errors from secondaries")
1194
1195 NOSHUTDOWN_OPT = cli_option("--noshutdown", dest="shutdown",
1196 action="store_false", default=True,
1197 help="Don't shutdown the instance (unsafe)")
1198
1199 TIMEOUT_OPT = cli_option("--timeout", dest="timeout", type="int",
1200 default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
1201 help="Maximum time to wait")
1202
1203 SHUTDOWN_TIMEOUT_OPT = cli_option("--shutdown-timeout",
1204 dest="shutdown_timeout", type="int",
1205 default=constants.DEFAULT_SHUTDOWN_TIMEOUT,
1206 help="Maximum time to wait for instance shutdown")
1207
1208 INTERVAL_OPT = cli_option("--interval", dest="interval", type="int",
1209 default=None,
1210 help=("Number of seconds between repetions of the"
1211 " command"))
1212
1213 EARLY_RELEASE_OPT = cli_option("--early-release",
1214 dest="early_release", default=False,
1215 action="store_true",
1216 help="Release the locks on the secondary"
1217 " node(s) early")
1218
1219 NEW_CLUSTER_CERT_OPT = cli_option("--new-cluster-certificate",
1220 dest="new_cluster_cert",
1221 default=False, action="store_true",
1222 help="Generate a new cluster certificate")
1223
1224 RAPI_CERT_OPT = cli_option("--rapi-certificate", dest="rapi_cert",
1225 default=None,
1226 help="File containing new RAPI certificate")
1227
1228 NEW_RAPI_CERT_OPT = cli_option("--new-rapi-certificate", dest="new_rapi_cert",
1229 default=None, action="store_true",
1230 help=("Generate a new self-signed RAPI"
1231 " certificate"))
1232
1233 SPICE_CERT_OPT = cli_option("--spice-certificate", dest="spice_cert",
1234 default=None,
1235 help="File containing new SPICE certificate")
1236
1237 SPICE_CACERT_OPT = cli_option("--spice-ca-certificate", dest="spice_cacert",
1238 default=None,
1239 help="File containing the certificate of the CA"
1240 " which signed the SPICE certificate")
1241
1242 NEW_SPICE_CERT_OPT = cli_option("--new-spice-certificate",
1243 dest="new_spice_cert", default=None,
1244 action="store_true",
1245 help=("Generate a new self-signed SPICE"
1246 " certificate"))
1247
1248 NEW_CONFD_HMAC_KEY_OPT = cli_option("--new-confd-hmac-key",
1249 dest="new_confd_hmac_key",
1250 default=False, action="store_true",
1251 help=("Create a new HMAC key for %s" %
1252 constants.CONFD))
1253
1254 CLUSTER_DOMAIN_SECRET_OPT = cli_option("--cluster-domain-secret",
1255 dest="cluster_domain_secret",
1256 default=None,
1257 help=("Load new new cluster domain"
1258 " secret from file"))
1259
1260 NEW_CLUSTER_DOMAIN_SECRET_OPT = cli_option("--new-cluster-domain-secret",
1261 dest="new_cluster_domain_secret",
1262 default=False, action="store_true",
1263 help=("Create a new cluster domain"
1264 " secret"))
1265
1266 USE_REPL_NET_OPT = cli_option("--use-replication-network",
1267 dest="use_replication_network",
1268 help="Whether to use the replication network"
1269 " for talking to the nodes",
1270 action="store_true", default=False)
1271
1272 MAINTAIN_NODE_HEALTH_OPT = \
1273 cli_option("--maintain-node-health", dest="maintain_node_health",
1274 metavar=_YORNO, default=None, type="bool",
1275 help="Configure the cluster to automatically maintain node"
1276 " health, by shutting down unknown instances, shutting down"
1277 " unknown DRBD devices, etc.")
1278
1279 IDENTIFY_DEFAULTS_OPT = \
1280 cli_option("--identify-defaults", dest="identify_defaults",
1281 default=False, action="store_true",
1282 help="Identify which saved instance parameters are equal to"
1283 " the current cluster defaults and set them as such, instead"
1284 " of marking them as overridden")
1285
1286 UIDPOOL_OPT = cli_option("--uid-pool", default=None,
1287 action="store", dest="uid_pool",
1288 help=("A list of user-ids or user-id"
1289 " ranges separated by commas"))
1290
1291 ADD_UIDS_OPT = cli_option("--add-uids", default=None,
1292 action="store", dest="add_uids",
1293 help=("A list of user-ids or user-id"
1294 " ranges separated by commas, to be"
1295 " added to the user-id pool"))
1296
1297 REMOVE_UIDS_OPT = cli_option("--remove-uids", default=None,
1298 action="store", dest="remove_uids",
1299 help=("A list of user-ids or user-id"
1300 " ranges separated by commas, to be"
1301 " removed from the user-id pool"))
1302
1303 RESERVED_LVS_OPT = cli_option("--reserved-lvs", default=None,
1304 action="store", dest="reserved_lvs",
1305 help=("A comma-separated list of reserved"
1306 " logical volumes names, that will be"
1307 " ignored by cluster verify"))
1308
1309 ROMAN_OPT = cli_option("--roman",
1310 dest="roman_integers", default=False,
1311 action="store_true",
1312 help="Use roman numbers for positive integers")
1313
1314 DRBD_HELPER_OPT = cli_option("--drbd-usermode-helper", dest="drbd_helper",
1315 action="store", default=None,
1316 help="Specifies usermode helper for DRBD")
1317
1318 NODRBD_STORAGE_OPT = cli_option("--no-drbd-storage", dest="drbd_storage",
1319 action="store_false", default=True,
1320 help="Disable support for DRBD")
1321
1322 PRIMARY_IP_VERSION_OPT = \
1323 cli_option("--primary-ip-version", default=constants.IP4_VERSION,
1324 action="store", dest="primary_ip_version",
1325 metavar="%d|%d" % (constants.IP4_VERSION,
1326 constants.IP6_VERSION),
1327 help="Cluster-wide IP version for primary IP")
1328
1329 PRIORITY_OPT = cli_option("--priority", default=None, dest="priority",
1330 metavar="|".join(name for name, _ in _PRIORITY_NAMES),
1331 choices=_PRIONAME_TO_VALUE.keys(),
1332 help="Priority for opcode processing")
1333
1334 HID_OS_OPT = cli_option("--hidden", dest="hidden",
1335 type="bool", default=None, metavar=_YORNO,
1336 help="Sets the hidden flag on the OS")
1337
1338 BLK_OS_OPT = cli_option("--blacklisted", dest="blacklisted",
1339 type="bool", default=None, metavar=_YORNO,
1340 help="Sets the blacklisted flag on the OS")
1341
1342 PREALLOC_WIPE_DISKS_OPT = cli_option("--prealloc-wipe-disks", default=None,
1343 type="bool", metavar=_YORNO,
1344 dest="prealloc_wipe_disks",
1345 help=("Wipe disks prior to instance"
1346 " creation"))
1347
1348 NODE_PARAMS_OPT = cli_option("--node-parameters", dest="ndparams",
1349 type="keyval", default=None,
1350 help="Node parameters")
1351
1352 ALLOC_POLICY_OPT = cli_option("--alloc-policy", dest="alloc_policy",
1353 action="store", metavar="POLICY", default=None,
1354 help="Allocation policy for the node group")
1355
1356 NODE_POWERED_OPT = cli_option("--node-powered", default=None,
1357 type="bool", metavar=_YORNO,
1358 dest="node_powered",
1359 help="Specify if the SoR for node is powered")
1360
1361 OOB_TIMEOUT_OPT = cli_option("--oob-timeout", dest="oob_timeout", type="int",
1362 default=constants.OOB_TIMEOUT,
1363 help="Maximum time to wait for out-of-band helper")
1364
1365 POWER_DELAY_OPT = cli_option("--power-delay", dest="power_delay", type="float",
1366 default=constants.OOB_POWER_DELAY,
1367 help="Time in seconds to wait between power-ons")
1368
1369 FORCE_FILTER_OPT = cli_option("-F", "--filter", dest="force_filter",
1370 action="store_true", default=False,
1371 help=("Whether command argument should be treated"
1372 " as filter"))
1373
1374 NO_REMEMBER_OPT = cli_option("--no-remember",
1375 dest="no_remember",
1376 action="store_true", default=False,
1377 help="Perform but do not record the change"
1378 " in the configuration")
1379
1380 PRIMARY_ONLY_OPT = cli_option("-p", "--primary-only",
1381 default=False, action="store_true",
1382 help="Evacuate primary instances only")
1383
1384 SECONDARY_ONLY_OPT = cli_option("-s", "--secondary-only",
1385 default=False, action="store_true",
1386 help="Evacuate secondary instances only"
1387 " (applies only to internally mirrored"
1388 " disk templates, e.g. %s)" %
1389 utils.CommaJoin(constants.DTS_INT_MIRROR))
1390
1391 STARTUP_PAUSED_OPT = cli_option("--paused", dest="startup_paused",
1392 action="store_true", default=False,
1393 help="Pause instance at startup")
1394
1395 TO_GROUP_OPT = cli_option("--to", dest="to", metavar="<group>",
1396 help="Destination node group (name or uuid)",
1397 default=None, action="append",
1398 completion_suggest=OPT_COMPL_ONE_NODEGROUP)
1399
1400 IGNORE_ERRORS_OPT = cli_option("-I", "--ignore-errors", default=[],
1401 action="append", dest="ignore_errors",
1402 choices=list(constants.CV_ALL_ECODES_STRINGS),
1403 help="Error code to be ignored")
1404
1405 DISK_STATE_OPT = cli_option("--disk-state", default=[], dest="disk_state",
1406 action="append",
1407 help=("Specify disk state information in the"
1408 " format"
1409 " storage_type/identifier:option=value,...;"
1410 " note this is unused for now"),
1411 type="identkeyval")
1412
1413 HV_STATE_OPT = cli_option("--hypervisor-state", default=[], dest="hv_state",
1414 action="append",
1415 help=("Specify hypervisor state information in the"
1416 " format hypervisor:option=value,...;"
1417 " note this is unused for now"),
1418 type="identkeyval")
1419
1420 IGNORE_IPOLICY_OPT = cli_option("--ignore-ipolicy", dest="ignore_ipolicy",
1421 action="store_true", default=False,
1422 help="Ignore instance policy violations")
1423
1424 RUNTIME_MEM_OPT = cli_option("-m", "--runtime-memory", dest="runtime_mem",
1425 help="Sets the instance's runtime memory,"
1426 " ballooning it up or down to the new value",
1427 default=None, type="unit", metavar="<size>")
1428
1429 ABSOLUTE_OPT = cli_option("--absolute", dest="absolute",
1430 action="store_true", default=False,
1431 help="Marks the grow as absolute instead of the"
1432 " (default) relative mode")
1433
1434
1435 COMMON_OPTS = [DEBUG_OPT]
1436
1437
1438
1439 COMMON_CREATE_OPTS = [
1440 BACKEND_OPT,
1441 DISK_OPT,
1442 DISK_TEMPLATE_OPT,
1443 FILESTORE_DIR_OPT,
1444 FILESTORE_DRIVER_OPT,
1445 HYPERVISOR_OPT,
1446 IALLOCATOR_OPT,
1447 NET_OPT,
1448 NODE_PLACEMENT_OPT,
1449 NOIPCHECK_OPT,
1450 NONAMECHECK_OPT,
1451 NONICS_OPT,
1452 NWSYNC_OPT,
1453 OSPARAMS_OPT,
1454 OS_SIZE_OPT,
1455 SUBMIT_OPT,
1456 TAG_ADD_OPT,
1457 DRY_RUN_OPT,
1458 PRIORITY_OPT,
1459 ]
1460
1461
1462 INSTANCE_POLICY_OPTS = [
1463 SPECS_CPU_COUNT_OPT,
1464 SPECS_DISK_COUNT_OPT,
1465 SPECS_DISK_SIZE_OPT,
1466 SPECS_MEM_SIZE_OPT,
1467 SPECS_NIC_COUNT_OPT,
1468 IPOLICY_DISK_TEMPLATES,
1469 IPOLICY_VCPU_RATIO,
1470 IPOLICY_SPINDLE_RATIO,
1471 ]
1472
1473
1474 -def _ParseArgs(argv, commands, aliases, env_override):
1475 """Parser for the command line arguments.
1476
1477 This function parses the arguments and returns the function which
1478 must be executed together with its (modified) arguments.
1479
1480 @param argv: the command line
1481 @param commands: dictionary with special contents, see the design
1482 doc for cmdline handling
1483 @param aliases: dictionary with command aliases {'alias': 'target, ...}
1484 @param env_override: list of env variables allowed for default args
1485
1486 """
1487 assert not (env_override - set(commands))
1488
1489 if len(argv) == 0:
1490 binary = "<command>"
1491 else:
1492 binary = argv[0].split("/")[-1]
1493
1494 if len(argv) > 1 and argv[1] == "--version":
1495 ToStdout("%s (ganeti %s) %s", binary, constants.VCS_VERSION,
1496 constants.RELEASE_VERSION)
1497
1498
1499 sys.exit(0)
1500
1501 if len(argv) < 2 or not (argv[1] in commands or
1502 argv[1] in aliases):
1503
1504 sortedcmds = commands.keys()
1505 sortedcmds.sort()
1506
1507 ToStdout("Usage: %s {command} [options...] [argument...]", binary)
1508 ToStdout("%s <command> --help to see details, or man %s", binary, binary)
1509 ToStdout("")
1510
1511
1512 mlen = max([len(" %s" % cmd) for cmd in commands])
1513 mlen = min(60, mlen)
1514
1515
1516 ToStdout("Commands:")
1517 for cmd in sortedcmds:
1518 cmdstr = " %s" % (cmd,)
1519 help_text = commands[cmd][4]
1520 help_lines = textwrap.wrap(help_text, 79 - 3 - mlen)
1521 ToStdout("%-*s - %s", mlen, cmdstr, help_lines.pop(0))
1522 for line in help_lines:
1523 ToStdout("%-*s %s", mlen, "", line)
1524
1525 ToStdout("")
1526
1527 return None, None, None
1528
1529
1530 cmd = argv.pop(1)
1531 if cmd in aliases:
1532 if cmd in commands:
1533 raise errors.ProgrammerError("Alias '%s' overrides an existing"
1534 " command" % cmd)
1535
1536 if aliases[cmd] not in commands:
1537 raise errors.ProgrammerError("Alias '%s' maps to non-existing"
1538 " command '%s'" % (cmd, aliases[cmd]))
1539
1540 cmd = aliases[cmd]
1541
1542 if cmd in env_override:
1543 args_env_name = ("%s_%s" % (binary.replace("-", "_"), cmd)).upper()
1544 env_args = os.environ.get(args_env_name)
1545 if env_args:
1546 argv = utils.InsertAtPos(argv, 1, shlex.split(env_args))
1547
1548 func, args_def, parser_opts, usage, description = commands[cmd]
1549 parser = OptionParser(option_list=parser_opts + COMMON_OPTS,
1550 description=description,
1551 formatter=TitledHelpFormatter(),
1552 usage="%%prog %s %s" % (cmd, usage))
1553 parser.disable_interspersed_args()
1554 options, args = parser.parse_args(args=argv[1:])
1555
1556 if not _CheckArguments(cmd, args_def, args):
1557 return None, None, None
1558
1559 return func, options, args
1560
1563 """Verifies the arguments using the argument definition.
1564
1565 Algorithm:
1566
1567 1. Abort with error if values specified by user but none expected.
1568
1569 1. For each argument in definition
1570
1571 1. Keep running count of minimum number of values (min_count)
1572 1. Keep running count of maximum number of values (max_count)
1573 1. If it has an unlimited number of values
1574
1575 1. Abort with error if it's not the last argument in the definition
1576
1577 1. If last argument has limited number of values
1578
1579 1. Abort with error if number of values doesn't match or is too large
1580
1581 1. Abort with error if user didn't pass enough values (min_count)
1582
1583 """
1584 if args and not args_def:
1585 ToStderr("Error: Command %s expects no arguments", cmd)
1586 return False
1587
1588 min_count = None
1589 max_count = None
1590 check_max = None
1591
1592 last_idx = len(args_def) - 1
1593
1594 for idx, arg in enumerate(args_def):
1595 if min_count is None:
1596 min_count = arg.min
1597 elif arg.min is not None:
1598 min_count += arg.min
1599
1600 if max_count is None:
1601 max_count = arg.max
1602 elif arg.max is not None:
1603 max_count += arg.max
1604
1605 if idx == last_idx:
1606 check_max = (arg.max is not None)
1607
1608 elif arg.max is None:
1609 raise errors.ProgrammerError("Only the last argument can have max=None")
1610
1611 if check_max:
1612
1613 if (min_count is not None and max_count is not None and
1614 min_count == max_count and len(args) != min_count):
1615 ToStderr("Error: Command %s expects %d argument(s)", cmd, min_count)
1616 return False
1617
1618
1619 if max_count is not None and len(args) > max_count:
1620 ToStderr("Error: Command %s expects only %d argument(s)",
1621 cmd, max_count)
1622 return False
1623
1624
1625 if min_count is not None and len(args) < min_count:
1626 ToStderr("Error: Command %s expects at least %d argument(s)",
1627 cmd, min_count)
1628 return False
1629
1630 return True
1631
1634 """Splits the value of a --node option.
1635
1636 """
1637 if value and ":" in value:
1638 return value.split(":", 1)
1639 else:
1640 return (value, None)
1641
1644 """Calculates all the names an OS can be called, according to its variants.
1645
1646 @type os_name: string
1647 @param os_name: base name of the os
1648 @type os_variants: list or None
1649 @param os_variants: list of supported variants
1650 @rtype: list
1651 @return: list of valid names
1652
1653 """
1654 if os_variants:
1655 return ["%s+%s" % (os_name, v) for v in os_variants]
1656 else:
1657 return [os_name]
1658
1661 """Parses the values of "--field"-like options.
1662
1663 @type selected: string or None
1664 @param selected: User-selected options
1665 @type default: list
1666 @param default: Default fields
1667
1668 """
1669 if selected is None:
1670 return default
1671
1672 if selected.startswith("+"):
1673 return default + selected[1:].split(",")
1674
1675 return selected.split(",")
1676
1677
1678 UsesRPC = rpc.RunWithRPC
1679
1680
1681 -def AskUser(text, choices=None):
1682 """Ask the user a question.
1683
1684 @param text: the question to ask
1685
1686 @param choices: list with elements tuples (input_char, return_value,
1687 description); if not given, it will default to: [('y', True,
1688 'Perform the operation'), ('n', False, 'Do no do the operation')];
1689 note that the '?' char is reserved for help
1690
1691 @return: one of the return values from the choices list; if input is
1692 not possible (i.e. not running with a tty, we return the last
1693 entry from the list
1694
1695 """
1696 if choices is None:
1697 choices = [("y", True, "Perform the operation"),
1698 ("n", False, "Do not perform the operation")]
1699 if not choices or not isinstance(choices, list):
1700 raise errors.ProgrammerError("Invalid choices argument to AskUser")
1701 for entry in choices:
1702 if not isinstance(entry, tuple) or len(entry) < 3 or entry[0] == "?":
1703 raise errors.ProgrammerError("Invalid choices element to AskUser")
1704
1705 answer = choices[-1][1]
1706 new_text = []
1707 for line in text.splitlines():
1708 new_text.append(textwrap.fill(line, 70, replace_whitespace=False))
1709 text = "\n".join(new_text)
1710 try:
1711 f = file("/dev/tty", "a+")
1712 except IOError:
1713 return answer
1714 try:
1715 chars = [entry[0] for entry in choices]
1716 chars[-1] = "[%s]" % chars[-1]
1717 chars.append("?")
1718 maps = dict([(entry[0], entry[1]) for entry in choices])
1719 while True:
1720 f.write(text)
1721 f.write("\n")
1722 f.write("/".join(chars))
1723 f.write(": ")
1724 line = f.readline(2).strip().lower()
1725 if line in maps:
1726 answer = maps[line]
1727 break
1728 elif line == "?":
1729 for entry in choices:
1730 f.write(" %s - %s\n" % (entry[0], entry[2]))
1731 f.write("\n")
1732 continue
1733 finally:
1734 f.close()
1735 return answer
1736
1739 """Job was submitted, client should exit.
1740
1741 This exception has one argument, the ID of the job that was
1742 submitted. The handler should print this ID.
1743
1744 This is not an error, just a structured way to exit from clients.
1745
1746 """
1747
1750 """Function to submit an opcode without waiting for the results.
1751
1752 @type ops: list
1753 @param ops: list of opcodes
1754 @type cl: luxi.Client
1755 @param cl: the luxi client to use for communicating with the master;
1756 if None, a new client will be created
1757
1758 """
1759 if cl is None:
1760 cl = GetClient()
1761
1762 job_id = cl.SubmitJob(ops)
1763
1764 return job_id
1765
1768 """Generic job-polling function.
1769
1770 @type job_id: number
1771 @param job_id: Job ID
1772 @type cbs: Instance of L{JobPollCbBase}
1773 @param cbs: Data callbacks
1774 @type report_cbs: Instance of L{JobPollReportCbBase}
1775 @param report_cbs: Reporting callbacks
1776
1777 """
1778 prev_job_info = None
1779 prev_logmsg_serial = None
1780
1781 status = None
1782
1783 while True:
1784 result = cbs.WaitForJobChangeOnce(job_id, ["status"], prev_job_info,
1785 prev_logmsg_serial)
1786 if not result:
1787
1788 raise errors.JobLost("Job with id %s lost" % job_id)
1789
1790 if result == constants.JOB_NOTCHANGED:
1791 report_cbs.ReportNotChanged(job_id, status)
1792
1793
1794 continue
1795
1796
1797 (job_info, log_entries) = result
1798 (status, ) = job_info
1799
1800 if log_entries:
1801 for log_entry in log_entries:
1802 (serial, timestamp, log_type, message) = log_entry
1803 report_cbs.ReportLogMessage(job_id, serial, timestamp,
1804 log_type, message)
1805 prev_logmsg_serial = max(prev_logmsg_serial, serial)
1806
1807
1808 elif status in (constants.JOB_STATUS_SUCCESS,
1809 constants.JOB_STATUS_ERROR,
1810 constants.JOB_STATUS_CANCELING,
1811 constants.JOB_STATUS_CANCELED):
1812 break
1813
1814 prev_job_info = job_info
1815
1816 jobs = cbs.QueryJobs([job_id], ["status", "opstatus", "opresult"])
1817 if not jobs:
1818 raise errors.JobLost("Job with id %s lost" % job_id)
1819
1820 status, opstatus, result = jobs[0]
1821
1822 if status == constants.JOB_STATUS_SUCCESS:
1823 return result
1824
1825 if status in (constants.JOB_STATUS_CANCELING, constants.JOB_STATUS_CANCELED):
1826 raise errors.OpExecError("Job was canceled")
1827
1828 has_ok = False
1829 for idx, (status, msg) in enumerate(zip(opstatus, result)):
1830 if status == constants.OP_STATUS_SUCCESS:
1831 has_ok = True
1832 elif status == constants.OP_STATUS_ERROR:
1833 errors.MaybeRaise(msg)
1834
1835 if has_ok:
1836 raise errors.OpExecError("partial failure (opcode %d): %s" %
1837 (idx, msg))
1838
1839 raise errors.OpExecError(str(msg))
1840
1841
1842 raise errors.OpExecError(result)
1843
1846 """Base class for L{GenericPollJob} callbacks.
1847
1848 """
1850 """Initializes this class.
1851
1852 """
1853
1856 """Waits for changes on a job.
1857
1858 """
1859 raise NotImplementedError()
1860
1862 """Returns the selected fields for the selected job IDs.
1863
1864 @type job_ids: list of numbers
1865 @param job_ids: Job IDs
1866 @type fields: list of strings
1867 @param fields: Fields
1868
1869 """
1870 raise NotImplementedError()
1871
1874 """Base class for L{GenericPollJob} reporting callbacks.
1875
1876 """
1878 """Initializes this class.
1879
1880 """
1881
1883 """Handles a log message.
1884
1885 """
1886 raise NotImplementedError()
1887
1889 """Called for if a job hasn't changed in a while.
1890
1891 @type job_id: number
1892 @param job_id: Job ID
1893 @type status: string or None
1894 @param status: Job status if available
1895
1896 """
1897 raise NotImplementedError()
1898
1907
1910 """Waits for changes on a job.
1911
1912 """
1913 return self.cl.WaitForJobChangeOnce(job_id, fields,
1914 prev_job_info, prev_log_serial)
1915
1917 """Returns the selected fields for the selected job IDs.
1918
1919 """
1920 return self.cl.QueryJobs(job_ids, fields)
1921
1925 """Initializes this class.
1926
1927 """
1928 JobPollReportCbBase.__init__(self)
1929
1930 self.feedback_fn = feedback_fn
1931
1932 assert callable(feedback_fn)
1933
1935 """Handles a log message.
1936
1937 """
1938 self.feedback_fn((timestamp, log_type, log_msg))
1939
1941 """Called if a job hasn't changed in a while.
1942
1943 """
1944
1949 """Initializes this class.
1950
1951 """
1952 JobPollReportCbBase.__init__(self)
1953
1954 self.notified_queued = False
1955 self.notified_waitlock = False
1956
1963
1965 """Called if a job hasn't changed in a while.
1966
1967 """
1968 if status is None:
1969 return
1970
1971 if status == constants.JOB_STATUS_QUEUED and not self.notified_queued:
1972 ToStderr("Job %s is waiting in queue", job_id)
1973 self.notified_queued = True
1974
1975 elif status == constants.JOB_STATUS_WAITING and not self.notified_waitlock:
1976 ToStderr("Job %s is trying to acquire all necessary locks", job_id)
1977 self.notified_waitlock = True
1978
1988
1989
1990 -def PollJob(job_id, cl=None, feedback_fn=None, reporter=None):
1991 """Function to poll for the result of a job.
1992
1993 @type job_id: job identified
1994 @param job_id: the job to poll for results
1995 @type cl: luxi.Client
1996 @param cl: the luxi client to use for communicating with the master;
1997 if None, a new client will be created
1998
1999 """
2000 if cl is None:
2001 cl = GetClient()
2002
2003 if reporter is None:
2004 if feedback_fn:
2005 reporter = FeedbackFnJobPollReportCb(feedback_fn)
2006 else:
2007 reporter = StdioJobPollReportCb()
2008 elif feedback_fn:
2009 raise errors.ProgrammerError("Can't specify reporter and feedback function")
2010
2011 return GenericPollJob(job_id, _LuxiJobPollCb(cl), reporter)
2012
2013
2014 -def SubmitOpCode(op, cl=None, feedback_fn=None, opts=None, reporter=None):
2015 """Legacy function to submit an opcode.
2016
2017 This is just a simple wrapper over the construction of the processor
2018 instance. It should be extended to better handle feedback and
2019 interaction functions.
2020
2021 """
2022 if cl is None:
2023 cl = GetClient()
2024
2025 SetGenericOpcodeOpts([op], opts)
2026
2027 job_id = SendJob([op], cl=cl)
2028
2029 op_results = PollJob(job_id, cl=cl, feedback_fn=feedback_fn,
2030 reporter=reporter)
2031
2032 return op_results[0]
2033
2034
2035 -def SubmitOrSend(op, opts, cl=None, feedback_fn=None):
2036 """Wrapper around SubmitOpCode or SendJob.
2037
2038 This function will decide, based on the 'opts' parameter, whether to
2039 submit and wait for the result of the opcode (and return it), or
2040 whether to just send the job and print its identifier. It is used in
2041 order to simplify the implementation of the '--submit' option.
2042
2043 It will also process the opcodes if we're sending the via SendJob
2044 (otherwise SubmitOpCode does it).
2045
2046 """
2047 if opts and opts.submit_only:
2048 job = [op]
2049 SetGenericOpcodeOpts(job, opts)
2050 job_id = SendJob(job, cl=cl)
2051 raise JobSubmittedException(job_id)
2052 else:
2053 return SubmitOpCode(op, cl=cl, feedback_fn=feedback_fn, opts=opts)
2054
2057 """Processor for generic options.
2058
2059 This function updates the given opcodes based on generic command
2060 line options (like debug, dry-run, etc.).
2061
2062 @param opcode_list: list of opcodes
2063 @param options: command line options or None
2064 @return: None (in-place modification)
2065
2066 """
2067 if not options:
2068 return
2069 for op in opcode_list:
2070 op.debug_level = options.debug
2071 if hasattr(options, "dry_run"):
2072 op.dry_run = options.dry_run
2073 if getattr(options, "priority", None) is not None:
2074 op.priority = _PRIONAME_TO_VALUE[options.priority]
2075
2098
2186
2187
2188 -def GenericMain(commands, override=None, aliases=None,
2189 env_override=frozenset()):
2190 """Generic main function for all the gnt-* commands.
2191
2192 @param commands: a dictionary with a special structure, see the design doc
2193 for command line handling.
2194 @param override: if not None, we expect a dictionary with keys that will
2195 override command line options; this can be used to pass
2196 options from the scripts to generic functions
2197 @param aliases: dictionary with command aliases {'alias': 'target, ...}
2198 @param env_override: list of environment names which are allowed to submit
2199 default args for commands
2200
2201 """
2202
2203 if sys.argv:
2204 binary = os.path.basename(sys.argv[0])
2205 if not binary:
2206 binary = sys.argv[0]
2207
2208 if len(sys.argv) >= 2:
2209 logname = utils.ShellQuoteArgs([binary, sys.argv[1]])
2210 else:
2211 logname = binary
2212
2213 cmdline = utils.ShellQuoteArgs([binary] + sys.argv[1:])
2214 else:
2215 binary = "<unknown program>"
2216 cmdline = "<unknown>"
2217
2218 if aliases is None:
2219 aliases = {}
2220
2221 try:
2222 func, options, args = _ParseArgs(sys.argv, commands, aliases, env_override)
2223 except errors.ParameterError, err:
2224 result, err_msg = FormatError(err)
2225 ToStderr(err_msg)
2226 return 1
2227
2228 if func is None:
2229 return 1
2230
2231 if override is not None:
2232 for key, val in override.iteritems():
2233 setattr(options, key, val)
2234
2235 utils.SetupLogging(constants.LOG_COMMANDS, logname, debug=options.debug,
2236 stderr_logging=True)
2237
2238 logging.info("Command line: %s", cmdline)
2239
2240 try:
2241 result = func(options, args)
2242 except (errors.GenericError, luxi.ProtocolError,
2243 JobSubmittedException), err:
2244 result, err_msg = FormatError(err)
2245 logging.exception("Error during command processing")
2246 ToStderr(err_msg)
2247 except KeyboardInterrupt:
2248 result = constants.EXIT_FAILURE
2249 ToStderr("Aborted. Note that if the operation created any jobs, they"
2250 " might have been submitted and"
2251 " will continue to run in the background.")
2252 except IOError, err:
2253 if err.errno == errno.EPIPE:
2254
2255 sys.exit(constants.EXIT_FAILURE)
2256 else:
2257 raise
2258
2259 return result
2260
2263 """Parses the value of the --net option(s).
2264
2265 """
2266 try:
2267 nic_max = max(int(nidx[0]) + 1 for nidx in optvalue)
2268 except (TypeError, ValueError), err:
2269 raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
2270
2271 nics = [{}] * nic_max
2272 for nidx, ndict in optvalue:
2273 nidx = int(nidx)
2274
2275 if not isinstance(ndict, dict):
2276 raise errors.OpPrereqError("Invalid nic/%d value: expected dict,"
2277 " got %s" % (nidx, ndict))
2278
2279 utils.ForceDictType(ndict, constants.INIC_PARAMS_TYPES)
2280
2281 nics[nidx] = ndict
2282
2283 return nics
2284
2287 """Add an instance to the cluster via either creation or import.
2288
2289 @param mode: constants.INSTANCE_CREATE or constants.INSTANCE_IMPORT
2290 @param opts: the command line options selected by the user
2291 @type args: list
2292 @param args: should contain only one element, the new instance name
2293 @rtype: int
2294 @return: the desired exit code
2295
2296 """
2297 instance = args[0]
2298
2299 (pnode, snode) = SplitNodeOption(opts.node)
2300
2301 hypervisor = None
2302 hvparams = {}
2303 if opts.hypervisor:
2304 hypervisor, hvparams = opts.hypervisor
2305
2306 if opts.nics:
2307 nics = ParseNicOption(opts.nics)
2308 elif opts.no_nics:
2309
2310 nics = []
2311 elif mode == constants.INSTANCE_CREATE:
2312
2313 nics = [{}]
2314 else:
2315
2316 nics = []
2317
2318 if opts.disk_template == constants.DT_DISKLESS:
2319 if opts.disks or opts.sd_size is not None:
2320 raise errors.OpPrereqError("Diskless instance but disk"
2321 " information passed")
2322 disks = []
2323 else:
2324 if (not opts.disks and not opts.sd_size
2325 and mode == constants.INSTANCE_CREATE):
2326 raise errors.OpPrereqError("No disk information specified")
2327 if opts.disks and opts.sd_size is not None:
2328 raise errors.OpPrereqError("Please use either the '--disk' or"
2329 " '-s' option")
2330 if opts.sd_size is not None:
2331 opts.disks = [(0, {constants.IDISK_SIZE: opts.sd_size})]
2332
2333 if opts.disks:
2334 try:
2335 disk_max = max(int(didx[0]) + 1 for didx in opts.disks)
2336 except ValueError, err:
2337 raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
2338 disks = [{}] * disk_max
2339 else:
2340 disks = []
2341 for didx, ddict in opts.disks:
2342 didx = int(didx)
2343 if not isinstance(ddict, dict):
2344 msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
2345 raise errors.OpPrereqError(msg)
2346 elif constants.IDISK_SIZE in ddict:
2347 if constants.IDISK_ADOPT in ddict:
2348 raise errors.OpPrereqError("Only one of 'size' and 'adopt' allowed"
2349 " (disk %d)" % didx)
2350 try:
2351 ddict[constants.IDISK_SIZE] = \
2352 utils.ParseUnit(ddict[constants.IDISK_SIZE])
2353 except ValueError, err:
2354 raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
2355 (didx, err))
2356 elif constants.IDISK_ADOPT in ddict:
2357 if mode == constants.INSTANCE_IMPORT:
2358 raise errors.OpPrereqError("Disk adoption not allowed for instance"
2359 " import")
2360 ddict[constants.IDISK_SIZE] = 0
2361 else:
2362 raise errors.OpPrereqError("Missing size or adoption source for"
2363 " disk %d" % didx)
2364 disks[didx] = ddict
2365
2366 if opts.tags is not None:
2367 tags = opts.tags.split(",")
2368 else:
2369 tags = []
2370
2371 utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_COMPAT)
2372 utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
2373
2374 if mode == constants.INSTANCE_CREATE:
2375 start = opts.start
2376 os_type = opts.os
2377 force_variant = opts.force_variant
2378 src_node = None
2379 src_path = None
2380 no_install = opts.no_install
2381 identify_defaults = False
2382 elif mode == constants.INSTANCE_IMPORT:
2383 start = False
2384 os_type = None
2385 force_variant = False
2386 src_node = opts.src_node
2387 src_path = opts.src_dir
2388 no_install = None
2389 identify_defaults = opts.identify_defaults
2390 else:
2391 raise errors.ProgrammerError("Invalid creation mode %s" % mode)
2392
2393 op = opcodes.OpInstanceCreate(instance_name=instance,
2394 disks=disks,
2395 disk_template=opts.disk_template,
2396 nics=nics,
2397 pnode=pnode, snode=snode,
2398 ip_check=opts.ip_check,
2399 name_check=opts.name_check,
2400 wait_for_sync=opts.wait_for_sync,
2401 file_storage_dir=opts.file_storage_dir,
2402 file_driver=opts.file_driver,
2403 iallocator=opts.iallocator,
2404 hypervisor=hypervisor,
2405 hvparams=hvparams,
2406 beparams=opts.beparams,
2407 osparams=opts.osparams,
2408 mode=mode,
2409 start=start,
2410 os_type=os_type,
2411 force_variant=force_variant,
2412 src_node=src_node,
2413 src_path=src_path,
2414 tags=tags,
2415 no_install=no_install,
2416 identify_defaults=identify_defaults,
2417 ignore_ipolicy=opts.ignore_ipolicy)
2418
2419 SubmitOrSend(op, opts)
2420 return 0
2421
2424 """Helper class for L{RunWhileClusterStopped} to simplify state management
2425
2426 """
2427 - def __init__(self, feedback_fn, cluster_name, master_node, online_nodes):
2428 """Initializes this class.
2429
2430 @type feedback_fn: callable
2431 @param feedback_fn: Feedback function
2432 @type cluster_name: string
2433 @param cluster_name: Cluster name
2434 @type master_node: string
2435 @param master_node Master node name
2436 @type online_nodes: list
2437 @param online_nodes: List of names of online nodes
2438
2439 """
2440 self.feedback_fn = feedback_fn
2441 self.cluster_name = cluster_name
2442 self.master_node = master_node
2443 self.online_nodes = online_nodes
2444
2445 self.ssh = ssh.SshRunner(self.cluster_name)
2446
2447 self.nonmaster_nodes = [name for name in online_nodes
2448 if name != master_node]
2449
2450 assert self.master_node not in self.nonmaster_nodes
2451
2452 - def _RunCmd(self, node_name, cmd):
2453 """Runs a command on the local or a remote machine.
2454
2455 @type node_name: string
2456 @param node_name: Machine name
2457 @type cmd: list
2458 @param cmd: Command
2459
2460 """
2461 if node_name is None or node_name == self.master_node:
2462
2463 result = utils.RunCmd(cmd)
2464 else:
2465 result = self.ssh.Run(node_name, "root", utils.ShellQuoteArgs(cmd))
2466
2467 if result.failed:
2468 errmsg = ["Failed to run command %s" % result.cmd]
2469 if node_name:
2470 errmsg.append("on node %s" % node_name)
2471 errmsg.append(": exitcode %s and error %s" %
2472 (result.exit_code, result.output))
2473 raise errors.OpExecError(" ".join(errmsg))
2474
2475 - def Call(self, fn, *args):
2476 """Call function while all daemons are stopped.
2477
2478 @type fn: callable
2479 @param fn: Function to be called
2480
2481 """
2482
2483 self.feedback_fn("Blocking watcher")
2484 watcher_block = utils.FileLock.Open(constants.WATCHER_LOCK_FILE)
2485 try:
2486
2487
2488 watcher_block.Exclusive(blocking=True)
2489
2490
2491
2492 self.feedback_fn("Stopping master daemons")
2493 self._RunCmd(None, [constants.DAEMON_UTIL, "stop-master"])
2494 try:
2495
2496 for node_name in self.online_nodes:
2497 self.feedback_fn("Stopping daemons on %s" % node_name)
2498 self._RunCmd(node_name, [constants.DAEMON_UTIL, "stop-all"])
2499
2500
2501 try:
2502 return fn(self, *args)
2503 except Exception, err:
2504 _, errmsg = FormatError(err)
2505 logging.exception("Caught exception")
2506 self.feedback_fn(errmsg)
2507 raise
2508 finally:
2509
2510 for node_name in self.nonmaster_nodes + [self.master_node]:
2511 self.feedback_fn("Starting daemons on %s" % node_name)
2512 self._RunCmd(node_name, [constants.DAEMON_UTIL, "start-all"])
2513 finally:
2514
2515 watcher_block.Close()
2516
2519 """Calls a function while all cluster daemons are stopped.
2520
2521 @type feedback_fn: callable
2522 @param feedback_fn: Feedback function
2523 @type fn: callable
2524 @param fn: Function to be called when daemons are stopped
2525
2526 """
2527 feedback_fn("Gathering cluster information")
2528
2529
2530 cl = GetClient()
2531
2532 (cluster_name, master_node) = \
2533 cl.QueryConfigValues(["cluster_name", "master_node"])
2534
2535 online_nodes = GetOnlineNodes([], cl=cl)
2536
2537
2538 del cl
2539
2540 assert master_node in online_nodes
2541
2542 return _RunWhileClusterStoppedHelper(feedback_fn, cluster_name, master_node,
2543 online_nodes).Call(fn, *args)
2544
2545
2546 -def GenerateTable(headers, fields, separator, data,
2547 numfields=None, unitfields=None,
2548 units=None):
2549 """Prints a table with headers and different fields.
2550
2551 @type headers: dict
2552 @param headers: dictionary mapping field names to headers for
2553 the table
2554 @type fields: list
2555 @param fields: the field names corresponding to each row in
2556 the data field
2557 @param separator: the separator to be used; if this is None,
2558 the default 'smart' algorithm is used which computes optimal
2559 field width, otherwise just the separator is used between
2560 each field
2561 @type data: list
2562 @param data: a list of lists, each sublist being one row to be output
2563 @type numfields: list
2564 @param numfields: a list with the fields that hold numeric
2565 values and thus should be right-aligned
2566 @type unitfields: list
2567 @param unitfields: a list with the fields that hold numeric
2568 values that should be formatted with the units field
2569 @type units: string or None
2570 @param units: the units we should use for formatting, or None for
2571 automatic choice (human-readable for non-separator usage, otherwise
2572 megabytes); this is a one-letter string
2573
2574 """
2575 if units is None:
2576 if separator:
2577 units = "m"
2578 else:
2579 units = "h"
2580
2581 if numfields is None:
2582 numfields = []
2583 if unitfields is None:
2584 unitfields = []
2585
2586 numfields = utils.FieldSet(*numfields)
2587 unitfields = utils.FieldSet(*unitfields)
2588
2589 format_fields = []
2590 for field in fields:
2591 if headers and field not in headers:
2592
2593
2594
2595 headers[field] = field
2596 if separator is not None:
2597 format_fields.append("%s")
2598 elif numfields.Matches(field):
2599 format_fields.append("%*s")
2600 else:
2601 format_fields.append("%-*s")
2602
2603 if separator is None:
2604 mlens = [0 for name in fields]
2605 format_str = " ".join(format_fields)
2606 else:
2607 format_str = separator.replace("%", "%%").join(format_fields)
2608
2609 for row in data:
2610 if row is None:
2611 continue
2612 for idx, val in enumerate(row):
2613 if unitfields.Matches(fields[idx]):
2614 try:
2615 val = int(val)
2616 except (TypeError, ValueError):
2617 pass
2618 else:
2619 val = row[idx] = utils.FormatUnit(val, units)
2620 val = row[idx] = str(val)
2621 if separator is None:
2622 mlens[idx] = max(mlens[idx], len(val))
2623
2624 result = []
2625 if headers:
2626 args = []
2627 for idx, name in enumerate(fields):
2628 hdr = headers[name]
2629 if separator is None:
2630 mlens[idx] = max(mlens[idx], len(hdr))
2631 args.append(mlens[idx])
2632 args.append(hdr)
2633 result.append(format_str % tuple(args))
2634
2635 if separator is None:
2636 assert len(mlens) == len(fields)
2637
2638 if fields and not numfields.Matches(fields[-1]):
2639 mlens[-1] = 0
2640
2641 for line in data:
2642 args = []
2643 if line is None:
2644 line = ["-" for _ in fields]
2645 for idx in range(len(fields)):
2646 if separator is None:
2647 args.append(mlens[idx])
2648 args.append(line[idx])
2649 result.append(format_str % tuple(args))
2650
2651 return result
2652
2661
2662
2663
2664 _DEFAULT_FORMAT_QUERY = {
2665 constants.QFT_TEXT: (str, False),
2666 constants.QFT_BOOL: (_FormatBool, False),
2667 constants.QFT_NUMBER: (str, True),
2668 constants.QFT_TIMESTAMP: (utils.FormatTime, False),
2669 constants.QFT_OTHER: (str, False),
2670 constants.QFT_UNKNOWN: (str, False),
2671 }
2703
2740
2761
2797
2798 columns = []
2799 for fdef in result.fields:
2800 assert fdef.title and fdef.name
2801 (fn, align_right) = _GetColumnFormatter(fdef, format_override, unit)
2802 columns.append(TableColumn(fdef.title,
2803 _QueryColumnFormatter(fn, _RecordStatus,
2804 verbose),
2805 align_right))
2806
2807 table = FormatTable(result.data, columns, header, separator)
2808
2809
2810 assert len(stats) == len(constants.RS_ALL)
2811 assert compat.all(count >= 0 for count in stats.values())
2812
2813
2814
2815 if (stats[constants.RS_UNKNOWN] or
2816 (not result.data and _GetUnknownFields(result.fields))):
2817 status = QR_UNKNOWN
2818 elif compat.any(count > 0 for key, count in stats.items()
2819 if key != constants.RS_NORMAL):
2820 status = QR_INCOMPLETE
2821 else:
2822 status = QR_NORMAL
2823
2824 return (status, table)
2825
2828 """Returns list of unknown fields included in C{fdefs}.
2829
2830 @type fdefs: list of L{objects.QueryFieldDefinition}
2831
2832 """
2833 return [fdef for fdef in fdefs
2834 if fdef.kind == constants.QFT_UNKNOWN]
2835
2838 """Prints a warning to stderr if a query included unknown fields.
2839
2840 @type fdefs: list of L{objects.QueryFieldDefinition}
2841
2842 """
2843 unknown = _GetUnknownFields(fdefs)
2844 if unknown:
2845 ToStderr("Warning: Queried for unknown fields %s",
2846 utils.CommaJoin(fdef.name for fdef in unknown))
2847 return True
2848
2849 return False
2850
2851
2852 -def GenericList(resource, fields, names, unit, separator, header, cl=None,
2853 format_override=None, verbose=False, force_filter=False,
2854 namefield=None, qfilter=None):
2855 """Generic implementation for listing all items of a resource.
2856
2857 @param resource: One of L{constants.QR_VIA_LUXI}
2858 @type fields: list of strings
2859 @param fields: List of fields to query for
2860 @type names: list of strings
2861 @param names: Names of items to query for
2862 @type unit: string or None
2863 @param unit: Unit used for formatting fields of type L{constants.QFT_UNIT} or
2864 None for automatic choice (human-readable for non-separator usage,
2865 otherwise megabytes); this is a one-letter string
2866 @type separator: string or None
2867 @param separator: String used to separate fields
2868 @type header: bool
2869 @param header: Whether to show header row
2870 @type force_filter: bool
2871 @param force_filter: Whether to always treat names as filter
2872 @type format_override: dict
2873 @param format_override: Dictionary for overriding field formatting functions,
2874 indexed by field name, contents like L{_DEFAULT_FORMAT_QUERY}
2875 @type verbose: boolean
2876 @param verbose: whether to use verbose field descriptions or not
2877 @type namefield: string
2878 @param namefield: Name of field to use for simple filters (see
2879 L{qlang.MakeFilter} for details)
2880 @type qfilter: list or None
2881 @param qfilter: Query filter (in addition to names)
2882
2883 """
2884 if not names:
2885 names = None
2886
2887 namefilter = qlang.MakeFilter(names, force_filter, namefield=namefield)
2888
2889 if qfilter is None:
2890 qfilter = namefilter
2891 elif namefilter is not None:
2892 qfilter = [qlang.OP_AND, namefilter, qfilter]
2893
2894 if cl is None:
2895 cl = GetClient()
2896
2897 response = cl.Query(resource, fields, qfilter)
2898
2899 found_unknown = _WarnUnknownFields(response.fields)
2900
2901 (status, data) = FormatQueryResult(response, unit=unit, separator=separator,
2902 header=header,
2903 format_override=format_override,
2904 verbose=verbose)
2905
2906 for line in data:
2907 ToStdout(line)
2908
2909 assert ((found_unknown and status == QR_UNKNOWN) or
2910 (not found_unknown and status != QR_UNKNOWN))
2911
2912 if status == QR_UNKNOWN:
2913 return constants.EXIT_UNKNOWN_FIELD
2914
2915
2916 return constants.EXIT_SUCCESS
2917
2920 """Generic implementation for listing fields for a resource.
2921
2922 @param resource: One of L{constants.QR_VIA_LUXI}
2923 @type fields: list of strings
2924 @param fields: List of fields to query for
2925 @type separator: string or None
2926 @param separator: String used to separate fields
2927 @type header: bool
2928 @param header: Whether to show header row
2929
2930 """
2931 if cl is None:
2932 cl = GetClient()
2933
2934 if not fields:
2935 fields = None
2936
2937 response = cl.QueryFields(resource, fields)
2938
2939 found_unknown = _WarnUnknownFields(response.fields)
2940
2941 columns = [
2942 TableColumn("Name", str, False),
2943 TableColumn("Title", str, False),
2944 TableColumn("Description", str, False),
2945 ]
2946
2947 rows = [[fdef.name, fdef.title, fdef.doc] for fdef in response.fields]
2948
2949 for line in FormatTable(rows, columns, header, separator):
2950 ToStdout(line)
2951
2952 if found_unknown:
2953 return constants.EXIT_UNKNOWN_FIELD
2954
2955 return constants.EXIT_SUCCESS
2956
2959 """Describes a column for L{FormatTable}.
2960
2961 """
2962 - def __init__(self, title, fn, align_right):
2963 """Initializes this class.
2964
2965 @type title: string
2966 @param title: Column title
2967 @type fn: callable
2968 @param fn: Formatting function
2969 @type align_right: bool
2970 @param align_right: Whether to align values on the right-hand side
2971
2972 """
2973 self.title = title
2974 self.format = fn
2975 self.align_right = align_right
2976
2988
3037
3054
3057 """Parse a time specification.
3058
3059 The following suffixed will be recognized:
3060
3061 - s: seconds
3062 - m: minutes
3063 - h: hours
3064 - d: day
3065 - w: weeks
3066
3067 Without any suffix, the value will be taken to be in seconds.
3068
3069 """
3070 value = str(value)
3071 if not value:
3072 raise errors.OpPrereqError("Empty time specification passed")
3073 suffix_map = {
3074 "s": 1,
3075 "m": 60,
3076 "h": 3600,
3077 "d": 86400,
3078 "w": 604800,
3079 }
3080 if value[-1] not in suffix_map:
3081 try:
3082 value = int(value)
3083 except (TypeError, ValueError):
3084 raise errors.OpPrereqError("Invalid time specification '%s'" % value)
3085 else:
3086 multiplier = suffix_map[value[-1]]
3087 value = value[:-1]
3088 if not value:
3089 raise errors.OpPrereqError("Invalid time specification (only"
3090 " suffix passed)")
3091 try:
3092 value = int(value) * multiplier
3093 except (TypeError, ValueError):
3094 raise errors.OpPrereqError("Invalid time specification '%s'" % value)
3095 return value
3096
3097
3098 -def GetOnlineNodes(nodes, cl=None, nowarn=False, secondary_ips=False,
3099 filter_master=False, nodegroup=None):
3100 """Returns the names of online nodes.
3101
3102 This function will also log a warning on stderr with the names of
3103 the online nodes.
3104
3105 @param nodes: if not empty, use only this subset of nodes (minus the
3106 offline ones)
3107 @param cl: if not None, luxi client to use
3108 @type nowarn: boolean
3109 @param nowarn: by default, this function will output a note with the
3110 offline nodes that are skipped; if this parameter is True the
3111 note is not displayed
3112 @type secondary_ips: boolean
3113 @param secondary_ips: if True, return the secondary IPs instead of the
3114 names, useful for doing network traffic over the replication interface
3115 (if any)
3116 @type filter_master: boolean
3117 @param filter_master: if True, do not return the master node in the list
3118 (useful in coordination with secondary_ips where we cannot check our
3119 node name against the list)
3120 @type nodegroup: string
3121 @param nodegroup: If set, only return nodes in this node group
3122
3123 """
3124 if cl is None:
3125 cl = GetClient()
3126
3127 qfilter = []
3128
3129 if nodes:
3130 qfilter.append(qlang.MakeSimpleFilter("name", nodes))
3131
3132 if nodegroup is not None:
3133 qfilter.append([qlang.OP_OR, [qlang.OP_EQUAL, "group", nodegroup],
3134 [qlang.OP_EQUAL, "group.uuid", nodegroup]])
3135
3136 if filter_master:
3137 qfilter.append([qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
3138
3139 if qfilter:
3140 if len(qfilter) > 1:
3141 final_filter = [qlang.OP_AND] + qfilter
3142 else:
3143 assert len(qfilter) == 1
3144 final_filter = qfilter[0]
3145 else:
3146 final_filter = None
3147
3148 result = cl.Query(constants.QR_NODE, ["name", "offline", "sip"], final_filter)
3149
3150 def _IsOffline(row):
3151 (_, (_, offline), _) = row
3152 return offline
3153
3154 def _GetName(row):
3155 ((_, name), _, _) = row
3156 return name
3157
3158 def _GetSip(row):
3159 (_, _, (_, sip)) = row
3160 return sip
3161
3162 (offline, online) = compat.partition(result.data, _IsOffline)
3163
3164 if offline and not nowarn:
3165 ToStderr("Note: skipping offline node(s): %s" %
3166 utils.CommaJoin(map(_GetName, offline)))
3167
3168 if secondary_ips:
3169 fn = _GetSip
3170 else:
3171 fn = _GetName
3172
3173 return map(fn, online)
3174
3177 """Write a message to a stream, bypassing the logging system
3178
3179 @type stream: file object
3180 @param stream: the file to which we should write
3181 @type txt: str
3182 @param txt: the message
3183
3184 """
3185 try:
3186 if args:
3187 args = tuple(args)
3188 stream.write(txt % args)
3189 else:
3190 stream.write(txt)
3191 stream.write("\n")
3192 stream.flush()
3193 except IOError, err:
3194 if err.errno == errno.EPIPE:
3195
3196 sys.exit(constants.EXIT_FAILURE)
3197 else:
3198 raise
3199
3202 """Write a message to stdout only, bypassing the logging system
3203
3204 This is just a wrapper over _ToStream.
3205
3206 @type txt: str
3207 @param txt: the message
3208
3209 """
3210 _ToStream(sys.stdout, txt, *args)
3211
3214 """Write a message to stderr only, bypassing the logging system
3215
3216 This is just a wrapper over _ToStream.
3217
3218 @type txt: str
3219 @param txt: the message
3220
3221 """
3222 _ToStream(sys.stderr, txt, *args)
3223
3226 """Class which manages the submission and execution of multiple jobs.
3227
3228 Note that instances of this class should not be reused between
3229 GetResults() calls.
3230
3231 """
3232 - def __init__(self, cl=None, verbose=True, opts=None, feedback_fn=None):
3233 self.queue = []
3234 if cl is None:
3235 cl = GetClient()
3236 self.cl = cl
3237 self.verbose = verbose
3238 self.jobs = []
3239 self.opts = opts
3240 self.feedback_fn = feedback_fn
3241 self._counter = itertools.count()
3242
3243 @staticmethod
3245 """Helper function for formatting name.
3246
3247 """
3248 if name:
3249 return fmt % name
3250
3251 return ""
3252
3254 """Record a job for later submit.
3255
3256 @type name: string
3257 @param name: a description of the job, will be used in WaitJobSet
3258
3259 """
3260 SetGenericOpcodeOpts(ops, self.opts)
3261 self.queue.append((self._counter.next(), name, ops))
3262
3263 - def AddJobId(self, name, status, job_id):
3264 """Adds a job ID to the internal queue.
3265
3266 """
3267 self.jobs.append((self._counter.next(), status, job_id, name))
3268
3270 """Submit all pending jobs.
3271
3272 """
3273 if each:
3274 results = []
3275 for (_, _, ops) in self.queue:
3276
3277
3278 results.append([True, self.cl.SubmitJob(ops)[0]])
3279 else:
3280 results = self.cl.SubmitManyJobs([ops for (_, _, ops) in self.queue])
3281 for ((status, data), (idx, name, _)) in zip(results, self.queue):
3282 self.jobs.append((idx, status, data, name))
3283
3285 """Choose a non-waiting/queued job to poll next.
3286
3287 """
3288 assert self.jobs, "_ChooseJob called with empty job list"
3289
3290 result = self.cl.QueryJobs([i[2] for i in self.jobs[:_CHOOSE_BATCH]],
3291 ["status"])
3292 assert result
3293
3294 for job_data, status in zip(self.jobs, result):
3295 if (isinstance(status, list) and status and
3296 status[0] in (constants.JOB_STATUS_QUEUED,
3297 constants.JOB_STATUS_WAITING,
3298 constants.JOB_STATUS_CANCELING)):
3299
3300 continue
3301
3302 self.jobs.remove(job_data)
3303 return job_data
3304
3305
3306 return self.jobs.pop(0)
3307
3309 """Wait for and return the results of all jobs.
3310
3311 @rtype: list
3312 @return: list of tuples (success, job results), in the same order
3313 as the submitted jobs; if a job has failed, instead of the result
3314 there will be the error message
3315
3316 """
3317 if not self.jobs:
3318 self.SubmitPending()
3319 results = []
3320 if self.verbose:
3321 ok_jobs = [row[2] for row in self.jobs if row[1]]
3322 if ok_jobs:
3323 ToStdout("Submitted jobs %s", utils.CommaJoin(ok_jobs))
3324
3325
3326 self.jobs, failures = compat.partition(self.jobs, lambda x: x[1])
3327 for idx, _, jid, name in failures:
3328 ToStderr("Failed to submit job%s: %s", self._IfName(name, " for %s"), jid)
3329 results.append((idx, False, jid))
3330
3331 while self.jobs:
3332 (idx, _, jid, name) = self._ChooseJob()
3333 ToStdout("Waiting for job %s%s ...", jid, self._IfName(name, " for %s"))
3334 try:
3335 job_result = PollJob(jid, cl=self.cl, feedback_fn=self.feedback_fn)
3336 success = True
3337 except errors.JobLost, err:
3338 _, job_result = FormatError(err)
3339 ToStderr("Job %s%s has been archived, cannot check its result",
3340 jid, self._IfName(name, " for %s"))
3341 success = False
3342 except (errors.GenericError, luxi.ProtocolError), err:
3343 _, job_result = FormatError(err)
3344 success = False
3345
3346 ToStderr("Job %s%s has failed: %s",
3347 jid, self._IfName(name, " for %s"), job_result)
3348
3349 results.append((idx, success, job_result))
3350
3351
3352 results.sort()
3353 results = [i[1:] for i in results]
3354
3355 return results
3356
3358 """Wait for job results or only print the job IDs.
3359
3360 @type wait: boolean
3361 @param wait: whether to wait or not
3362
3363 """
3364 if wait:
3365 return self.GetResults()
3366 else:
3367 if not self.jobs:
3368 self.SubmitPending()
3369 for _, status, result, name in self.jobs:
3370 if status:
3371 ToStdout("%s: %s", result, name)
3372 else:
3373 ToStderr("Failure for %s: %s", name, result)
3374 return [row[1:3] for row in self.jobs]
3375
3402
3405 """Ask the user to confirm an operation on a list of list_type.
3406
3407 This function is used to request confirmation for doing an operation
3408 on a given list of list_type.
3409
3410 @type names: list
3411 @param names: the list of names that we display when
3412 we ask for confirmation
3413 @type list_type: str
3414 @param list_type: Human readable name for elements in the list (e.g. nodes)
3415 @type text: str
3416 @param text: the operation that the user should confirm
3417 @rtype: boolean
3418 @return: True or False depending on user's confirmation.
3419
3420 """
3421 count = len(names)
3422 msg = ("The %s will operate on %d %s.\n%s"
3423 "Do you want to continue?" % (text, count, list_type, extra))
3424 affected = (("\nAffected %s:\n" % list_type) +
3425 "\n".join([" %s" % name for name in names]))
3426
3427 choices = [("y", True, "Yes, execute the %s" % text),
3428 ("n", False, "No, abort the %s" % text)]
3429
3430 if count > 20:
3431 choices.insert(1, ("v", "v", "View the list of affected %s" % list_type))
3432 question = msg
3433 else:
3434 question = msg + affected
3435
3436 choice = AskUser(question, choices)
3437 if choice == "v":
3438 choices.pop(1)
3439 choice = AskUser(msg + affected, choices)
3440 return choice
3441
3444 """Parses and returns an array of potential values with units.
3445
3446 """
3447 parsed = {}
3448 for k, v in elements.items():
3449 if v == constants.VALUE_DEFAULT:
3450 parsed[k] = v
3451 else:
3452 parsed[k] = utils.ParseUnit(v)
3453 return parsed
3454
3455
3456 -def CreateIPolicyFromOpts(ispecs_mem_size=None,
3457 ispecs_cpu_count=None,
3458 ispecs_disk_count=None,
3459 ispecs_disk_size=None,
3460 ispecs_nic_count=None,
3461 ipolicy_disk_templates=None,
3462 ipolicy_vcpu_ratio=None,
3463 ipolicy_spindle_ratio=None,
3464 group_ipolicy=False,
3465 allowed_values=None,
3466 fill_all=False):
3467 """Creation of instance policy based on command line options.
3468
3469 @param fill_all: whether for cluster policies we should ensure that
3470 all values are filled
3471
3472
3473 """
3474 try:
3475 if ispecs_mem_size:
3476 ispecs_mem_size = _MaybeParseUnit(ispecs_mem_size)
3477 if ispecs_disk_size:
3478 ispecs_disk_size = _MaybeParseUnit(ispecs_disk_size)
3479 except (TypeError, ValueError, errors.UnitParseError), err:
3480 raise errors.OpPrereqError("Invalid disk (%s) or memory (%s) size"
3481 " in policy: %s" %
3482 (ispecs_disk_size, ispecs_mem_size, err),
3483 errors.ECODE_INVAL)
3484
3485
3486 ipolicy_transposed = {
3487 constants.ISPEC_MEM_SIZE: ispecs_mem_size,
3488 constants.ISPEC_CPU_COUNT: ispecs_cpu_count,
3489 constants.ISPEC_DISK_COUNT: ispecs_disk_count,
3490 constants.ISPEC_DISK_SIZE: ispecs_disk_size,
3491 constants.ISPEC_NIC_COUNT: ispecs_nic_count,
3492 }
3493
3494
3495 if group_ipolicy:
3496 forced_type = TISPECS_GROUP_TYPES
3497 else:
3498 forced_type = TISPECS_CLUSTER_TYPES
3499
3500 for specs in ipolicy_transposed.values():
3501 utils.ForceDictType(specs, forced_type, allowed_values=allowed_values)
3502
3503
3504 ipolicy_out = objects.MakeEmptyIPolicy()
3505 for name, specs in ipolicy_transposed.iteritems():
3506 assert name in constants.ISPECS_PARAMETERS
3507 for key, val in specs.items():
3508 ipolicy_out[key][name] = val
3509
3510
3511 if not group_ipolicy and fill_all:
3512 if ipolicy_disk_templates is None:
3513 ipolicy_disk_templates = constants.DISK_TEMPLATES
3514 if ipolicy_vcpu_ratio is None:
3515 ipolicy_vcpu_ratio = \
3516 constants.IPOLICY_DEFAULTS[constants.IPOLICY_VCPU_RATIO]
3517 if ipolicy_spindle_ratio is None:
3518 ipolicy_spindle_ratio = \
3519 constants.IPOLICY_DEFAULTS[constants.IPOLICY_SPINDLE_RATIO]
3520 if ipolicy_disk_templates is not None:
3521 ipolicy_out[constants.IPOLICY_DTS] = list(ipolicy_disk_templates)
3522 if ipolicy_vcpu_ratio is not None:
3523 ipolicy_out[constants.IPOLICY_VCPU_RATIO] = ipolicy_vcpu_ratio
3524 if ipolicy_spindle_ratio is not None:
3525 ipolicy_out[constants.IPOLICY_SPINDLE_RATIO] = ipolicy_spindle_ratio
3526
3527 assert not (frozenset(ipolicy_out.keys()) - constants.IPOLICY_ALL_KEYS)
3528
3529 return ipolicy_out
3530