1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Instance related commands"""
22
23
24
25
26
27
28 import itertools
29 import simplejson
30 import logging
31 from cStringIO import StringIO
32
33 from ganeti.cli import *
34 from ganeti import opcodes
35 from ganeti import constants
36 from ganeti import compat
37 from ganeti import utils
38 from ganeti import errors
39 from ganeti import netutils
40 from ganeti import ssh
41 from ganeti import objects
42
43
44 _EXPAND_CLUSTER = "cluster"
45 _EXPAND_NODES_BOTH = "nodes"
46 _EXPAND_NODES_PRI = "nodes-pri"
47 _EXPAND_NODES_SEC = "nodes-sec"
48 _EXPAND_NODES_BOTH_BY_TAGS = "nodes-by-tags"
49 _EXPAND_NODES_PRI_BY_TAGS = "nodes-pri-by-tags"
50 _EXPAND_NODES_SEC_BY_TAGS = "nodes-sec-by-tags"
51 _EXPAND_INSTANCES = "instances"
52 _EXPAND_INSTANCES_BY_TAGS = "instances-by-tags"
53
54 _EXPAND_NODES_TAGS_MODES = frozenset([
55 _EXPAND_NODES_BOTH_BY_TAGS,
56 _EXPAND_NODES_PRI_BY_TAGS,
57 _EXPAND_NODES_SEC_BY_TAGS,
58 ])
59
60
61
62 _LIST_DEF_FIELDS = [
63 "name", "hypervisor", "os", "pnode", "status", "oper_ram",
64 ]
65
66
68 """Expand the given names using the passed mode.
69
70 For _EXPAND_CLUSTER, all instances will be returned. For
71 _EXPAND_NODES_PRI/SEC, all instances having those nodes as
72 primary/secondary will be returned. For _EXPAND_NODES_BOTH, all
73 instances having those nodes as either primary or secondary will be
74 returned. For _EXPAND_INSTANCES, the given instances will be
75 returned.
76
77 @param mode: one of L{_EXPAND_CLUSTER}, L{_EXPAND_NODES_BOTH},
78 L{_EXPAND_NODES_PRI}, L{_EXPAND_NODES_SEC} or
79 L{_EXPAND_INSTANCES}
80 @param names: a list of names; for cluster, it must be empty,
81 and for node and instance it must be a list of valid item
82 names (short names are valid as usual, e.g. node1 instead of
83 node1.example.com)
84 @rtype: list
85 @return: the list of names after the expansion
86 @raise errors.ProgrammerError: for unknown selection type
87 @raise errors.OpPrereqError: for invalid input parameters
88
89 """
90
91
92 if client is None:
93 client = GetClient()
94 if mode == _EXPAND_CLUSTER:
95 if names:
96 raise errors.OpPrereqError("Cluster filter mode takes no arguments",
97 errors.ECODE_INVAL)
98 idata = client.QueryInstances([], ["name"], False)
99 inames = [row[0] for row in idata]
100
101 elif (mode in _EXPAND_NODES_TAGS_MODES or
102 mode in (_EXPAND_NODES_BOTH, _EXPAND_NODES_PRI, _EXPAND_NODES_SEC)):
103 if mode in _EXPAND_NODES_TAGS_MODES:
104 if not names:
105 raise errors.OpPrereqError("No node tags passed", errors.ECODE_INVAL)
106 ndata = client.QueryNodes([], ["name", "pinst_list",
107 "sinst_list", "tags"], False)
108 ndata = [row for row in ndata if set(row[3]).intersection(names)]
109 else:
110 if not names:
111 raise errors.OpPrereqError("No node names passed", errors.ECODE_INVAL)
112 ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"],
113 False)
114
115 ipri = [row[1] for row in ndata]
116 pri_names = list(itertools.chain(*ipri))
117 isec = [row[2] for row in ndata]
118 sec_names = list(itertools.chain(*isec))
119 if mode in (_EXPAND_NODES_BOTH, _EXPAND_NODES_BOTH_BY_TAGS):
120 inames = pri_names + sec_names
121 elif mode in (_EXPAND_NODES_PRI, _EXPAND_NODES_PRI_BY_TAGS):
122 inames = pri_names
123 elif mode in (_EXPAND_NODES_SEC, _EXPAND_NODES_SEC_BY_TAGS):
124 inames = sec_names
125 else:
126 raise errors.ProgrammerError("Unhandled shutdown type")
127 elif mode == _EXPAND_INSTANCES:
128 if not names:
129 raise errors.OpPrereqError("No instance names passed",
130 errors.ECODE_INVAL)
131 idata = client.QueryInstances(names, ["name"], False)
132 inames = [row[0] for row in idata]
133 elif mode == _EXPAND_INSTANCES_BY_TAGS:
134 if not names:
135 raise errors.OpPrereqError("No instance tags passed",
136 errors.ECODE_INVAL)
137 idata = client.QueryInstances([], ["name", "tags"], False)
138 inames = [row[0] for row in idata if set(row[1]).intersection(names)]
139 else:
140 raise errors.OpPrereqError("Unknown mode '%s'" % mode, errors.ECODE_INVAL)
141
142 return inames
143
144
146 """Check for and ensure the given instance names exist.
147
148 This function will raise an OpPrereqError in case they don't
149 exist. Otherwise it will exit cleanly.
150
151 @type client: L{ganeti.luxi.Client}
152 @param client: the client to use for the query
153 @type names: list
154 @param names: the list of instance names to query
155 @raise errors.OpPrereqError: in case any instance is missing
156
157 """
158
159
160 result = client.QueryInstances(names, ["name"], False)
161 for orig_name, row in zip(names, result):
162 if row[0] is None:
163 raise errors.OpPrereqError("Instance '%s' does not exist" % orig_name,
164 errors.ECODE_NOENT)
165
166
168 """Generic multi-instance operations.
169
170 The will return a wrapper that processes the options and arguments
171 given, and uses the passed function to build the opcode needed for
172 the specific operation. Thus all the generic loop/confirmation code
173 is abstracted into this function.
174
175 """
176 def realfn(opts, args):
177 if opts.multi_mode is None:
178 opts.multi_mode = _EXPAND_INSTANCES
179 cl = GetClient()
180 inames = _ExpandMultiNames(opts.multi_mode, args, client=cl)
181 if not inames:
182 if opts.multi_mode == _EXPAND_CLUSTER:
183 ToStdout("Cluster is empty, no instances to shutdown")
184 return 0
185 raise errors.OpPrereqError("Selection filter does not match"
186 " any instances", errors.ECODE_INVAL)
187 multi_on = opts.multi_mode != _EXPAND_INSTANCES or len(inames) > 1
188 if not (opts.force_multi or not multi_on
189 or ConfirmOperation(inames, "instances", operation)):
190 return 1
191 jex = JobExecutor(verbose=multi_on, cl=cl, opts=opts)
192 for name in inames:
193 op = fn(name, opts)
194 jex.QueueJob(name, op)
195 results = jex.WaitOrShow(not opts.submit_only)
196 rcode = compat.all(row[0] for row in results)
197 return int(not rcode)
198 return realfn
199
200
202 """List instances and their properties.
203
204 @param opts: the command line options selected by the user
205 @type args: list
206 @param args: should be an empty list
207 @rtype: int
208 @return: the desired exit code
209
210 """
211 selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
212
213 fmtoverride = dict.fromkeys(["tags", "disk.sizes", "nic.macs", "nic.ips",
214 "nic.modes", "nic.links", "nic.bridges",
215 "snodes", "snodes.group", "snodes.group.uuid"],
216 (lambda value: ",".join(str(item)
217 for item in value),
218 False))
219
220 return GenericList(constants.QR_INSTANCE, selected_fields, args, opts.units,
221 opts.separator, not opts.no_headers,
222 format_override=fmtoverride, verbose=opts.verbose,
223 force_filter=opts.force_filter)
224
225
227 """List instance fields.
228
229 @param opts: the command line options selected by the user
230 @type args: list
231 @param args: fields to list, or empty for all
232 @rtype: int
233 @return: the desired exit code
234
235 """
236 return GenericListFields(constants.QR_INSTANCE, args, opts.separator,
237 not opts.no_headers)
238
239
247
248
250 """Create instances using a definition file.
251
252 This function reads a json file with instances defined
253 in the form::
254
255 {"instance-name":{
256 "disk_size": [20480],
257 "template": "drbd",
258 "backend": {
259 "memory": 512,
260 "vcpus": 1 },
261 "os": "debootstrap",
262 "primary_node": "firstnode",
263 "secondary_node": "secondnode",
264 "iallocator": "dumb"}
265 }
266
267 Note that I{primary_node} and I{secondary_node} have precedence over
268 I{iallocator}.
269
270 @param opts: the command line options selected by the user
271 @type args: list
272 @param args: should contain one element, the json filename
273 @rtype: int
274 @return: the desired exit code
275
276 """
277 _DEFAULT_SPECS = {"disk_size": [20 * 1024],
278 "backend": {},
279 "iallocator": None,
280 "primary_node": None,
281 "secondary_node": None,
282 "nics": None,
283 "start": True,
284 "ip_check": True,
285 "name_check": True,
286 "hypervisor": None,
287 "hvparams": {},
288 "file_storage_dir": None,
289 "force_variant": False,
290 "file_driver": "loop"}
291
292 def _PopulateWithDefaults(spec):
293 """Returns a new hash combined with default values."""
294 mydict = _DEFAULT_SPECS.copy()
295 mydict.update(spec)
296 return mydict
297
298 def _Validate(spec):
299 """Validate the instance specs."""
300
301 for required_field in ("os", "template"):
302 if required_field not in spec:
303 raise errors.OpPrereqError('Required field "%s" is missing.' %
304 required_field, errors.ECODE_INVAL)
305
306 if spec["primary_node"] is not None:
307 if (spec["template"] in constants.DTS_INT_MIRROR and
308 spec["secondary_node"] is None):
309 raise errors.OpPrereqError("Template requires secondary node, but"
310 " there was no secondary provided.",
311 errors.ECODE_INVAL)
312 elif spec["iallocator"] is None:
313 raise errors.OpPrereqError("You have to provide at least a primary_node"
314 " or an iallocator.",
315 errors.ECODE_INVAL)
316
317 if (spec["hvparams"] and
318 not isinstance(spec["hvparams"], dict)):
319 raise errors.OpPrereqError("Hypervisor parameters must be a dict.",
320 errors.ECODE_INVAL)
321
322 json_filename = args[0]
323 try:
324 instance_data = simplejson.loads(utils.ReadFile(json_filename))
325 except Exception, err:
326 ToStderr("Can't parse the instance definition file: %s" % str(err))
327 return 1
328
329 if not isinstance(instance_data, dict):
330 ToStderr("The instance definition file is not in dict format.")
331 return 1
332
333 jex = JobExecutor(opts=opts)
334
335
336
337
338 i_names = utils.NiceSort(instance_data.keys())
339 for name in i_names:
340 specs = instance_data[name]
341 specs = _PopulateWithDefaults(specs)
342 _Validate(specs)
343
344 hypervisor = specs["hypervisor"]
345 hvparams = specs["hvparams"]
346
347 disks = []
348 for elem in specs["disk_size"]:
349 try:
350 size = utils.ParseUnit(elem)
351 except (TypeError, ValueError), err:
352 raise errors.OpPrereqError("Invalid disk size '%s' for"
353 " instance %s: %s" %
354 (elem, name, err), errors.ECODE_INVAL)
355 disks.append({"size": size})
356
357 utils.ForceDictType(specs["backend"], constants.BES_PARAMETER_TYPES)
358 utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
359
360 tmp_nics = []
361 for field in constants.INIC_PARAMS:
362 if field in specs:
363 if not tmp_nics:
364 tmp_nics.append({})
365 tmp_nics[0][field] = specs[field]
366
367 if specs["nics"] is not None and tmp_nics:
368 raise errors.OpPrereqError("'nics' list incompatible with using"
369 " individual nic fields as well",
370 errors.ECODE_INVAL)
371 elif specs["nics"] is not None:
372 tmp_nics = specs["nics"]
373 elif not tmp_nics:
374 tmp_nics = [{}]
375
376 op = opcodes.OpInstanceCreate(instance_name=name,
377 disks=disks,
378 disk_template=specs["template"],
379 mode=constants.INSTANCE_CREATE,
380 os_type=specs["os"],
381 force_variant=specs["force_variant"],
382 pnode=specs["primary_node"],
383 snode=specs["secondary_node"],
384 nics=tmp_nics,
385 start=specs["start"],
386 ip_check=specs["ip_check"],
387 name_check=specs["name_check"],
388 wait_for_sync=True,
389 iallocator=specs["iallocator"],
390 hypervisor=hypervisor,
391 hvparams=hvparams,
392 beparams=specs["backend"],
393 file_storage_dir=specs["file_storage_dir"],
394 file_driver=specs["file_driver"])
395
396 jex.QueueJob(name, op)
397
398 jex.WaitOrShow(False)
399
400 return 0
401
402
404 """Reinstall an instance.
405
406 @param opts: the command line options selected by the user
407 @type args: list
408 @param args: should contain only one element, the name of the
409 instance to be reinstalled
410 @rtype: int
411 @return: the desired exit code
412
413 """
414
415 if opts.multi_mode is None:
416 opts.multi_mode = _EXPAND_INSTANCES
417
418 inames = _ExpandMultiNames(opts.multi_mode, args)
419 if not inames:
420 raise errors.OpPrereqError("Selection filter does not match any instances",
421 errors.ECODE_INVAL)
422
423
424 if opts.select_os is True:
425 op = opcodes.OpOsDiagnose(output_fields=["name", "variants"], names=[])
426 result = SubmitOpCode(op, opts=opts)
427
428 if not result:
429 ToStdout("Can't get the OS list")
430 return 1
431
432 ToStdout("Available OS templates:")
433 number = 0
434 choices = []
435 for (name, variants) in result:
436 for entry in CalculateOSNames(name, variants):
437 ToStdout("%3s: %s", number, entry)
438 choices.append(("%s" % number, entry, entry))
439 number += 1
440
441 choices.append(("x", "exit", "Exit gnt-instance reinstall"))
442 selected = AskUser("Enter OS template number (or x to abort):",
443 choices)
444
445 if selected == "exit":
446 ToStderr("User aborted reinstall, exiting")
447 return 1
448
449 os_name = selected
450 os_msg = "change the OS to '%s'" % selected
451 else:
452 os_name = opts.os
453 if opts.os is not None:
454 os_msg = "change the OS to '%s'" % os_name
455 else:
456 os_msg = "keep the same OS"
457
458
459
460
461 multi_on = opts.multi_mode != _EXPAND_INSTANCES or len(inames) > 1
462 if multi_on:
463 warn_msg = ("Note: this will remove *all* data for the"
464 " below instances! It will %s.\n" % os_msg)
465 if not (opts.force_multi or
466 ConfirmOperation(inames, "instances", "reinstall", extra=warn_msg)):
467 return 1
468 else:
469 if not (opts.force or opts.force_multi):
470 usertext = ("This will reinstall the instance '%s' (and %s) which"
471 " removes all data. Continue?") % (inames[0], os_msg)
472 if not AskUser(usertext):
473 return 1
474
475 jex = JobExecutor(verbose=multi_on, opts=opts)
476 for instance_name in inames:
477 op = opcodes.OpInstanceReinstall(instance_name=instance_name,
478 os_type=os_name,
479 force_variant=opts.force_variant,
480 osparams=opts.osparams)
481 jex.QueueJob(instance_name, op)
482
483 jex.WaitOrShow(not opts.submit_only)
484 return 0
485
486
488 """Remove an instance.
489
490 @param opts: the command line options selected by the user
491 @type args: list
492 @param args: should contain only one element, the name of
493 the instance to be removed
494 @rtype: int
495 @return: the desired exit code
496
497 """
498 instance_name = args[0]
499 force = opts.force
500 cl = GetClient()
501
502 if not force:
503 _EnsureInstancesExist(cl, [instance_name])
504
505 usertext = ("This will remove the volumes of the instance %s"
506 " (including mirrors), thus removing all the data"
507 " of the instance. Continue?") % instance_name
508 if not AskUser(usertext):
509 return 1
510
511 op = opcodes.OpInstanceRemove(instance_name=instance_name,
512 ignore_failures=opts.ignore_failures,
513 shutdown_timeout=opts.shutdown_timeout)
514 SubmitOrSend(op, opts, cl=cl)
515 return 0
516
517
519 """Rename an instance.
520
521 @param opts: the command line options selected by the user
522 @type args: list
523 @param args: should contain two elements, the old and the
524 new instance names
525 @rtype: int
526 @return: the desired exit code
527
528 """
529 if not opts.name_check:
530 if not AskUser("As you disabled the check of the DNS entry, please verify"
531 " that '%s' is a FQDN. Continue?" % args[1]):
532 return 1
533
534 op = opcodes.OpInstanceRename(instance_name=args[0],
535 new_name=args[1],
536 ip_check=opts.ip_check,
537 name_check=opts.name_check)
538 result = SubmitOrSend(op, opts)
539
540 if result:
541 ToStdout("Instance '%s' renamed to '%s'", args[0], result)
542
543 return 0
544
545
547 """Activate an instance's disks.
548
549 This serves two purposes:
550 - it allows (as long as the instance is not running)
551 mounting the disks and modifying them from the node
552 - it repairs inactive secondary drbds
553
554 @param opts: the command line options selected by the user
555 @type args: list
556 @param args: should contain only one element, the instance name
557 @rtype: int
558 @return: the desired exit code
559
560 """
561 instance_name = args[0]
562 op = opcodes.OpInstanceActivateDisks(instance_name=instance_name,
563 ignore_size=opts.ignore_size)
564 disks_info = SubmitOrSend(op, opts)
565 for host, iname, nname in disks_info:
566 ToStdout("%s:%s:%s", host, iname, nname)
567 return 0
568
569
571 """Deactivate an instance's disks.
572
573 This function takes the instance name, looks for its primary node
574 and the tries to shutdown its block devices on that node.
575
576 @param opts: the command line options selected by the user
577 @type args: list
578 @param args: should contain only one element, the instance name
579 @rtype: int
580 @return: the desired exit code
581
582 """
583 instance_name = args[0]
584 op = opcodes.OpInstanceDeactivateDisks(instance_name=instance_name,
585 force=opts.force)
586 SubmitOrSend(op, opts)
587 return 0
588
589
591 """Recreate an instance's disks.
592
593 @param opts: the command line options selected by the user
594 @type args: list
595 @param args: should contain only one element, the instance name
596 @rtype: int
597 @return: the desired exit code
598
599 """
600 instance_name = args[0]
601 if opts.disks:
602 try:
603 opts.disks = [int(v) for v in opts.disks.split(",")]
604 except (ValueError, TypeError), err:
605 ToStderr("Invalid disks value: %s" % str(err))
606 return 1
607 else:
608 opts.disks = []
609
610 if opts.node:
611 pnode, snode = SplitNodeOption(opts.node)
612 nodes = [pnode]
613 if snode is not None:
614 nodes.append(snode)
615 else:
616 nodes = []
617
618 op = opcodes.OpInstanceRecreateDisks(instance_name=instance_name,
619 disks=opts.disks,
620 nodes=nodes)
621 SubmitOrSend(op, opts)
622 return 0
623
624
626 """Grow an instance's disks.
627
628 @param opts: the command line options selected by the user
629 @type args: list
630 @param args: should contain two elements, the instance name
631 whose disks we grow and the disk name, e.g. I{sda}
632 @rtype: int
633 @return: the desired exit code
634
635 """
636 instance = args[0]
637 disk = args[1]
638 try:
639 disk = int(disk)
640 except (TypeError, ValueError), err:
641 raise errors.OpPrereqError("Invalid disk index: %s" % str(err),
642 errors.ECODE_INVAL)
643 try:
644 amount = utils.ParseUnit(args[2])
645 except errors.UnitParseError:
646 raise errors.OpPrereqError("Can't parse the given amount '%s'" % args[2],
647 errors.ECODE_INVAL)
648 op = opcodes.OpInstanceGrowDisk(instance_name=instance,
649 disk=disk, amount=amount,
650 wait_for_sync=opts.wait_for_sync)
651 SubmitOrSend(op, opts)
652 return 0
653
654
656 """Startup instances.
657
658 This returns the opcode to start an instance, and its decorator will
659 wrap this into a loop starting all desired instances.
660
661 @param name: the name of the instance to act on
662 @param opts: the command line options selected by the user
663 @return: the opcode needed for the operation
664
665 """
666 op = opcodes.OpInstanceStartup(instance_name=name,
667 force=opts.force,
668 ignore_offline_nodes=opts.ignore_offline,
669 no_remember=opts.no_remember,
670 startup_paused=opts.startup_paused)
671
672 if opts.hvparams:
673 op.hvparams = opts.hvparams
674 if opts.beparams:
675 op.beparams = opts.beparams
676 return op
677
678
680 """Reboot instance(s).
681
682 This returns the opcode to reboot an instance, and its decorator
683 will wrap this into a loop rebooting all desired instances.
684
685 @param name: the name of the instance to act on
686 @param opts: the command line options selected by the user
687 @return: the opcode needed for the operation
688
689 """
690 return opcodes.OpInstanceReboot(instance_name=name,
691 reboot_type=opts.reboot_type,
692 ignore_secondaries=opts.ignore_secondaries,
693 shutdown_timeout=opts.shutdown_timeout)
694
695
697 """Shutdown an instance.
698
699 This returns the opcode to shutdown an instance, and its decorator
700 will wrap this into a loop shutting down all desired instances.
701
702 @param name: the name of the instance to act on
703 @param opts: the command line options selected by the user
704 @return: the opcode needed for the operation
705
706 """
707 return opcodes.OpInstanceShutdown(instance_name=name,
708 timeout=opts.timeout,
709 ignore_offline_nodes=opts.ignore_offline,
710 no_remember=opts.no_remember)
711
712
714 """Replace the disks of an instance
715
716 @param opts: the command line options selected by the user
717 @type args: list
718 @param args: should contain only one element, the instance name
719 @rtype: int
720 @return: the desired exit code
721
722 """
723 new_2ndary = opts.dst_node
724 iallocator = opts.iallocator
725 if opts.disks is None:
726 disks = []
727 else:
728 try:
729 disks = [int(i) for i in opts.disks.split(",")]
730 except (TypeError, ValueError), err:
731 raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err),
732 errors.ECODE_INVAL)
733 cnt = [opts.on_primary, opts.on_secondary, opts.auto,
734 new_2ndary is not None, iallocator is not None].count(True)
735 if cnt != 1:
736 raise errors.OpPrereqError("One and only one of the -p, -s, -a, -n and -I"
737 " options must be passed", errors.ECODE_INVAL)
738 elif opts.on_primary:
739 mode = constants.REPLACE_DISK_PRI
740 elif opts.on_secondary:
741 mode = constants.REPLACE_DISK_SEC
742 elif opts.auto:
743 mode = constants.REPLACE_DISK_AUTO
744 if disks:
745 raise errors.OpPrereqError("Cannot specify disks when using automatic"
746 " mode", errors.ECODE_INVAL)
747 elif new_2ndary is not None or iallocator is not None:
748
749 mode = constants.REPLACE_DISK_CHG
750
751 op = opcodes.OpInstanceReplaceDisks(instance_name=args[0], disks=disks,
752 remote_node=new_2ndary, mode=mode,
753 iallocator=iallocator,
754 early_release=opts.early_release)
755 SubmitOrSend(op, opts)
756 return 0
757
758
760 """Failover an instance.
761
762 The failover is done by shutting it down on its present node and
763 starting it on the secondary.
764
765 @param opts: the command line options selected by the user
766 @type args: list
767 @param args: should contain only one element, the instance name
768 @rtype: int
769 @return: the desired exit code
770
771 """
772 cl = GetClient()
773 instance_name = args[0]
774 force = opts.force
775 iallocator = opts.iallocator
776 target_node = opts.dst_node
777
778 if iallocator and target_node:
779 raise errors.OpPrereqError("Specify either an iallocator (-I), or a target"
780 " node (-n) but not both", errors.ECODE_INVAL)
781
782 if not force:
783 _EnsureInstancesExist(cl, [instance_name])
784
785 usertext = ("Failover will happen to image %s."
786 " This requires a shutdown of the instance. Continue?" %
787 (instance_name,))
788 if not AskUser(usertext):
789 return 1
790
791 op = opcodes.OpInstanceFailover(instance_name=instance_name,
792 ignore_consistency=opts.ignore_consistency,
793 shutdown_timeout=opts.shutdown_timeout,
794 iallocator=iallocator,
795 target_node=target_node)
796 SubmitOrSend(op, opts, cl=cl)
797 return 0
798
799
801 """Migrate an instance.
802
803 The migrate is done without shutdown.
804
805 @param opts: the command line options selected by the user
806 @type args: list
807 @param args: should contain only one element, the instance name
808 @rtype: int
809 @return: the desired exit code
810
811 """
812 cl = GetClient()
813 instance_name = args[0]
814 force = opts.force
815 iallocator = opts.iallocator
816 target_node = opts.dst_node
817
818 if iallocator and target_node:
819 raise errors.OpPrereqError("Specify either an iallocator (-I), or a target"
820 " node (-n) but not both", errors.ECODE_INVAL)
821
822 if not force:
823 _EnsureInstancesExist(cl, [instance_name])
824
825 if opts.cleanup:
826 usertext = ("Instance %s will be recovered from a failed migration."
827 " Note that the migration procedure (including cleanup)" %
828 (instance_name,))
829 else:
830 usertext = ("Instance %s will be migrated. Note that migration" %
831 (instance_name,))
832 usertext += (" might impact the instance if anything goes wrong"
833 " (e.g. due to bugs in the hypervisor). Continue?")
834 if not AskUser(usertext):
835 return 1
836
837
838 if not opts.live and opts.migration_mode is not None:
839 raise errors.OpPrereqError("Only one of the --non-live and "
840 "--migration-mode options can be passed",
841 errors.ECODE_INVAL)
842 if not opts.live:
843 mode = constants.HT_MIGRATION_NONLIVE
844 else:
845 mode = opts.migration_mode
846
847 op = opcodes.OpInstanceMigrate(instance_name=instance_name, mode=mode,
848 cleanup=opts.cleanup, iallocator=iallocator,
849 target_node=target_node,
850 allow_failover=opts.allow_failover)
851 SubmitOpCode(op, cl=cl, opts=opts)
852 return 0
853
854
856 """Move an instance.
857
858 @param opts: the command line options selected by the user
859 @type args: list
860 @param args: should contain only one element, the instance name
861 @rtype: int
862 @return: the desired exit code
863
864 """
865 cl = GetClient()
866 instance_name = args[0]
867 force = opts.force
868
869 if not force:
870 usertext = ("Instance %s will be moved."
871 " This requires a shutdown of the instance. Continue?" %
872 (instance_name,))
873 if not AskUser(usertext):
874 return 1
875
876 op = opcodes.OpInstanceMove(instance_name=instance_name,
877 target_node=opts.node,
878 shutdown_timeout=opts.shutdown_timeout,
879 ignore_consistency=opts.ignore_consistency)
880 SubmitOrSend(op, opts, cl=cl)
881 return 0
882
883
885 """Connect to the console of an instance.
886
887 @param opts: the command line options selected by the user
888 @type args: list
889 @param args: should contain only one element, the instance name
890 @rtype: int
891 @return: the desired exit code
892
893 """
894 instance_name = args[0]
895
896 cl = GetClient()
897 try:
898 cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
899 ((console_data, oper_state), ) = \
900 cl.QueryInstances([instance_name], ["console", "oper_state"], False)
901 finally:
902
903 cl.Close()
904
905 del cl
906
907 if not console_data:
908 if oper_state:
909
910 raise errors.OpExecError("Console information for instance %s is"
911 " unavailable" % instance_name)
912 else:
913 raise errors.OpExecError("Instance %s is not running, can't get console" %
914 instance_name)
915
916 return _DoConsole(objects.InstanceConsole.FromDict(console_data),
917 opts.show_command, cluster_name)
918
919
922 """Acts based on the result of L{opcodes.OpInstanceConsole}.
923
924 @type console: L{objects.InstanceConsole}
925 @param console: Console object
926 @type show_command: bool
927 @param show_command: Whether to just display commands
928 @type cluster_name: string
929 @param cluster_name: Cluster name as retrieved from master daemon
930
931 """
932 assert console.Validate()
933
934 if console.kind == constants.CONS_MESSAGE:
935 feedback_fn(console.message)
936 elif console.kind == constants.CONS_VNC:
937 feedback_fn("Instance %s has VNC listening on %s:%s (display %s),"
938 " URL <vnc://%s:%s/>",
939 console.instance, console.host, console.port,
940 console.display, console.host, console.port)
941 elif console.kind == constants.CONS_SSH:
942
943 if isinstance(console.command, basestring):
944 cmd = console.command
945 else:
946 cmd = utils.ShellQuoteArgs(console.command)
947
948 srun = ssh.SshRunner(cluster_name=cluster_name)
949 ssh_cmd = srun.BuildCmd(console.host, console.user, cmd,
950 batch=True, quiet=False, tty=True)
951
952 if show_command:
953 feedback_fn(utils.ShellQuoteArgs(ssh_cmd))
954 else:
955 result = _runcmd_fn(ssh_cmd, interactive=True)
956 if result.failed:
957 logging.error("Console command \"%s\" failed with reason '%s' and"
958 " output %r", result.cmd, result.fail_reason,
959 result.output)
960 raise errors.OpExecError("Connection to console of instance %s failed,"
961 " please check cluster configuration" %
962 console.instance)
963 else:
964 raise errors.GenericError("Unknown console type '%s'" % console.kind)
965
966 return constants.EXIT_SUCCESS
967
968
990
991
1062
1063
1064 if top_level:
1065 if dev["iv_name"] is not None:
1066 txt = dev["iv_name"]
1067 else:
1068 txt = "disk %s" % compat.TryToRoman(idx, convert=roman)
1069 else:
1070 txt = "child %s" % compat.TryToRoman(idx, convert=roman)
1071 if isinstance(dev["size"], int):
1072 nice_size = utils.FormatUnit(dev["size"], "h")
1073 else:
1074 nice_size = dev["size"]
1075 d1 = ["- %s: %s, size %s" % (txt, dev["dev_type"], nice_size)]
1076 data = []
1077 if top_level:
1078 data.append(("access mode", dev["mode"]))
1079 if dev["logical_id"] is not None:
1080 try:
1081 l_id = _FormatLogicalID(dev["dev_type"], dev["logical_id"], roman)
1082 except ValueError:
1083 l_id = [str(dev["logical_id"])]
1084 if len(l_id) == 1:
1085 data.append(("logical_id", l_id[0]))
1086 else:
1087 data.extend(l_id)
1088 elif dev["physical_id"] is not None:
1089 data.append("physical_id:")
1090 data.append([dev["physical_id"]])
1091
1092 if dev["pstatus"]:
1093 data.append(("on primary", helper(dev["dev_type"], dev["pstatus"])))
1094
1095 if dev["sstatus"]:
1096 data.append(("on secondary", helper(dev["dev_type"], dev["sstatus"])))
1097
1098 if dev["children"]:
1099 data.append("child devices:")
1100 for c_idx, child in enumerate(dev["children"]):
1101 data.append(_FormatBlockDevInfo(c_idx, False, child, roman))
1102 d1.append(data)
1103 return d1
1104
1105
1133
1134
1136 """Compute instance run-time status.
1137
1138 @param opts: the command line options selected by the user
1139 @type args: list
1140 @param args: either an empty list, and then we query all
1141 instances, or should contain a list of instance names
1142 @rtype: int
1143 @return: the desired exit code
1144
1145 """
1146 if not args and not opts.show_all:
1147 ToStderr("No instance selected."
1148 " Please pass in --all if you want to query all instances.\n"
1149 "Note that this can take a long time on a big cluster.")
1150 return 1
1151 elif args and opts.show_all:
1152 ToStderr("Cannot use --all if you specify instance names.")
1153 return 1
1154
1155 retcode = 0
1156 op = opcodes.OpInstanceQueryData(instances=args, static=opts.static,
1157 use_locking=not opts.static)
1158 result = SubmitOpCode(op, opts=opts)
1159 if not result:
1160 ToStdout("No instances.")
1161 return 1
1162
1163 buf = StringIO()
1164 retcode = 0
1165 for instance_name in result:
1166 instance = result[instance_name]
1167 buf.write("Instance name: %s\n" % instance["name"])
1168 buf.write("UUID: %s\n" % instance["uuid"])
1169 buf.write("Serial number: %s\n" %
1170 compat.TryToRoman(instance["serial_no"],
1171 convert=opts.roman_integers))
1172 buf.write("Creation time: %s\n" % utils.FormatTime(instance["ctime"]))
1173 buf.write("Modification time: %s\n" % utils.FormatTime(instance["mtime"]))
1174 buf.write("State: configured to be %s" % instance["config_state"])
1175 if instance["run_state"]:
1176 buf.write(", actual state is %s" % instance["run_state"])
1177 buf.write("\n")
1178
1179
1180 buf.write(" Nodes:\n")
1181 buf.write(" - primary: %s\n" % instance["pnode"])
1182 buf.write(" - secondaries: %s\n" % utils.CommaJoin(instance["snodes"]))
1183 buf.write(" Operating system: %s\n" % instance["os"])
1184 FormatParameterDict(buf, instance["os_instance"], instance["os_actual"],
1185 level=2)
1186 if "network_port" in instance:
1187 buf.write(" Allocated network port: %s\n" %
1188 compat.TryToRoman(instance["network_port"],
1189 convert=opts.roman_integers))
1190 buf.write(" Hypervisor: %s\n" % instance["hypervisor"])
1191
1192
1193 vnc_bind_address = instance["hv_actual"].get(constants.HV_VNC_BIND_ADDRESS,
1194 None)
1195 if vnc_bind_address:
1196 port = instance["network_port"]
1197 display = int(port) - constants.VNC_BASE_PORT
1198 if display > 0 and vnc_bind_address == constants.IP4_ADDRESS_ANY:
1199 vnc_console_port = "%s:%s (display %s)" % (instance["pnode"],
1200 port,
1201 display)
1202 elif display > 0 and netutils.IP4Address.IsValid(vnc_bind_address):
1203 vnc_console_port = ("%s:%s (node %s) (display %s)" %
1204 (vnc_bind_address, port,
1205 instance["pnode"], display))
1206 else:
1207
1208 vnc_console_port = "%s:%s" % (instance["pnode"],
1209 vnc_bind_address)
1210 buf.write(" - console connection: vnc to %s\n" % vnc_console_port)
1211
1212 FormatParameterDict(buf, instance["hv_instance"], instance["hv_actual"],
1213 level=2)
1214 buf.write(" Hardware:\n")
1215 buf.write(" - VCPUs: %s\n" %
1216 compat.TryToRoman(instance["be_actual"][constants.BE_VCPUS],
1217 convert=opts.roman_integers))
1218 buf.write(" - memory: %sMiB\n" %
1219 compat.TryToRoman(instance["be_actual"][constants.BE_MEMORY],
1220 convert=opts.roman_integers))
1221 buf.write(" - NICs:\n")
1222 for idx, (ip, mac, mode, link) in enumerate(instance["nics"]):
1223 buf.write(" - nic/%d: MAC: %s, IP: %s, mode: %s, link: %s\n" %
1224 (idx, mac, ip, mode, link))
1225 buf.write(" Disk template: %s\n" % instance["disk_template"])
1226 buf.write(" Disks:\n")
1227
1228 for idx, device in enumerate(instance["disks"]):
1229 _FormatList(buf, _FormatBlockDevInfo(idx, True, device,
1230 opts.roman_integers), 2)
1231
1232 ToStdout(buf.getvalue().rstrip("\n"))
1233 return retcode
1234
1235
1237 """Modifies an instance.
1238
1239 All parameters take effect only at the next restart of the instance.
1240
1241 @param opts: the command line options selected by the user
1242 @type args: list
1243 @param args: should contain only one element, the instance name
1244 @rtype: int
1245 @return: the desired exit code
1246
1247 """
1248 if not (opts.nics or opts.disks or opts.disk_template or
1249 opts.hvparams or opts.beparams or opts.os or opts.osparams):
1250 ToStderr("Please give at least one of the parameters.")
1251 return 1
1252
1253 for param in opts.beparams:
1254 if isinstance(opts.beparams[param], basestring):
1255 if opts.beparams[param].lower() == "default":
1256 opts.beparams[param] = constants.VALUE_DEFAULT
1257
1258 utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES,
1259 allowed_values=[constants.VALUE_DEFAULT])
1260
1261 for param in opts.hvparams:
1262 if isinstance(opts.hvparams[param], basestring):
1263 if opts.hvparams[param].lower() == "default":
1264 opts.hvparams[param] = constants.VALUE_DEFAULT
1265
1266 utils.ForceDictType(opts.hvparams, constants.HVS_PARAMETER_TYPES,
1267 allowed_values=[constants.VALUE_DEFAULT])
1268
1269 for idx, (nic_op, nic_dict) in enumerate(opts.nics):
1270 try:
1271 nic_op = int(nic_op)
1272 opts.nics[idx] = (nic_op, nic_dict)
1273 except (TypeError, ValueError):
1274 pass
1275
1276 for idx, (disk_op, disk_dict) in enumerate(opts.disks):
1277 try:
1278 disk_op = int(disk_op)
1279 opts.disks[idx] = (disk_op, disk_dict)
1280 except (TypeError, ValueError):
1281 pass
1282 if disk_op == constants.DDM_ADD:
1283 if "size" not in disk_dict:
1284 raise errors.OpPrereqError("Missing required parameter 'size'",
1285 errors.ECODE_INVAL)
1286 disk_dict["size"] = utils.ParseUnit(disk_dict["size"])
1287
1288 if (opts.disk_template and
1289 opts.disk_template in constants.DTS_INT_MIRROR and
1290 not opts.node):
1291 ToStderr("Changing the disk template to a mirrored one requires"
1292 " specifying a secondary node")
1293 return 1
1294
1295 op = opcodes.OpInstanceSetParams(instance_name=args[0],
1296 nics=opts.nics,
1297 disks=opts.disks,
1298 disk_template=opts.disk_template,
1299 remote_node=opts.node,
1300 hvparams=opts.hvparams,
1301 beparams=opts.beparams,
1302 os_name=opts.os,
1303 osparams=opts.osparams,
1304 force_variant=opts.force_variant,
1305 force=opts.force,
1306 wait_for_sync=opts.wait_for_sync)
1307
1308
1309 result = SubmitOrSend(op, opts)
1310
1311 if result:
1312 ToStdout("Modified instance %s", args[0])
1313 for param, data in result:
1314 ToStdout(" - %-5s -> %s", param, data)
1315 ToStdout("Please don't forget that most parameters take effect"
1316 " only at the next start of the instance.")
1317 return 0
1318
1319
1321 """Moves an instance to another group.
1322
1323 """
1324 (instance_name, ) = args
1325
1326 cl = GetClient()
1327
1328 op = opcodes.OpInstanceChangeGroup(instance_name=instance_name,
1329 iallocator=opts.iallocator,
1330 target_groups=opts.to,
1331 early_release=opts.early_release)
1332 result = SubmitOpCode(op, cl=cl, opts=opts)
1333
1334
1335 jex = JobExecutor(cl=cl, opts=opts)
1336
1337 for (status, job_id) in result[constants.JOB_IDS_KEY]:
1338 jex.AddJobId(None, status, job_id)
1339
1340 results = jex.GetResults()
1341 bad_cnt = len([row for row in results if not row[0]])
1342 if bad_cnt == 0:
1343 ToStdout("Instance '%s' changed group successfully.", instance_name)
1344 rcode = constants.EXIT_SUCCESS
1345 else:
1346 ToStdout("There were %s errors while changing group of instance '%s'.",
1347 bad_cnt, instance_name)
1348 rcode = constants.EXIT_FAILURE
1349
1350 return rcode
1351
1352
1353
1354 m_force_multi = cli_option("--force-multiple", dest="force_multi",
1355 help="Do not ask for confirmation when more than"
1356 " one instance is affected",
1357 action="store_true", default=False)
1358
1359 m_pri_node_opt = cli_option("--primary", dest="multi_mode",
1360 help="Filter by nodes (primary only)",
1361 const=_EXPAND_NODES_PRI, action="store_const")
1362
1363 m_sec_node_opt = cli_option("--secondary", dest="multi_mode",
1364 help="Filter by nodes (secondary only)",
1365 const=_EXPAND_NODES_SEC, action="store_const")
1366
1367 m_node_opt = cli_option("--node", dest="multi_mode",
1368 help="Filter by nodes (primary and secondary)",
1369 const=_EXPAND_NODES_BOTH, action="store_const")
1370
1371 m_clust_opt = cli_option("--all", dest="multi_mode",
1372 help="Select all instances in the cluster",
1373 const=_EXPAND_CLUSTER, action="store_const")
1374
1375 m_inst_opt = cli_option("--instance", dest="multi_mode",
1376 help="Filter by instance name [default]",
1377 const=_EXPAND_INSTANCES, action="store_const")
1378
1379 m_node_tags_opt = cli_option("--node-tags", dest="multi_mode",
1380 help="Filter by node tag",
1381 const=_EXPAND_NODES_BOTH_BY_TAGS,
1382 action="store_const")
1383
1384 m_pri_node_tags_opt = cli_option("--pri-node-tags", dest="multi_mode",
1385 help="Filter by primary node tag",
1386 const=_EXPAND_NODES_PRI_BY_TAGS,
1387 action="store_const")
1388
1389 m_sec_node_tags_opt = cli_option("--sec-node-tags", dest="multi_mode",
1390 help="Filter by secondary node tag",
1391 const=_EXPAND_NODES_SEC_BY_TAGS,
1392 action="store_const")
1393
1394 m_inst_tags_opt = cli_option("--tags", dest="multi_mode",
1395 help="Filter by instance tag",
1396 const=_EXPAND_INSTANCES_BY_TAGS,
1397 action="store_const")
1398
1399
1400 add_opts = [
1401 NOSTART_OPT,
1402 OS_OPT,
1403 FORCE_VARIANT_OPT,
1404 NO_INSTALL_OPT,
1405 ]
1406
1407 commands = {
1408 "add": (
1409 AddInstance, [ArgHost(min=1, max=1)], COMMON_CREATE_OPTS + add_opts,
1410 "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
1411 "Creates and adds a new instance to the cluster"),
1412 "batch-create": (
1413 BatchCreate, [ArgFile(min=1, max=1)], [DRY_RUN_OPT, PRIORITY_OPT],
1414 "<instances.json>",
1415 "Create a bunch of instances based on specs in the file."),
1416 "console": (
1417 ConnectToInstanceConsole, ARGS_ONE_INSTANCE,
1418 [SHOWCMD_OPT, PRIORITY_OPT],
1419 "[--show-cmd] <instance>", "Opens a console on the specified instance"),
1420 "failover": (
1421 FailoverInstance, ARGS_ONE_INSTANCE,
1422 [FORCE_OPT, IGNORE_CONSIST_OPT, SUBMIT_OPT, SHUTDOWN_TIMEOUT_OPT,
1423 DRY_RUN_OPT, PRIORITY_OPT, DST_NODE_OPT, IALLOCATOR_OPT],
1424 "[-f] <instance>", "Stops the instance, changes its primary node and"
1425 " (if it was originally running) starts it on the new node"
1426 " (the secondary for mirrored instances or any node"
1427 " for shared storage)."),
1428 "migrate": (
1429 MigrateInstance, ARGS_ONE_INSTANCE,
1430 [FORCE_OPT, NONLIVE_OPT, MIGRATION_MODE_OPT, CLEANUP_OPT, DRY_RUN_OPT,
1431 PRIORITY_OPT, DST_NODE_OPT, IALLOCATOR_OPT, ALLOW_FAILOVER_OPT],
1432 "[-f] <instance>", "Migrate instance to its secondary node"
1433 " (only for mirrored instances)"),
1434 "move": (
1435 MoveInstance, ARGS_ONE_INSTANCE,
1436 [FORCE_OPT, SUBMIT_OPT, SINGLE_NODE_OPT, SHUTDOWN_TIMEOUT_OPT,
1437 DRY_RUN_OPT, PRIORITY_OPT, IGNORE_CONSIST_OPT],
1438 "[-f] <instance>", "Move instance to an arbitrary node"
1439 " (only for instances of type file and lv)"),
1440 "info": (
1441 ShowInstanceConfig, ARGS_MANY_INSTANCES,
1442 [STATIC_OPT, ALL_OPT, ROMAN_OPT, PRIORITY_OPT],
1443 "[-s] {--all | <instance>...}",
1444 "Show information on the specified instance(s)"),
1445 "list": (
1446 ListInstances, ARGS_MANY_INSTANCES,
1447 [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, VERBOSE_OPT,
1448 FORCE_FILTER_OPT],
1449 "[<instance>...]",
1450 "Lists the instances and their status. The available fields can be shown"
1451 " using the \"list-fields\" command (see the man page for details)."
1452 " The default field list is (in order): %s." %
1453 utils.CommaJoin(_LIST_DEF_FIELDS),
1454 ),
1455 "list-fields": (
1456 ListInstanceFields, [ArgUnknown()],
1457 [NOHDR_OPT, SEP_OPT],
1458 "[fields...]",
1459 "Lists all available fields for instances"),
1460 "reinstall": (
1461 ReinstallInstance, [ArgInstance()],
1462 [FORCE_OPT, OS_OPT, FORCE_VARIANT_OPT, m_force_multi, m_node_opt,
1463 m_pri_node_opt, m_sec_node_opt, m_clust_opt, m_inst_opt, m_node_tags_opt,
1464 m_pri_node_tags_opt, m_sec_node_tags_opt, m_inst_tags_opt, SELECT_OS_OPT,
1465 SUBMIT_OPT, DRY_RUN_OPT, PRIORITY_OPT, OSPARAMS_OPT],
1466 "[-f] <instance>", "Reinstall a stopped instance"),
1467 "remove": (
1468 RemoveInstance, ARGS_ONE_INSTANCE,
1469 [FORCE_OPT, SHUTDOWN_TIMEOUT_OPT, IGNORE_FAILURES_OPT, SUBMIT_OPT,
1470 DRY_RUN_OPT, PRIORITY_OPT],
1471 "[-f] <instance>", "Shuts down the instance and removes it"),
1472 "rename": (
1473 RenameInstance,
1474 [ArgInstance(min=1, max=1), ArgHost(min=1, max=1)],
1475 [NOIPCHECK_OPT, NONAMECHECK_OPT, SUBMIT_OPT, DRY_RUN_OPT, PRIORITY_OPT],
1476 "<instance> <new_name>", "Rename the instance"),
1477 "replace-disks": (
1478 ReplaceDisks, ARGS_ONE_INSTANCE,
1479 [AUTO_REPLACE_OPT, DISKIDX_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT,
1480 NEW_SECONDARY_OPT, ON_PRIMARY_OPT, ON_SECONDARY_OPT, SUBMIT_OPT,
1481 DRY_RUN_OPT, PRIORITY_OPT],
1482 "[-s|-p|-n NODE|-I NAME] <instance>",
1483 "Replaces all disks for the instance"),
1484 "modify": (
1485 SetInstanceParams, ARGS_ONE_INSTANCE,
1486 [BACKEND_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, NET_OPT, SUBMIT_OPT,
1487 DISK_TEMPLATE_OPT, SINGLE_NODE_OPT, OS_OPT, FORCE_VARIANT_OPT,
1488 OSPARAMS_OPT, DRY_RUN_OPT, PRIORITY_OPT, NWSYNC_OPT],
1489 "<instance>", "Alters the parameters of an instance"),
1490 "shutdown": (
1491 GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
1492 [m_node_opt, m_pri_node_opt, m_sec_node_opt, m_clust_opt,
1493 m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
1494 m_inst_tags_opt, m_inst_opt, m_force_multi, TIMEOUT_OPT, SUBMIT_OPT,
1495 DRY_RUN_OPT, PRIORITY_OPT, IGNORE_OFFLINE_OPT, NO_REMEMBER_OPT],
1496 "<instance>", "Stops an instance"),
1497 "startup": (
1498 GenericManyOps("startup", _StartupInstance), [ArgInstance()],
1499 [FORCE_OPT, m_force_multi, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1500 m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
1501 m_inst_tags_opt, m_clust_opt, m_inst_opt, SUBMIT_OPT, HVOPTS_OPT,
1502 BACKEND_OPT, DRY_RUN_OPT, PRIORITY_OPT, IGNORE_OFFLINE_OPT,
1503 NO_REMEMBER_OPT, STARTUP_PAUSED_OPT],
1504 "<instance>", "Starts an instance"),
1505 "reboot": (
1506 GenericManyOps("reboot", _RebootInstance), [ArgInstance()],
1507 [m_force_multi, REBOOT_TYPE_OPT, IGNORE_SECONDARIES_OPT, m_node_opt,
1508 m_pri_node_opt, m_sec_node_opt, m_clust_opt, m_inst_opt, SUBMIT_OPT,
1509 m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
1510 m_inst_tags_opt, SHUTDOWN_TIMEOUT_OPT, DRY_RUN_OPT, PRIORITY_OPT],
1511 "<instance>", "Reboots an instance"),
1512 "activate-disks": (
1513 ActivateDisks, ARGS_ONE_INSTANCE,
1514 [SUBMIT_OPT, IGNORE_SIZE_OPT, PRIORITY_OPT],
1515 "<instance>", "Activate an instance's disks"),
1516 "deactivate-disks": (
1517 DeactivateDisks, ARGS_ONE_INSTANCE,
1518 [FORCE_OPT, SUBMIT_OPT, DRY_RUN_OPT, PRIORITY_OPT],
1519 "[-f] <instance>", "Deactivate an instance's disks"),
1520 "recreate-disks": (
1521 RecreateDisks, ARGS_ONE_INSTANCE,
1522 [SUBMIT_OPT, DISKIDX_OPT, NODE_PLACEMENT_OPT, DRY_RUN_OPT, PRIORITY_OPT],
1523 "<instance>", "Recreate an instance's disks"),
1524 "grow-disk": (
1525 GrowDisk,
1526 [ArgInstance(min=1, max=1), ArgUnknown(min=1, max=1),
1527 ArgUnknown(min=1, max=1)],
1528 [SUBMIT_OPT, NWSYNC_OPT, DRY_RUN_OPT, PRIORITY_OPT],
1529 "<instance> <disk> <size>", "Grow an instance's disk"),
1530 "change-group": (
1531 ChangeGroup, ARGS_ONE_INSTANCE,
1532 [TO_GROUP_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT],
1533 "[-I <iallocator>] [--to <group>]", "Change group of instance"),
1534 "list-tags": (
1535 ListTags, ARGS_ONE_INSTANCE, [PRIORITY_OPT],
1536 "<instance_name>", "List the tags of the given instance"),
1537 "add-tags": (
1538 AddTags, [ArgInstance(min=1, max=1), ArgUnknown()],
1539 [TAG_SRC_OPT, PRIORITY_OPT],
1540 "<instance_name> tag...", "Add tags to the given instance"),
1541 "remove-tags": (
1542 RemoveTags, [ArgInstance(min=1, max=1), ArgUnknown()],
1543 [TAG_SRC_OPT, PRIORITY_OPT],
1544 "<instance_name> tag...", "Remove tags from given instance"),
1545 }
1546
1547
1548 aliases = {
1549 "start": "startup",
1550 "stop": "shutdown",
1551 }
1552
1553
1557