1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 """Instance related commands"""
31
32
33
34
35
36
37 import copy
38 import itertools
39 import simplejson
40 import logging
41
42 from ganeti.cli import *
43 from ganeti import opcodes
44 from ganeti import constants
45 from ganeti import compat
46 from ganeti import utils
47 from ganeti import errors
48 from ganeti import netutils
49 from ganeti import ssh
50 from ganeti import objects
51 from ganeti import ht
52
53
54 _EXPAND_CLUSTER = "cluster"
55 _EXPAND_NODES_BOTH = "nodes"
56 _EXPAND_NODES_PRI = "nodes-pri"
57 _EXPAND_NODES_SEC = "nodes-sec"
58 _EXPAND_NODES_BOTH_BY_TAGS = "nodes-by-tags"
59 _EXPAND_NODES_PRI_BY_TAGS = "nodes-pri-by-tags"
60 _EXPAND_NODES_SEC_BY_TAGS = "nodes-sec-by-tags"
61 _EXPAND_INSTANCES = "instances"
62 _EXPAND_INSTANCES_BY_TAGS = "instances-by-tags"
63
64 _EXPAND_NODES_TAGS_MODES = compat.UniqueFrozenset([
65 _EXPAND_NODES_BOTH_BY_TAGS,
66 _EXPAND_NODES_PRI_BY_TAGS,
67 _EXPAND_NODES_SEC_BY_TAGS,
68 ])
69
70
71 _LIST_DEF_FIELDS = [
72 "name", "hypervisor", "os", "pnode", "status", "oper_ram",
73 ]
74
75 _MISSING = object()
76 _ENV_OVERRIDE = compat.UniqueFrozenset(["list"])
77
78 _INST_DATA_VAL = ht.TListOf(ht.TDict)
79
80
82 """Expand the given names using the passed mode.
83
84 For _EXPAND_CLUSTER, all instances will be returned. For
85 _EXPAND_NODES_PRI/SEC, all instances having those nodes as
86 primary/secondary will be returned. For _EXPAND_NODES_BOTH, all
87 instances having those nodes as either primary or secondary will be
88 returned. For _EXPAND_INSTANCES, the given instances will be
89 returned.
90
91 @param mode: one of L{_EXPAND_CLUSTER}, L{_EXPAND_NODES_BOTH},
92 L{_EXPAND_NODES_PRI}, L{_EXPAND_NODES_SEC} or
93 L{_EXPAND_INSTANCES}
94 @param names: a list of names; for cluster, it must be empty,
95 and for node and instance it must be a list of valid item
96 names (short names are valid as usual, e.g. node1 instead of
97 node1.example.com)
98 @rtype: list
99 @return: the list of names after the expansion
100 @raise errors.ProgrammerError: for unknown selection type
101 @raise errors.OpPrereqError: for invalid input parameters
102
103 """
104
105
106 if client is None:
107 client = GetClient()
108 if mode == _EXPAND_CLUSTER:
109 if names:
110 raise errors.OpPrereqError("Cluster filter mode takes no arguments",
111 errors.ECODE_INVAL)
112 idata = client.QueryInstances([], ["name"], False)
113 inames = [row[0] for row in idata]
114
115 elif (mode in _EXPAND_NODES_TAGS_MODES or
116 mode in (_EXPAND_NODES_BOTH, _EXPAND_NODES_PRI, _EXPAND_NODES_SEC)):
117 if mode in _EXPAND_NODES_TAGS_MODES:
118 if not names:
119 raise errors.OpPrereqError("No node tags passed", errors.ECODE_INVAL)
120 ndata = client.QueryNodes([], ["name", "pinst_list",
121 "sinst_list", "tags"], False)
122 ndata = [row for row in ndata if set(row[3]).intersection(names)]
123 else:
124 if not names:
125 raise errors.OpPrereqError("No node names passed", errors.ECODE_INVAL)
126 ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"],
127 False)
128
129 ipri = [row[1] for row in ndata]
130 pri_names = list(itertools.chain(*ipri))
131 isec = [row[2] for row in ndata]
132 sec_names = list(itertools.chain(*isec))
133 if mode in (_EXPAND_NODES_BOTH, _EXPAND_NODES_BOTH_BY_TAGS):
134 inames = pri_names + sec_names
135 elif mode in (_EXPAND_NODES_PRI, _EXPAND_NODES_PRI_BY_TAGS):
136 inames = pri_names
137 elif mode in (_EXPAND_NODES_SEC, _EXPAND_NODES_SEC_BY_TAGS):
138 inames = sec_names
139 else:
140 raise errors.ProgrammerError("Unhandled shutdown type")
141 elif mode == _EXPAND_INSTANCES:
142 if not names:
143 raise errors.OpPrereqError("No instance names passed",
144 errors.ECODE_INVAL)
145 idata = client.QueryInstances(names, ["name"], False)
146 inames = [row[0] for row in idata]
147 elif mode == _EXPAND_INSTANCES_BY_TAGS:
148 if not names:
149 raise errors.OpPrereqError("No instance tags passed",
150 errors.ECODE_INVAL)
151 idata = client.QueryInstances([], ["name", "tags"], False)
152 inames = [row[0] for row in idata if set(row[1]).intersection(names)]
153 else:
154 raise errors.OpPrereqError("Unknown mode '%s'" % mode, errors.ECODE_INVAL)
155
156 return inames
157
158
160 """Check for and ensure the given instance names exist.
161
162 This function will raise an OpPrereqError in case they don't
163 exist. Otherwise it will exit cleanly.
164
165 @type client: L{ganeti.luxi.Client}
166 @param client: the client to use for the query
167 @type names: list
168 @param names: the list of instance names to query
169 @raise errors.OpPrereqError: in case any instance is missing
170
171 """
172
173
174 result = client.QueryInstances(names, ["name"], False)
175 for orig_name, row in zip(names, result):
176 if row[0] is None:
177 raise errors.OpPrereqError("Instance '%s' does not exist" % orig_name,
178 errors.ECODE_NOENT)
179
180
182 """Generic multi-instance operations.
183
184 The will return a wrapper that processes the options and arguments
185 given, and uses the passed function to build the opcode needed for
186 the specific operation. Thus all the generic loop/confirmation code
187 is abstracted into this function.
188
189 """
190 def realfn(opts, args):
191 if opts.multi_mode is None:
192 opts.multi_mode = _EXPAND_INSTANCES
193 cl = GetClient()
194 inames = _ExpandMultiNames(opts.multi_mode, args, client=cl)
195 if not inames:
196 if opts.multi_mode == _EXPAND_CLUSTER:
197 ToStdout("Cluster is empty, no instances to shutdown")
198 return 0
199 raise errors.OpPrereqError("Selection filter does not match"
200 " any instances", errors.ECODE_INVAL)
201 multi_on = opts.multi_mode != _EXPAND_INSTANCES or len(inames) > 1
202 if not (opts.force_multi or not multi_on
203 or ConfirmOperation(inames, "instances", operation)):
204 return 1
205 jex = JobExecutor(verbose=multi_on, cl=cl, opts=opts)
206 for name in inames:
207 op = fn(name, opts)
208 jex.QueueJob(name, op)
209 results = jex.WaitOrShow(not opts.submit_only)
210 rcode = compat.all(row[0] for row in results)
211 return int(not rcode)
212 return realfn
213
214
216 """List instances and their properties.
217
218 @param opts: the command line options selected by the user
219 @type args: list
220 @param args: should be an empty list
221 @rtype: int
222 @return: the desired exit code
223
224 """
225 selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
226
227 fmtoverride = dict.fromkeys(["tags", "disk.sizes", "nic.macs", "nic.ips",
228 "nic.modes", "nic.links", "nic.bridges",
229 "nic.networks",
230 "snodes", "snodes.group", "snodes.group.uuid"],
231 (lambda value: ",".join(str(item)
232 for item in value),
233 False))
234
235 cl = GetClient()
236
237 return GenericList(constants.QR_INSTANCE, selected_fields, args, opts.units,
238 opts.separator, not opts.no_headers,
239 format_override=fmtoverride, verbose=opts.verbose,
240 force_filter=opts.force_filter, cl=cl)
241
242
244 """List instance fields.
245
246 @param opts: the command line options selected by the user
247 @type args: list
248 @param args: fields to list, or empty for all
249 @rtype: int
250 @return: the desired exit code
251
252 """
253 return GenericListFields(constants.QR_INSTANCE, args, opts.separator,
254 not opts.no_headers)
255
256
264
265
267 """Create instances using a definition file.
268
269 This function reads a json file with L{opcodes.OpInstanceCreate}
270 serialisations.
271
272 @param opts: the command line options selected by the user
273 @type args: list
274 @param args: should contain one element, the json filename
275 @rtype: int
276 @return: the desired exit code
277
278 """
279 (json_filename,) = args
280 cl = GetClient()
281
282 try:
283 instance_data = simplejson.loads(utils.ReadFile(json_filename))
284 except Exception, err:
285 ToStderr("Can't parse the instance definition file: %s" % str(err))
286 return 1
287
288 if not _INST_DATA_VAL(instance_data):
289 ToStderr("The instance definition file is not %s" % _INST_DATA_VAL)
290 return 1
291
292 instances = []
293 possible_params = set(opcodes.OpInstanceCreate.GetAllSlots())
294 for (idx, inst) in enumerate(instance_data):
295 unknown = set(inst.keys()) - possible_params
296
297 if unknown:
298
299 raise errors.OpPrereqError("Unknown fields in definition %s: %s" %
300 (idx, utils.CommaJoin(unknown)),
301 errors.ECODE_INVAL)
302
303 op = opcodes.OpInstanceCreate(**inst)
304 op.Validate(False)
305 instances.append(op)
306
307 op = opcodes.OpInstanceMultiAlloc(iallocator=opts.iallocator,
308 instances=instances)
309 result = SubmitOrSend(op, opts, cl=cl)
310
311
312 jex = JobExecutor(cl=cl, opts=opts)
313
314 for (status, job_id) in result[constants.JOB_IDS_KEY]:
315 jex.AddJobId(None, status, job_id)
316
317 results = jex.GetResults()
318 bad_cnt = len([row for row in results if not row[0]])
319 if bad_cnt == 0:
320 ToStdout("All instances created successfully.")
321 rcode = constants.EXIT_SUCCESS
322 else:
323 ToStdout("There were %s errors during the creation.", bad_cnt)
324 rcode = constants.EXIT_FAILURE
325
326 return rcode
327
328
330 """Reinstall an instance.
331
332 @param opts: the command line options selected by the user
333 @type args: list
334 @param args: should contain only one element, the name of the
335 instance to be reinstalled
336 @rtype: int
337 @return: the desired exit code
338
339 """
340
341 if opts.multi_mode is None:
342 opts.multi_mode = _EXPAND_INSTANCES
343
344 inames = _ExpandMultiNames(opts.multi_mode, args)
345 if not inames:
346 raise errors.OpPrereqError("Selection filter does not match any instances",
347 errors.ECODE_INVAL)
348
349
350 if opts.select_os is True:
351 op = opcodes.OpOsDiagnose(output_fields=["name", "variants"], names=[])
352 result = SubmitOpCode(op, opts=opts)
353
354 if not result:
355 ToStdout("Can't get the OS list")
356 return 1
357
358 ToStdout("Available OS templates:")
359 number = 0
360 choices = []
361 for (name, variants) in result:
362 for entry in CalculateOSNames(name, variants):
363 ToStdout("%3s: %s", number, entry)
364 choices.append(("%s" % number, entry, entry))
365 number += 1
366
367 choices.append(("x", "exit", "Exit gnt-instance reinstall"))
368 selected = AskUser("Enter OS template number (or x to abort):",
369 choices)
370
371 if selected == "exit":
372 ToStderr("User aborted reinstall, exiting")
373 return 1
374
375 os_name = selected
376 os_msg = "change the OS to '%s'" % selected
377 else:
378 os_name = opts.os
379 if opts.os is not None:
380 os_msg = "change the OS to '%s'" % os_name
381 else:
382 os_msg = "keep the same OS"
383
384
385
386
387 multi_on = opts.multi_mode != _EXPAND_INSTANCES or len(inames) > 1
388 if multi_on:
389 warn_msg = ("Note: this will remove *all* data for the"
390 " below instances! It will %s.\n" % os_msg)
391 if not (opts.force_multi or
392 ConfirmOperation(inames, "instances", "reinstall", extra=warn_msg)):
393 return 1
394 else:
395 if not (opts.force or opts.force_multi):
396 usertext = ("This will reinstall the instance '%s' (and %s) which"
397 " removes all data. Continue?") % (inames[0], os_msg)
398 if not AskUser(usertext):
399 return 1
400
401 jex = JobExecutor(verbose=multi_on, opts=opts)
402 for instance_name in inames:
403 op = opcodes.OpInstanceReinstall(instance_name=instance_name,
404 os_type=os_name,
405 force_variant=opts.force_variant,
406 osparams=opts.osparams,
407 osparams_private=opts.osparams_private,
408 osparams_secret=opts.osparams_secret)
409 jex.QueueJob(instance_name, op)
410
411 results = jex.WaitOrShow(not opts.submit_only)
412
413 if compat.all(map(compat.fst, results)):
414 return constants.EXIT_SUCCESS
415 else:
416 return constants.EXIT_FAILURE
417
418
420 """Remove an instance.
421
422 @param opts: the command line options selected by the user
423 @type args: list
424 @param args: should contain only one element, the name of
425 the instance to be removed
426 @rtype: int
427 @return: the desired exit code
428
429 """
430 instance_name = args[0]
431 force = opts.force
432 cl = GetClient()
433
434 if not force:
435 _EnsureInstancesExist(cl, [instance_name])
436
437 usertext = ("This will remove the volumes of the instance %s"
438 " (including mirrors), thus removing all the data"
439 " of the instance. Continue?") % instance_name
440 if not AskUser(usertext):
441 return 1
442
443 op = opcodes.OpInstanceRemove(instance_name=instance_name,
444 ignore_failures=opts.ignore_failures,
445 shutdown_timeout=opts.shutdown_timeout)
446 SubmitOrSend(op, opts, cl=cl)
447 return 0
448
449
451 """Rename an instance.
452
453 @param opts: the command line options selected by the user
454 @type args: list
455 @param args: should contain two elements, the old and the
456 new instance names
457 @rtype: int
458 @return: the desired exit code
459
460 """
461 if not opts.name_check:
462 if not AskUser("As you disabled the check of the DNS entry, please verify"
463 " that '%s' is a FQDN. Continue?" % args[1]):
464 return 1
465
466 op = opcodes.OpInstanceRename(instance_name=args[0],
467 new_name=args[1],
468 ip_check=opts.ip_check,
469 name_check=opts.name_check)
470 result = SubmitOrSend(op, opts)
471
472 if result:
473 ToStdout("Instance '%s' renamed to '%s'", args[0], result)
474
475 return 0
476
477
479 """Activate an instance's disks.
480
481 This serves two purposes:
482 - it allows (as long as the instance is not running)
483 mounting the disks and modifying them from the node
484 - it repairs inactive secondary drbds
485
486 @param opts: the command line options selected by the user
487 @type args: list
488 @param args: should contain only one element, the instance name
489 @rtype: int
490 @return: the desired exit code
491
492 """
493 instance_name = args[0]
494 op = opcodes.OpInstanceActivateDisks(instance_name=instance_name,
495 ignore_size=opts.ignore_size,
496 wait_for_sync=opts.wait_for_sync)
497 disks_info = SubmitOrSend(op, opts)
498 for host, iname, nname in disks_info:
499 ToStdout("%s:%s:%s", host, iname, nname)
500 return 0
501
502
504 """Deactivate an instance's disks.
505
506 This function takes the instance name, looks for its primary node
507 and the tries to shutdown its block devices on that node.
508
509 @param opts: the command line options selected by the user
510 @type args: list
511 @param args: should contain only one element, the instance name
512 @rtype: int
513 @return: the desired exit code
514
515 """
516 instance_name = args[0]
517 op = opcodes.OpInstanceDeactivateDisks(instance_name=instance_name,
518 force=opts.force)
519 SubmitOrSend(op, opts)
520 return 0
521
522
524 """Recreate an instance's disks.
525
526 @param opts: the command line options selected by the user
527 @type args: list
528 @param args: should contain only one element, the instance name
529 @rtype: int
530 @return: the desired exit code
531
532 """
533 instance_name = args[0]
534
535 disks = []
536
537 if opts.disks:
538 for didx, ddict in opts.disks:
539 didx = int(didx)
540
541 if not ht.TDict(ddict):
542 msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict)
543 raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
544
545 if constants.IDISK_SIZE in ddict:
546 try:
547 ddict[constants.IDISK_SIZE] = \
548 utils.ParseUnit(ddict[constants.IDISK_SIZE])
549 except ValueError, err:
550 raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
551 (didx, err), errors.ECODE_INVAL)
552
553 if constants.IDISK_SPINDLES in ddict:
554 try:
555 ddict[constants.IDISK_SPINDLES] = \
556 int(ddict[constants.IDISK_SPINDLES])
557 except ValueError, err:
558 raise errors.OpPrereqError("Invalid spindles for disk %d: %s" %
559 (didx, err), errors.ECODE_INVAL)
560
561 disks.append((didx, ddict))
562
563
564
565
566 if opts.node:
567 if opts.iallocator:
568 msg = "At most one of either --nodes or --iallocator can be passed"
569 raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
570 pnode, snode = SplitNodeOption(opts.node)
571 nodes = [pnode]
572 if snode is not None:
573 nodes.append(snode)
574 else:
575 nodes = []
576
577 op = opcodes.OpInstanceRecreateDisks(instance_name=instance_name,
578 disks=disks, nodes=nodes,
579 iallocator=opts.iallocator)
580 SubmitOrSend(op, opts)
581
582 return 0
583
584
586 """Grow an instance's disks.
587
588 @param opts: the command line options selected by the user
589 @type args: list
590 @param args: should contain three elements, the target instance name,
591 the target disk id, and the target growth
592 @rtype: int
593 @return: the desired exit code
594
595 """
596 instance = args[0]
597 disk = args[1]
598 try:
599 disk = int(disk)
600 except (TypeError, ValueError), err:
601 raise errors.OpPrereqError("Invalid disk index: %s" % str(err),
602 errors.ECODE_INVAL)
603 try:
604 amount = utils.ParseUnit(args[2])
605 except errors.UnitParseError:
606 raise errors.OpPrereqError("Can't parse the given amount '%s'" % args[2],
607 errors.ECODE_INVAL)
608 op = opcodes.OpInstanceGrowDisk(instance_name=instance,
609 disk=disk, amount=amount,
610 wait_for_sync=opts.wait_for_sync,
611 absolute=opts.absolute,
612 ignore_ipolicy=opts.ignore_ipolicy
613 )
614 SubmitOrSend(op, opts)
615 return 0
616
617
619 """Startup instances.
620
621 This returns the opcode to start an instance, and its decorator will
622 wrap this into a loop starting all desired instances.
623
624 @param name: the name of the instance to act on
625 @param opts: the command line options selected by the user
626 @return: the opcode needed for the operation
627
628 """
629 op = opcodes.OpInstanceStartup(instance_name=name,
630 force=opts.force,
631 ignore_offline_nodes=opts.ignore_offline,
632 no_remember=opts.no_remember,
633 startup_paused=opts.startup_paused)
634
635 if opts.hvparams:
636 op.hvparams = opts.hvparams
637 if opts.beparams:
638 op.beparams = opts.beparams
639 return op
640
641
643 """Reboot instance(s).
644
645 This returns the opcode to reboot an instance, and its decorator
646 will wrap this into a loop rebooting all desired instances.
647
648 @param name: the name of the instance to act on
649 @param opts: the command line options selected by the user
650 @return: the opcode needed for the operation
651
652 """
653 return opcodes.OpInstanceReboot(instance_name=name,
654 reboot_type=opts.reboot_type,
655 ignore_secondaries=opts.ignore_secondaries,
656 shutdown_timeout=opts.shutdown_timeout)
657
658
660 """Shutdown an instance.
661
662 This returns the opcode to shutdown an instance, and its decorator
663 will wrap this into a loop shutting down all desired instances.
664
665 @param name: the name of the instance to act on
666 @param opts: the command line options selected by the user
667 @return: the opcode needed for the operation
668
669 """
670 return opcodes.OpInstanceShutdown(instance_name=name,
671 force=opts.force,
672 timeout=opts.timeout,
673 ignore_offline_nodes=opts.ignore_offline,
674 no_remember=opts.no_remember)
675
676
678 """Replace the disks of an instance
679
680 @param opts: the command line options selected by the user
681 @type args: list
682 @param args: should contain only one element, the instance name
683 @rtype: int
684 @return: the desired exit code
685
686 """
687 new_2ndary = opts.dst_node
688 iallocator = opts.iallocator
689 if opts.disks is None:
690 disks = []
691 else:
692 try:
693 disks = [int(i) for i in opts.disks.split(",")]
694 except (TypeError, ValueError), err:
695 raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err),
696 errors.ECODE_INVAL)
697 cnt = [opts.on_primary, opts.on_secondary, opts.auto,
698 new_2ndary is not None, iallocator is not None].count(True)
699 if cnt != 1:
700 raise errors.OpPrereqError("One and only one of the -p, -s, -a, -n and -I"
701 " options must be passed", errors.ECODE_INVAL)
702 elif opts.on_primary:
703 mode = constants.REPLACE_DISK_PRI
704 elif opts.on_secondary:
705 mode = constants.REPLACE_DISK_SEC
706 elif opts.auto:
707 mode = constants.REPLACE_DISK_AUTO
708 if disks:
709 raise errors.OpPrereqError("Cannot specify disks when using automatic"
710 " mode", errors.ECODE_INVAL)
711 elif new_2ndary is not None or iallocator is not None:
712
713 mode = constants.REPLACE_DISK_CHG
714
715 op = opcodes.OpInstanceReplaceDisks(instance_name=args[0], disks=disks,
716 remote_node=new_2ndary, mode=mode,
717 iallocator=iallocator,
718 early_release=opts.early_release,
719 ignore_ipolicy=opts.ignore_ipolicy)
720 SubmitOrSend(op, opts)
721 return 0
722
723
725 """Failover an instance.
726
727 The failover is done by shutting it down on its present node and
728 starting it on the secondary.
729
730 @param opts: the command line options selected by the user
731 @type args: list
732 @param args: should contain only one element, the instance name
733 @rtype: int
734 @return: the desired exit code
735
736 """
737 cl = GetClient()
738 instance_name = args[0]
739 force = opts.force
740 iallocator = opts.iallocator
741 target_node = opts.dst_node
742
743 if iallocator and target_node:
744 raise errors.OpPrereqError("Specify either an iallocator (-I), or a target"
745 " node (-n) but not both", errors.ECODE_INVAL)
746
747 if not force:
748 _EnsureInstancesExist(cl, [instance_name])
749
750 usertext = ("Failover will happen to image %s."
751 " This requires a shutdown of the instance. Continue?" %
752 (instance_name,))
753 if not AskUser(usertext):
754 return 1
755
756 op = opcodes.OpInstanceFailover(instance_name=instance_name,
757 ignore_consistency=opts.ignore_consistency,
758 shutdown_timeout=opts.shutdown_timeout,
759 iallocator=iallocator,
760 target_node=target_node,
761 ignore_ipolicy=opts.ignore_ipolicy)
762 SubmitOrSend(op, opts, cl=cl)
763 return 0
764
765
767 """Migrate an instance.
768
769 The migrate is done without shutdown.
770
771 @param opts: the command line options selected by the user
772 @type args: list
773 @param args: should contain only one element, the instance name
774 @rtype: int
775 @return: the desired exit code
776
777 """
778 cl = GetClient()
779 instance_name = args[0]
780 force = opts.force
781 iallocator = opts.iallocator
782 target_node = opts.dst_node
783
784 if iallocator and target_node:
785 raise errors.OpPrereqError("Specify either an iallocator (-I), or a target"
786 " node (-n) but not both", errors.ECODE_INVAL)
787
788 if not force:
789 _EnsureInstancesExist(cl, [instance_name])
790
791 if opts.cleanup:
792 usertext = ("Instance %s will be recovered from a failed migration."
793 " Note that the migration procedure (including cleanup)" %
794 (instance_name,))
795 else:
796 usertext = ("Instance %s will be migrated. Note that migration" %
797 (instance_name,))
798 usertext += (" might impact the instance if anything goes wrong"
799 " (e.g. due to bugs in the hypervisor). Continue?")
800 if not AskUser(usertext):
801 return 1
802
803
804 if not opts.live and opts.migration_mode is not None:
805 raise errors.OpPrereqError("Only one of the --non-live and "
806 "--migration-mode options can be passed",
807 errors.ECODE_INVAL)
808 if not opts.live:
809 mode = constants.HT_MIGRATION_NONLIVE
810 else:
811 mode = opts.migration_mode
812
813 op = opcodes.OpInstanceMigrate(instance_name=instance_name, mode=mode,
814 cleanup=opts.cleanup, iallocator=iallocator,
815 target_node=target_node,
816 allow_failover=opts.allow_failover,
817 allow_runtime_changes=opts.allow_runtime_chgs,
818 ignore_ipolicy=opts.ignore_ipolicy,
819 ignore_hvversions=opts.ignore_hvversions)
820 SubmitOrSend(op, cl=cl, opts=opts)
821 return 0
822
823
825 """Move an instance.
826
827 @param opts: the command line options selected by the user
828 @type args: list
829 @param args: should contain only one element, the instance name
830 @rtype: int
831 @return: the desired exit code
832
833 """
834 cl = GetClient()
835 instance_name = args[0]
836 force = opts.force
837
838 if not force:
839 usertext = ("Instance %s will be moved."
840 " This requires a shutdown of the instance. Continue?" %
841 (instance_name,))
842 if not AskUser(usertext):
843 return 1
844
845 op = opcodes.OpInstanceMove(instance_name=instance_name,
846 target_node=opts.node,
847 compress=opts.compress,
848 shutdown_timeout=opts.shutdown_timeout,
849 ignore_consistency=opts.ignore_consistency,
850 ignore_ipolicy=opts.ignore_ipolicy)
851 SubmitOrSend(op, opts, cl=cl)
852 return 0
853
854
856 """Connect to the console of 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 instance_name = args[0]
866
867 cl = GetClient()
868 try:
869 cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
870 idata = cl.QueryInstances([instance_name], ["console", "oper_state"], False)
871 if not idata:
872 raise errors.OpPrereqError("Instance '%s' does not exist" % instance_name,
873 errors.ECODE_NOENT)
874 finally:
875
876 cl.Close()
877
878 del cl
879
880 ((console_data, oper_state), ) = idata
881 if not console_data:
882 if oper_state:
883
884 raise errors.OpExecError("Console information for instance %s is"
885 " unavailable" % instance_name)
886 else:
887 raise errors.OpExecError("Instance %s is not running, can't get console" %
888 instance_name)
889
890 return _DoConsole(objects.InstanceConsole.FromDict(console_data),
891 opts.show_command, cluster_name)
892
893
896 """Acts based on the result of L{opcodes.OpInstanceConsole}.
897
898 @type console: L{objects.InstanceConsole}
899 @param console: Console object
900 @type show_command: bool
901 @param show_command: Whether to just display commands
902 @type cluster_name: string
903 @param cluster_name: Cluster name as retrieved from master daemon
904
905 """
906 console.Validate()
907
908 if console.kind == constants.CONS_MESSAGE:
909 feedback_fn(console.message)
910 elif console.kind == constants.CONS_VNC:
911 feedback_fn("Instance %s has VNC listening on %s:%s (display %s),"
912 " URL <vnc://%s:%s/>",
913 console.instance, console.host, console.port,
914 console.display, console.host, console.port)
915 elif console.kind == constants.CONS_SPICE:
916 feedback_fn("Instance %s has SPICE listening on %s:%s", console.instance,
917 console.host, console.port)
918 elif console.kind == constants.CONS_SSH:
919
920 if isinstance(console.command, basestring):
921 cmd = console.command
922 else:
923 cmd = utils.ShellQuoteArgs(console.command)
924
925 srun = ssh.SshRunner(cluster_name=cluster_name)
926 ssh_cmd = srun.BuildCmd(console.host, console.user, cmd,
927 port=console.port,
928 batch=True, quiet=False, tty=True)
929
930 if show_command:
931 feedback_fn(utils.ShellQuoteArgs(ssh_cmd))
932 else:
933 result = _runcmd_fn(ssh_cmd, interactive=True)
934 if result.failed:
935 logging.error("Console command \"%s\" failed with reason '%s' and"
936 " output %r", result.cmd, result.fail_reason,
937 result.output)
938 raise errors.OpExecError("Connection to console of instance %s failed,"
939 " please check cluster configuration" %
940 console.instance)
941 else:
942 raise errors.GenericError("Unknown console type '%s'" % console.kind)
943
944 return constants.EXIT_SUCCESS
945
946
972
973
1044
1045
1046 if top_level:
1047 if dev["iv_name"] is not None:
1048 txt = dev["iv_name"]
1049 else:
1050 txt = "disk %s" % compat.TryToRoman(idx, convert=roman)
1051 else:
1052 txt = "child %s" % compat.TryToRoman(idx, convert=roman)
1053 if isinstance(dev["size"], int):
1054 nice_size = utils.FormatUnit(dev["size"], "h", roman)
1055 else:
1056 nice_size = str(dev["size"])
1057 data = [(txt, "%s, size %s" % (dev["dev_type"], nice_size))]
1058 if top_level:
1059 if dev["spindles"] is not None:
1060 data.append(("spindles", dev["spindles"]))
1061 data.append(("access mode", dev["mode"]))
1062 if dev["logical_id"] is not None:
1063 try:
1064 l_id = _FormatDiskDetails(dev["dev_type"], dev, roman)
1065 except ValueError:
1066 l_id = [str(dev["logical_id"])]
1067 if len(l_id) == 1:
1068 data.append(("logical_id", l_id[0]))
1069 else:
1070 data.extend(l_id)
1071
1072 if dev["pstatus"]:
1073 data.append(("on primary", helper(dev["dev_type"], dev["pstatus"])))
1074
1075 if dev["sstatus"]:
1076 data.append(("on secondary", helper(dev["dev_type"], dev["sstatus"])))
1077
1078 data.append(("name", dev["name"]))
1079 data.append(("UUID", dev["uuid"]))
1080
1081 if dev["children"]:
1082 data.append(("child devices", [
1083 _FormatBlockDevInfo(c_idx, False, child, roman)
1084 for c_idx, child in enumerate(dev["children"])
1085 ]))
1086 return data
1087
1088
1106
1107
1125
1126
1128 """Helper function for L{_FormatInstanceInfo()}"""
1129 vnc_bind_address = instance["hv_actual"].get(constants.HV_VNC_BIND_ADDRESS,
1130 None)
1131 if vnc_bind_address:
1132 port = instance["network_port"]
1133 display = int(port) - constants.VNC_BASE_PORT
1134 if display > 0 and vnc_bind_address == constants.IP4_ADDRESS_ANY:
1135 vnc_console_port = "%s:%s (display %s)" % (instance["pnode"],
1136 port,
1137 display)
1138 elif display > 0 and netutils.IP4Address.IsValid(vnc_bind_address):
1139 vnc_console_port = ("%s:%s (node %s) (display %s)" %
1140 (vnc_bind_address, port,
1141 instance["pnode"], display))
1142 else:
1143
1144 vnc_console_port = "%s:%s" % (instance["pnode"],
1145 vnc_bind_address)
1146 ret = "vnc to %s" % vnc_console_port
1147 else:
1148 ret = None
1149 return ret
1150
1151
1202
1203
1205 """Compute instance run-time status.
1206
1207 @param opts: the command line options selected by the user
1208 @type args: list
1209 @param args: either an empty list, and then we query all
1210 instances, or should contain a list of instance names
1211 @rtype: int
1212 @return: the desired exit code
1213
1214 """
1215 if not args and not opts.show_all:
1216 ToStderr("No instance selected."
1217 " Please pass in --all if you want to query all instances.\n"
1218 "Note that this can take a long time on a big cluster.")
1219 return 1
1220 elif args and opts.show_all:
1221 ToStderr("Cannot use --all if you specify instance names.")
1222 return 1
1223
1224 retcode = 0
1225 op = opcodes.OpInstanceQueryData(instances=args, static=opts.static,
1226 use_locking=not opts.static)
1227 result = SubmitOpCode(op, opts=opts)
1228 if not result:
1229 ToStdout("No instances.")
1230 return 1
1231
1232 PrintGenericInfo([
1233 _FormatInstanceInfo(instance, opts.roman_integers)
1234 for instance in result.values()
1235 ])
1236 return retcode
1237
1238
1240 """Converts NIC/disk modifications from CLI to opcode.
1241
1242 When L{opcodes.OpInstanceSetParams} was changed to support adding/removing
1243 disks at arbitrary indices, its parameter format changed. This function
1244 converts legacy requests (e.g. "--net add" or "--disk add:size=4G") to the
1245 newer format and adds support for new-style requests (e.g. "--new 4:add").
1246
1247 @type mods: list of tuples
1248 @param mods: Modifications as given by command line parser
1249 @rtype: list of tuples
1250 @return: Modifications as understood by L{opcodes.OpInstanceSetParams}
1251
1252 """
1253 result = []
1254
1255 for (identifier, params) in mods:
1256 if identifier == constants.DDM_ADD:
1257
1258 action = constants.DDM_ADD
1259 identifier = -1
1260 elif identifier == constants.DDM_ATTACH:
1261
1262 action = constants.DDM_ATTACH
1263 identifier = -1
1264 elif identifier == constants.DDM_REMOVE:
1265
1266 action = constants.DDM_REMOVE
1267 identifier = -1
1268 elif identifier == constants.DDM_DETACH:
1269
1270 action = constants.DDM_DETACH
1271 identifier = -1
1272 else:
1273
1274
1275 add = params.pop(constants.DDM_ADD, _MISSING)
1276 attach = params.pop(constants.DDM_ATTACH, _MISSING)
1277 remove = params.pop(constants.DDM_REMOVE, _MISSING)
1278 detach = params.pop(constants.DDM_DETACH, _MISSING)
1279 modify = params.pop(constants.DDM_MODIFY, _MISSING)
1280
1281
1282
1283 action = constants.DDM_MODIFY
1284 ops = {
1285 constants.DDM_ADD: add,
1286 constants.DDM_ATTACH: attach,
1287 constants.DDM_REMOVE: remove,
1288 constants.DDM_DETACH: detach,
1289 constants.DDM_MODIFY: modify,
1290 }
1291 count = 0
1292 for op, param in ops.items():
1293 if param is not _MISSING:
1294 count += 1
1295 action = op
1296 if count > 1:
1297 raise errors.OpPrereqError(
1298 "Cannot do more than one of the following operations at the"
1299 " same time: %s" % ", ".join(ops.keys()),
1300 errors.ECODE_INVAL)
1301
1302 assert not (constants.DDMS_VALUES_WITH_MODIFY & set(params.keys()))
1303
1304 if action in (constants.DDM_REMOVE, constants.DDM_DETACH) and params:
1305 raise errors.OpPrereqError("Not accepting parameters on removal/detach",
1306 errors.ECODE_INVAL)
1307
1308 result.append((action, identifier, params))
1309
1310 return result
1311
1312
1332
1333
1350
1351
1353 """Modifies an instance.
1354
1355 All parameters take effect only at the next restart of the instance.
1356
1357 @param opts: the command line options selected by the user
1358 @type args: list
1359 @param args: should contain only one element, the instance name
1360 @rtype: int
1361 @return: the desired exit code
1362
1363 """
1364 if not (opts.nics or opts.disks or opts.disk_template or opts.hvparams or
1365 opts.beparams or opts.os or opts.osparams or opts.osparams_private
1366 or opts.offline_inst or opts.online_inst or opts.runtime_mem or
1367 opts.new_primary_node or opts.instance_communication is not None):
1368 ToStderr("Please give at least one of the parameters.")
1369 return 1
1370
1371 for param in opts.beparams:
1372 if isinstance(opts.beparams[param], basestring):
1373 if opts.beparams[param].lower() == "default":
1374 opts.beparams[param] = constants.VALUE_DEFAULT
1375
1376 utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_COMPAT,
1377 allowed_values=[constants.VALUE_DEFAULT])
1378
1379 for param in opts.hvparams:
1380 if isinstance(opts.hvparams[param], basestring):
1381 if opts.hvparams[param].lower() == "default":
1382 opts.hvparams[param] = constants.VALUE_DEFAULT
1383
1384 utils.ForceDictType(opts.hvparams, constants.HVS_PARAMETER_TYPES,
1385 allowed_values=[constants.VALUE_DEFAULT])
1386 FixHvParams(opts.hvparams)
1387
1388 nics = _ConvertNicDiskModifications(opts.nics)
1389 for action, _, __ in nics:
1390 if action == constants.DDM_MODIFY and opts.hotplug and not opts.force:
1391 usertext = ("You are about to hot-modify a NIC. This will be done"
1392 " by removing the existing NIC and then adding a new one."
1393 " Network connection might be lost. Continue?")
1394 if not AskUser(usertext):
1395 return 1
1396
1397 disks = _ParseDiskSizes(_ConvertNicDiskModifications(opts.disks))
1398
1399
1400 if opts.disk_template:
1401 if (not opts.node and
1402 opts.disk_template in constants.DTS_INT_MIRROR):
1403 ToStderr("Changing the disk template to a mirrored one requires"
1404 " specifying a secondary node")
1405 return 1
1406 elif (opts.ext_params and
1407 opts.disk_template != constants.DT_EXT):
1408 ToStderr("Specifying ExtStorage parameters requires converting"
1409 " to the '%s' disk template" % constants.DT_EXT)
1410 return 1
1411 elif (not opts.ext_params and
1412 opts.disk_template == constants.DT_EXT):
1413 ToStderr("Provider option is missing, use either the"
1414 " '--ext-params' or '-e' option")
1415 return 1
1416
1417 if ((opts.file_driver or
1418 opts.file_storage_dir) and
1419 not opts.disk_template in constants.DTS_FILEBASED):
1420 ToStderr("Specifying file-based configuration arguments requires"
1421 " converting to a file-based disk template")
1422 return 1
1423
1424 ext_params = _ParseExtStorageParams(opts.ext_params)
1425
1426 if opts.offline_inst:
1427 offline = True
1428 elif opts.online_inst:
1429 offline = False
1430 else:
1431 offline = None
1432
1433 instance_comm = opts.instance_communication
1434
1435 op = opcodes.OpInstanceSetParams(instance_name=args[0],
1436 nics=nics,
1437 disks=disks,
1438 hotplug=opts.hotplug,
1439 hotplug_if_possible=opts.hotplug_if_possible,
1440 disk_template=opts.disk_template,
1441 ext_params=ext_params,
1442 file_driver=opts.file_driver,
1443 file_storage_dir=opts.file_storage_dir,
1444 remote_node=opts.node,
1445 pnode=opts.new_primary_node,
1446 hvparams=opts.hvparams,
1447 beparams=opts.beparams,
1448 runtime_mem=opts.runtime_mem,
1449 os_name=opts.os,
1450 osparams=opts.osparams,
1451 osparams_private=opts.osparams_private,
1452 force_variant=opts.force_variant,
1453 force=opts.force,
1454 wait_for_sync=opts.wait_for_sync,
1455 offline=offline,
1456 conflicts_check=opts.conflicts_check,
1457 ignore_ipolicy=opts.ignore_ipolicy,
1458 instance_communication=instance_comm)
1459
1460
1461 result = SubmitOrSend(op, opts)
1462
1463 if result:
1464 ToStdout("Modified instance %s", args[0])
1465 for param, data in result:
1466 ToStdout(" - %-5s -> %s", param, data)
1467 ToStdout("Please don't forget that most parameters take effect"
1468 " only at the next (re)start of the instance initiated by"
1469 " ganeti; restarting from within the instance will"
1470 " not be enough.")
1471 if opts.hvparams:
1472 ToStdout("Note that changing hypervisor parameters without performing a"
1473 " restart might lead to a crash while performing a live"
1474 " migration. This will be addressed in future Ganeti versions.")
1475 return 0
1476
1477
1479 """Moves an instance to another group.
1480
1481 """
1482 (instance_name, ) = args
1483
1484 cl = GetClient()
1485
1486 op = opcodes.OpInstanceChangeGroup(instance_name=instance_name,
1487 iallocator=opts.iallocator,
1488 target_groups=opts.to,
1489 early_release=opts.early_release)
1490 result = SubmitOrSend(op, opts, cl=cl)
1491
1492
1493 jex = JobExecutor(cl=cl, opts=opts)
1494
1495 for (status, job_id) in result[constants.JOB_IDS_KEY]:
1496 jex.AddJobId(None, status, job_id)
1497
1498 results = jex.GetResults()
1499 bad_cnt = len([row for row in results if not row[0]])
1500 if bad_cnt == 0:
1501 ToStdout("Instance '%s' changed group successfully.", instance_name)
1502 rcode = constants.EXIT_SUCCESS
1503 else:
1504 ToStdout("There were %s errors while changing group of instance '%s'.",
1505 bad_cnt, instance_name)
1506 rcode = constants.EXIT_FAILURE
1507
1508 return rcode
1509
1510
1511
1512 m_force_multi = cli_option("--force-multiple", dest="force_multi",
1513 help="Do not ask for confirmation when more than"
1514 " one instance is affected",
1515 action="store_true", default=False)
1516
1517 m_pri_node_opt = cli_option("--primary", dest="multi_mode",
1518 help="Filter by nodes (primary only)",
1519 const=_EXPAND_NODES_PRI, action="store_const")
1520
1521 m_sec_node_opt = cli_option("--secondary", dest="multi_mode",
1522 help="Filter by nodes (secondary only)",
1523 const=_EXPAND_NODES_SEC, action="store_const")
1524
1525 m_node_opt = cli_option("--node", dest="multi_mode",
1526 help="Filter by nodes (primary and secondary)",
1527 const=_EXPAND_NODES_BOTH, action="store_const")
1528
1529 m_clust_opt = cli_option("--all", dest="multi_mode",
1530 help="Select all instances in the cluster",
1531 const=_EXPAND_CLUSTER, action="store_const")
1532
1533 m_inst_opt = cli_option("--instance", dest="multi_mode",
1534 help="Filter by instance name [default]",
1535 const=_EXPAND_INSTANCES, action="store_const")
1536
1537 m_node_tags_opt = cli_option("--node-tags", dest="multi_mode",
1538 help="Filter by node tag",
1539 const=_EXPAND_NODES_BOTH_BY_TAGS,
1540 action="store_const")
1541
1542 m_pri_node_tags_opt = cli_option("--pri-node-tags", dest="multi_mode",
1543 help="Filter by primary node tag",
1544 const=_EXPAND_NODES_PRI_BY_TAGS,
1545 action="store_const")
1546
1547 m_sec_node_tags_opt = cli_option("--sec-node-tags", dest="multi_mode",
1548 help="Filter by secondary node tag",
1549 const=_EXPAND_NODES_SEC_BY_TAGS,
1550 action="store_const")
1551
1552 m_inst_tags_opt = cli_option("--tags", dest="multi_mode",
1553 help="Filter by instance tag",
1554 const=_EXPAND_INSTANCES_BY_TAGS,
1555 action="store_const")
1556
1557
1558 add_opts = [
1559 FORTHCOMING_OPT,
1560 COMMIT_OPT,
1561 NOSTART_OPT,
1562 OS_OPT,
1563 FORCE_VARIANT_OPT,
1564 NO_INSTALL_OPT,
1565 IGNORE_IPOLICY_OPT,
1566 INSTANCE_COMMUNICATION_OPT,
1567 HELPER_STARTUP_TIMEOUT_OPT,
1568 HELPER_SHUTDOWN_TIMEOUT_OPT,
1569 ]
1570
1571 commands = {
1572 "add": (
1573 AddInstance, [ArgHost(min=1, max=1)],
1574 COMMON_CREATE_OPTS + add_opts,
1575 "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
1576 "Creates and adds a new instance to the cluster"),
1577 "batch-create": (
1578 BatchCreate, [ArgFile(min=1, max=1)],
1579 [DRY_RUN_OPT, PRIORITY_OPT, IALLOCATOR_OPT] + SUBMIT_OPTS,
1580 "<instances.json>",
1581 "Create a bunch of instances based on specs in the file."),
1582 "console": (
1583 ConnectToInstanceConsole, ARGS_ONE_INSTANCE,
1584 [SHOWCMD_OPT, PRIORITY_OPT],
1585 "[--show-cmd] <instance>", "Opens a console on the specified instance"),
1586 "failover": (
1587 FailoverInstance, ARGS_ONE_INSTANCE,
1588 [FORCE_OPT, IGNORE_CONSIST_OPT] + SUBMIT_OPTS +
1589 [SHUTDOWN_TIMEOUT_OPT,
1590 DRY_RUN_OPT, PRIORITY_OPT, DST_NODE_OPT, IALLOCATOR_OPT,
1591 IGNORE_IPOLICY_OPT, CLEANUP_OPT],
1592 "[-f] <instance>", "Stops the instance, changes its primary node and"
1593 " (if it was originally running) starts it on the new node"
1594 " (the secondary for mirrored instances or any node"
1595 " for shared storage)."),
1596 "migrate": (
1597 MigrateInstance, ARGS_ONE_INSTANCE,
1598 [FORCE_OPT, NONLIVE_OPT, MIGRATION_MODE_OPT, CLEANUP_OPT, DRY_RUN_OPT,
1599 PRIORITY_OPT, DST_NODE_OPT, IALLOCATOR_OPT, ALLOW_FAILOVER_OPT,
1600 IGNORE_IPOLICY_OPT, IGNORE_HVVERSIONS_OPT, NORUNTIME_CHGS_OPT]
1601 + SUBMIT_OPTS,
1602 "[-f] <instance>", "Migrate instance to its secondary node"
1603 " (only for mirrored instances)"),
1604 "move": (
1605 MoveInstance, ARGS_ONE_INSTANCE,
1606 [FORCE_OPT] + SUBMIT_OPTS +
1607 [SINGLE_NODE_OPT, COMPRESS_OPT,
1608 SHUTDOWN_TIMEOUT_OPT, DRY_RUN_OPT, PRIORITY_OPT, IGNORE_CONSIST_OPT,
1609 IGNORE_IPOLICY_OPT],
1610 "[-f] <instance>", "Move instance to an arbitrary node"
1611 " (only for instances of type file and lv)"),
1612 "info": (
1613 ShowInstanceConfig, ARGS_MANY_INSTANCES,
1614 [STATIC_OPT, ALL_OPT, ROMAN_OPT, PRIORITY_OPT],
1615 "[-s] {--all | <instance>...}",
1616 "Show information on the specified instance(s)"),
1617 "list": (
1618 ListInstances, ARGS_MANY_INSTANCES,
1619 [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, VERBOSE_OPT,
1620 FORCE_FILTER_OPT],
1621 "[<instance>...]",
1622 "Lists the instances and their status. The available fields can be shown"
1623 " using the \"list-fields\" command (see the man page for details)."
1624 " The default field list is (in order): %s." %
1625 utils.CommaJoin(_LIST_DEF_FIELDS),
1626 ),
1627 "list-fields": (
1628 ListInstanceFields, [ArgUnknown()],
1629 [NOHDR_OPT, SEP_OPT],
1630 "[fields...]",
1631 "Lists all available fields for instances"),
1632 "reinstall": (
1633 ReinstallInstance, [ArgInstance()],
1634 [FORCE_OPT, OS_OPT, FORCE_VARIANT_OPT, m_force_multi, m_node_opt,
1635 m_pri_node_opt, m_sec_node_opt, m_clust_opt, m_inst_opt, m_node_tags_opt,
1636 m_pri_node_tags_opt, m_sec_node_tags_opt, m_inst_tags_opt, SELECT_OS_OPT]
1637 + SUBMIT_OPTS + [DRY_RUN_OPT, PRIORITY_OPT, OSPARAMS_OPT,
1638 OSPARAMS_PRIVATE_OPT, OSPARAMS_SECRET_OPT],
1639 "[-f] <instance>", "Reinstall a stopped instance"),
1640 "remove": (
1641 RemoveInstance, ARGS_ONE_INSTANCE,
1642 [FORCE_OPT, SHUTDOWN_TIMEOUT_OPT, IGNORE_FAILURES_OPT] + SUBMIT_OPTS
1643 + [DRY_RUN_OPT, PRIORITY_OPT],
1644 "[-f] <instance>", "Shuts down the instance and removes it"),
1645 "rename": (
1646 RenameInstance,
1647 [ArgInstance(min=1, max=1), ArgHost(min=1, max=1)],
1648 [NOIPCHECK_OPT, NONAMECHECK_OPT] + SUBMIT_OPTS
1649 + [DRY_RUN_OPT, PRIORITY_OPT],
1650 "<instance> <new_name>", "Rename the instance"),
1651 "replace-disks": (
1652 ReplaceDisks, ARGS_ONE_INSTANCE,
1653 [AUTO_REPLACE_OPT, DISKIDX_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT,
1654 NEW_SECONDARY_OPT, ON_PRIMARY_OPT, ON_SECONDARY_OPT] + SUBMIT_OPTS
1655 + [DRY_RUN_OPT, PRIORITY_OPT, IGNORE_IPOLICY_OPT],
1656 "[-s|-p|-a|-n NODE|-I NAME] <instance>",
1657 "Replaces disks for the instance"),
1658 "modify": (
1659 SetInstanceParams, ARGS_ONE_INSTANCE,
1660 [BACKEND_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, NET_OPT] + SUBMIT_OPTS +
1661 [DISK_TEMPLATE_OPT, SINGLE_NODE_OPT, OS_OPT, FORCE_VARIANT_OPT,
1662 OSPARAMS_OPT, OSPARAMS_PRIVATE_OPT, DRY_RUN_OPT, PRIORITY_OPT, NWSYNC_OPT,
1663 OFFLINE_INST_OPT, ONLINE_INST_OPT, IGNORE_IPOLICY_OPT, RUNTIME_MEM_OPT,
1664 NOCONFLICTSCHECK_OPT, NEW_PRIMARY_OPT, HOTPLUG_OPT,
1665 HOTPLUG_IF_POSSIBLE_OPT, INSTANCE_COMMUNICATION_OPT,
1666 EXT_PARAMS_OPT, FILESTORE_DRIVER_OPT, FILESTORE_DIR_OPT],
1667 "<instance>", "Alters the parameters of an instance"),
1668 "shutdown": (
1669 GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],
1670 [FORCE_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt, m_clust_opt,
1671 m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
1672 m_inst_tags_opt, m_inst_opt, m_force_multi, TIMEOUT_OPT] + SUBMIT_OPTS
1673 + [DRY_RUN_OPT, PRIORITY_OPT, IGNORE_OFFLINE_OPT, NO_REMEMBER_OPT],
1674 "<instance>", "Stops an instance"),
1675 "startup": (
1676 GenericManyOps("startup", _StartupInstance), [ArgInstance()],
1677 [FORCE_OPT, m_force_multi, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1678 m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
1679 m_inst_tags_opt, m_clust_opt, m_inst_opt] + SUBMIT_OPTS +
1680 [HVOPTS_OPT,
1681 BACKEND_OPT, DRY_RUN_OPT, PRIORITY_OPT, IGNORE_OFFLINE_OPT,
1682 NO_REMEMBER_OPT, STARTUP_PAUSED_OPT],
1683 "<instance>", "Starts an instance"),
1684 "reboot": (
1685 GenericManyOps("reboot", _RebootInstance), [ArgInstance()],
1686 [m_force_multi, REBOOT_TYPE_OPT, IGNORE_SECONDARIES_OPT, m_node_opt,
1687 m_pri_node_opt, m_sec_node_opt, m_clust_opt, m_inst_opt] + SUBMIT_OPTS +
1688 [m_node_tags_opt, m_pri_node_tags_opt, m_sec_node_tags_opt,
1689 m_inst_tags_opt, SHUTDOWN_TIMEOUT_OPT, DRY_RUN_OPT, PRIORITY_OPT],
1690 "<instance>", "Reboots an instance"),
1691 "activate-disks": (
1692 ActivateDisks, ARGS_ONE_INSTANCE,
1693 SUBMIT_OPTS + [IGNORE_SIZE_OPT, PRIORITY_OPT, WFSYNC_OPT],
1694 "<instance>", "Activate an instance's disks"),
1695 "deactivate-disks": (
1696 DeactivateDisks, ARGS_ONE_INSTANCE,
1697 [FORCE_OPT] + SUBMIT_OPTS + [DRY_RUN_OPT, PRIORITY_OPT],
1698 "[-f] <instance>", "Deactivate an instance's disks"),
1699 "recreate-disks": (
1700 RecreateDisks, ARGS_ONE_INSTANCE,
1701 SUBMIT_OPTS +
1702 [DISK_OPT, NODE_PLACEMENT_OPT, DRY_RUN_OPT, PRIORITY_OPT,
1703 IALLOCATOR_OPT],
1704 "<instance>", "Recreate an instance's disks"),
1705 "grow-disk": (
1706 GrowDisk,
1707 [ArgInstance(min=1, max=1), ArgUnknown(min=1, max=1),
1708 ArgUnknown(min=1, max=1)],
1709 SUBMIT_OPTS +
1710 [NWSYNC_OPT, DRY_RUN_OPT, PRIORITY_OPT, ABSOLUTE_OPT, IGNORE_IPOLICY_OPT],
1711 "<instance> <disk> <size>", "Grow an instance's disk"),
1712 "change-group": (
1713 ChangeGroup, ARGS_ONE_INSTANCE,
1714 [TO_GROUP_OPT, IALLOCATOR_OPT, EARLY_RELEASE_OPT, PRIORITY_OPT]
1715 + SUBMIT_OPTS,
1716 "[-I <iallocator>] [--to <group>]", "Change group of instance"),
1717 "list-tags": (
1718 ListTags, ARGS_ONE_INSTANCE, [],
1719 "<instance_name>", "List the tags of the given instance"),
1720 "add-tags": (
1721 AddTags, [ArgInstance(min=1, max=1), ArgUnknown()],
1722 [TAG_SRC_OPT, PRIORITY_OPT] + SUBMIT_OPTS,
1723 "<instance_name> tag...", "Add tags to the given instance"),
1724 "remove-tags": (
1725 RemoveTags, [ArgInstance(min=1, max=1), ArgUnknown()],
1726 [TAG_SRC_OPT, PRIORITY_OPT] + SUBMIT_OPTS,
1727 "<instance_name> tag...", "Remove tags from given instance"),
1728 }
1729
1730
1731 aliases = {
1732 "start": "startup",
1733 "stop": "shutdown",
1734 "show": "info",
1735 }
1736
1737
1739 return GenericMain(commands, aliases=aliases,
1740 override={"tag_type": constants.TAG_INSTANCE},
1741 env_override=_ENV_OVERRIDE)
1742