Convert replace-disks (same nodes) to multi-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 # pylint: disable-msg=W0401,W0614
23 # W0401: Wildcard import ganeti.cli
24 # W0614: Unused import %s from wildcard import (since we need cli)
25
26 import sys
27 import os
28 import itertools
29 import simplejson
30 from optparse import make_option
31 from cStringIO import StringIO
32
33 from ganeti.cli import *
34 from ganeti import cli
35 from ganeti import opcodes
36 from ganeti import constants
37 from ganeti import utils
38 from ganeti import errors
39
40
41 _SHUTDOWN_CLUSTER = "cluster"
42 _SHUTDOWN_NODES_BOTH = "nodes"
43 _SHUTDOWN_NODES_PRI = "nodes-pri"
44 _SHUTDOWN_NODES_SEC = "nodes-sec"
45 _SHUTDOWN_INSTANCES = "instances"
46
47
48 _VALUE_TRUE = "true"
49
50 #: default list of options for L{ListInstances}
51 _LIST_DEF_FIELDS = [
52   "name", "hypervisor", "os", "pnode", "status", "oper_ram",
53   ]
54
55
56 def _ExpandMultiNames(mode, names):
57   """Expand the given names using the passed mode.
58
59   For _SHUTDOWN_CLUSTER, all instances will be returned. For
60   _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
61   primary/secondary will be returned. For _SHUTDOWN_NODES_BOTH, all
62   instances having those nodes as either primary or secondary will be
63   returned. For _SHUTDOWN_INSTANCES, the given instances will be
64   returned.
65
66   @param mode: one of L{_SHUTDOWN_CLUSTER}, L{_SHUTDOWN_NODES_BOTH},
67       L{_SHUTDOWN_NODES_PRI}, L{_SHUTDOWN_NODES_SEC} or
68       L{_SHUTDOWN_INSTANCES}
69   @param names: a list of names; for cluster, it must be empty,
70       and for node and instance it must be a list of valid item
71       names (short names are valid as usual, e.g. node1 instead of
72       node1.example.com)
73   @rtype: list
74   @return: the list of names after the expansion
75   @raise errors.ProgrammerError: for unknown selection type
76   @raise errors.OpPrereqError: for invalid input parameters
77
78   """
79   if mode == _SHUTDOWN_CLUSTER:
80     if names:
81       raise errors.OpPrereqError("Cluster filter mode takes no arguments")
82     client = GetClient()
83     idata = client.QueryInstances([], ["name"])
84     inames = [row[0] for row in idata]
85
86   elif mode in (_SHUTDOWN_NODES_BOTH,
87                 _SHUTDOWN_NODES_PRI,
88                 _SHUTDOWN_NODES_SEC):
89     if not names:
90       raise errors.OpPrereqError("No node names passed")
91     client = GetClient()
92     ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"])
93     ipri = [row[1] for row in ndata]
94     pri_names = list(itertools.chain(*ipri))
95     isec = [row[2] for row in ndata]
96     sec_names = list(itertools.chain(*isec))
97     if mode == _SHUTDOWN_NODES_BOTH:
98       inames = pri_names + sec_names
99     elif mode == _SHUTDOWN_NODES_PRI:
100       inames = pri_names
101     elif mode == _SHUTDOWN_NODES_SEC:
102       inames = sec_names
103     else:
104       raise errors.ProgrammerError("Unhandled shutdown type")
105
106   elif mode == _SHUTDOWN_INSTANCES:
107     if not names:
108       raise errors.OpPrereqError("No instance names passed")
109     client = GetClient()
110     idata = client.QueryInstances(names, ["name"])
111     inames = [row[0] for row in idata]
112
113   else:
114     raise errors.OpPrereqError("Unknown mode '%s'" % mode)
115
116   return inames
117
118
119 def _ConfirmOperation(inames, text):
120   """Ask the user to confirm an operation on a list of instances.
121
122   This function is used to request confirmation for doing an operation
123   on a given list of instances.
124
125   @type inames: list
126   @param inames: the list of names that we display when
127       we ask for confirmation
128   @type text: str
129   @param text: the operation that the user should confirm
130       (e.g. I{shutdown} or I{startup})
131   @rtype: boolean
132   @return: True or False depending on user's confirmation.
133
134   """
135   count = len(inames)
136   msg = ("The %s will operate on %d instances.\n"
137          "Do you want to continue?" % (text, count))
138   affected = ("\nAffected instances:\n" +
139               "\n".join(["  %s" % name for name in inames]))
140
141   choices = [('y', True, 'Yes, execute the %s' % text),
142              ('n', False, 'No, abort the %s' % text)]
143
144   if count > 20:
145     choices.insert(1, ('v', 'v', 'View the list of affected instances'))
146     ask = msg
147   else:
148     ask = msg + affected
149
150   choice = AskUser(ask, choices)
151   if choice == 'v':
152     choices.pop(1)
153     choice = AskUser(msg + affected, choices)
154   return choice
155
156
157 def _TransformPath(user_input):
158   """Transform a user path into a canonical value.
159
160   This function transforms the a path passed as textual information
161   into the constants that the LU code expects.
162
163   """
164   if user_input:
165     if user_input.lower() == "default":
166       result_path = constants.VALUE_DEFAULT
167     elif user_input.lower() == "none":
168       result_path = constants.VALUE_NONE
169     else:
170       if not os.path.isabs(user_input):
171         raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
172                                    user_input)
173       result_path = user_input
174   else:
175     result_path = constants.VALUE_DEFAULT
176
177   return result_path
178
179
180 def ListInstances(opts, args):
181   """List instances and their properties.
182
183   @param opts: the command line options selected by the user
184   @type args: list
185   @param args: should be an empty list
186   @rtype: int
187   @return: the desired exit code
188
189   """
190   if opts.output is None:
191     selected_fields = _LIST_DEF_FIELDS
192   elif opts.output.startswith("+"):
193     selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
194   else:
195     selected_fields = opts.output.split(",")
196
197   output = GetClient().QueryInstances([], selected_fields)
198
199   if not opts.no_headers:
200     headers = {
201       "name": "Instance", "os": "OS", "pnode": "Primary_node",
202       "snodes": "Secondary_Nodes", "admin_state": "Autostart",
203       "oper_state": "Running",
204       "oper_ram": "Memory", "disk_template": "Disk_template",
205       "ip": "IP_address", "mac": "MAC_address",
206       "bridge": "Bridge",
207       "sda_size": "Disk/0", "sdb_size": "Disk/1",
208       "status": "Status", "tags": "Tags",
209       "network_port": "Network_port",
210       "hv/kernel_path": "Kernel_path",
211       "hv/initrd_path": "Initrd_path",
212       "hv/boot_order": "HVM_boot_order",
213       "hv/acpi": "HVM_ACPI",
214       "hv/pae": "HVM_PAE",
215       "hv/cdrom_image_path": "HVM_CDROM_image_path",
216       "hv/nic_type": "HVM_NIC_type",
217       "hv/disk_type": "HVM_Disk_type",
218       "hv/vnc_bind_address": "VNC_bind_address",
219       "serial_no": "SerialNo", "hypervisor": "Hypervisor",
220       "hvparams": "Hypervisor_parameters",
221       "be/memory": "Configured_memory",
222       "be/vcpus": "VCPUs",
223       "be/auto_balance": "Auto_balance",
224       }
225   else:
226     headers = None
227
228   if opts.human_readable:
229     unitfields = ["be/memory", "oper_ram", "sda_size", "sdb_size"]
230   else:
231     unitfields = None
232
233   numfields = ["be/memory", "oper_ram", "sda_size", "sdb_size", "be/vcpus",
234                "serial_no"]
235
236   list_type_fields = ("tags",)
237   # change raw values to nicer strings
238   for row in output:
239     for idx, field in enumerate(selected_fields):
240       val = row[idx]
241       if field == "snodes":
242         val = ",".join(val) or "-"
243       elif field == "admin_state":
244         if val:
245           val = "yes"
246         else:
247           val = "no"
248       elif field == "oper_state":
249         if val is None:
250           val = "(node down)"
251         elif val: # True
252           val = "running"
253         else:
254           val = "stopped"
255       elif field == "oper_ram":
256         if val is None:
257           val = "(node down)"
258       elif field == "sda_size" or field == "sdb_size":
259         if val is None:
260           val = "N/A"
261       elif field in list_type_fields:
262         val = ",".join(val)
263       elif val is None:
264         val = "-"
265       row[idx] = str(val)
266
267   data = GenerateTable(separator=opts.separator, headers=headers,
268                        fields=selected_fields, unitfields=unitfields,
269                        numfields=numfields, data=output)
270
271   for line in data:
272     ToStdout(line)
273
274   return 0
275
276
277 def AddInstance(opts, args):
278   """Add an instance to the cluster.
279
280   @param opts: the command line options selected by the user
281   @type args: list
282   @param args: should contain only one element, the new instance name
283   @rtype: int
284   @return: the desired exit code
285
286   """
287   instance = args[0]
288
289   (pnode, snode) = SplitNodeOption(opts.node)
290
291   hypervisor = None
292   hvparams = {}
293   if opts.hypervisor:
294     hypervisor, hvparams = opts.hypervisor
295
296   if opts.nics:
297     try:
298       nic_max = max(int(nidx[0])+1 for nidx in opts.nics)
299     except ValueError, err:
300       raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
301     nics = [{}] * nic_max
302     for nidx, ndict in opts.nics.items():
303       nidx = int(nidx)
304       nics[nidx] = ndict
305   else:
306     # default of one nic, all auto
307     nics = [{}]
308
309   if not opts.disks and opts.disk_template != constants.DT_DISKLESS:
310     raise errors.OpPrereqError("No disk information specified")
311   elif opts.disks and opts.disk_template == constants.DT_DISKLESS:
312     raise errors.OpPrereqError("Diskless instance but disk information passeD")
313   else:
314     try:
315       disk_max = max(int(didx[0])+1 for didx in opts.disks)
316     except ValueError, err:
317       raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
318     disks = [{}] * disk_max
319     for didx, ddict in opts.disks:
320       didx = int(didx)
321       if "size" not in ddict:
322         raise errors.OpPrereqError("Missing size for disk %d" % didx)
323       try:
324         ddict["size"] = utils.ParseUnit(ddict["size"])
325       except ValueError, err:
326         raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
327                                    (didx, err))
328       disks[didx] = ddict
329
330   ValidateBeParams(opts.beparams)
331
332 ##  kernel_path = _TransformPath(opts.kernel_path)
333 ##  initrd_path = _TransformPath(opts.initrd_path)
334
335 ##  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
336 ##  hvm_pae = opts.hvm_pae == _VALUE_TRUE
337
338 ##  if ((opts.hvm_cdrom_image_path is not None) and
339 ##      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
340 ##    hvm_cdrom_image_path = None
341 ##  else:
342 ##    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
343
344   op = opcodes.OpCreateInstance(instance_name=instance,
345                                 disks=disks,
346                                 disk_template=opts.disk_template,
347                                 nics=nics,
348                                 mode=constants.INSTANCE_CREATE,
349                                 os_type=opts.os, pnode=pnode,
350                                 snode=snode,
351                                 start=opts.start, ip_check=opts.ip_check,
352                                 wait_for_sync=opts.wait_for_sync,
353                                 hypervisor=hypervisor,
354                                 hvparams=hvparams,
355                                 beparams=opts.beparams,
356                                 iallocator=opts.iallocator,
357                                 file_storage_dir=opts.file_storage_dir,
358                                 file_driver=opts.file_driver,
359                                 )
360
361   SubmitOrSend(op, opts)
362   return 0
363
364
365 def BatchCreate(opts, args):
366   """Create instances using a definition file.
367
368   This function reads a json file with instances defined
369   in the form::
370
371     {"instance-name":{
372       "disk_size": 25,
373       "swap_size": 1024,
374       "template": "drbd",
375       "backend": {
376         "memory": 512,
377         "vcpus": 1 },
378       "os": "etch-image",
379       "primary_node": "firstnode",
380       "secondary_node": "secondnode",
381       "iallocator": "dumb"}
382     }
383
384   Note that I{primary_node} and I{secondary_node} have precedence over
385   I{iallocator}.
386
387   @param opts: the command line options selected by the user
388   @type args: list
389   @param args: should contain one element, the json filename
390   @rtype: int
391   @return: the desired exit code
392
393   """
394   _DEFAULT_SPECS = {"disk_size": 20 * 1024,
395                     "swap_size": 4 * 1024,
396                     "backend": {},
397                     "iallocator": None,
398                     "primary_node": None,
399                     "secondary_node": None,
400                     "ip": 'none',
401                     "mac": 'auto',
402                     "bridge": None,
403                     "start": True,
404                     "ip_check": True,
405                     "hypervisor": None,
406                     "file_storage_dir": None,
407                     "file_driver": 'loop'}
408
409   def _PopulateWithDefaults(spec):
410     """Returns a new hash combined with default values."""
411     mydict = _DEFAULT_SPECS.copy()
412     mydict.update(spec)
413     return mydict
414
415   def _Validate(spec):
416     """Validate the instance specs."""
417     # Validate fields required under any circumstances
418     for required_field in ('os', 'template'):
419       if required_field not in spec:
420         raise errors.OpPrereqError('Required field "%s" is missing.' %
421                                    required_field)
422     # Validate special fields
423     if spec['primary_node'] is not None:
424       if (spec['template'] in constants.DTS_NET_MIRROR and
425           spec['secondary_node'] is None):
426         raise errors.OpPrereqError('Template requires secondary node, but'
427                                    ' there was no secondary provided.')
428     elif spec['iallocator'] is None:
429       raise errors.OpPrereqError('You have to provide at least a primary_node'
430                                  ' or an iallocator.')
431
432     if (spec['hypervisor'] and
433         not isinstance(spec['hypervisor'], dict)):
434       raise errors.OpPrereqError('Hypervisor parameters must be a dict.')
435
436   json_filename = args[0]
437   fd = open(json_filename, 'r')
438   try:
439     instance_data = simplejson.load(fd)
440   finally:
441     fd.close()
442
443   # Iterate over the instances and do:
444   #  * Populate the specs with default value
445   #  * Validate the instance specs
446   for (name, specs) in instance_data.iteritems():
447     specs = _PopulateWithDefaults(specs)
448     _Validate(specs)
449
450     hypervisor = None
451     hvparams = {}
452     if specs['hypervisor']:
453       hypervisor, hvparams = specs['hypervisor'].iteritems()
454
455     op = opcodes.OpCreateInstance(instance_name=name,
456                                   disk_size=specs['disk_size'],
457                                   swap_size=specs['swap_size'],
458                                   disk_template=specs['template'],
459                                   mode=constants.INSTANCE_CREATE,
460                                   os_type=specs['os'],
461                                   pnode=specs['primary_node'],
462                                   snode=specs['secondary_node'],
463                                   ip=specs['ip'], bridge=specs['bridge'],
464                                   start=specs['start'],
465                                   ip_check=specs['ip_check'],
466                                   wait_for_sync=True,
467                                   mac=specs['mac'],
468                                   iallocator=specs['iallocator'],
469                                   hypervisor=hypervisor,
470                                   hvparams=hvparams,
471                                   beparams=specs['backend'],
472                                   file_storage_dir=specs['file_storage_dir'],
473                                   file_driver=specs['file_driver'])
474
475     ToStdout("%s: %s", name, cli.SendJob([op]))
476
477   return 0
478
479
480 def ReinstallInstance(opts, args):
481   """Reinstall an instance.
482
483   @param opts: the command line options selected by the user
484   @type args: list
485   @param args: should contain only one element, the name of the
486       instance to be reinstalled
487   @rtype: int
488   @return: the desired exit code
489
490   """
491   instance_name = args[0]
492
493   if opts.select_os is True:
494     op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
495     result = SubmitOpCode(op)
496
497     if not result:
498       ToStdout("Can't get the OS list")
499       return 1
500
501     ToStdout("Available OS templates:")
502     number = 0
503     choices = []
504     for entry in result:
505       ToStdout("%3s: %s", number, entry[0])
506       choices.append(("%s" % number, entry[0], entry[0]))
507       number = number + 1
508
509     choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
510     selected = AskUser("Enter OS template name or number (or x to abort):",
511                        choices)
512
513     if selected == 'exit':
514       ToStdout("User aborted reinstall, exiting")
515       return 1
516
517     os_name = selected
518   else:
519     os_name = opts.os
520
521   if not opts.force:
522     usertext = ("This will reinstall the instance %s and remove"
523                 " all data. Continue?") % instance_name
524     if not AskUser(usertext):
525       return 1
526
527   op = opcodes.OpReinstallInstance(instance_name=instance_name,
528                                    os_type=os_name)
529   SubmitOrSend(op, opts)
530
531   return 0
532
533
534 def RemoveInstance(opts, args):
535   """Remove an instance.
536
537   @param opts: the command line options selected by the user
538   @type args: list
539   @param args: should contain only one element, the name of
540       the instance to be removed
541   @rtype: int
542   @return: the desired exit code
543
544   """
545   instance_name = args[0]
546   force = opts.force
547
548   if not force:
549     usertext = ("This will remove the volumes of the instance %s"
550                 " (including mirrors), thus removing all the data"
551                 " of the instance. Continue?") % instance_name
552     if not AskUser(usertext):
553       return 1
554
555   op = opcodes.OpRemoveInstance(instance_name=instance_name,
556                                 ignore_failures=opts.ignore_failures)
557   SubmitOrSend(op, opts)
558   return 0
559
560
561 def RenameInstance(opts, args):
562   """Rename an instance.
563
564   @param opts: the command line options selected by the user
565   @type args: list
566   @param args: should contain two elements, the old and the
567       new instance names
568   @rtype: int
569   @return: the desired exit code
570
571   """
572   op = opcodes.OpRenameInstance(instance_name=args[0],
573                                 new_name=args[1],
574                                 ignore_ip=opts.ignore_ip)
575   SubmitOrSend(op, opts)
576   return 0
577
578
579 def ActivateDisks(opts, args):
580   """Activate an instance's disks.
581
582   This serves two purposes:
583     - it allows (as long as the instance is not running)
584       mounting the disks and modifying them from the node
585     - it repairs inactive secondary drbds
586
587   @param opts: the command line options selected by the user
588   @type args: list
589   @param args: should contain only one element, the instance name
590   @rtype: int
591   @return: the desired exit code
592
593   """
594   instance_name = args[0]
595   op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
596   disks_info = SubmitOrSend(op, opts)
597   for host, iname, nname in disks_info:
598     ToStdout("%s:%s:%s", host, iname, nname)
599   return 0
600
601
602 def DeactivateDisks(opts, args):
603   """Deactivate an instance's disks..
604
605   This function takes the instance name, looks for its primary node
606   and the tries to shutdown its block devices on that node.
607
608   @param opts: the command line options selected by the user
609   @type args: list
610   @param args: should contain only one element, the instance name
611   @rtype: int
612   @return: the desired exit code
613
614   """
615   instance_name = args[0]
616   op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
617   SubmitOrSend(op, opts)
618   return 0
619
620
621 def GrowDisk(opts, args):
622   """Grow an instance's disks.
623
624   @param opts: the command line options selected by the user
625   @type args: list
626   @param args: should contain two elements, the instance name
627       whose disks we grow and the disk name, e.g. I{sda}
628   @rtype: int
629   @return: the desired exit code
630
631   """
632   instance = args[0]
633   disk = args[1]
634   amount = utils.ParseUnit(args[2])
635   op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount,
636                           wait_for_sync=opts.wait_for_sync)
637   SubmitOrSend(op, opts)
638   return 0
639
640
641 def StartupInstance(opts, args):
642   """Startup instances.
643
644   Depending on the options given, this will start one or more
645   instances.
646
647   @param opts: the command line options selected by the user
648   @type args: list
649   @param args: the instance or node names based on which we
650       create the final selection (in conjunction with the
651       opts argument)
652   @rtype: int
653   @return: the desired exit code
654
655   """
656   if opts.multi_mode is None:
657     opts.multi_mode = _SHUTDOWN_INSTANCES
658   inames = _ExpandMultiNames(opts.multi_mode, args)
659   if not inames:
660     raise errors.OpPrereqError("Selection filter does not match any instances")
661   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
662   if not (opts.force_multi or not multi_on
663           or _ConfirmOperation(inames, "startup")):
664     return 1
665   for name in inames:
666     op = opcodes.OpStartupInstance(instance_name=name,
667                                    force=opts.force,
668                                    extra_args=opts.extra_args)
669     if multi_on:
670       ToStdout("Starting up %s", name)
671     try:
672       SubmitOrSend(op, opts)
673     except JobSubmittedException, err:
674       _, txt = FormatError(err)
675       ToStdout("%s", txt)
676   return 0
677
678
679 def RebootInstance(opts, args):
680   """Reboot instance(s).
681
682   Depending on the parameters given, this will reboot one or more
683   instances.
684
685   @param opts: the command line options selected by the user
686   @type args: list
687   @param args: the instance or node names based on which we
688       create the final selection (in conjunction with the
689       opts argument)
690   @rtype: int
691   @return: the desired exit code
692
693   """
694   if opts.multi_mode is None:
695     opts.multi_mode = _SHUTDOWN_INSTANCES
696   inames = _ExpandMultiNames(opts.multi_mode, args)
697   if not inames:
698     raise errors.OpPrereqError("Selection filter does not match any instances")
699   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
700   if not (opts.force_multi or not multi_on
701           or _ConfirmOperation(inames, "reboot")):
702     return 1
703   for name in inames:
704     op = opcodes.OpRebootInstance(instance_name=name,
705                                   reboot_type=opts.reboot_type,
706                                   ignore_secondaries=opts.ignore_secondaries)
707
708     SubmitOrSend(op, opts)
709   return 0
710
711
712 def ShutdownInstance(opts, args):
713   """Shutdown an instance.
714
715   @param opts: the command line options selected by the user
716   @type args: list
717   @param args: the instance or node names based on which we
718       create the final selection (in conjunction with the
719       opts argument)
720   @rtype: int
721   @return: the desired exit code
722
723   """
724   if opts.multi_mode is None:
725     opts.multi_mode = _SHUTDOWN_INSTANCES
726   inames = _ExpandMultiNames(opts.multi_mode, args)
727   if not inames:
728     raise errors.OpPrereqError("Selection filter does not match any instances")
729   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
730   if not (opts.force_multi or not multi_on
731           or _ConfirmOperation(inames, "shutdown")):
732     return 1
733   for name in inames:
734     op = opcodes.OpShutdownInstance(instance_name=name)
735     if multi_on:
736       ToStdout("Shutting down %s", name)
737     try:
738       SubmitOrSend(op, opts)
739     except JobSubmittedException, err:
740       _, txt = FormatError(err)
741       ToStdout("%s", txt)
742   return 0
743
744
745 def ReplaceDisks(opts, args):
746   """Replace the disks of an instance
747
748   @param opts: the command line options selected by the user
749   @type args: list
750   @param args: should contain only one element, the instance name
751   @rtype: int
752   @return: the desired exit code
753
754   """
755   instance_name = args[0]
756   new_2ndary = opts.new_secondary
757   iallocator = opts.iallocator
758   if opts.disks is None:
759     disks = []
760   else:
761     try:
762       disks = [int(i) for i in opts.disks.split(",")]
763     except ValueError, err:
764       raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
765   if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
766     mode = constants.REPLACE_DISK_ALL
767   elif opts.on_primary: # only on primary:
768     mode = constants.REPLACE_DISK_PRI
769     if new_2ndary is not None or iallocator is not None:
770       raise errors.OpPrereqError("Can't change secondary node on primary disk"
771                                  " replacement")
772   elif opts.on_secondary is not None or iallocator is not None:
773     # only on secondary
774     mode = constants.REPLACE_DISK_SEC
775
776   op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
777                               remote_node=new_2ndary, mode=mode,
778                               iallocator=iallocator)
779   SubmitOrSend(op, opts)
780   return 0
781
782
783 def FailoverInstance(opts, args):
784   """Failover an instance.
785
786   The failover is done by shutting it down on its present node and
787   starting it on the secondary.
788
789   @param opts: the command line options selected by the user
790   @type args: list
791   @param args: should contain only one element, the instance name
792   @rtype: int
793   @return: the desired exit code
794
795   """
796   instance_name = args[0]
797   force = opts.force
798
799   if not force:
800     usertext = ("Failover will happen to image %s."
801                 " This requires a shutdown of the instance. Continue?" %
802                 (instance_name,))
803     if not AskUser(usertext):
804       return 1
805
806   op = opcodes.OpFailoverInstance(instance_name=instance_name,
807                                   ignore_consistency=opts.ignore_consistency)
808   SubmitOrSend(op, opts)
809   return 0
810
811
812 def ConnectToInstanceConsole(opts, args):
813   """Connect to the console of an instance.
814
815   @param opts: the command line options selected by the user
816   @type args: list
817   @param args: should contain only one element, the instance name
818   @rtype: int
819   @return: the desired exit code
820
821   """
822   instance_name = args[0]
823
824   op = opcodes.OpConnectConsole(instance_name=instance_name)
825   cmd = SubmitOpCode(op)
826
827   if opts.show_command:
828     ToStdout("%s", utils.ShellQuoteArgs(cmd))
829   else:
830     try:
831       os.execvp(cmd[0], cmd)
832     finally:
833       ToStderr("Can't run console command %s with arguments:\n'%s'",
834                cmd[0], " ".join(cmd))
835       os._exit(1)
836
837
838 def _FormatBlockDevInfo(buf, dev, indent_level, static):
839   """Show block device information.
840
841   This is only used by L{ShowInstanceConfig}, but it's too big to be
842   left for an inline definition.
843
844   @type buf: StringIO
845   @param buf: buffer that will accumulate the output
846   @type dev: dict
847   @param dev: dictionary with disk information
848   @type indent_level: int
849   @param indent_level: the indendation level we are at, used for
850       the layout of the device tree
851   @type static: boolean
852   @param static: wheter the device information doesn't contain
853       runtime information but only static data
854
855   """
856   def helper(buf, dtype, status):
857     """Format one line for physical device status.
858
859     @type buf: StringIO
860     @param buf: buffer that will accumulate the output
861     @type dtype: str
862     @param dtype: a constant from the L{constants.LDS_BLOCK} set
863     @type status: tuple
864     @param status: a tuple as returned from L{backend.FindBlockDevice}
865
866     """
867     if not status:
868       buf.write("not active\n")
869     else:
870       (path, major, minor, syncp, estt, degr, ldisk) = status
871       if major is None:
872         major_string = "N/A"
873       else:
874         major_string = str(major)
875
876       if minor is None:
877         minor_string = "N/A"
878       else:
879         minor_string = str(minor)
880
881       buf.write("%s (%s:%s)" % (path, major_string, minor_string))
882       if dtype in (constants.LD_DRBD8, ):
883         if syncp is not None:
884           sync_text = "*RECOVERING* %5.2f%%," % syncp
885           if estt:
886             sync_text += " ETA %ds" % estt
887           else:
888             sync_text += " ETA unknown"
889         else:
890           sync_text = "in sync"
891         if degr:
892           degr_text = "*DEGRADED*"
893         else:
894           degr_text = "ok"
895         if ldisk:
896           ldisk_text = " *MISSING DISK*"
897         else:
898           ldisk_text = ""
899         buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
900       elif dtype == constants.LD_LV:
901         if ldisk:
902           ldisk_text = " *FAILED* (failed drive?)"
903         else:
904           ldisk_text = ""
905         buf.write(ldisk_text)
906       buf.write("\n")
907
908   if dev["iv_name"] is not None:
909     data = "  - %s, " % dev["iv_name"]
910   else:
911     data = "  - "
912   data += "type: %s" % dev["dev_type"]
913   if dev["logical_id"] is not None:
914     data += ", logical_id: %s" % (dev["logical_id"],)
915   elif dev["physical_id"] is not None:
916     data += ", physical_id: %s" % (dev["physical_id"],)
917   buf.write("%*s%s\n" % (2*indent_level, "", data))
918   if not static:
919     buf.write("%*s    primary:   " % (2*indent_level, ""))
920     helper(buf, dev["dev_type"], dev["pstatus"])
921
922   if dev["sstatus"] and not static:
923     buf.write("%*s    secondary: " % (2*indent_level, ""))
924     helper(buf, dev["dev_type"], dev["sstatus"])
925
926   if dev["children"]:
927     for child in dev["children"]:
928       _FormatBlockDevInfo(buf, child, indent_level+1, static)
929
930
931 def ShowInstanceConfig(opts, args):
932   """Compute instance run-time status.
933
934   @param opts: the command line options selected by the user
935   @type args: list
936   @param args: either an empty list, and then we query all
937       instances, or should contain a list of instance names
938   @rtype: int
939   @return: the desired exit code
940
941   """
942   retcode = 0
943   op = opcodes.OpQueryInstanceData(instances=args, static=opts.static)
944   result = SubmitOpCode(op)
945   if not result:
946     ToStdout("No instances.")
947     return 1
948
949   buf = StringIO()
950   retcode = 0
951   for instance_name in result:
952     instance = result[instance_name]
953     buf.write("Instance name: %s\n" % instance["name"])
954     buf.write("State: configured to be %s" % instance["config_state"])
955     if not opts.static:
956       buf.write(", actual state is %s" % instance["run_state"])
957     buf.write("\n")
958     ##buf.write("Considered for memory checks in cluster verify: %s\n" %
959     ##          instance["auto_balance"])
960     buf.write("  Nodes:\n")
961     buf.write("    - primary: %s\n" % instance["pnode"])
962     buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
963     buf.write("  Operating system: %s\n" % instance["os"])
964     if instance.has_key("network_port"):
965       buf.write("  Allocated network port: %s\n" % instance["network_port"])
966     buf.write("  Hypervisor: %s\n" % instance["hypervisor"])
967     if instance["hypervisor"] == constants.HT_XEN_PVM:
968       hvattrs = ((constants.HV_KERNEL_PATH, "kernel path"),
969                  (constants.HV_INITRD_PATH, "initrd path"))
970     elif instance["hypervisor"] == constants.HT_XEN_HVM:
971       hvattrs = ((constants.HV_BOOT_ORDER, "boot order"),
972                  (constants.HV_ACPI, "ACPI"),
973                  (constants.HV_PAE, "PAE"),
974                  (constants.HV_CDROM_IMAGE_PATH, "virtual CDROM"),
975                  (constants.HV_NIC_TYPE, "NIC type"),
976                  (constants.HV_DISK_TYPE, "Disk type"),
977                  (constants.HV_VNC_BIND_ADDRESS, "VNC bind address"),
978                  )
979       # custom console information for HVM
980       vnc_bind_address = instance["hv_actual"][constants.HV_VNC_BIND_ADDRESS]
981       if vnc_bind_address == constants.BIND_ADDRESS_GLOBAL:
982         vnc_console_port = "%s:%s" % (instance["pnode"],
983                                       instance["network_port"])
984       elif vnc_bind_address == constants.LOCALHOST_IP_ADDRESS:
985         vnc_console_port = "%s:%s on node %s" % (vnc_bind_address,
986                                                  instance["network_port"],
987                                                  instance["pnode"])
988       else:
989         vnc_console_port = "%s:%s" % (vnc_bind_address,
990                                       instance["network_port"])
991       buf.write("    - console connection: vnc to %s\n" % vnc_console_port)
992
993     else:
994       # auto-handle other hypervisor types
995       hvattrs = [(key, key) for key in instance["hv_actual"]]
996
997     for key, desc in hvattrs:
998       if key in instance["hv_instance"]:
999         val = instance["hv_instance"][key]
1000       else:
1001         val = "default (%s)" % instance["hv_actual"][key]
1002       buf.write("    - %s: %s\n" % (desc, val))
1003     buf.write("  Hardware:\n")
1004     buf.write("    - VCPUs: %d\n" %
1005               instance["be_actual"][constants.BE_VCPUS])
1006     buf.write("    - memory: %dMiB\n" %
1007               instance["be_actual"][constants.BE_MEMORY])
1008     buf.write("    - NICs: %s\n" %
1009               ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
1010                          (mac, ip, bridge)
1011                          for mac, ip, bridge in instance["nics"]]))
1012     buf.write("  Block devices:\n")
1013
1014     for device in instance["disks"]:
1015       _FormatBlockDevInfo(buf, device, 1, opts.static)
1016
1017   ToStdout(buf.getvalue().rstrip('\n'))
1018   return retcode
1019
1020
1021 def SetInstanceParams(opts, args):
1022   """Modifies an instance.
1023
1024   All parameters take effect only at the next restart of the instance.
1025
1026   @param opts: the command line options selected by the user
1027   @type args: list
1028   @param args: should contain only one element, the instance name
1029   @rtype: int
1030   @return: the desired exit code
1031
1032   """
1033   if not (opts.ip or opts.bridge or opts.mac or
1034           opts.hypervisor or opts.beparams):
1035     ToStderr("Please give at least one of the parameters.")
1036     return 1
1037
1038   if constants.BE_MEMORY in opts.beparams:
1039     opts.beparams[constants.BE_MEMORY] = utils.ParseUnit(
1040       opts.beparams[constants.BE_MEMORY])
1041
1042   op = opcodes.OpSetInstanceParams(instance_name=args[0],
1043                                    ip=opts.ip,
1044                                    bridge=opts.bridge, mac=opts.mac,
1045                                    hvparams=opts.hypervisor,
1046                                    beparams=opts.beparams,
1047                                    force=opts.force)
1048
1049   # even if here we process the result, we allow submit only
1050   result = SubmitOrSend(op, opts)
1051
1052   if result:
1053     ToStdout("Modified instance %s", args[0])
1054     for param, data in result:
1055       ToStdout(" - %-5s -> %s", param, data)
1056     ToStdout("Please don't forget that these parameters take effect"
1057              " only at the next start of the instance.")
1058   return 0
1059
1060
1061 # options used in more than one cmd
1062 node_opt = make_option("-n", "--node", dest="node", help="Target node",
1063                        metavar="<node>")
1064
1065 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
1066                     metavar="<os>")
1067
1068 # multi-instance selection options
1069 m_force_multi = make_option("--force-multiple", dest="force_multi",
1070                             help="Do not ask for confirmation when more than"
1071                             " one instance is affected",
1072                             action="store_true", default=False)
1073
1074 m_pri_node_opt = make_option("--primary", dest="multi_mode",
1075                              help="Filter by nodes (primary only)",
1076                              const=_SHUTDOWN_NODES_PRI, action="store_const")
1077
1078 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
1079                              help="Filter by nodes (secondary only)",
1080                              const=_SHUTDOWN_NODES_SEC, action="store_const")
1081
1082 m_node_opt = make_option("--node", dest="multi_mode",
1083                          help="Filter by nodes (primary and secondary)",
1084                          const=_SHUTDOWN_NODES_BOTH, action="store_const")
1085
1086 m_clust_opt = make_option("--all", dest="multi_mode",
1087                           help="Select all instances in the cluster",
1088                           const=_SHUTDOWN_CLUSTER, action="store_const")
1089
1090 m_inst_opt = make_option("--instance", dest="multi_mode",
1091                          help="Filter by instance name [default]",
1092                          const=_SHUTDOWN_INSTANCES, action="store_const")
1093
1094
1095 # this is defined separately due to readability only
1096 add_opts = [
1097   DEBUG_OPT,
1098   make_option("-n", "--node", dest="node",
1099               help="Target node and optional secondary node",
1100               metavar="<pnode>[:<snode>]"),
1101   cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
1102              " a suffix is used",
1103              default=20 * 1024, type="unit", metavar="<size>"),
1104   cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
1105              " suffix is used",
1106              default=4 * 1024, type="unit", metavar="<size>"),
1107   os_opt,
1108   keyval_option("-B", "--backend", dest="beparams",
1109                 type="keyval", default={},
1110                 help="Backend parameters"),
1111   make_option("-t", "--disk-template", dest="disk_template",
1112               help="Custom disk setup (diskless, file, plain or drbd)",
1113               default=None, metavar="TEMPL"),
1114   ikv_option("--disk", help="Disk information",
1115              default=[], dest="disks",
1116              action="append",
1117              type="identkeyval"),
1118   ikv_option("--net", help="NIC information",
1119              default=[], dest="nics",
1120              action="append",
1121              type="identkeyval"),
1122   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
1123               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
1124   make_option("--no-start", dest="start", default=True,
1125               action="store_false", help="Don't start the instance after"
1126               " creation"),
1127   make_option("--no-ip-check", dest="ip_check", default=True,
1128               action="store_false", help="Don't check that the instance's IP"
1129               " is alive (only valid with --no-start)"),
1130   make_option("--file-storage-dir", dest="file_storage_dir",
1131               help="Relative path under default cluster-wide file storage dir"
1132               " to store file-based disks", default=None,
1133               metavar="<DIR>"),
1134   make_option("--file-driver", dest="file_driver", help="Driver to use"
1135               " for image files", default="loop", metavar="<DRIVER>"),
1136   make_option("--iallocator", metavar="<NAME>",
1137               help="Select nodes for the instance automatically using the"
1138               " <NAME> iallocator plugin", default=None, type="string"),
1139   ikv_option("-H", "--hypervisor", dest="hypervisor",
1140               help="Hypervisor and hypervisor options, in the format"
1141               " hypervisor:option=value,option=value,...", default=None,
1142               type="identkeyval"),
1143   SUBMIT_OPT,
1144   ]
1145
1146 commands = {
1147   'add': (AddInstance, ARGS_ONE, add_opts,
1148           "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
1149           "Creates and adds a new instance to the cluster"),
1150   'batch-create': (BatchCreate, ARGS_ONE,
1151                    [DEBUG_OPT],
1152                    "<instances_file.json>",
1153                    "Create a bunch of instances based on specs in the file."),
1154   'console': (ConnectToInstanceConsole, ARGS_ONE,
1155               [DEBUG_OPT,
1156                make_option("--show-cmd", dest="show_command",
1157                            action="store_true", default=False,
1158                            help=("Show command instead of executing it"))],
1159               "[--show-cmd] <instance>",
1160               "Opens a console on the specified instance"),
1161   'failover': (FailoverInstance, ARGS_ONE,
1162                [DEBUG_OPT, FORCE_OPT,
1163                 make_option("--ignore-consistency", dest="ignore_consistency",
1164                             action="store_true", default=False,
1165                             help="Ignore the consistency of the disks on"
1166                             " the secondary"),
1167                 SUBMIT_OPT,
1168                 ],
1169                "[-f] <instance>",
1170                "Stops the instance and starts it on the backup node, using"
1171                " the remote mirror (only for instances of type drbd)"),
1172   'info': (ShowInstanceConfig, ARGS_ANY,
1173            [DEBUG_OPT,
1174             make_option("-s", "--static", dest="static",
1175                         action="store_true", default=False,
1176                         help="Only show configuration data, not runtime data"),
1177             ], "[-s] [<instance>...]",
1178            "Show information on the specified instance(s)"),
1179   'list': (ListInstances, ARGS_NONE,
1180            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
1181            "Lists the instances and their status. The available fields are"
1182            " (see the man page for details): status, oper_state, oper_ram,"
1183            " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
1184            " ip, mac, bridge, sda_size, sdb_size, vcpus, serial_no,"
1185            " hypervisor."
1186            " The default field"
1187            " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
1188            ),
1189   'reinstall': (ReinstallInstance, ARGS_ONE,
1190                 [DEBUG_OPT, FORCE_OPT, os_opt,
1191                  make_option("--select-os", dest="select_os",
1192                              action="store_true", default=False,
1193                              help="Interactive OS reinstall, lists available"
1194                              " OS templates for selection"),
1195                  SUBMIT_OPT,
1196                  ],
1197                 "[-f] <instance>", "Reinstall a stopped instance"),
1198   'remove': (RemoveInstance, ARGS_ONE,
1199              [DEBUG_OPT, FORCE_OPT,
1200               make_option("--ignore-failures", dest="ignore_failures",
1201                           action="store_true", default=False,
1202                           help=("Remove the instance from the cluster even"
1203                                 " if there are failures during the removal"
1204                                 " process (shutdown, disk removal, etc.)")),
1205               SUBMIT_OPT,
1206               ],
1207              "[-f] <instance>", "Shuts down the instance and removes it"),
1208   'rename': (RenameInstance, ARGS_FIXED(2),
1209              [DEBUG_OPT,
1210               make_option("--no-ip-check", dest="ignore_ip",
1211                           help="Do not check that the IP of the new name"
1212                           " is alive",
1213                           default=False, action="store_true"),
1214               SUBMIT_OPT,
1215               ],
1216              "<instance> <new_name>", "Rename the instance"),
1217   'replace-disks': (ReplaceDisks, ARGS_ONE,
1218                     [DEBUG_OPT,
1219                      make_option("-n", "--new-secondary", dest="new_secondary",
1220                                  help=("New secondary node (for secondary"
1221                                        " node change)"), metavar="NODE"),
1222                      make_option("-p", "--on-primary", dest="on_primary",
1223                                  default=False, action="store_true",
1224                                  help=("Replace the disk(s) on the primary"
1225                                        " node (only for the drbd template)")),
1226                      make_option("-s", "--on-secondary", dest="on_secondary",
1227                                  default=False, action="store_true",
1228                                  help=("Replace the disk(s) on the secondary"
1229                                        " node (only for the drbd template)")),
1230                      make_option("--disks", dest="disks", default=None,
1231                                  help=("Comma-separated list of disks"
1232                                        " to replace (e.g. sda) (optional,"
1233                                        " defaults to all disks")),
1234                      make_option("--iallocator", metavar="<NAME>",
1235                                  help="Select new secondary for the instance"
1236                                  " automatically using the"
1237                                  " <NAME> iallocator plugin (enables"
1238                                  " secondary node replacement)",
1239                                  default=None, type="string"),
1240                      SUBMIT_OPT,
1241                      ],
1242                     "[-s|-p|-n NODE] <instance>",
1243                     "Replaces all disks for the instance"),
1244   'modify': (SetInstanceParams, ARGS_ONE,
1245              [DEBUG_OPT, FORCE_OPT,
1246               make_option("-i", "--ip", dest="ip",
1247                           help="IP address ('none' or numeric IP)",
1248                           default=None, type="string", metavar="<ADDRESS>"),
1249               make_option("-b", "--bridge", dest="bridge",
1250                           help="Bridge to connect this instance to",
1251                           default=None, type="string", metavar="<bridge>"),
1252               make_option("--mac", dest="mac",
1253                           help="MAC address", default=None,
1254                           type="string", metavar="<MACADDRESS>"),
1255               keyval_option("-H", "--hypervisor", type="keyval",
1256                             default={}, dest="hypervisor",
1257                             help="Change hypervisor parameters"),
1258               keyval_option("-B", "--backend", type="keyval",
1259                             default={}, dest="beparams",
1260                             help="Change backend parameters"),
1261               SUBMIT_OPT,
1262               ],
1263              "<instance>", "Alters the parameters of an instance"),
1264   'shutdown': (ShutdownInstance, ARGS_ANY,
1265                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1266                 m_clust_opt, m_inst_opt, m_force_multi,
1267                 SUBMIT_OPT,
1268                 ],
1269                "<instance>", "Stops an instance"),
1270   'startup': (StartupInstance, ARGS_ANY,
1271               [DEBUG_OPT, FORCE_OPT, m_force_multi,
1272                make_option("-e", "--extra", dest="extra_args",
1273                            help="Extra arguments for the instance's kernel",
1274                            default=None, type="string", metavar="<PARAMS>"),
1275                m_node_opt, m_pri_node_opt, m_sec_node_opt,
1276                m_clust_opt, m_inst_opt,
1277                SUBMIT_OPT,
1278                ],
1279             "<instance>", "Starts an instance"),
1280
1281   'reboot': (RebootInstance, ARGS_ANY,
1282               [DEBUG_OPT, m_force_multi,
1283                make_option("-e", "--extra", dest="extra_args",
1284                            help="Extra arguments for the instance's kernel",
1285                            default=None, type="string", metavar="<PARAMS>"),
1286                make_option("-t", "--type", dest="reboot_type",
1287                            help="Type of reboot: soft/hard/full",
1288                            default=constants.INSTANCE_REBOOT_HARD,
1289                            type="string", metavar="<REBOOT>"),
1290                make_option("--ignore-secondaries", dest="ignore_secondaries",
1291                            default=False, action="store_true",
1292                            help="Ignore errors from secondaries"),
1293                m_node_opt, m_pri_node_opt, m_sec_node_opt,
1294                m_clust_opt, m_inst_opt,
1295                SUBMIT_OPT,
1296                ],
1297             "<instance>", "Reboots an instance"),
1298   'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1299                      "<instance>",
1300                      "Activate an instance's disks"),
1301   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1302                        "<instance>",
1303                        "Deactivate an instance's disks"),
1304   'grow-disk': (GrowDisk, ARGS_FIXED(3),
1305                 [DEBUG_OPT, SUBMIT_OPT,
1306                  make_option("--no-wait-for-sync",
1307                              dest="wait_for_sync", default=True,
1308                              action="store_false",
1309                              help="Don't wait for sync (DANGEROUS!)"),
1310                  ],
1311                 "<instance> <disk> <size>", "Grow an instance's disk"),
1312   'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1313                 "<instance_name>", "List the tags of the given instance"),
1314   'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1315                "<instance_name> tag...", "Add tags to the given instance"),
1316   'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1317                   "<instance_name> tag...", "Remove tags from given instance"),
1318   }
1319
1320 #: dictionary with aliases for commands
1321 aliases = {
1322   'activate_block_devs': 'activate-disks',
1323   'replace_disks': 'replace-disks',
1324   'start': 'startup',
1325   'stop': 'shutdown',
1326   }
1327
1328 if __name__ == '__main__':
1329   sys.exit(GenericMain(commands, aliases=aliases,
1330                        override={"tag_type": constants.TAG_INSTANCE}))