Add a ‘tags’ field to instance and node listing
[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     # drop lock and exec so other commands can run while we have console
560     utils.Unlock("cmd")
561     try:
562       os.execvp(cmd[0], cmd)
563     finally:
564       sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
565                        (cmd, " ".join(argv)))
566       os._exit(1)
567
568
569 def _FormatBlockDevInfo(buf, dev, indent_level):
570   """Show block device information.
571
572   This is only used by ShowInstanceConfig(), but it's too big to be
573   left for an inline definition.
574
575   """
576   def helper(buf, dtype, status):
577     """Format one line for physical device status."""
578     if not status:
579       buf.write("not active\n")
580     else:
581       (path, major, minor, syncp, estt, degr, ldisk) = status
582       if major is None:
583         major_string = "N/A"
584       else:
585         major_string = str(major)
586
587       if minor is None:
588         minor_string = "N/A"
589       else:
590         minor_string = str(minor)
591
592       buf.write("%s (%s:%s)" % (path, major_string, minor_string))
593       if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
594         if syncp is not None:
595           sync_text = "*RECOVERING* %5.2f%%," % syncp
596           if estt:
597             sync_text += " ETA %ds" % estt
598           else:
599             sync_text += " ETA unknown"
600         else:
601           sync_text = "in sync"
602         if degr:
603           degr_text = "*DEGRADED*"
604         else:
605           degr_text = "ok"
606         if ldisk:
607           ldisk_text = " *MISSING DISK*"
608         else:
609           ldisk_text = ""
610         buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
611       elif dtype == constants.LD_LV:
612         if ldisk:
613           ldisk_text = " *FAILED* (failed drive?)"
614         else:
615           ldisk_text = ""
616         buf.write(ldisk_text)
617       buf.write("\n")
618
619   if dev["iv_name"] is not None:
620     data = "  - %s, " % dev["iv_name"]
621   else:
622     data = "  - "
623   data += "type: %s" % dev["dev_type"]
624   if dev["logical_id"] is not None:
625     data += ", logical_id: %s" % (dev["logical_id"],)
626   elif dev["physical_id"] is not None:
627     data += ", physical_id: %s" % (dev["physical_id"],)
628   buf.write("%*s%s\n" % (2*indent_level, "", data))
629   buf.write("%*s    primary:   " % (2*indent_level, ""))
630   helper(buf, dev["dev_type"], dev["pstatus"])
631
632   if dev["sstatus"]:
633     buf.write("%*s    secondary: " % (2*indent_level, ""))
634     helper(buf, dev["dev_type"], dev["sstatus"])
635
636   if dev["children"]:
637     for child in dev["children"]:
638       _FormatBlockDevInfo(buf, child, indent_level+1)
639
640
641 def ShowInstanceConfig(opts, args):
642   """Compute instance run-time status.
643
644   """
645   retcode = 0
646   op = opcodes.OpQueryInstanceData(instances=args)
647   result = SubmitOpCode(op)
648   hvm_parameters = ("hvm_acpi", "hvm_pae", "hvm_cdrom_image_path",
649                     "hvm_boot_order")
650
651   pvm_parameters = ("kernel_path", "initrd_path")
652
653   if not result:
654     logger.ToStdout("No instances.")
655     return 1
656
657   buf = StringIO()
658   retcode = 0
659   for instance_name in result:
660     instance = result[instance_name]
661     buf.write("Instance name: %s\n" % instance["name"])
662     buf.write("State: configured to be %s, actual state is %s\n" %
663               (instance["config_state"], instance["run_state"]))
664     buf.write("  Nodes:\n")
665     buf.write("    - primary: %s\n" % instance["pnode"])
666     buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
667     buf.write("  Operating system: %s\n" % instance["os"])
668     if instance.has_key("network_port"):
669       buf.write("  Allocated network port: %s\n" % instance["network_port"])
670     if False not in map(instance.has_key, pvm_parameters):
671       if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
672         kpath = "(default: %s)" % constants.XEN_KERNEL
673       else:
674         kpath = instance["kernel_path"]
675       buf.write("  Kernel path: %s\n" % kpath)
676       if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
677         initrd = "(default: %s)" % constants.XEN_INITRD
678       elif instance["initrd_path"] == constants.VALUE_NONE:
679         initrd = "(none)"
680       else:
681         initrd = instance["initrd_path"]
682       buf.write("       initrd: %s\n" % initrd)
683     if False not in map(instance.has_key, hvm_parameters):
684       buf.write("  HVM:\n")
685       buf.write("    - boot order: %s\n" % instance["hvm_boot_order"])
686       buf.write("    - ACPI support: %s\n" % instance["hvm_acpi"])
687       buf.write("    - PAE support: %s\n" % instance["hvm_pae"])
688       buf.write("    - virtual CDROM: %s\n" % instance["hvm_cdrom_image_path"])
689     if instance.has_key("vnc_bind_address"):
690       buf.write("  VNC bind address: %s\n" % instance["vnc_bind_address"])
691     buf.write("  Hardware:\n")
692     buf.write("    - VCPUs: %d\n" % instance["vcpus"])
693     buf.write("    - memory: %dMiB\n" % instance["memory"])
694     buf.write("    - NICs: %s\n" %
695         ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
696                    (mac, ip, bridge)
697                      for mac, ip, bridge in instance["nics"]]))
698     buf.write("  Block devices:\n")
699
700     for device in instance["disks"]:
701       _FormatBlockDevInfo(buf, device, 1)
702
703   logger.ToStdout(buf.getvalue().rstrip('\n'))
704   return retcode
705
706
707 def SetInstanceParams(opts, args):
708   """Modifies an instance.
709
710   All parameters take effect only at the next restart of the instance.
711
712   Args:
713     opts - class with options as members
714     args - list with a single element, the instance name
715   Opts used:
716     memory - the new memory size
717     vcpus - the new number of cpus
718     mac - the new MAC address of the instance
719
720   """
721   if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
722           opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
723           opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
724           opts.vnc_bind_address):
725     logger.ToStdout("Please give at least one of the parameters.")
726     return 1
727
728   kernel_path = _TransformPath(opts.kernel_path)
729   initrd_path = _TransformPath(opts.initrd_path)
730   if opts.hvm_boot_order == 'default':
731     hvm_boot_order = constants.VALUE_DEFAULT
732   else:
733     hvm_boot_order = opts.hvm_boot_order
734
735   hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
736   hvm_pae = opts.hvm_pae == _VALUE_TRUE
737
738   if ((opts.hvm_cdrom_image_path is not None) and
739       (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
740     hvm_cdrom_image_path = None
741   else:
742     hvm_cdrom_image_path = opts.hvm_cdrom_image_path
743
744   op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
745                                    vcpus=opts.vcpus, ip=opts.ip,
746                                    bridge=opts.bridge, mac=opts.mac,
747                                    kernel_path=opts.kernel_path,
748                                    initrd_path=opts.initrd_path,
749                                    hvm_boot_order=hvm_boot_order,
750                                    hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
751                                    hvm_cdrom_image_path=hvm_cdrom_image_path,
752                                    vnc_bind_address=opts.vnc_bind_address)
753
754   result = SubmitOpCode(op)
755
756   if result:
757     logger.ToStdout("Modified instance %s" % args[0])
758     for param, data in result:
759       logger.ToStdout(" - %-5s -> %s" % (param, data))
760     logger.ToStdout("Please don't forget that these parameters take effect"
761                     " only at the next start of the instance.")
762   return 0
763
764
765 # options used in more than one cmd
766 node_opt = make_option("-n", "--node", dest="node", help="Target node",
767                        metavar="<node>")
768
769 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
770                     metavar="<os>")
771
772 # multi-instance selection options
773 m_force_multi = make_option("--force-multiple", dest="force_multi",
774                             help="Do not ask for confirmation when more than"
775                             " one instance is affected",
776                             action="store_true", default=False)
777
778 m_pri_node_opt = make_option("--primary", dest="multi_mode",
779                              help="Filter by nodes (primary only)",
780                              const=_SHUTDOWN_NODES_PRI, action="store_const")
781
782 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
783                              help="Filter by nodes (secondary only)",
784                              const=_SHUTDOWN_NODES_SEC, action="store_const")
785
786 m_node_opt = make_option("--node", dest="multi_mode",
787                          help="Filter by nodes (primary and secondary)",
788                          const=_SHUTDOWN_NODES_BOTH, action="store_const")
789
790 m_clust_opt = make_option("--all", dest="multi_mode",
791                           help="Select all instances in the cluster",
792                           const=_SHUTDOWN_CLUSTER, action="store_const")
793
794 m_inst_opt = make_option("--instance", dest="multi_mode",
795                          help="Filter by instance name [default]",
796                          const=_SHUTDOWN_INSTANCES, action="store_const")
797
798
799 # this is defined separately due to readability only
800 add_opts = [
801   DEBUG_OPT,
802   make_option("-n", "--node", dest="node",
803               help="Target node and optional secondary node",
804               metavar="<pnode>[:<snode>]"),
805   cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
806              " a suffix is used",
807              default=20 * 1024, type="unit", metavar="<size>"),
808   cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
809              " suffix is used",
810              default=4 * 1024, type="unit", metavar="<size>"),
811   os_opt,
812   cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
813               default=128, type="unit", metavar="<mem>"),
814   make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
815               default=1, type="int", metavar="<PROC>"),
816   make_option("-t", "--disk-template", dest="disk_template",
817               help="Custom disk setup (diskless, file, plain or drbd)",
818               default=None, metavar="TEMPL"),
819   make_option("-i", "--ip", dest="ip",
820               help="IP address ('none' [default], 'auto', or specify address)",
821               default='none', type="string", metavar="<ADDRESS>"),
822   make_option("--mac", dest="mac",
823               help="MAC address ('auto' [default], or specify address)",
824               default='auto', type="string", metavar="<MACADDRESS>"),
825   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
826               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
827   make_option("-b", "--bridge", dest="bridge",
828               help="Bridge to connect this instance to",
829               default=None, metavar="<bridge>"),
830   make_option("--no-start", dest="start", default=True,
831               action="store_false", help="Don't start the instance after"
832               " creation"),
833   make_option("--no-ip-check", dest="ip_check", default=True,
834               action="store_false", help="Don't check that the instance's IP"
835               " is alive (only valid with --no-start)"),
836   make_option("--kernel", dest="kernel_path",
837               help="Path to the instances' kernel (or 'default')",
838               default=None,
839               type="string", metavar="<FILENAME>"),
840   make_option("--initrd", dest="initrd_path",
841               help="Path to the instances' initrd (or 'none', or 'default')",
842               default=None,
843               type="string", metavar="<FILENAME>"),
844   make_option("--hvm-boot-order", dest="hvm_boot_order",
845               help="Boot device order for HVM (one or more of [acdn])",
846               default=None, type="string", metavar="<BOOTORDER>"),
847   make_option("--file-storage-dir", dest="file_storage_dir",
848               help="Relative path under default cluster-wide file storage dir"
849               " to store file-based disks", default=None,
850               metavar="<DIR>"),
851   make_option("--file-driver", dest="file_driver", help="Driver to use"
852               " for image files", default="loop", metavar="<DRIVER>"),
853   make_option("--iallocator", metavar="<NAME>",
854               help="Select nodes for the instance automatically using the"
855               " <NAME> iallocator plugin", default=None, type="string"),
856   make_option("--hvm-acpi", dest="hvm_acpi",
857               help="ACPI support for HVM (true|false)",
858               metavar="<BOOL>", choices=["true", "false"]),
859   make_option("--hvm-pae", dest="hvm_pae",
860               help="PAE support for HVM (true|false)",
861               metavar="<BOOL>", choices=["true", "false"]),
862   make_option("--hvm-cdrom-image-path", dest="hvm_cdrom_image_path",
863               help="CDROM image path for HVM (absolute path or None)",
864               default=None, type="string", metavar="<CDROMIMAGE>"),
865   make_option("--vnc-bind-address", dest="vnc_bind_address",
866               help="bind address for VNC (IP address)",
867               default=None, type="string", metavar="<VNCADDRESS>"),
868   ]
869
870 commands = {
871   'add': (AddInstance, ARGS_ONE, add_opts,
872           "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
873           "Creates and adds a new instance to the cluster"),
874   'console': (ConnectToInstanceConsole, ARGS_ONE,
875               [DEBUG_OPT,
876                make_option("--show-cmd", dest="show_command",
877                            action="store_true", default=False,
878                            help=("Show command instead of executing it"))],
879               "[--show-cmd] <instance>",
880               "Opens a console on the specified instance"),
881   'failover': (FailoverInstance, ARGS_ONE,
882                [DEBUG_OPT, FORCE_OPT,
883                 make_option("--ignore-consistency", dest="ignore_consistency",
884                             action="store_true", default=False,
885                             help="Ignore the consistency of the disks on"
886                             " the secondary"),
887                 ],
888                "[-f] <instance>",
889                "Stops the instance and starts it on the backup node, using"
890                " the remote mirror (only for instances of type drbd)"),
891   'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
892            "Show information on the specified instance"),
893   'list': (ListInstances, ARGS_NONE,
894            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
895            "Lists the instances and their status. The available fields are"
896            " (see the man page for details): status, oper_state, oper_ram,"
897            " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
898            " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
899            " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
900            ),
901   'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
902                 "[-f] <instance>", "Reinstall a stopped instance"),
903   'remove': (RemoveInstance, ARGS_ONE,
904              [DEBUG_OPT, FORCE_OPT,
905               make_option("--ignore-failures", dest="ignore_failures",
906                           action="store_true", default=False,
907                           help=("Remove the instance from the cluster even"
908                                 " if there are failures during the removal"
909                                 " process (shutdown, disk removal, etc.)")),
910               ],
911              "[-f] <instance>", "Shuts down the instance and removes it"),
912   'rename': (RenameInstance, ARGS_FIXED(2),
913              [DEBUG_OPT,
914               make_option("--no-ip-check", dest="ignore_ip",
915                           help="Do not check that the IP of the new name"
916                           " is alive",
917                           default=False, action="store_true"),
918               ],
919              "<instance> <new_name>", "Rename the instance"),
920   'replace-disks': (ReplaceDisks, ARGS_ONE,
921                     [DEBUG_OPT,
922                      make_option("-n", "--new-secondary", dest="new_secondary",
923                                  help=("New secondary node (for secondary"
924                                        " node change)"), metavar="NODE"),
925                      make_option("-p", "--on-primary", dest="on_primary",
926                                  default=False, action="store_true",
927                                  help=("Replace the disk(s) on the primary"
928                                        " node (only for the drbd template)")),
929                      make_option("-s", "--on-secondary", dest="on_secondary",
930                                  default=False, action="store_true",
931                                  help=("Replace the disk(s) on the secondary"
932                                        " node (only for the drbd template)")),
933                      make_option("--disks", dest="disks", default=None,
934                                  help=("Comma-separated list of disks"
935                                        " to replace (e.g. sda) (optional,"
936                                        " defaults to all disks")),
937                      make_option("--iallocator", metavar="<NAME>",
938                                  help="Select new secondary for the instance"
939                                  " automatically using the"
940                                  " <NAME> iallocator plugin (enables"
941                                  " secondary node replacement)",
942                                  default=None, type="string"),
943                      ],
944                     "[-s|-p|-n NODE] <instance>",
945                     "Replaces all disks for the instance"),
946   'modify': (SetInstanceParams, ARGS_ONE,
947              [DEBUG_OPT, FORCE_OPT,
948               cli_option("-m", "--memory", dest="mem",
949                          help="Memory size",
950                          default=None, type="unit", metavar="<mem>"),
951               make_option("-p", "--cpu", dest="vcpus",
952                           help="Number of virtual CPUs",
953                           default=None, type="int", metavar="<PROC>"),
954               make_option("-i", "--ip", dest="ip",
955                           help="IP address ('none' or numeric IP)",
956                           default=None, type="string", metavar="<ADDRESS>"),
957               make_option("-b", "--bridge", dest="bridge",
958                           help="Bridge to connect this instance to",
959                           default=None, type="string", metavar="<bridge>"),
960               make_option("--mac", dest="mac",
961                           help="MAC address", default=None,
962                           type="string", metavar="<MACADDRESS>"),
963               make_option("--kernel", dest="kernel_path",
964                           help="Path to the instances' kernel (or"
965                           " 'default')", default=None,
966                           type="string", metavar="<FILENAME>"),
967               make_option("--initrd", dest="initrd_path",
968                           help="Path to the instances' initrd (or 'none', or"
969                           " 'default')", default=None,
970                           type="string", metavar="<FILENAME>"),
971               make_option("--hvm-boot-order", dest="hvm_boot_order",
972                           help="boot device order for HVM"
973                           "(either one or more of [acdn] or 'default')",
974                           default=None, type="string", metavar="<BOOTORDER>"),
975               make_option("--hvm-acpi", dest="hvm_acpi",
976                           help="ACPI support for HVM (true|false)",
977                           metavar="<BOOL>", choices=["true", "false"]),
978               make_option("--hvm-pae", dest="hvm_pae",
979                           help="PAE support for HVM (true|false)",
980                           metavar="<BOOL>", choices=["true", "false"]),
981               make_option("--hvm-cdrom-image-path",
982                           dest="hvm_cdrom_image_path",
983                           help="CDROM image path for HVM"
984                           "(absolute path or None)",
985                           default=None, type="string", metavar="<CDROMIMAGE>"),
986               make_option("--vnc-bind-address", dest="vnc_bind_address",
987                           help="bind address for VNC (IP address)",
988                           default=None, type="string", metavar="<VNCADDRESS>"),
989               ],
990              "<instance>", "Alters the parameters of an instance"),
991   'shutdown': (ShutdownInstance, ARGS_ANY,
992                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
993                 m_clust_opt, m_inst_opt, m_force_multi],
994                "<instance>", "Stops an instance"),
995   'startup': (StartupInstance, ARGS_ANY,
996               [DEBUG_OPT, FORCE_OPT, m_force_multi,
997                make_option("-e", "--extra", dest="extra_args",
998                            help="Extra arguments for the instance's kernel",
999                            default=None, type="string", metavar="<PARAMS>"),
1000                m_node_opt, m_pri_node_opt, m_sec_node_opt,
1001                m_clust_opt, m_inst_opt,
1002                ],
1003             "<instance>", "Starts an instance"),
1004
1005   'reboot': (RebootInstance, ARGS_ANY,
1006               [DEBUG_OPT, m_force_multi,
1007                make_option("-e", "--extra", dest="extra_args",
1008                            help="Extra arguments for the instance's kernel",
1009                            default=None, type="string", metavar="<PARAMS>"),
1010                make_option("-t", "--type", dest="reboot_type",
1011                            help="Type of reboot: soft/hard/full",
1012                            default=constants.INSTANCE_REBOOT_SOFT,
1013                            type="string", metavar="<REBOOT>"),
1014                make_option("--ignore-secondaries", dest="ignore_secondaries",
1015                            default=False, action="store_true",
1016                            help="Ignore errors from secondaries"),
1017                m_node_opt, m_pri_node_opt, m_sec_node_opt,
1018                m_clust_opt, m_inst_opt,
1019                ],
1020             "<instance>", "Reboots an instance"),
1021   'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
1022                      "<instance>",
1023                      "Activate an instance's disks"),
1024   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
1025                        "<instance>",
1026                        "Deactivate an instance's disks"),
1027   'grow-disk': (GrowDisk, ARGS_FIXED(3), [DEBUG_OPT],
1028                 "<instance> <disk> <size>", "Grow an instance's disk"),
1029   'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1030                 "<node_name>", "List the tags of the given instance"),
1031   'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1032                "<node_name> tag...", "Add tags to the given instance"),
1033   'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1034                   "<node_name> tag...", "Remove tags from given instance"),
1035   }
1036
1037 aliases = {
1038   'activate_block_devs': 'activate-disks',
1039   'replace_disks': 'replace-disks',
1040   'start': 'startup',
1041   'stop': 'shutdown',
1042   }
1043
1044 if __name__ == '__main__':
1045   sys.exit(GenericMain(commands, aliases=aliases,
1046                        override={"tag_type": constants.TAG_INSTANCE}))