Implement gnt-instance grow-disk
[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",
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   # 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       row[idx] = str(val)
228
229   data = GenerateTable(separator=opts.separator, headers=headers,
230                        fields=selected_fields, unitfields=unitfields,
231                        numfields=numfields, data=output)
232
233   for line in data:
234     logger.ToStdout(line)
235
236   return 0
237
238
239 def AddInstance(opts, args):
240   """Add an instance to the cluster.
241
242   Args:
243     opts - class with options as members
244     args - list with a single element, the instance name
245   Opts used:
246     mem - amount of memory to allocate to instance (MiB)
247     size - amount of disk space to allocate to instance (MiB)
248     os - which OS to run on instance
249     node - node to run new instance on
250
251   """
252   instance = args[0]
253
254   (pnode, snode) = SplitNodeOption(opts.node)
255
256   kernel_path = _TransformPath(opts.kernel_path)
257   initrd_path = _TransformPath(opts.initrd_path)
258
259   hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
260   hvm_pae = opts.hvm_pae == _VALUE_TRUE
261
262   if ((opts.hvm_cdrom_image_path is not None) and
263       (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
264     hvm_cdrom_image_path = None
265   else:
266     hvm_cdrom_image_path = opts.hvm_cdrom_image_path
267
268   op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
269                                 disk_size=opts.size, swap_size=opts.swap,
270                                 disk_template=opts.disk_template,
271                                 mode=constants.INSTANCE_CREATE,
272                                 os_type=opts.os, pnode=pnode,
273                                 snode=snode, vcpus=opts.vcpus,
274                                 ip=opts.ip, bridge=opts.bridge,
275                                 start=opts.start, ip_check=opts.ip_check,
276                                 wait_for_sync=opts.wait_for_sync,
277                                 mac=opts.mac,
278                                 kernel_path=kernel_path,
279                                 initrd_path=initrd_path,
280                                 iallocator=opts.iallocator,
281                                 hvm_boot_order=opts.hvm_boot_order,
282                                 file_storage_dir=opts.file_storage_dir,
283                                 file_driver=opts.file_driver,
284                                 hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
285                                 hvm_cdrom_image_path=hvm_cdrom_image_path,
286                                 vnc_bind_address=opts.vnc_bind_address)
287
288   SubmitOpCode(op)
289   return 0
290
291
292 def ReinstallInstance(opts, args):
293   """Reinstall an instance.
294
295   Args:
296     opts - class with options as members
297     args - list containing a single element, the instance name
298
299   """
300   instance_name = args[0]
301
302   if not opts.force:
303     usertext = ("This will reinstall the instance %s and remove"
304                 " all data. Continue?") % instance_name
305     if not AskUser(usertext):
306       return 1
307
308   op = opcodes.OpReinstallInstance(instance_name=instance_name,
309                                    os_type=opts.os)
310   SubmitOpCode(op)
311
312   return 0
313
314
315 def RemoveInstance(opts, args):
316   """Remove an instance.
317
318   Args:
319     opts - class with options as members
320     args - list containing a single element, the instance name
321
322   """
323   instance_name = args[0]
324   force = opts.force
325
326   if not force:
327     usertext = ("This will remove the volumes of the instance %s"
328                 " (including mirrors), thus removing all the data"
329                 " of the instance. Continue?") % instance_name
330     if not AskUser(usertext):
331       return 1
332
333   op = opcodes.OpRemoveInstance(instance_name=instance_name,
334                                 ignore_failures=opts.ignore_failures)
335   SubmitOpCode(op)
336   return 0
337
338
339 def RenameInstance(opts, args):
340   """Rename an instance.
341
342   Args:
343     opts - class with options as members
344     args - list containing two elements, the instance name and the new name
345
346   """
347   op = opcodes.OpRenameInstance(instance_name=args[0],
348                                 new_name=args[1],
349                                 ignore_ip=opts.ignore_ip)
350   SubmitOpCode(op)
351
352   return 0
353
354
355 def ActivateDisks(opts, args):
356   """Activate an instance's disks.
357
358   This serves two purposes:
359     - it allows one (as long as the instance is not running) to mount
360     the disks and modify them from the node
361     - it repairs inactive secondary drbds
362
363   """
364   instance_name = args[0]
365   op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
366   disks_info = SubmitOpCode(op)
367   for host, iname, nname in disks_info:
368     print "%s:%s:%s" % (host, iname, nname)
369   return 0
370
371
372 def DeactivateDisks(opts, args):
373   """Command-line interface for _ShutdownInstanceBlockDevices.
374
375   This function takes the instance name, looks for its primary node
376   and the tries to shutdown its block devices on that node.
377
378   """
379   instance_name = args[0]
380   op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
381   SubmitOpCode(op)
382   return 0
383
384
385 def GrowDisk(opts, args):
386   """Command-line interface for _ShutdownInstanceBlockDevices.
387
388   This function takes the instance name, looks for its primary node
389   and the tries to shutdown its block devices on that node.
390
391   """
392   instance = args[0]
393   disk = args[1]
394   amount = utils.ParseUnit(args[2])
395   op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount)
396   SubmitOpCode(op)
397   return 0
398
399
400 def StartupInstance(opts, args):
401   """Startup an instance.
402
403   Args:
404     opts - class with options as members
405     args - list containing a single element, the instance name
406
407   """
408   if opts.multi_mode is None:
409     opts.multi_mode = _SHUTDOWN_INSTANCES
410   inames = _ExpandMultiNames(opts.multi_mode, args)
411   if not inames:
412     raise errors.OpPrereqError("Selection filter does not match any instances")
413   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
414   if not (opts.force_multi or not multi_on
415           or _ConfirmOperation(inames, "startup")):
416     return 1
417   for name in inames:
418     op = opcodes.OpStartupInstance(instance_name=name,
419                                    force=opts.force,
420                                    extra_args=opts.extra_args)
421     if multi_on:
422       logger.ToStdout("Starting up %s" % name)
423     SubmitOpCode(op)
424   return 0
425
426
427 def RebootInstance(opts, args):
428   """Reboot an instance
429
430   Args:
431     opts - class with options as members
432     args - list containing a single element, the instance name
433
434   """
435   if opts.multi_mode is None:
436     opts.multi_mode = _SHUTDOWN_INSTANCES
437   inames = _ExpandMultiNames(opts.multi_mode, args)
438   if not inames:
439     raise errors.OpPrereqError("Selection filter does not match any instances")
440   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
441   if not (opts.force_multi or not multi_on
442           or _ConfirmOperation(inames, "reboot")):
443     return 1
444   for name in inames:
445     op = opcodes.OpRebootInstance(instance_name=name,
446                                   reboot_type=opts.reboot_type,
447                                   ignore_secondaries=opts.ignore_secondaries)
448
449     SubmitOpCode(op)
450   return 0
451
452
453 def ShutdownInstance(opts, args):
454   """Shutdown an instance.
455
456   Args:
457     opts - class with options as members
458     args - list containing a single element, the instance name
459
460   """
461   if opts.multi_mode is None:
462     opts.multi_mode = _SHUTDOWN_INSTANCES
463   inames = _ExpandMultiNames(opts.multi_mode, args)
464   if not inames:
465     raise errors.OpPrereqError("Selection filter does not match any instances")
466   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
467   if not (opts.force_multi or not multi_on
468           or _ConfirmOperation(inames, "shutdown")):
469     return 1
470   for name in inames:
471     op = opcodes.OpShutdownInstance(instance_name=name)
472     if multi_on:
473       logger.ToStdout("Shutting down %s" % name)
474     SubmitOpCode(op)
475   return 0
476
477
478 def ReplaceDisks(opts, args):
479   """Replace the disks of an instance
480
481   Args:
482     opts - class with options as members
483     args - list with a single element, the instance name
484
485   """
486   instance_name = args[0]
487   new_2ndary = opts.new_secondary
488   iallocator = opts.iallocator
489   if opts.disks is None:
490     disks = ["sda", "sdb"]
491   else:
492     disks = opts.disks.split(",")
493   if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
494     mode = constants.REPLACE_DISK_ALL
495   elif opts.on_primary: # only on primary:
496     mode = constants.REPLACE_DISK_PRI
497     if new_2ndary is not None or iallocator is not None:
498       raise errors.OpPrereqError("Can't change secondary node on primary disk"
499                                  " replacement")
500   elif opts.on_secondary is not None or iallocator is not None:
501     # only on secondary
502     mode = constants.REPLACE_DISK_SEC
503
504   op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
505                               remote_node=new_2ndary, mode=mode,
506                               iallocator=iallocator)
507   SubmitOpCode(op)
508   return 0
509
510
511 def FailoverInstance(opts, args):
512   """Failover an instance.
513
514   The failover is done by shutting it down on its present node and
515   starting it on the secondary.
516
517   Args:
518     opts - class with options as members
519     args - list with a single element, the instance name
520   Opts used:
521     force - whether to failover without asking questions.
522
523   """
524   instance_name = args[0]
525   force = opts.force
526
527   if not force:
528     usertext = ("Failover will happen to image %s."
529                 " This requires a shutdown of the instance. Continue?" %
530                 (instance_name,))
531     if not AskUser(usertext):
532       return 1
533
534   op = opcodes.OpFailoverInstance(instance_name=instance_name,
535                                   ignore_consistency=opts.ignore_consistency)
536   SubmitOpCode(op)
537   return 0
538
539
540 def ConnectToInstanceConsole(opts, args):
541   """Connect to the console of an instance.
542
543   Args:
544     opts - class with options as members
545     args - list with a single element, the instance name
546
547   """
548   instance_name = args[0]
549
550   op = opcodes.OpConnectConsole(instance_name=instance_name)
551   cmd = SubmitOpCode(op)
552
553   if opts.show_command:
554     print utils.ShellQuoteArgs(cmd)
555   else:
556     # drop lock and exec so other commands can run while we have console
557     utils.Unlock("cmd")
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_MD_R1, constants.LD_DRBD7, 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   hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
733   hvm_pae = opts.hvm_pae == _VALUE_TRUE
734
735   if ((opts.hvm_cdrom_image_path is not None) and
736       (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
737     hvm_cdrom_image_path = None
738   else:
739     hvm_cdrom_image_path = opts.hvm_cdrom_image_path
740
741   op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
742                                    vcpus=opts.vcpus, ip=opts.ip,
743                                    bridge=opts.bridge, mac=opts.mac,
744                                    kernel_path=opts.kernel_path,
745                                    initrd_path=opts.initrd_path,
746                                    hvm_boot_order=hvm_boot_order,
747                                    hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
748                                    hvm_cdrom_image_path=hvm_cdrom_image_path,
749                                    vnc_bind_address=opts.vnc_bind_address)
750
751   result = SubmitOpCode(op)
752
753   if result:
754     logger.ToStdout("Modified instance %s" % args[0])
755     for param, data in result:
756       logger.ToStdout(" - %-5s -> %s" % (param, data))
757     logger.ToStdout("Please don't forget that these parameters take effect"
758                     " only at the next start of the instance.")
759   return 0
760
761
762 # options used in more than one cmd
763 node_opt = make_option("-n", "--node", dest="node", help="Target node",
764                        metavar="<node>")
765
766 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
767                     metavar="<os>")
768
769 # multi-instance selection options
770 m_force_multi = make_option("--force-multiple", dest="force_multi",
771                             help="Do not ask for confirmation when more than"
772                             " one instance is affected",
773                             action="store_true", default=False)
774
775 m_pri_node_opt = make_option("--primary", dest="multi_mode",
776                              help="Filter by nodes (primary only)",
777                              const=_SHUTDOWN_NODES_PRI, action="store_const")
778
779 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
780                              help="Filter by nodes (secondary only)",
781                              const=_SHUTDOWN_NODES_SEC, action="store_const")
782
783 m_node_opt = make_option("--node", dest="multi_mode",
784                          help="Filter by nodes (primary and secondary)",
785                          const=_SHUTDOWN_NODES_BOTH, action="store_const")
786
787 m_clust_opt = make_option("--all", dest="multi_mode",
788                           help="Select all instances in the cluster",
789                           const=_SHUTDOWN_CLUSTER, action="store_const")
790
791 m_inst_opt = make_option("--instance", dest="multi_mode",
792                          help="Filter by instance name [default]",
793                          const=_SHUTDOWN_INSTANCES, action="store_const")
794
795
796 # this is defined separately due to readability only
797 add_opts = [
798   DEBUG_OPT,
799   make_option("-n", "--node", dest="node",
800               help="Target node and optional secondary node",
801               metavar="<pnode>[:<snode>]"),
802   cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
803              " a suffix is used",
804              default=20 * 1024, type="unit", metavar="<size>"),
805   cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
806              " suffix is used",
807              default=4 * 1024, type="unit", metavar="<size>"),
808   os_opt,
809   cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
810               default=128, type="unit", metavar="<mem>"),
811   make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
812               default=1, type="int", metavar="<PROC>"),
813   make_option("-t", "--disk-template", dest="disk_template",
814               help="Custom disk setup (diskless, file, plain or drbd)",
815               default=None, metavar="TEMPL"),
816   make_option("-i", "--ip", dest="ip",
817               help="IP address ('none' [default], 'auto', or specify address)",
818               default='none', type="string", metavar="<ADDRESS>"),
819   make_option("--mac", dest="mac",
820               help="MAC address ('auto' [default], or specify address)",
821               default='auto', type="string", metavar="<MACADDRESS>"),
822   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
823               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
824   make_option("-b", "--bridge", dest="bridge",
825               help="Bridge to connect this instance to",
826               default=None, metavar="<bridge>"),
827   make_option("--no-start", dest="start", default=True,
828               action="store_false", help="Don't start the instance after"
829               " creation"),
830   make_option("--no-ip-check", dest="ip_check", default=True,
831               action="store_false", help="Don't check that the instance's IP"
832               " is alive (only valid with --no-start)"),
833   make_option("--kernel", dest="kernel_path",
834               help="Path to the instances' kernel (or 'default')",
835               default=None,
836               type="string", metavar="<FILENAME>"),
837   make_option("--initrd", dest="initrd_path",
838               help="Path to the instances' initrd (or 'none', or 'default')",
839               default=None,
840               type="string", metavar="<FILENAME>"),
841   make_option("--hvm-boot-order", dest="hvm_boot_order",
842               help="Boot device order for HVM (one or more of [acdn])",
843               default=None, type="string", metavar="<BOOTORDER>"),
844   make_option("--file-storage-dir", dest="file_storage_dir",
845               help="Relative path under default cluster-wide file storage dir"
846               " to store file-based disks", default=None,
847               metavar="<DIR>"),
848   make_option("--file-driver", dest="file_driver", help="Driver to use"
849               " for image files", default="loop", metavar="<DRIVER>"),
850   make_option("--iallocator", metavar="<NAME>",
851               help="Select nodes for the instance automatically using the"
852               " <NAME> iallocator plugin", default=None, type="string"),
853   make_option("--hvm-acpi", dest="hvm_acpi",
854               help="ACPI support for HVM (true|false)",
855               metavar="<BOOL>", choices=["true", "false"]),
856   make_option("--hvm-pae", dest="hvm_pae",
857               help="PAE support for HVM (true|false)",
858               metavar="<BOOL>", choices=["true", "false"]),
859   make_option("--hvm-cdrom-image-path", dest="hvm_cdrom_image_path",
860               help="CDROM image path for HVM (absolute path or None)",
861               default=None, type="string", metavar="<CDROMIMAGE>"),
862   make_option("--vnc-bind-address", dest="vnc_bind_address",
863               help="bind address for VNC (IP address)",
864               default=None, type="string", metavar="<VNCADDRESS>"),
865   ]
866
867 commands = {
868   'add': (AddInstance, ARGS_ONE, add_opts,
869           "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
870           "Creates and adds a new instance to the cluster"),
871   'console': (ConnectToInstanceConsole, ARGS_ONE,
872               [DEBUG_OPT,
873                make_option("--show-cmd", dest="show_command",
874                            action="store_true", default=False,
875                            help=("Show command instead of executing it"))],
876               "[--show-cmd] <instance>",
877               "Opens a console on the specified instance"),
878   'failover': (FailoverInstance, ARGS_ONE,
879                [DEBUG_OPT, FORCE_OPT,
880                 make_option("--ignore-consistency", dest="ignore_consistency",
881                             action="store_true", default=False,
882                             help="Ignore the consistency of the disks on"
883                             " the secondary"),
884                 ],
885                "[-f] <instance>",
886                "Stops the instance and starts it on the backup node, using"
887                " the remote mirror (only for instances of type drbd)"),
888   'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
889            "Show information on the specified instance"),
890   'list': (ListInstances, ARGS_NONE,
891            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
892            "Lists the instances and their status. The available fields are"
893            " (see the man page for details): status, oper_state, oper_ram,"
894            " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
895            " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
896            " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
897            ),
898   'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
899                 "[-f] <instance>", "Reinstall a stopped instance"),
900   'remove': (RemoveInstance, ARGS_ONE,
901              [DEBUG_OPT, FORCE_OPT,
902               make_option("--ignore-failures", dest="ignore_failures",
903                           action="store_true", default=False,
904                           help=("Remove the instance from the cluster even"
905                                 " if there are failures during the removal"
906                                 " process (shutdown, disk removal, etc.)")),
907               ],
908              "[-f] <instance>", "Shuts down the instance and removes it"),
909   'rename': (RenameInstance, ARGS_FIXED(2),
910              [DEBUG_OPT,
911               make_option("--no-ip-check", dest="ignore_ip",
912                           help="Do not check that the IP of the new name"
913                           " is alive",
914                           default=False, action="store_true"),
915               ],
916              "<instance> <new_name>", "Rename the instance"),
917   'replace-disks': (ReplaceDisks, ARGS_ONE,
918                     [DEBUG_OPT,
919                      make_option("-n", "--new-secondary", dest="new_secondary",
920                                  help=("New secondary node (for secondary"
921                                        " node change)"), metavar="NODE"),
922                      make_option("-p", "--on-primary", dest="on_primary",
923                                  default=False, action="store_true",
924                                  help=("Replace the disk(s) on the primary"
925                                        " node (only for the drbd template)")),
926                      make_option("-s", "--on-secondary", dest="on_secondary",
927                                  default=False, action="store_true",
928                                  help=("Replace the disk(s) on the secondary"
929                                        " node (only for the drbd template)")),
930                      make_option("--disks", dest="disks", default=None,
931                                  help=("Comma-separated list of disks"
932                                        " to replace (e.g. sda) (optional,"
933                                        " defaults to all disks")),
934                      make_option("--iallocator", metavar="<NAME>",
935                                  help="Select new secondary for the instance"
936                                  " automatically using the"
937                                  " <NAME> iallocator plugin (enables"
938                                  " secondary node replacement)",
939                                  default=None, type="string"),
940                      ],
941                     "[-s|-p|-n NODE] <instance>",
942                     "Replaces all disks for the instance"),
943   'modify': (SetInstanceParams, ARGS_ONE,
944              [DEBUG_OPT, FORCE_OPT,
945               cli_option("-m", "--memory", dest="mem",
946                          help="Memory size",
947                          default=None, type="unit", metavar="<mem>"),
948               make_option("-p", "--cpu", dest="vcpus",
949                           help="Number of virtual CPUs",
950                           default=None, type="int", metavar="<PROC>"),
951               make_option("-i", "--ip", dest="ip",
952                           help="IP address ('none' or numeric IP)",
953                           default=None, type="string", metavar="<ADDRESS>"),
954               make_option("-b", "--bridge", dest="bridge",
955                           help="Bridge to connect this instance to",
956                           default=None, type="string", metavar="<bridge>"),
957               make_option("--mac", dest="mac",
958                           help="MAC address", default=None,
959                           type="string", metavar="<MACADDRESS>"),
960               make_option("--kernel", dest="kernel_path",
961                           help="Path to the instances' kernel (or"
962                           " 'default')", default=None,
963                           type="string", metavar="<FILENAME>"),
964               make_option("--initrd", dest="initrd_path",
965                           help="Path to the instances' initrd (or 'none', or"
966                           " 'default')", default=None,
967                           type="string", metavar="<FILENAME>"),
968               make_option("--hvm-boot-order", dest="hvm_boot_order",
969                           help="boot device order for HVM"
970                           "(either one or more of [acdn] or 'default')",
971                           default=None, type="string", metavar="<BOOTORDER>"),
972               make_option("--hvm-acpi", dest="hvm_acpi",
973                           help="ACPI support for HVM (true|false)",
974                           metavar="<BOOL>", choices=["true", "false"]),
975               make_option("--hvm-pae", dest="hvm_pae",
976                           help="PAE support for HVM (true|false)",
977                           metavar="<BOOL>", choices=["true", "false"]),
978               make_option("--hvm-cdrom-image-path",
979                           dest="hvm_cdrom_image_path",
980                           help="CDROM image path for HVM"
981                           "(absolute path or None)",
982                           default=None, type="string", metavar="<CDROMIMAGE>"),
983               make_option("--vnc-bind-address", dest="vnc_bind_address",
984                           help="bind address for VNC (IP address)",
985                           default=None, type="string", metavar="<VNCADDRESS>"),
986               ],
987              "<instance>", "Alters the parameters of an instance"),
988   'shutdown': (ShutdownInstance, ARGS_ANY,
989                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
990                 m_clust_opt, m_inst_opt, m_force_multi],
991                "<instance>", "Stops an instance"),
992   'startup': (StartupInstance, ARGS_ANY,
993               [DEBUG_OPT, FORCE_OPT, m_force_multi,
994                make_option("-e", "--extra", dest="extra_args",
995                            help="Extra arguments for the instance's kernel",
996                            default=None, type="string", metavar="<PARAMS>"),
997                m_node_opt, m_pri_node_opt, m_sec_node_opt,
998                m_clust_opt, m_inst_opt,
999                ],
1000             "<instance>", "Starts an instance"),
1001
1002   'reboot': (RebootInstance, ARGS_ANY,
1003               [DEBUG_OPT, m_force_multi,
1004                make_option("-e", "--extra", dest="extra_args",
1005                            help="Extra arguments for the instance's kernel",
1006                            default=None, type="string", metavar="<PARAMS>"),
1007                make_option("-t", "--type", dest="reboot_type",
1008                            help="Type of reboot: soft/hard/full",
1009                            default=constants.INSTANCE_REBOOT_SOFT,
1010                            type="string", metavar="<REBOOT>"),
1011                make_option("--ignore-secondaries", dest="ignore_secondaries",
1012                            default=False, action="store_true",
1013                            help="Ignore errors from secondaries"),
1014                m_node_opt, m_pri_node_opt, m_sec_node_opt,
1015                m_clust_opt, m_inst_opt,
1016                ],
1017             "<instance>", "Reboots an instance"),
1018   'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
1019                      "<instance>",
1020                      "Activate an instance's disks"),
1021   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
1022                        "<instance>",
1023                        "Deactivate an instance's disks"),
1024   'grow-disk': (GrowDisk, ARGS_FIXED(3), [DEBUG_OPT],
1025                 "<instance> <disk> <size>", "Grow an instance's disk"),
1026   'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1027                 "<node_name>", "List the tags of the given instance"),
1028   'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1029                "<node_name> tag...", "Add tags to the given instance"),
1030   'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1031                   "<node_name> tag...", "Remove tags from given instance"),
1032   }
1033
1034 aliases = {
1035   'activate_block_devs': 'activate-disks',
1036   'replace_disks': 'replace-disks',
1037   'start': 'startup',
1038   'stop': 'shutdown',
1039   }
1040
1041 if __name__ == '__main__':
1042   sys.exit(GenericMain(commands, aliases=aliases,
1043                        override={"tag_type": constants.TAG_INSTANCE}))