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