Improve the documentation of query output fields
[ganeti-local] / scripts / gnt-instance
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 import sys
23 import os
24 import itertools
25 from optparse import make_option
26 from cStringIO import StringIO
27
28 from ganeti.cli import *
29 from ganeti import opcodes
30 from ganeti import logger
31 from ganeti import constants
32 from ganeti import utils
33 from ganeti import errors
34
35
36 _SHUTDOWN_CLUSTER = "cluster"
37 _SHUTDOWN_NODES_BOTH = "nodes"
38 _SHUTDOWN_NODES_PRI = "nodes-pri"
39 _SHUTDOWN_NODES_SEC = "nodes-sec"
40 _SHUTDOWN_INSTANCES = "instances"
41
42 def _ExpandMultiNames(mode, names):
43   """Expand the given names using the passed mode.
44
45   Args:
46     - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
47       _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
48     - names, which is a list of names; for cluster, it must be empty,
49       and for node and instance it must be a list of valid item
50       names (short names are valid as usual, e.g. node1 instead of
51       node1.example.com)
52
53   For _SHUTDOWN_CLUSTER, all instances will be returned. For
54   _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
55   primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
56   instances having those nodes as either primary or secondary will be
57   returned. For _SHUTDOWN_INSTANCES, the given instances will be
58   returned.
59
60   """
61   if mode == _SHUTDOWN_CLUSTER:
62     if names:
63       raise errors.OpPrereqError("Cluster filter mode takes no arguments")
64     op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
65     idata = SubmitOpCode(op)
66     inames = [row[0] for row in idata]
67
68   elif mode in (_SHUTDOWN_NODES_BOTH,
69                 _SHUTDOWN_NODES_PRI,
70                 _SHUTDOWN_NODES_SEC):
71     if not names:
72       raise errors.OpPrereqError("No node names passed")
73     op = opcodes.OpQueryNodes(output_fields=["name", "pinst_list",
74                                              "sinst_list"], names=names)
75     ndata = SubmitOpCode(op)
76     ipri = [row[1] for row in ndata]
77     pri_names = list(itertools.chain(*ipri))
78     isec = [row[2] for row in ndata]
79     sec_names = list(itertools.chain(*isec))
80     if mode == _SHUTDOWN_NODES_BOTH:
81       inames = pri_names + sec_names
82     elif mode == _SHUTDOWN_NODES_PRI:
83       inames = pri_names
84     elif mode == _SHUTDOWN_NODES_SEC:
85       inames = sec_names
86     else:
87       raise errors.ProgrammerError("Unhandled shutdown type")
88
89   elif mode == _SHUTDOWN_INSTANCES:
90     if not names:
91       raise errors.OpPrereqError("No instance names passed")
92     op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
93     idata = SubmitOpCode(op)
94     inames = [row[0] for row in idata]
95
96   else:
97     raise errors.OpPrereqError("Unknown mode '%s'" % mode)
98
99   return inames
100
101
102 def _ConfirmOperation(inames, text):
103   """Ask the user to confirm an operation on a list of instances.
104
105   This function is used to request confirmation for doing an operation
106   on a given list of instances.
107
108   The inames argument is what the selection algorithm computed, and
109   the text argument is the operation we should tell the user to
110   confirm (e.g. 'shutdown' or 'startup').
111
112   Returns: boolean depending on user's confirmation.
113
114   """
115   count = len(inames)
116   msg = ("The %s will operate on %d instances.\n"
117          "Do you want to continue?" % (text, count))
118   affected = ("\nAffected instances:\n" +
119               "\n".join(["  %s" % name for name in inames]))
120
121   choices = [('y', True, 'Yes, execute the %s' % text),
122              ('n', False, 'No, abort the %s' % text)]
123
124   if count > 20:
125     choices.insert(1, ('v', 'v', 'View the list of affected instances'))
126     ask = msg
127   else:
128     ask = msg + affected
129
130   choice = AskUser(ask, choices)
131   if choice == 'v':
132     choices.pop(1)
133     choice = AskUser(choices, msg + affected)
134   return choice
135
136
137 def _TransformPath(user_input):
138   """Transform a user path into a canonical value.
139
140   This function transforms the a path passed as textual information
141   into the constants that the LU code expects.
142
143   """
144   if user_input:
145     if user_input.lower() == "default":
146       result_path = constants.VALUE_DEFAULT
147     elif user_input.lower() == "none":
148       result_path = constants.VALUE_NONE
149     else:
150       if not os.path.isabs(user_input):
151         raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
152                                    user_input)
153       result_path = user_input
154   else:
155     result_path = constants.VALUE_DEFAULT
156
157   return result_path
158
159
160 def ListInstances(opts, args):
161   """List instances and their properties.
162
163   """
164   if opts.output is None:
165     selected_fields = ["name", "os", "pnode", "admin_state",
166                        "oper_state", "oper_ram"]
167   else:
168     selected_fields = opts.output.split(",")
169
170   op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
171   output = SubmitOpCode(op)
172
173   if not opts.no_headers:
174     headers = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
175                "snodes": "Secondary_Nodes", "admin_state": "Autostart",
176                "oper_state": "Status", "admin_ram": "Configured_memory",
177                "oper_ram": "Memory", "disk_template": "Disk_template",
178                "ip": "IP Address", "mac": "MAC Address",
179                "bridge": "Bridge", "vcpus": "VCPUs",
180                "sda_size": "Disk/0", "sdb_size": "Disk/1"}
181   else:
182     headers = None
183
184   if opts.human_readable:
185     unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
186   else:
187     unitfields = None
188
189   numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
190
191   # change raw values to nicer strings
192   for row in output:
193     for idx, field in enumerate(selected_fields):
194       val = row[idx]
195       if field == "snodes":
196         val = ",".join(val) or "-"
197       elif field == "admin_state":
198         if val:
199           val = "yes"
200         else:
201           val = "no"
202       elif field == "oper_state":
203         if val is None:
204           val = "(node down)"
205         elif val: # True
206           val = "running"
207         else:
208           val = "stopped"
209       elif field == "oper_ram":
210         if val is None:
211           val = "(node down)"
212       elif field == "sda_size" or field == "sdb_size":
213         if val is None:
214           val = "N/A"
215       row[idx] = str(val)
216
217   data = GenerateTable(separator=opts.separator, headers=headers,
218                        fields=selected_fields, unitfields=unitfields,
219                        numfields=numfields, data=output)
220
221   for line in data:
222     logger.ToStdout(line)
223
224   return 0
225
226
227 def AddInstance(opts, args):
228   """Add an instance to the cluster.
229
230   Args:
231     opts - class with options as members
232     args - list with a single element, the instance name
233   Opts used:
234     mem - amount of memory to allocate to instance (MiB)
235     size - amount of disk space to allocate to instance (MiB)
236     os - which OS to run on instance
237     node - node to run new instance on
238
239   """
240   instance = args[0]
241
242   (pnode, snode) = SplitNodeOption(opts.node)
243
244   kernel_path = _TransformPath(opts.kernel_path)
245   initrd_path = _TransformPath(opts.initrd_path)
246
247   op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
248                                 disk_size=opts.size, swap_size=opts.swap,
249                                 disk_template=opts.disk_template,
250                                 mode=constants.INSTANCE_CREATE,
251                                 os_type=opts.os, pnode=pnode,
252                                 snode=snode, vcpus=opts.vcpus,
253                                 ip=opts.ip, bridge=opts.bridge,
254                                 start=opts.start, ip_check=opts.ip_check,
255                                 wait_for_sync=opts.wait_for_sync,
256                                 mac=opts.mac,
257                                 kernel_path=kernel_path,
258                                 initrd_path=initrd_path,
259                                 hvm_boot_order=opts.hvm_boot_order)
260   SubmitOpCode(op)
261   return 0
262
263
264 def ReinstallInstance(opts, args):
265   """Reinstall an instance.
266
267   Args:
268     opts - class with options as members
269     args - list containing a single element, the instance name
270
271   """
272   instance_name = args[0]
273
274   if not opts.force:
275     usertext = ("This will reinstall the instance %s and remove"
276                 " all data. Continue?") % instance_name
277     if not AskUser(usertext):
278       return 1
279
280   op = opcodes.OpReinstallInstance(instance_name=instance_name,
281                                    os_type=opts.os)
282   SubmitOpCode(op)
283
284   return 0
285
286
287 def RemoveInstance(opts, args):
288   """Remove an instance.
289
290   Args:
291     opts - class with options as members
292     args - list containing a single element, the instance name
293
294   """
295   instance_name = args[0]
296   force = opts.force
297
298   if not force:
299     usertext = ("This will remove the volumes of the instance %s"
300                 " (including mirrors), thus removing all the data"
301                 " of the instance. Continue?") % instance_name
302     if not AskUser(usertext):
303       return 1
304
305   op = opcodes.OpRemoveInstance(instance_name=instance_name,
306                                 ignore_failures=opts.ignore_failures)
307   SubmitOpCode(op)
308   return 0
309
310
311 def RenameInstance(opts, args):
312   """Rename an instance.
313
314   Args:
315     opts - class with options as members
316     args - list containing two elements, the instance name and the new name
317
318   """
319   op = opcodes.OpRenameInstance(instance_name=args[0],
320                                 new_name=args[1],
321                                 ignore_ip=opts.ignore_ip)
322   SubmitOpCode(op)
323
324   return 0
325
326
327 def ActivateDisks(opts, args):
328   """Activate an instance's disks.
329
330   This serves two purposes:
331     - it allows one (as long as the instance is not running) to mount
332     the disks and modify them from the node
333     - it repairs inactive secondary drbds
334
335   """
336   instance_name = args[0]
337   op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
338   disks_info = SubmitOpCode(op)
339   for host, iname, nname in disks_info:
340     print "%s:%s:%s" % (host, iname, nname)
341   return 0
342
343
344 def DeactivateDisks(opts, args):
345   """Command-line interface for _ShutdownInstanceBlockDevices.
346
347   This function takes the instance name, looks for its primary node
348   and the tries to shutdown its block devices on that node.
349
350   """
351   instance_name = args[0]
352   op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
353   SubmitOpCode(op)
354   return 0
355
356
357 def StartupInstance(opts, args):
358   """Startup an instance.
359
360   Args:
361     opts - class with options as members
362     args - list containing a single element, the instance name
363
364   """
365   if opts.multi_mode is None:
366     opts.multi_mode = _SHUTDOWN_INSTANCES
367   inames = _ExpandMultiNames(opts.multi_mode, args)
368   if not inames:
369     raise errors.OpPrereqError("Selection filter does not match any instances")
370   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
371   if not (opts.force_multi or not multi_on
372           or _ConfirmOperation(inames, "startup")):
373     return 1
374   for name in inames:
375     op = opcodes.OpStartupInstance(instance_name=name,
376                                    force=opts.force,
377                                    extra_args=opts.extra_args)
378     if multi_on:
379       logger.ToStdout("Starting up %s" % name)
380     SubmitOpCode(op)
381   return 0
382
383 def RebootInstance(opts, args):
384   """Reboot an instance
385
386   Args:
387     opts - class with options as members
388     args - list containing a single element, the instance name
389
390   """
391   if opts.multi_mode is None:
392     opts.multi_mode = _SHUTDOWN_INSTANCES
393   inames = _ExpandMultiNames(opts.multi_mode, args)
394   if not inames:
395     raise errors.OpPrereqError("Selection filter does not match any instances")
396   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
397   if not (opts.force_multi or not multi_on
398           or _ConfirmOperation(inames, "reboot")):
399     return 1
400   for name in inames:
401     op = opcodes.OpRebootInstance(instance_name=name,
402                                   reboot_type=opts.reboot_type,
403                                   ignore_secondaries=opts.ignore_secondaries)
404
405     SubmitOpCode(op)
406   return 0
407
408 def ShutdownInstance(opts, args):
409   """Shutdown an instance.
410
411   Args:
412     opts - class with options as members
413     args - list containing a single element, the instance name
414
415   """
416   if opts.multi_mode is None:
417     opts.multi_mode = _SHUTDOWN_INSTANCES
418   inames = _ExpandMultiNames(opts.multi_mode, args)
419   if not inames:
420     raise errors.OpPrereqError("Selection filter does not match any instances")
421   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
422   if not (opts.force_multi or not multi_on
423           or _ConfirmOperation(inames, "shutdown")):
424     return 1
425   for name in inames:
426     op = opcodes.OpShutdownInstance(instance_name=name)
427     if multi_on:
428       logger.ToStdout("Shutting down %s" % name)
429     SubmitOpCode(op)
430   return 0
431
432
433 def AddMDDRBDComponent(opts, args):
434   """Add a new component to a remote_raid1 disk.
435
436   Args:
437     opts - class with options as members
438     args - list with a single element, the instance name
439
440   """
441   op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
442                                     disk_name=opts.disk,
443                                     remote_node=opts.node)
444   SubmitOpCode(op)
445   return 0
446
447
448 def RemoveMDDRBDComponent(opts, args):
449   """Remove a component from a remote_raid1 disk.
450
451   Args:
452     opts - class with options as members
453     args - list with a single element, the instance name
454
455   """
456   op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
457                                        disk_name=opts.disk,
458                                        disk_id=opts.port)
459   SubmitOpCode(op)
460   return 0
461
462
463 def ReplaceDisks(opts, args):
464   """Replace the disks of an instance
465
466   Args:
467     opts - class with options as members
468     args - list with a single element, the instance name
469
470   """
471   instance_name = args[0]
472   new_2ndary = opts.new_secondary
473   if opts.disks is None:
474     disks = ["sda", "sdb"]
475   else:
476     disks = opts.disks.split(",")
477   if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
478     mode = constants.REPLACE_DISK_ALL
479   elif opts.on_primary: # only on primary:
480     mode = constants.REPLACE_DISK_PRI
481     if new_2ndary is not None:
482       raise errors.OpPrereqError("Can't change secondary node on primary disk"
483                                  " replacement")
484   elif opts.on_secondary is not None: # only on secondary
485     mode = constants.REPLACE_DISK_SEC
486
487   op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
488                               remote_node=new_2ndary, mode=mode)
489   SubmitOpCode(op)
490   return 0
491
492
493 def FailoverInstance(opts, args):
494   """Failover an instance.
495
496   The failover is done by shutting it down on its present node and
497   starting it on the secondary.
498
499   Args:
500     opts - class with options as members
501     args - list with a single element, the instance name
502   Opts used:
503     force - whether to failover without asking questions.
504
505   """
506   instance_name = args[0]
507   force = opts.force
508
509   if not force:
510     usertext = ("Failover will happen to image %s."
511                 " This requires a shutdown of the instance. Continue?" %
512                 (instance_name,))
513     if not AskUser(usertext):
514       return 1
515
516   op = opcodes.OpFailoverInstance(instance_name=instance_name,
517                                   ignore_consistency=opts.ignore_consistency)
518   SubmitOpCode(op)
519   return 0
520
521
522 def ConnectToInstanceConsole(opts, args):
523   """Connect to the console of an instance.
524
525   Args:
526     opts - class with options as members
527     args - list with a single element, the instance name
528
529   """
530   instance_name = args[0]
531
532   op = opcodes.OpConnectConsole(instance_name=instance_name)
533   cmd, argv = SubmitOpCode(op)
534   # drop lock and exec so other commands can run while we have console
535   utils.Unlock("cmd")
536   try:
537     os.execvp(cmd, argv)
538   finally:
539     sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
540                      (cmd, " ".join(argv)))
541     os._exit(1)
542
543
544 def _FormatBlockDevInfo(buf, dev, indent_level):
545   """Show block device information.
546
547   This is only used by ShowInstanceConfig(), but it's too big to be
548   left for an inline definition.
549
550   """
551   def helper(buf, dtype, status):
552     """Format one line for phsyical device status."""
553     if not status:
554       buf.write("not active\n")
555     else:
556       (path, major, minor, syncp, estt, degr, ldisk) = status
557       buf.write("%s (%d:%d)" % (path, major, minor))
558       if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
559         if syncp is not None:
560           sync_text = "*RECOVERING* %5.2f%%," % syncp
561           if estt:
562             sync_text += " ETA %ds" % estt
563           else:
564             sync_text += " ETA unknown"
565         else:
566           sync_text = "in sync"
567         if degr:
568           degr_text = "*DEGRADED*"
569         else:
570           degr_text = "ok"
571         if ldisk:
572           ldisk_text = " *MISSING DISK*"
573         else:
574           ldisk_text = ""
575         buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
576       elif dtype == constants.LD_LV:
577         if ldisk:
578           ldisk_text = " *FAILED* (failed drive?)"
579         else:
580           ldisk_text = ""
581         buf.write(ldisk_text)
582       buf.write("\n")
583
584   if dev["iv_name"] is not None:
585     data = "  - %s, " % dev["iv_name"]
586   else:
587     data = "  - "
588   data += "type: %s" % dev["dev_type"]
589   if dev["logical_id"] is not None:
590     data += ", logical_id: %s" % (dev["logical_id"],)
591   elif dev["physical_id"] is not None:
592     data += ", physical_id: %s" % (dev["physical_id"],)
593   buf.write("%*s%s\n" % (2*indent_level, "", data))
594   buf.write("%*s    primary:   " % (2*indent_level, ""))
595   helper(buf, dev["dev_type"], dev["pstatus"])
596
597   if dev["sstatus"]:
598     buf.write("%*s    secondary: " % (2*indent_level, ""))
599     helper(buf, dev["dev_type"], dev["sstatus"])
600
601   if dev["children"]:
602     for child in dev["children"]:
603       _FormatBlockDevInfo(buf, child, indent_level+1)
604
605
606 def ShowInstanceConfig(opts, args):
607   """Compute instance run-time status.
608
609   """
610   retcode = 0
611   op = opcodes.OpQueryInstanceData(instances=args)
612   result = SubmitOpCode(op)
613
614   if not result:
615     logger.ToStdout("No instances.")
616     return 1
617
618   buf = StringIO()
619   retcode = 0
620   for instance_name in result:
621     instance = result[instance_name]
622     buf.write("Instance name: %s\n" % instance["name"])
623     buf.write("State: configured to be %s, actual state is %s\n" %
624               (instance["config_state"], instance["run_state"]))
625     buf.write("  Nodes:\n")
626     buf.write("    - primary: %s\n" % instance["pnode"])
627     buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
628     buf.write("  Operating system: %s\n" % instance["os"])
629     buf.write("  Allocated network port: %s\n" % instance["network_port"])
630     if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
631       kpath = "(default: %s)" % constants.XEN_KERNEL
632     else:
633       kpath = instance["kernel_path"]
634     buf.write("  Kernel path: %s\n" % kpath)
635     if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
636       initrd = "(default: %s)" % constants.XEN_INITRD
637     elif instance["initrd_path"] == constants.VALUE_NONE:
638       initrd = "(none)"
639     else:
640       initrd = instance["initrd_path"]
641     buf.write("       initrd: %s\n" % initrd)
642     buf.write("  HVM boot order: %s\n" % instance["hvm_boot_order"])
643     buf.write("  Hardware:\n")
644     buf.write("    - VCPUs: %d\n" % instance["vcpus"])
645     buf.write("    - memory: %dMiB\n" % instance["memory"])
646     buf.write("    - NICs: %s\n" %
647         ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
648                    (mac, ip, bridge)
649                      for mac, ip, bridge in instance["nics"]]))
650     buf.write("  Block devices:\n")
651
652     for device in instance["disks"]:
653       _FormatBlockDevInfo(buf, device, 1)
654
655   logger.ToStdout(buf.getvalue().rstrip('\n'))
656   return retcode
657
658
659 def SetInstanceParms(opts, args):
660   """Modifies an instance.
661
662   All parameters take effect only at the next restart of the instance.
663
664   Args:
665     opts - class with options as members
666     args - list with a single element, the instance name
667   Opts used:
668     memory - the new memory size
669     vcpus - the new number of cpus
670     mac - the new MAC address of the instance
671
672   """
673   if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
674           opts.kernel_path or opts.initrd_path or opts.hvm_boot_order):
675     logger.ToStdout("Please give at least one of the parameters.")
676     return 1
677
678   kernel_path = _TransformPath(opts.kernel_path)
679   initrd_path = _TransformPath(opts.initrd_path)
680   if opts.hvm_boot_order == 'default':
681     hvm_boot_order = constants.VALUE_DEFAULT
682   else:
683     hvm_boot_order = opts.hvm_boot_order
684
685   op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
686                                   vcpus=opts.vcpus, ip=opts.ip,
687                                   bridge=opts.bridge, mac=opts.mac,
688                                   kernel_path=opts.kernel_path,
689                                   initrd_path=opts.initrd_path,
690                                   hvm_boot_order=hvm_boot_order)
691   result = SubmitOpCode(op)
692
693   if result:
694     logger.ToStdout("Modified instance %s" % args[0])
695     for param, data in result:
696       logger.ToStdout(" - %-5s -> %s" % (param, data))
697     logger.ToStdout("Please don't forget that these parameters take effect"
698                     " only at the next start of the instance.")
699   return 0
700
701
702 # options used in more than one cmd
703 node_opt = make_option("-n", "--node", dest="node", help="Target node",
704                        metavar="<node>")
705
706 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
707                     metavar="<os>")
708
709 # multi-instance selection options
710 m_force_multi = make_option("--force-multiple", dest="force_multi",
711                             help="Do not ask for confirmation when more than"
712                             " one instance is affected",
713                             action="store_true", default=False)
714
715 m_pri_node_opt = make_option("--primary", dest="multi_mode",
716                              help="Filter by nodes (primary only)",
717                              const=_SHUTDOWN_NODES_PRI, action="store_const")
718
719 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
720                              help="Filter by nodes (secondary only)",
721                              const=_SHUTDOWN_NODES_SEC, action="store_const")
722
723 m_node_opt = make_option("--node", dest="multi_mode",
724                          help="Filter by nodes (primary and secondary)",
725                          const=_SHUTDOWN_NODES_BOTH, action="store_const")
726
727 m_clust_opt = make_option("--all", dest="multi_mode",
728                           help="Select all instances in the cluster",
729                           const=_SHUTDOWN_CLUSTER, action="store_const")
730
731 m_inst_opt = make_option("--instance", dest="multi_mode",
732                          help="Filter by instance name [default]",
733                          const=_SHUTDOWN_INSTANCES, action="store_const")
734
735
736 # this is defined separately due to readability only
737 add_opts = [
738   DEBUG_OPT,
739   make_option("-n", "--node", dest="node",
740               help="Target node and optional secondary node",
741               metavar="<pnode>[:<snode>]"),
742   cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
743              " a suffix is used",
744              default=20 * 1024, type="unit", metavar="<size>"),
745   cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
746              " suffix is used",
747              default=4 * 1024, type="unit", metavar="<size>"),
748   os_opt,
749   cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
750               default=128, type="unit", metavar="<mem>"),
751   make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
752               default=1, type="int", metavar="<PROC>"),
753   make_option("-t", "--disk-template", dest="disk_template",
754               help="Custom disk setup (diskless, plain, local_raid1,"
755               " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
756   make_option("-i", "--ip", dest="ip",
757               help="IP address ('none' [default], 'auto', or specify address)",
758               default='none', type="string", metavar="<ADDRESS>"),
759   make_option("--mac", dest="mac",
760               help="MAC address ('auto' [default], or specify address)",
761               default='auto', type="string", metavar="<MACADDRESS>"),
762   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
763               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
764   make_option("-b", "--bridge", dest="bridge",
765               help="Bridge to connect this instance to",
766               default=None, metavar="<bridge>"),
767   make_option("--no-start", dest="start", default=True,
768               action="store_false", help="Don't start the instance after"
769               " creation"),
770   make_option("--no-ip-check", dest="ip_check", default=True,
771               action="store_false", help="Don't check that the instance's IP"
772               " is alive (only valid with --no-start)"),
773   make_option("--kernel", dest="kernel_path",
774               help="Path to the instances' kernel (or 'default')",
775               default=None,
776               type="string", metavar="<FILENAME>"),
777   make_option("--initrd", dest="initrd_path",
778               help="Path to the instances' initrd (or 'none', or 'default')",
779               default=None,
780               type="string", metavar="<FILENAME>"),
781   make_option("--hvm-boot-order", dest="hvm_boot_order",
782               help="boot device order for HVM (one or more of [acdn])",
783               default=None, type="string", metavar="<BOOTORDER>"),
784   ]
785
786 commands = {
787   'add': (AddInstance, ARGS_ONE, add_opts,
788           "[opts...] <name>",
789           "Creates and adds a new instance to the cluster"),
790   'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
791                 [DEBUG_OPT, node_opt,
792                  make_option("-b", "--disk", dest="disk", metavar="sdX",
793                              help=("The name of the instance disk for which to"
794                                    " add the mirror"))],
795                 "-n node -b disk <instance>",
796                 "Creates a new mirror for the instance"),
797   'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
798               "<instance>",
799               "Opens a console on the specified instance"),
800   'failover': (FailoverInstance, ARGS_ONE,
801                [DEBUG_OPT, FORCE_OPT,
802                 make_option("--ignore-consistency", dest="ignore_consistency",
803                             action="store_true", default=False,
804                             help="Ignore the consistency of the disks on"
805                             " the secondary"),
806                 ],
807                "[-f] <instance>",
808                "Stops the instance and starts it on the backup node, using"
809                " the remote mirror (only for instances of type remote_raid1)"),
810   'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
811            "Show information on the specified instance"),
812   'list': (ListInstances, ARGS_NONE,
813            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
814            "", "Lists the instances and their status. The available fields"
815            " are (see the man page for details): oper_state, oper_ram,"
816            " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
817            " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
818            " list is (in order): name, os, pnode, admin_state, oper_state,"
819            " oper_ram."),
820   'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
821                 "[-f] <instance>", "Reinstall the instance"),
822   'remove': (RemoveInstance, ARGS_ONE,
823              [DEBUG_OPT, FORCE_OPT,
824               make_option("--ignore-failures", dest="ignore_failures",
825                           action="store_true", default=False,
826                           help=("Remove the instance from the cluster even"
827                                 " if there are failures during the removal"
828                                 " process (shutdown, disk removal, etc.)")),
829               ],
830              "[-f] <instance>", "Shuts down the instance and removes it"),
831   'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
832                    [DEBUG_OPT, node_opt,
833                     make_option("-b", "--disk", dest="disk", metavar="sdX",
834                                 help=("The name of the instance disk"
835                                       " for which to add the mirror")),
836                     make_option("-p", "--port", dest="port", metavar="PORT",
837                                 help=("The port of the drbd device"
838                                       " which to remove from the mirror"),
839                                 type="int"),
840                     ],
841                    "-b disk -p port <instance>",
842                    "Removes a mirror from the instance"),
843   'rename': (RenameInstance, ARGS_FIXED(2),
844              [DEBUG_OPT,
845               make_option("--no-ip-check", dest="ignore_ip",
846                           help="Do not check that the IP of the new name"
847                           " is alive",
848                           default=False, action="store_true"),
849               ],
850              "<instance> <new_name>", "Rename the instance"),
851   'replace-disks': (ReplaceDisks, ARGS_ONE,
852                     [DEBUG_OPT,
853                      make_option("-n", "--new-secondary", dest="new_secondary",
854                                  help=("New secondary node (for secondary"
855                                        " node change)"), metavar="NODE"),
856                      make_option("-p", "--on-primary", dest="on_primary",
857                                  default=False, action="store_true",
858                                  help=("Replace the disk(s) on the primary"
859                                        " node (only for the drbd template)")),
860                      make_option("-s", "--on-secondary", dest="on_secondary",
861                                  default=False, action="store_true",
862                                  help=("Replace the disk(s) on the secondary"
863                                        " node (only for the drbd template)")),
864                      make_option("--disks", dest="disks", default=None,
865                                  help=("Comma-separated list of disks"
866                                        " to replace (e.g. sda) (optional,"
867                                        " defaults to all disks")),
868                      ],
869                     "[-n NODE] <instance>",
870                     "Replaces all disks for the instance"),
871   'modify': (SetInstanceParms, ARGS_ONE,
872              [DEBUG_OPT, FORCE_OPT,
873               cli_option("-m", "--memory", dest="mem",
874                          help="Memory size",
875                          default=None, type="unit", metavar="<mem>"),
876               make_option("-p", "--cpu", dest="vcpus",
877                           help="Number of virtual CPUs",
878                           default=None, type="int", metavar="<PROC>"),
879               make_option("-i", "--ip", dest="ip",
880                           help="IP address ('none' or numeric IP)",
881                           default=None, type="string", metavar="<ADDRESS>"),
882               make_option("-b", "--bridge", dest="bridge",
883                           help="Bridge to connect this instance to",
884                           default=None, type="string", metavar="<bridge>"),
885               make_option("--mac", dest="mac",
886                           help="MAC address", default=None,
887                           type="string", metavar="<MACADDRESS>"),
888               make_option("--kernel", dest="kernel_path",
889                           help="Path to the instances' kernel (or"
890                           " 'default')", default=None,
891                           type="string", metavar="<FILENAME>"),
892               make_option("--initrd", dest="initrd_path",
893                           help="Path to the instances' initrd (or 'none', or"
894                           " 'default')", default=None,
895                           type="string", metavar="<FILENAME>"),
896               make_option("--hvm-boot-order", dest="hvm_boot_order",
897                           help="boot device order for HVM"
898                           "(either one or more of [acdn] or 'default')",
899                           default=None, type="string", metavar="<BOOTORDER>"),
900               ],
901              "<instance>", "Alters the parameters of an instance"),
902   'shutdown': (ShutdownInstance, ARGS_ANY,
903                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
904                 m_clust_opt, m_inst_opt, m_force_multi],
905                "<instance>", "Stops an instance"),
906   'startup': (StartupInstance, ARGS_ANY,
907               [DEBUG_OPT, FORCE_OPT, m_force_multi,
908                make_option("-e", "--extra", dest="extra_args",
909                            help="Extra arguments for the instance's kernel",
910                            default=None, type="string", metavar="<PARAMS>"),
911                m_node_opt, m_pri_node_opt, m_sec_node_opt,
912                m_clust_opt, m_inst_opt,
913                ],
914             "<instance>", "Starts an instance"),
915
916   'reboot': (RebootInstance, ARGS_ANY,
917               [DEBUG_OPT, m_force_multi,
918                make_option("-e", "--extra", dest="extra_args",
919                            help="Extra arguments for the instance's kernel",
920                            default=None, type="string", metavar="<PARAMS>"),
921                make_option("-t", "--type", dest="reboot_type",
922                            help="Type of reboot: soft/hard/full",
923                            default=constants.INSTANCE_REBOOT_SOFT,
924                            type="string", metavar="<REBOOT>"),
925                make_option("--ignore-secondaries", dest="ignore_secondaries",
926                            default=False, action="store_true",
927                            help="Ignore errors from secondaries"),
928                m_node_opt, m_pri_node_opt, m_sec_node_opt,
929                m_clust_opt, m_inst_opt,
930                ],
931             "<instance>", "Reboots an instance"),
932   'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
933                      "<instance>",
934                      "Activate an instance's disks"),
935   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
936                        "<instance>",
937                        "Deactivate an instance's disks"),
938   'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
939                 "<node_name>", "List the tags of the given instance"),
940   'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
941                "<node_name> tag...", "Add tags to the given instance"),
942   'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
943                   "<node_name> tag...", "Remove tags from given instance"),
944   }
945
946 aliases = {
947   'activate_block_devs': 'activate-disks',
948   'start': 'startup',
949   'stop': 'shutdown',
950   }
951
952 if __name__ == '__main__':
953   sys.exit(GenericMain(commands, aliases=aliases,
954                        override={"tag_type": constants.TAG_INSTANCE}))