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