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