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