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