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