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