Implement instance rename operation
[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 ListInstances(opts, args):
103   """List nodes and their properties.
104
105   """
106   if opts.output is None:
107     selected_fields = ["name", "os", "pnode", "admin_state",
108                        "oper_state", "oper_ram"]
109   else:
110     selected_fields = opts.output.split(",")
111
112   op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
113   output = SubmitOpCode(op)
114
115   if not opts.no_headers:
116     headers = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
117                "snodes": "Secondary_Nodes", "admin_state": "Autostart",
118                "oper_state": "Status", "admin_ram": "Configured_memory",
119                "oper_ram": "Memory", "disk_template": "Disk_template",
120                "ip": "IP Address", "mac": "MAC Address",
121                "bridge": "Bridge",
122                "sda_size": "Disk/0", "sdb_size": "Disk/1"}
123   else:
124     headers = None
125
126   if opts.human_readable:
127     unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
128   else:
129     unitfields = None
130
131   numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
132
133   # change raw values to nicer strings
134   for row in output:
135     for idx, field in enumerate(selected_fields):
136       val = row[idx]
137       if field == "snodes":
138         val = ",".join(val) or "-"
139       elif field == "admin_state":
140         if val:
141           val = "yes"
142         else:
143           val = "no"
144       elif field == "oper_state":
145         if val is None:
146           val = "(node down)"
147         elif val: # True
148           val = "running"
149         else:
150           val = "stopped"
151       elif field == "oper_ram":
152         if val is None:
153           val = "(node down)"
154       elif field == "sda_size" or field == "sdb_size":
155         if val is None:
156           val = "N/A"
157       row[idx] = str(val)
158
159   data = GenerateTable(separator=opts.separator, headers=headers,
160                        fields=selected_fields, unitfields=unitfields,
161                        numfields=numfields, data=output)
162
163   for line in data:
164     logger.ToStdout(line)
165
166   return 0
167
168
169 def AddInstance(opts, args):
170   """Add an instance to the cluster.
171
172   Args:
173     opts - class with options as members
174     args - list with a single element, the instance name
175   Opts used:
176     mem - amount of memory to allocate to instance (MiB)
177     size - amount of disk space to allocate to instance (MiB)
178     os - which OS to run on instance
179     node - node to run new instance on
180
181   """
182   instance = args[0]
183
184   op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
185                                 disk_size=opts.size, swap_size=opts.swap,
186                                 disk_template=opts.disk_template,
187                                 mode=constants.INSTANCE_CREATE,
188                                 os_type=opts.os, pnode=opts.node,
189                                 snode=opts.snode, vcpus=opts.vcpus,
190                                 ip=opts.ip, bridge=opts.bridge, start=True,
191                                 wait_for_sync=opts.wait_for_sync)
192   SubmitOpCode(op)
193   return 0
194
195
196 def ReinstallInstance(opts, args):
197   """Reinstall an instance.
198
199   Args:
200     opts - class with options as members
201     args - list containing a single element, the instance name
202
203   """
204   instance_name = args[0]
205
206   if not opts.force:
207     usertext = ("This will reinstall the instance %s and remove "
208                 "all data. Continue?") % instance_name
209     if not opts._ask_user(usertext):
210       return 1
211
212   op = opcodes.OpReinstallInstance(instance_name=instance_name,
213                                    os_type=opts.os)
214   SubmitOpCode(op)
215
216   return 0
217
218
219 def RemoveInstance(opts, args):
220   """Remove an instance.
221
222   Args:
223     opts - class with options as members
224     args - list containing a single element, the instance name
225
226   """
227   instance_name = args[0]
228   force = opts.force
229
230   if not force:
231     usertext = ("This will remove the volumes of the instance %s"
232                 " (including mirrors), thus removing all the data"
233                 " of the instance. Continue?") % instance_name
234     if not opts._ask_user(usertext):
235       return 1
236
237   op = opcodes.OpRemoveInstance(instance_name=instance_name)
238   SubmitOpCode(op)
239   return 0
240
241
242 def RenameInstance(opts, args):
243   """Reinstall an instance.
244
245   Args:
246     opts - class with options as members
247     args - list containing two elements, the instance name and the new name
248
249   """
250   op = opcodes.OpRenameInstance(instance_name=args[0],
251                                 new_name=args[1],
252                                 ignore_ip=opts.ignore_ip)
253   SubmitOpCode(op)
254
255   return 0
256
257
258 def ActivateDisks(opts, args):
259   """Activate an instance's disks.
260
261   This serves two purposes:
262     - it allows one (as long as the instance is not running) to mount
263     the disks and modify them from the node
264     - it repairs inactive secondary drbds
265
266   """
267   instance_name = args[0]
268   op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
269   disks_info = SubmitOpCode(op)
270   for host, iname, nname in disks_info:
271     print "%s:%s:%s" % (host, iname, nname)
272   return 0
273
274
275 def DeactivateDisks(opts, args):
276   """Command-line interface for _ShutdownInstanceBlockDevices.
277
278   This function takes the instance name, looks for its primary node
279   and the tries to shutdown its block devices on that node.
280
281   """
282   instance_name = args[0]
283   op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
284   SubmitOpCode(op)
285   return 0
286
287
288 def StartupInstance(opts, args):
289   """Startup an instance.
290
291   Args:
292     opts - class with options as members
293     args - list containing a single element, the instance name
294
295   """
296   if opts.multi_mode is None:
297     opts.multi_mode = _SHUTDOWN_INSTANCES
298   inames = _ExpandMultiNames(opts.multi_mode, args)
299   multi_on = len(inames) > 1
300   for name in inames:
301     op = opcodes.OpStartupInstance(instance_name=name,
302                                    force=opts.force,
303                                    extra_args=opts.extra_args)
304     if multi_on:
305       logger.ToStdout("Starting up %s" % name)
306     SubmitOpCode(op)
307   return 0
308
309
310 def ShutdownInstance(opts, args):
311   """Shutdown an instance.
312
313   Args:
314     opts - class with options as members
315     args - list containing a single element, the instance name
316
317   """
318   if opts.multi_mode is None:
319     opts.multi_mode = _SHUTDOWN_INSTANCES
320   inames = _ExpandMultiNames(opts.multi_mode, args)
321   multi_on = len(inames) > 1
322   for name in inames:
323     op = opcodes.OpShutdownInstance(instance_name=name)
324     if multi_on:
325       logger.ToStdout("Shutting down %s" % name)
326     SubmitOpCode(op)
327   return 0
328
329
330 def AddMDDRBDComponent(opts, args):
331   """Add a new component to a remote_raid1 disk.
332
333   Args:
334     opts - class with options as members
335     args - list with a single element, the instance name
336
337   """
338   op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
339                                     disk_name=opts.disk,
340                                     remote_node=opts.node)
341   SubmitOpCode(op)
342   return 0
343
344
345 def RemoveMDDRBDComponent(opts, args):
346   """Remove a component from a remote_raid1 disk.
347
348   Args:
349     opts - class with options as members
350     args - list with a single element, the instance name
351
352   """
353   op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
354                                        disk_name=opts.disk,
355                                        disk_id=opts.port)
356   SubmitOpCode(op)
357   return 0
358
359
360 def ReplaceDisks(opts, args):
361   """Replace the disks of an instance
362
363   Args:
364     opts - class with options as members
365     args - list with a single element, the instance name
366
367   """
368   instance_name = args[0]
369   new_secondary = opts.new_secondary
370   op = opcodes.OpReplaceDisks(instance_name=args[0],
371                               remote_node=opts.new_secondary)
372   SubmitOpCode(op)
373   return 0
374
375
376 def FailoverInstance(opts, args):
377   """Failover an instance.
378
379   The failover is done by shutting it down on its present node and
380   starting it on the secondary.
381
382   Args:
383     opts - class with options as members
384     args - list with a single element, the instance name
385   Opts used:
386     force - whether to failover without asking questions.
387
388   """
389   instance_name = args[0]
390   force = opts.force
391
392   if not force:
393     usertext = ("Failover will happen to image %s."
394                 " This requires a shutdown of the instance. Continue?" %
395                 (instance_name,))
396     if not opts._ask_user(usertext):
397       return 1
398
399   op = opcodes.OpFailoverInstance(instance_name=instance_name,
400                                   ignore_consistency=opts.ignore_consistency)
401   SubmitOpCode(op)
402   return 0
403
404
405 def ConnectToInstanceConsole(opts, args):
406   """Connect to the console of an instance.
407
408   Args:
409     opts - class with options as members
410     args - list with a single element, the instance name
411
412   """
413   instance_name = args[0]
414
415   op = opcodes.OpConnectConsole(instance_name=instance_name)
416   cmd, argv = SubmitOpCode(op)
417   # drop lock and exec so other commands can run while we have console
418   utils.Unlock("cmd")
419   try:
420     os.execvp(cmd, argv)
421   finally:
422     sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
423                      (cmd, " ".join(argv)))
424     os._exit(1)
425
426
427 def _FormatBlockDevInfo(buf, dev, indent_level):
428   """Show block device information.
429
430   This is only used by ShowInstanceConfig(), but it's too big to be
431   left for an inline definition.
432
433   """
434   def helper(buf, dtype, status):
435     """Format one line for phsyical device status."""
436     if not status:
437       buf.write("not active\n")
438     else:
439       (path, major, minor, syncp, estt, degr) = status
440       buf.write("%s (%d:%d)" % (path, major, minor))
441       if dtype in ("md_raid1", "drbd"):
442         if syncp is not None:
443           sync_text = "*RECOVERING* %5.2f%%," % syncp
444           if estt:
445             sync_text += " ETA %ds" % estt
446           else:
447             sync_text += " ETA unknown"
448         else:
449           sync_text = "in sync"
450         if degr:
451           degr_text = "*DEGRADED*"
452         else:
453           degr_text = "ok"
454         buf.write(" %s, status %s" % (sync_text, degr_text))
455       buf.write("\n")
456
457   if dev["iv_name"] is not None:
458     data = "  - %s, " % dev["iv_name"]
459   else:
460     data = "  - "
461   data += "type: %s" % dev["dev_type"]
462   if dev["logical_id"] is not None:
463     data += ", logical_id: %s" % (dev["logical_id"],)
464   elif dev["physical_id"] is not None:
465     data += ", physical_id: %s" % (dev["physical_id"],)
466   buf.write("%*s%s\n" % (2*indent_level, "", data))
467   buf.write("%*s    primary:   " % (2*indent_level, ""))
468   helper(buf, dev["dev_type"], dev["pstatus"])
469
470   if dev["sstatus"]:
471     buf.write("%*s    secondary: " % (2*indent_level, ""))
472     helper(buf, dev["dev_type"], dev["sstatus"])
473
474   if dev["children"]:
475     for child in dev["children"]:
476       _FormatBlockDevInfo(buf, child, indent_level+1)
477
478
479 def ShowInstanceConfig(opts, args):
480   """Compute instance run-time status.
481
482   """
483   retcode = 0
484   op = opcodes.OpQueryInstanceData(instances=args)
485   result = SubmitOpCode(op)
486
487   if not result:
488     logger.ToStdout("No instances.")
489     return 1
490
491   buf = StringIO()
492   retcode = 0
493   for instance_name in result:
494     instance = result[instance_name]
495     buf.write("Instance name: %s\n" % instance["name"])
496     buf.write("State: configured to be %s, actual state is %s\n" %
497               (instance["config_state"], instance["run_state"]))
498     buf.write("  Nodes:\n")
499     buf.write("    - primary: %s\n" % instance["pnode"])
500     buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
501     buf.write("  Operating system: %s\n" % instance["os"])
502     buf.write("  Hardware:\n")
503     buf.write("    - memory: %dMiB\n" % instance["memory"])
504     buf.write("    - NICs: %s\n" %
505         ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
506                    (mac, ip, bridge)
507                      for mac, ip, bridge in instance["nics"]]))
508     buf.write("  Block devices:\n")
509
510     for device in instance["disks"]:
511       _FormatBlockDevInfo(buf, device, 1)
512
513   logger.ToStdout(buf.getvalue().rstrip('\n'))
514   return retcode
515
516
517 def SetInstanceParms(opts, args):
518   """Modifies an instance.
519
520   All parameters take effect only at the next restart of the instance.
521
522   Args:
523     opts - class with options as members
524     args - list with a single element, the instance name
525   Opts used:
526     memory - the new memory size
527     vcpus - the new number of cpus
528
529   """
530   if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
531     logger.ToStdout("Please give at least one of the parameters.")
532     return 1
533
534   op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
535                                   vcpus=opts.vcpus, ip=opts.ip,
536                                   bridge=opts.bridge)
537   result = SubmitOpCode(op)
538
539   if result:
540     logger.ToStdout("Modified instance %s" % args[0])
541     for param, data in result:
542       logger.ToStdout(" - %-5s -> %s" % (param, data))
543     logger.ToStdout("Please don't forget that these parameters take effect"
544                     " only at the next start of the instance.")
545   return 0
546
547
548 # options used in more than one cmd
549 node_opt = make_option("-n", "--node", dest="node", help="Target node",
550                        metavar="<node>")
551
552 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
553                     metavar="<os>")
554
555 # multi-instance selection options
556 m_pri_node_opt = make_option("--primary", dest="multi_mode",
557                              help="Filter by nodes (primary only)",
558                              const=_SHUTDOWN_NODES_PRI, action="store_const")
559
560 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
561                              help="Filter by nodes (secondary only)",
562                              const=_SHUTDOWN_NODES_SEC, action="store_const")
563
564 m_node_opt = make_option("--node", dest="multi_mode",
565                          help="Filter by nodes (primary and secondary)",
566                          const=_SHUTDOWN_NODES_BOTH, action="store_const")
567
568 m_clust_opt = make_option("--all", dest="multi_mode",
569                           help="Select all instances in the cluster",
570                           const=_SHUTDOWN_CLUSTER, action="store_const")
571
572 m_inst_opt = make_option("--instance", dest="multi_mode",
573                          help="Filter by instance name [default]",
574                          const=_SHUTDOWN_INSTANCES, action="store_const")
575
576
577 # this is defined separately due to readability only
578 add_opts = [
579   DEBUG_OPT,
580   node_opt,
581   cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
582              " a suffix is used",
583              default=20 * 1024, type="unit", metavar="<size>"),
584   cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
585              " suffix is used",
586              default=4 * 1024, type="unit", metavar="<size>"),
587   os_opt,
588   cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
589               default=128, type="unit", metavar="<mem>"),
590   make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
591               default=1, type="int", metavar="<PROC>"),
592   make_option("-t", "--disk-template", dest="disk_template",
593               help="Custom disk setup (diskless, plain, local_raid1 or"
594               " remote_raid1)", default=None, metavar="TEMPL"),
595   make_option("-i", "--ip", dest="ip",
596               help="IP address ('none' [default], 'auto', or specify address)",
597               default='none', type="string", metavar="<ADDRESS>"),
598   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
599               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
600   make_option("--secondary-node", dest="snode",
601               help="Secondary node for remote_raid1 disk layout",
602               metavar="<node>"),
603   make_option("-b", "--bridge", dest="bridge",
604               help="Bridge to connect this instance to",
605               default=None, metavar="<bridge>")
606   ]
607
608 commands = {
609   'add': (AddInstance, ARGS_ONE, add_opts,
610           "[opts...] <name>",
611           "Creates and adds a new instance to the cluster"),
612   'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
613                 [DEBUG_OPT, node_opt,
614                  make_option("-b", "--disk", dest="disk", metavar="sdX",
615                              help=("The name of the instance disk for which to"
616                                    " add the mirror"))],
617                 "-n node -b disk <instance>",
618                 "Creates a new mirror for the instance"),
619   'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
620               "<instance>",
621               "Opens a console on the specified instance"),
622   'failover': (FailoverInstance, ARGS_ONE,
623                [DEBUG_OPT, FORCE_OPT,
624                 make_option("--ignore-consistency", dest="ignore_consistency",
625                             action="store_true", default=False,
626                             help="Ignore the consistency of the disks on"
627                             " the secondary"),
628                 ],
629                "[-f] <instance>",
630                "Stops the instance and starts it on the backup node, using"
631                " the remote mirror (only for instances of type remote_raid1)"),
632   'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
633            "Show information on the specified instance"),
634   'list': (ListInstances, ARGS_NONE,
635            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
636            "", "Lists the instances and their status"),
637   'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
638                 "[-f] <instance>", "Reinstall the instance"),
639   'remove': (RemoveInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
640              "[-f] <instance>", "Shuts down the instance and removes it"),
641   'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
642                    [DEBUG_OPT, node_opt,
643                     make_option("-b", "--disk", dest="disk", metavar="sdX",
644                                 help=("The name of the instance disk"
645                                       " for which to add the mirror")),
646                     make_option("-p", "--port", dest="port", metavar="PORT",
647                                 help=("The port of the drbd device"
648                                       " which to remove from the mirror"),
649                                 type="int"),
650                     ],
651                    "-b disk -p port <instance>",
652                    "Removes a mirror from the instance"),
653   'rename': (RenameInstance, ARGS_FIXED(2),
654              [DEBUG_OPT,
655               make_option("--no-ip-check", dest="ignore_ip",
656                           help="Do not check that the IP of the new name"
657                           " is alive",
658                           default=False, action="store_true"),
659               ],
660              "<instance> <new_name>", "Rename the instance"),
661   'replace-disks': (ReplaceDisks, ARGS_ONE,
662                     [DEBUG_OPT,
663                      make_option("-n", "--new-secondary", dest="new_secondary",
664                                  metavar="NODE",
665                                  help=("New secondary node (if you want to"
666                                        " change the secondary)"))],
667                     "[-n NODE] <instance>",
668                     "Replaces all disks for the instance"),
669   'modify': (SetInstanceParms, ARGS_ONE,
670              [DEBUG_OPT, FORCE_OPT,
671               cli_option("-m", "--memory", dest="mem",
672                          help="Memory size",
673                          default=None, type="unit", metavar="<mem>"),
674               make_option("-p", "--cpu", dest="vcpus",
675                           help="Number of virtual CPUs",
676                           default=None, type="int", metavar="<PROC>"),
677               make_option("-i", "--ip", dest="ip",
678                           help="IP address ('none' or numeric IP)",
679                           default=None, type="string", metavar="<ADDRESS>"),
680               make_option("-b", "--bridge", dest="bridge",
681                           help="Bridge to connect this instance to",
682                           default=None, type="string", metavar="<bridge>"),
683               ],
684              "<instance>", "Alters the parameters of an instance"),
685   'shutdown': (ShutdownInstance, ARGS_ANY,
686                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
687                 m_clust_opt, m_inst_opt],
688                "<instance>", "Stops an instance"),
689   'startup': (StartupInstance, ARGS_ANY,
690               [DEBUG_OPT, FORCE_OPT,
691                make_option("-e", "--extra", dest="extra_args",
692                            help="Extra arguments for the instance's kernel",
693                            default=None, type="string", metavar="<PARAMS>"),
694                m_node_opt, m_pri_node_opt, m_sec_node_opt,
695                m_clust_opt, m_inst_opt,
696                ],
697             "<instance>", "Starts an instance"),
698   'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
699                      "<instance>",
700                      "Activate an instance's disks"),
701   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
702                        "<instance>",
703                        "Deactivate an instance's disks"),
704   }
705
706 if __name__ == '__main__':
707   sys.exit(GenericMain(commands))