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