gnt-instance 43.1 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python
#

# Copyright (C) 2006, 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


22
23
24
25
# pylint: disable-msg=W0401,W0614
# W0401: Wildcard import ganeti.cli
# W0614: Unused import %s from wildcard import (since we need cli)

Iustin Pop's avatar
Iustin Pop committed
26
27
import sys
import os
28
import itertools
29
import simplejson
Iustin Pop's avatar
Iustin Pop committed
30
31
32
33
from optparse import make_option
from cStringIO import StringIO

from ganeti.cli import *
34
from ganeti import cli
Iustin Pop's avatar
Iustin Pop committed
35
36
37
from ganeti import opcodes
from ganeti import constants
from ganeti import utils
38
39
40
41
42
43
44
45
46
from ganeti import errors


_SHUTDOWN_CLUSTER = "cluster"
_SHUTDOWN_NODES_BOTH = "nodes"
_SHUTDOWN_NODES_PRI = "nodes-pri"
_SHUTDOWN_NODES_SEC = "nodes-sec"
_SHUTDOWN_INSTANCES = "instances"

47

48
49
_VALUE_TRUE = "true"

50
_LIST_DEF_FIELDS = [
51
  "name", "hypervisor", "os", "pnode", "status", "oper_ram",
52
53
  ]

54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def _ExpandMultiNames(mode, names):
  """Expand the given names using the passed mode.

  Args:
    - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
      _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
    - names, which is a list of names; for cluster, it must be empty,
      and for node and instance it must be a list of valid item
      names (short names are valid as usual, e.g. node1 instead of
      node1.example.com)

  For _SHUTDOWN_CLUSTER, all instances will be returned. For
  _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
  primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
  instances having those nodes as either primary or secondary will be
  returned. For _SHUTDOWN_INSTANCES, the given instances will be
  returned.

  """
  if mode == _SHUTDOWN_CLUSTER:
    if names:
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
77
78
    client = GetClient()
    idata = client.QueryInstances([], ["name"])
79
80
81
82
83
84
85
    inames = [row[0] for row in idata]

  elif mode in (_SHUTDOWN_NODES_BOTH,
                _SHUTDOWN_NODES_PRI,
                _SHUTDOWN_NODES_SEC):
    if not names:
      raise errors.OpPrereqError("No node names passed")
86
87
    client = GetClient()
    ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"])
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    ipri = [row[1] for row in ndata]
    pri_names = list(itertools.chain(*ipri))
    isec = [row[2] for row in ndata]
    sec_names = list(itertools.chain(*isec))
    if mode == _SHUTDOWN_NODES_BOTH:
      inames = pri_names + sec_names
    elif mode == _SHUTDOWN_NODES_PRI:
      inames = pri_names
    elif mode == _SHUTDOWN_NODES_SEC:
      inames = sec_names
    else:
      raise errors.ProgrammerError("Unhandled shutdown type")

  elif mode == _SHUTDOWN_INSTANCES:
    if not names:
      raise errors.OpPrereqError("No instance names passed")
104
105
    client = GetClient()
    idata = client.QueryInstances(names, ["name"])
106
107
108
109
110
111
    inames = [row[0] for row in idata]

  else:
    raise errors.OpPrereqError("Unknown mode '%s'" % mode)

  return inames
Iustin Pop's avatar
Iustin Pop committed
112
113


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
def _ConfirmOperation(inames, text):
  """Ask the user to confirm an operation on a list of instances.

  This function is used to request confirmation for doing an operation
  on a given list of instances.

  The inames argument is what the selection algorithm computed, and
  the text argument is the operation we should tell the user to
  confirm (e.g. 'shutdown' or 'startup').

  Returns: boolean depending on user's confirmation.

  """
  count = len(inames)
  msg = ("The %s will operate on %d instances.\n"
         "Do you want to continue?" % (text, count))
  affected = ("\nAffected instances:\n" +
              "\n".join(["  %s" % name for name in inames]))

  choices = [('y', True, 'Yes, execute the %s' % text),
             ('n', False, 'No, abort the %s' % text)]

  if count > 20:
    choices.insert(1, ('v', 'v', 'View the list of affected instances'))
    ask = msg
  else:
    ask = msg + affected

  choice = AskUser(ask, choices)
  if choice == 'v':
    choices.pop(1)
145
    choice = AskUser(msg + affected, choices)
146
147
148
  return choice


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
def _TransformPath(user_input):
  """Transform a user path into a canonical value.

  This function transforms the a path passed as textual information
  into the constants that the LU code expects.

  """
  if user_input:
    if user_input.lower() == "default":
      result_path = constants.VALUE_DEFAULT
    elif user_input.lower() == "none":
      result_path = constants.VALUE_NONE
    else:
      if not os.path.isabs(user_input):
        raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
                                   user_input)
      result_path = user_input
  else:
    result_path = constants.VALUE_DEFAULT

  return result_path


Iustin Pop's avatar
Iustin Pop committed
172
def ListInstances(opts, args):
Oleksiy Mishchenko's avatar
Oleksiy Mishchenko committed
173
  """List instances and their properties.
Iustin Pop's avatar
Iustin Pop committed
174
175
176

  """
  if opts.output is None:
177
178
179
    selected_fields = _LIST_DEF_FIELDS
  elif opts.output.startswith("+"):
    selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
Iustin Pop's avatar
Iustin Pop committed
180
181
182
  else:
    selected_fields = opts.output.split(",")

183
  output = GetClient().QueryInstances([], selected_fields)
Iustin Pop's avatar
Iustin Pop committed
184
185

  if not opts.no_headers:
186
187
188
    headers = {
      "name": "Instance", "os": "OS", "pnode": "Primary_node",
      "snodes": "Secondary_Nodes", "admin_state": "Autostart",
Iustin Pop's avatar
Iustin Pop committed
189
      "oper_state": "Running",
190
      "oper_ram": "Memory", "disk_template": "Disk_template",
191
      "ip": "IP_address", "mac": "MAC_address",
Iustin Pop's avatar
Iustin Pop committed
192
      "bridge": "Bridge",
193
      "sda_size": "Disk/0", "sdb_size": "Disk/1",
194
      "status": "Status", "tags": "Tags",
195
      "network_port": "Network_port",
196
197
198
199
200
201
202
203
      "hv/kernel_path": "Kernel_path",
      "hv/initrd_path": "Initrd_path",
      "hv/boot_order": "HVM_boot_order",
      "hv/acpi": "HVM_ACPI",
      "hv/pae": "HVM_PAE",
      "hv/cdrom_image_path": "HVM_CDROM_image_path",
      "hv/nic_type": "HVM_NIC_type",
      "hv/disk_type": "HVM_Disk_type",
204
      "hv/vnc_bind_address": "VNC_bind_address",
205
      "serial_no": "SerialNo", "hypervisor": "Hypervisor",
206
      "hvparams": "Hypervisor_parameters",
Iustin Pop's avatar
Iustin Pop committed
207
208
      "be/memory": "Configured_memory",
      "be/vcpus": "VCPUs",
209
      "be/auto_balance": "Auto_balance",
210
      }
211
212
213
214
  else:
    headers = None

  if opts.human_readable:
Iustin Pop's avatar
Iustin Pop committed
215
    unitfields = ["be/memory", "oper_ram", "sda_size", "sdb_size"]
216
217
218
  else:
    unitfields = None

Iustin Pop's avatar
Iustin Pop committed
219
  numfields = ["be/memory", "oper_ram", "sda_size", "sdb_size", "be/vcpus",
220
               "serial_no"]
221

222
  list_type_fields = ("tags",)
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  # change raw values to nicer strings
  for row in output:
    for idx, field in enumerate(selected_fields):
      val = row[idx]
      if field == "snodes":
        val = ",".join(val) or "-"
      elif field == "admin_state":
        if val:
          val = "yes"
        else:
          val = "no"
      elif field == "oper_state":
        if val is None:
          val = "(node down)"
        elif val: # True
          val = "running"
        else:
          val = "stopped"
      elif field == "oper_ram":
        if val is None:
          val = "(node down)"
      elif field == "sda_size" or field == "sdb_size":
        if val is None:
          val = "N/A"
247
248
      elif field in list_type_fields:
        val = ",".join(val)
249
250
      elif val is None:
        val = "-"
251
252
      row[idx] = str(val)

253
254
255
256
257
  data = GenerateTable(separator=opts.separator, headers=headers,
                       fields=selected_fields, unitfields=unitfields,
                       numfields=numfields, data=output)

  for line in data:
258
    ToStdout(line)
Iustin Pop's avatar
Iustin Pop committed
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

  return 0


def AddInstance(opts, args):
  """Add an instance to the cluster.

  Args:
    opts - class with options as members
    args - list with a single element, the instance name
  Opts used:
    mem - amount of memory to allocate to instance (MiB)
    size - amount of disk space to allocate to instance (MiB)
    os - which OS to run on instance
    node - node to run new instance on

  """
  instance = args[0]

278
279
  (pnode, snode) = SplitNodeOption(opts.node)

280
281
282
283
  hypervisor = None
  hvparams = {}
  if opts.hypervisor:
    hypervisor, hvparams = opts.hypervisor
284

285
  ValidateBeParams(opts.beparams)
Iustin Pop's avatar
Iustin Pop committed
286

287
288
##  kernel_path = _TransformPath(opts.kernel_path)
##  initrd_path = _TransformPath(opts.initrd_path)
289

290
291
292
293
294
295
296
297
##  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
##  hvm_pae = opts.hvm_pae == _VALUE_TRUE

##  if ((opts.hvm_cdrom_image_path is not None) and
##      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
##    hvm_cdrom_image_path = None
##  else:
##    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
298

Iustin Pop's avatar
Iustin Pop committed
299
  op = opcodes.OpCreateInstance(instance_name=instance,
Iustin Pop's avatar
Iustin Pop committed
300
301
302
                                disk_size=opts.size, swap_size=opts.swap,
                                disk_template=opts.disk_template,
                                mode=constants.INSTANCE_CREATE,
303
                                os_type=opts.os, pnode=pnode,
Iustin Pop's avatar
Iustin Pop committed
304
                                snode=snode,
305
306
                                ip=opts.ip, bridge=opts.bridge,
                                start=opts.start, ip_check=opts.ip_check,
307
308
                                wait_for_sync=opts.wait_for_sync,
                                mac=opts.mac,
309
310
                                hypervisor=hypervisor,
                                hvparams=hvparams,
Iustin Pop's avatar
Iustin Pop committed
311
                                beparams=opts.beparams,
312
                                iallocator=opts.iallocator,
313
314
                                file_storage_dir=opts.file_storage_dir,
                                file_driver=opts.file_driver,
315
                                )
316

317
  SubmitOrSend(op, opts)
Iustin Pop's avatar
Iustin Pop committed
318
319
320
  return 0


321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
def BatchCreate(opts, args):
  """Create instances on a batched base.

  This function reads a json with instances defined in the form:

  {"instance-name": {"disk_size": 25,
                     "swap_size": 1024,
                     "template": "drbd",
                     "backend": { "memory": 512,
                                  "vcpus": 1 },
                     "os": "etch-image",
                     "primary_node": "firstnode",
                     "secondary_node": "secondnode",
                     "iallocator": "dumb"}}

  primary_node and secondary_node has precedence over iallocator.

  Args:
    opts: The parsed command line options
    args: Argument passed to the command in our case the json file

  """
  _DEFAULT_SPECS = {"disk_size": 20 * 1024,
                    "swap_size": 4 * 1024,
                    "backend": {},
                    "iallocator": None,
                    "primary_node": None,
                    "secondary_node": None,
                    "ip": 'none',
                    "mac": 'auto',
                    "bridge": None,
                    "start": True,
                    "ip_check": True,
                    "hypervisor": None,
                    "file_storage_dir": None,
                    "file_driver": 'loop'}

  def _PopulateWithDefaults(spec):
    """Returns a new hash combined with default values."""
360
361
362
    mydict = _DEFAULT_SPECS.copy()
    mydict.update(spec)
    return mydict
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

  def _Validate(spec):
    """Validate the instance specs."""
    # Validate fields required under any circumstances
    for required_field in ('os', 'template'):
      if required_field not in spec:
        raise errors.OpPrereqError('Required field "%s" is missing.' %
                                   required_field)
    # Validate special fields
    if spec['primary_node'] is not None:
      if (spec['template'] in constants.DTS_NET_MIRROR and
          spec['secondary_node'] is None):
        raise errors.OpPrereqError('Template requires secondary node, but'
                                   ' there was no secondary provided.')
    elif spec['iallocator'] is None:
      raise errors.OpPrereqError('You have to provide at least a primary_node'
                                 ' or an iallocator.')

    if (spec['hypervisor'] and
        not isinstance(spec['hypervisor'], dict)):
      raise errors.OpPrereqError('Hypervisor parameters must be a dict.')

  json_filename = args[0]
  fd = open(json_filename, 'r')
  try:
    instance_data = simplejson.load(fd)
  finally:
    fd.close()

  # Iterate over the instances and do:
  #  * Populate the specs with default value
  #  * Validate the instance specs
  for (name, specs) in instance_data.iteritems():
    specs = _PopulateWithDefaults(specs)
    _Validate(specs)

    hypervisor = None
    hvparams = {}
    if specs['hypervisor']:
      hypervisor, hvparams = specs['hypervisor'].iteritems()

    op = opcodes.OpCreateInstance(instance_name=name,
                                  disk_size=specs['disk_size'],
                                  swap_size=specs['swap_size'],
                                  disk_template=specs['template'],
                                  mode=constants.INSTANCE_CREATE,
                                  os_type=specs['os'],
                                  pnode=specs['primary_node'],
                                  snode=specs['secondary_node'],
                                  ip=specs['ip'], bridge=specs['bridge'],
                                  start=specs['start'],
                                  ip_check=specs['ip_check'],
                                  wait_for_sync=True,
                                  mac=specs['mac'],
                                  iallocator=specs['iallocator'],
                                  hypervisor=hypervisor,
                                  hvparams=hvparams,
                                  beparams=specs['backend'],
                                  file_storage_dir=specs['file_storage_dir'],
                                  file_driver=specs['file_driver'])

424
    ToStdout("%s: %s", name, cli.SendJob([op]))
425
426
427
428

  return 0


429
430
431
432
433
434
435
436
437
438
def ReinstallInstance(opts, args):
  """Reinstall an instance.

  Args:
    opts - class with options as members
    args - list containing a single element, the instance name

  """
  instance_name = args[0]

439
440
441
442
443
  if opts.select_os is True:
    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
    result = SubmitOpCode(op)

    if not result:
444
      ToStdout("Can't get the OS list")
445
446
      return 1

447
    ToStdout("Available OS templates:")
448
449
450
    number = 0
    choices = []
    for entry in result:
451
      ToStdout("%3s: %s", number, entry[0])
452
453
454
455
456
457
458
459
      choices.append(("%s" % number, entry[0], entry[0]))
      number = number + 1

    choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
    selected = AskUser("Enter OS template name or number (or x to abort):",
                       choices)

    if selected == 'exit':
460
      ToStdout("User aborted reinstall, exiting")
461
462
      return 1

463
    os_name = selected
464
  else:
465
    os_name = opts.os
466

467
  if not opts.force:
468
469
    usertext = ("This will reinstall the instance %s and remove"
                " all data. Continue?") % instance_name
470
    if not AskUser(usertext):
471
472
      return 1

473
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
474
                                   os_type=os_name)
475
  SubmitOrSend(op, opts)
476
477
478
479

  return 0


Iustin Pop's avatar
Iustin Pop committed
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
def RemoveInstance(opts, args):
  """Remove an instance.

  Args:
    opts - class with options as members
    args - list containing a single element, the instance name

  """
  instance_name = args[0]
  force = opts.force

  if not force:
    usertext = ("This will remove the volumes of the instance %s"
                " (including mirrors), thus removing all the data"
                " of the instance. Continue?") % instance_name
495
    if not AskUser(usertext):
Iustin Pop's avatar
Iustin Pop committed
496
497
      return 1

Iustin Pop's avatar
Iustin Pop committed
498
499
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
                                ignore_failures=opts.ignore_failures)
500
  SubmitOrSend(op, opts)
Iustin Pop's avatar
Iustin Pop committed
501
502
503
  return 0


504
def RenameInstance(opts, args):
Guido Trotter's avatar
Guido Trotter committed
505
  """Rename an instance.
506
507
508
509
510
511
512
513
514

  Args:
    opts - class with options as members
    args - list containing two elements, the instance name and the new name

  """
  op = opcodes.OpRenameInstance(instance_name=args[0],
                                new_name=args[1],
                                ignore_ip=opts.ignore_ip)
515
  SubmitOrSend(op, opts)
516
517
518
  return 0


Iustin Pop's avatar
Iustin Pop committed
519
520
521
522
523
524
525
526
527
528
529
def ActivateDisks(opts, args):
  """Activate an instance's disks.

  This serves two purposes:
    - it allows one (as long as the instance is not running) to mount
    the disks and modify them from the node
    - it repairs inactive secondary drbds

  """
  instance_name = args[0]
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
530
  disks_info = SubmitOrSend(op, opts)
Iustin Pop's avatar
Iustin Pop committed
531
  for host, iname, nname in disks_info:
532
    ToStdout("%s:%s:%s", host, iname, nname)
Iustin Pop's avatar
Iustin Pop committed
533
534
535
536
537
538
539
540
541
542
543
544
  return 0


def DeactivateDisks(opts, args):
  """Command-line interface for _ShutdownInstanceBlockDevices.

  This function takes the instance name, looks for its primary node
  and the tries to shutdown its block devices on that node.

  """
  instance_name = args[0]
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
545
  SubmitOrSend(op, opts)
Iustin Pop's avatar
Iustin Pop committed
546
547
548
  return 0


Iustin Pop's avatar
Iustin Pop committed
549
550
551
552
553
554
555
556
557
558
def GrowDisk(opts, args):
  """Command-line interface for _ShutdownInstanceBlockDevices.

  This function takes the instance name, looks for its primary node
  and the tries to shutdown its block devices on that node.

  """
  instance = args[0]
  disk = args[1]
  amount = utils.ParseUnit(args[2])
559
560
  op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount,
                          wait_for_sync=opts.wait_for_sync)
561
  SubmitOrSend(op, opts)
Iustin Pop's avatar
Iustin Pop committed
562
563
564
  return 0


Iustin Pop's avatar
Iustin Pop committed
565
def StartupInstance(opts, args):
566
  """Startup an instance.
Iustin Pop's avatar
Iustin Pop committed
567
568
569
570
571
572

  Args:
    opts - class with options as members
    args - list containing a single element, the instance name

  """
573
574
575
  if opts.multi_mode is None:
    opts.multi_mode = _SHUTDOWN_INSTANCES
  inames = _ExpandMultiNames(opts.multi_mode, args)
576
577
  if not inames:
    raise errors.OpPrereqError("Selection filter does not match any instances")
578
579
580
581
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
  if not (opts.force_multi or not multi_on
          or _ConfirmOperation(inames, "startup")):
    return 1
582
583
584
585
586
  for name in inames:
    op = opcodes.OpStartupInstance(instance_name=name,
                                   force=opts.force,
                                   extra_args=opts.extra_args)
    if multi_on:
587
      ToStdout("Starting up %s", name)
588
589
590
591
    try:
      SubmitOrSend(op, opts)
    except JobSubmittedException, err:
      _, txt = FormatError(err)
592
      ToStdout("%s", txt)
Iustin Pop's avatar
Iustin Pop committed
593
594
  return 0

595

596
597
598
599
600
601
602
603
604
605
606
def RebootInstance(opts, args):
  """Reboot an instance

  Args:
    opts - class with options as members
    args - list containing a single element, the instance name

  """
  if opts.multi_mode is None:
    opts.multi_mode = _SHUTDOWN_INSTANCES
  inames = _ExpandMultiNames(opts.multi_mode, args)
607
608
  if not inames:
    raise errors.OpPrereqError("Selection filter does not match any instances")
609
610
611
612
613
614
615
616
617
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
  if not (opts.force_multi or not multi_on
          or _ConfirmOperation(inames, "reboot")):
    return 1
  for name in inames:
    op = opcodes.OpRebootInstance(instance_name=name,
                                  reboot_type=opts.reboot_type,
                                  ignore_secondaries=opts.ignore_secondaries)

618
    SubmitOrSend(op, opts)
619
  return 0
Iustin Pop's avatar
Iustin Pop committed
620

621

Iustin Pop's avatar
Iustin Pop committed
622
623
624
625
626
627
628
629
def ShutdownInstance(opts, args):
  """Shutdown an instance.

  Args:
    opts - class with options as members
    args - list containing a single element, the instance name

  """
630
631
632
  if opts.multi_mode is None:
    opts.multi_mode = _SHUTDOWN_INSTANCES
  inames = _ExpandMultiNames(opts.multi_mode, args)
633
634
  if not inames:
    raise errors.OpPrereqError("Selection filter does not match any instances")
635
636
637
638
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
  if not (opts.force_multi or not multi_on
          or _ConfirmOperation(inames, "shutdown")):
    return 1
639
640
641
  for name in inames:
    op = opcodes.OpShutdownInstance(instance_name=name)
    if multi_on:
642
      ToStdout("Shutting down %s", name)
643
644
645
646
    try:
      SubmitOrSend(op, opts)
    except JobSubmittedException, err:
      _, txt = FormatError(err)
647
      ToStdout("%s", txt)
Iustin Pop's avatar
Iustin Pop committed
648
649
650
651
652
653
654
655
656
657
658
659
  return 0


def ReplaceDisks(opts, args):
  """Replace the disks of an instance

  Args:
    opts - class with options as members
    args - list with a single element, the instance name

  """
  instance_name = args[0]
660
  new_2ndary = opts.new_secondary
661
  iallocator = opts.iallocator
662
663
664
665
666
667
668
669
  if opts.disks is None:
    disks = ["sda", "sdb"]
  else:
    disks = opts.disks.split(",")
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
    mode = constants.REPLACE_DISK_ALL
  elif opts.on_primary: # only on primary:
    mode = constants.REPLACE_DISK_PRI
670
    if new_2ndary is not None or iallocator is not None:
671
672
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
                                 " replacement")
673
674
  elif opts.on_secondary is not None or iallocator is not None:
    # only on secondary
675
676
677
    mode = constants.REPLACE_DISK_SEC

  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
678
679
                              remote_node=new_2ndary, mode=mode,
                              iallocator=iallocator)
680
  SubmitOrSend(op, opts)
Iustin Pop's avatar
Iustin Pop committed
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  return 0


def FailoverInstance(opts, args):
  """Failover an instance.

  The failover is done by shutting it down on its present node and
  starting it on the secondary.

  Args:
    opts - class with options as members
    args - list with a single element, the instance name
  Opts used:
    force - whether to failover without asking questions.

  """
697
698
  instance_name = args[0]
  force = opts.force
Iustin Pop's avatar
Iustin Pop committed
699

700
701
702
703
704
705
  if not force:
    usertext = ("Failover will happen to image %s."
                " This requires a shutdown of the instance. Continue?" %
                (instance_name,))
    if not AskUser(usertext):
      return 1
Iustin Pop's avatar
Iustin Pop committed
706

707
708
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
                                  ignore_consistency=opts.ignore_consistency)
709
  SubmitOrSend(op, opts)
710
  return 0
Iustin Pop's avatar
Iustin Pop committed
711
712
713
714
715
716
717
718
719
720
721
722
723


def ConnectToInstanceConsole(opts, args):
  """Connect to the console of an instance.

  Args:
    opts - class with options as members
    args - list with a single element, the instance name

  """
  instance_name = args[0]

  op = opcodes.OpConnectConsole(instance_name=instance_name)
724
  cmd = SubmitOpCode(op)
725
726

  if opts.show_command:
727
    ToStdout("%s", utils.ShellQuoteArgs(cmd))
728
729
730
731
  else:
    try:
      os.execvp(cmd[0], cmd)
    finally:
732
      ToStderr("Can't run console command %s with arguments:\n'%s'",
733
               cmd[0], " ".join(cmd))
734
      os._exit(1)
Iustin Pop's avatar
Iustin Pop committed
735
736


737
def _FormatBlockDevInfo(buf, dev, indent_level, static):
Iustin Pop's avatar
Iustin Pop committed
738
739
740
741
742
743
744
  """Show block device information.

  This is only used by ShowInstanceConfig(), but it's too big to be
  left for an inline definition.

  """
  def helper(buf, dtype, status):
Alexander Schreiber's avatar
Alexander Schreiber committed
745
    """Format one line for physical device status."""
Iustin Pop's avatar
Iustin Pop committed
746
747
748
    if not status:
      buf.write("not active\n")
    else:
749
      (path, major, minor, syncp, estt, degr, ldisk) = status
750
751
752
753
754
755
756
757
758
759
760
      if major is None:
        major_string = "N/A"
      else:
        major_string = str(major)

      if minor is None:
        minor_string = "N/A"
      else:
        minor_string = str(minor)

      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
761
      if dtype in (constants.LD_DRBD8, ):
Iustin Pop's avatar
Iustin Pop committed
762
763
764
765
766
767
768
769
770
771
772
773
        if syncp is not None:
          sync_text = "*RECOVERING* %5.2f%%," % syncp
          if estt:
            sync_text += " ETA %ds" % estt
          else:
            sync_text += " ETA unknown"
        else:
          sync_text = "in sync"
        if degr:
          degr_text = "*DEGRADED*"
        else:
          degr_text = "ok"
774
775
776
777
778
        if ldisk:
          ldisk_text = " *MISSING DISK*"
        else:
          ldisk_text = ""
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
779
      elif dtype == constants.LD_LV:
780
781
        if ldisk:
          ldisk_text = " *FAILED* (failed drive?)"
782
        else:
783
784
          ldisk_text = ""
        buf.write(ldisk_text)
Iustin Pop's avatar
Iustin Pop committed
785
786
787
788
789
790
791
792
793
794
795
796
      buf.write("\n")

  if dev["iv_name"] is not None:
    data = "  - %s, " % dev["iv_name"]
  else:
    data = "  - "
  data += "type: %s" % dev["dev_type"]
  if dev["logical_id"] is not None:
    data += ", logical_id: %s" % (dev["logical_id"],)
  elif dev["physical_id"] is not None:
    data += ", physical_id: %s" % (dev["physical_id"],)
  buf.write("%*s%s\n" % (2*indent_level, "", data))
797
798
799
  if not static:
    buf.write("%*s    primary:   " % (2*indent_level, ""))
    helper(buf, dev["dev_type"], dev["pstatus"])
Iustin Pop's avatar
Iustin Pop committed
800

801
  if dev["sstatus"] and not static:
Iustin Pop's avatar
Iustin Pop committed
802
803
804
805
806
    buf.write("%*s    secondary: " % (2*indent_level, ""))
    helper(buf, dev["dev_type"], dev["sstatus"])

  if dev["children"]:
    for child in dev["children"]:
807
      _FormatBlockDevInfo(buf, child, indent_level+1, static)
Iustin Pop's avatar
Iustin Pop committed
808
809
810
811
812
813
814


def ShowInstanceConfig(opts, args):
  """Compute instance run-time status.

  """
  retcode = 0
815
  op = opcodes.OpQueryInstanceData(instances=args, static=opts.static)
Iustin Pop's avatar
Iustin Pop committed
816
817
  result = SubmitOpCode(op)
  if not result:
818
    ToStdout("No instances.")
Iustin Pop's avatar
Iustin Pop committed
819
820
821
822
823
824
825
    return 1

  buf = StringIO()
  retcode = 0
  for instance_name in result:
    instance = result[instance_name]
    buf.write("Instance name: %s\n" % instance["name"])
826
827
828
829
830
831
    buf.write("State: configured to be %s" % instance["config_state"])
    if not opts.static:
      buf.write(", actual state is %s" % instance["run_state"])
    buf.write("\n")
    ##buf.write("Considered for memory checks in cluster verify: %s\n" %
    ##          instance["auto_balance"])
Iustin Pop's avatar
Iustin Pop committed
832
833
834
835
    buf.write("  Nodes:\n")
    buf.write("    - primary: %s\n" % instance["pnode"])
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
    buf.write("  Operating system: %s\n" % instance["os"])
836
837
    if instance.has_key("network_port"):
      buf.write("  Allocated network port: %s\n" % instance["network_port"])
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
    buf.write("  Hypervisor: %s\n" % instance["hypervisor"])
    if instance["hypervisor"] == constants.HT_XEN_PVM:
      hvattrs = ((constants.HV_KERNEL_PATH, "kernel path"),
                 (constants.HV_INITRD_PATH, "initrd path"))
    elif instance["hypervisor"] == constants.HT_XEN_HVM:
      hvattrs = ((constants.HV_BOOT_ORDER, "boot order"),
                 (constants.HV_ACPI, "ACPI"),
                 (constants.HV_PAE, "PAE"),
                 (constants.HV_CDROM_IMAGE_PATH, "virtual CDROM"),
                 (constants.HV_NIC_TYPE, "NIC type"),
                 (constants.HV_DISK_TYPE, "Disk type"),
                 (constants.HV_VNC_BIND_ADDRESS, "VNC bind address"),
                 )
      # custom console information for HVM
      vnc_bind_address = instance["hv_actual"][constants.HV_VNC_BIND_ADDRESS]
      if vnc_bind_address == constants.BIND_ADDRESS_GLOBAL:
        vnc_console_port = "%s:%s" % (instance["pnode"],
                                      instance["network_port"])
      elif vnc_bind_address == constants.LOCALHOST_IP_ADDRESS:
        vnc_console_port = "%s:%s on node %s" % (vnc_bind_address,
                                                 instance["network_port"],
                                                 instance["pnode"])
860
      else:
861
862
863
864
865
866
867
868
869
870
871
        vnc_console_port = "%s:%s" % (vnc_bind_address,
                                      instance["network_port"])
      buf.write("    - console connection: vnc to %s\n" % vnc_console_port)

    else:
      # auto-handle other hypervisor types
      hvattrs = [(key, key) for key in instance["hv_actual"]]

    for key, desc in hvattrs:
      if key in instance["hv_instance"]:
        val = instance["hv_instance"][key]
872
      else:
873
874
        val = "default (%s)" % instance["hv_actual"][key]
      buf.write("    - %s: %s\n" % (desc, val))
Iustin Pop's avatar
Iustin Pop committed
875
    buf.write("  Hardware:\n")
Iustin Pop's avatar
Iustin Pop committed
876
877
878
879
    buf.write("    - VCPUs: %d\n" %
              instance["be_actual"][constants.BE_VCPUS])
    buf.write("    - memory: %dMiB\n" %
              instance["be_actual"][constants.BE_MEMORY])
Iustin Pop's avatar
Iustin Pop committed
880
    buf.write("    - NICs: %s\n" %
881
882
883
              ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
                         (mac, ip, bridge)
                         for mac, ip, bridge in instance["nics"]]))
Iustin Pop's avatar
Iustin Pop committed
884
885
886
    buf.write("  Block devices:\n")

    for device in instance["disks"]:
887
      _FormatBlockDevInfo(buf, device, 1, opts.static)
Iustin Pop's avatar
Iustin Pop committed
888

889
  ToStdout(buf.getvalue().rstrip('\n'))
Iustin Pop's avatar
Iustin Pop committed
890
891
892
  return retcode


893
def SetInstanceParams(opts, args):
Iustin Pop's avatar
Iustin Pop committed
894
895
896
897
898
899
900
901
  """Modifies an instance.

  All parameters take effect only at the next restart of the instance.

  Args:
    opts - class with options as members
    args - list with a single element, the instance name
  Opts used:
902
    mac - the new MAC address of the instance
Iustin Pop's avatar
Iustin Pop committed
903
904

  """
905
906
  if not (opts.ip or opts.bridge or opts.mac or
          opts.hypervisor or opts.beparams):
907
    ToStderr("Please give at least one of the parameters.")
Iustin Pop's avatar
Iustin Pop committed
908
909
    return 1

910
911
912
913
  if constants.BE_MEMORY in opts.beparams:
    opts.beparams[constants.BE_MEMORY] = utils.ParseUnit(
      opts.beparams[constants.BE_MEMORY])

Iustin Pop's avatar
Iustin Pop committed
914
915
  op = opcodes.OpSetInstanceParams(instance_name=args[0],
                                   ip=opts.ip,
916
                                   bridge=opts.bridge, mac=opts.mac,
917
                                   hvparams=opts.hypervisor,
Iustin Pop's avatar
Iustin Pop committed
918
                                   beparams=opts.beparams,
919
                                   force=opts.force)
920

921
922
  # even if here we process the result, we allow submit only
  result = SubmitOrSend(op, opts)
Iustin Pop's avatar
Iustin Pop committed
923
924

  if result:
925
    ToStdout("Modified instance %s", args[0])
Iustin Pop's avatar
Iustin Pop committed
926
    for param, data in result:
927
928
929
      ToStdout(" - %-5s -> %s", param, data)
    ToStdout("Please don't forget that these parameters take effect"
             " only at the next start of the instance.")
Iustin Pop's avatar
Iustin Pop committed
930
931
932
933
934
935
936
  return 0


# options used in more than one cmd
node_opt = make_option("-n", "--node", dest="node", help="Target node",
                       metavar="<node>")

937
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
Michael Hanselmann's avatar
Michael Hanselmann committed
938
                    metavar="<os>")
939

940
# multi-instance selection options
941
942
943
944
945
m_force_multi = make_option("--force-multiple", dest="force_multi",
                            help="Do not ask for confirmation when more than"
                            " one instance is affected",
                            action="store_true", default=False)

946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
m_pri_node_opt = make_option("--primary", dest="multi_mode",
                             help="Filter by nodes (primary only)",
                             const=_SHUTDOWN_NODES_PRI, action="store_const")

m_sec_node_opt = make_option("--secondary", dest="multi_mode",
                             help="Filter by nodes (secondary only)",
                             const=_SHUTDOWN_NODES_SEC, action="store_const")

m_node_opt = make_option("--node", dest="multi_mode",
                         help="Filter by nodes (primary and secondary)",
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")

m_clust_opt = make_option("--all", dest="multi_mode",
                          help="Select all instances in the cluster",
                          const=_SHUTDOWN_CLUSTER, action="store_const")

m_inst_opt = make_option("--instance", dest="multi_mode",
                         help="Filter by instance name [default]",
                         const=_SHUTDOWN_INSTANCES, action="store_const")


Iustin Pop's avatar
Iustin Pop committed
967
968
969
# this is defined separately due to readability only
add_opts = [
  DEBUG_OPT,
970
971
972
  make_option("-n", "--node", dest="node",
              help="Target node and optional secondary node",
              metavar="<pnode>[:<snode>]"),
973
974
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
             " a suffix is used",
Iustin Pop's avatar
Iustin Pop committed
975
             default=20 * 1024, type="unit", metavar="<size>"),
976
977
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
             " suffix is used",
Iustin Pop's avatar
Iustin Pop committed
978
             default=4 * 1024, type="unit", metavar="<size>"),
979
  os_opt,
Iustin Pop's avatar
Iustin Pop committed
980
981
982
  keyval_option("-B", "--backend", dest="beparams",
                type="keyval", default={},
                help="Backend parameters"),
Iustin Pop's avatar
Iustin Pop committed
983
  make_option("-t", "--disk-template", dest="disk_template",
984
              help="Custom disk setup (diskless, file, plain or drbd)",
985
              default=None, metavar="TEMPL"),
Iustin Pop's avatar
Iustin Pop committed
986
987
988
  make_option("-i", "--ip", dest="ip",
              help="IP address ('none' [default], 'auto', or specify address)",
              default='none', type="string", metavar="<ADDRESS>"),
989
990
991
  make_option("--mac", dest="mac",
              help="MAC address ('auto' [default], or specify address)",
              default='auto', type="string", metavar="<MACADDRESS>"),
Iustin Pop's avatar
Iustin Pop committed
992
993
994
995
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
  make_option("-b", "--bridge", dest="bridge",
              help="Bridge to connect this instance to",
996
997
998
999
1000
1001
1002
              default=None, metavar="<bridge>"),
  make_option("--no-start", dest="start", default=True,
              action="store_false", help="Don't start the instance after"
              " creation"),
  make_option("--no-ip-check", dest="ip_check", default=True,
              action="store_false", help="Don't check that the instance's IP"
              " is alive (only valid with --no-start)"),
1003
1004
1005
1006
1007
  make_option("--file-storage-dir", dest="file_storage_dir",
              help="Relative path under default cluster-wide file storage dir"
              " to store file-based disks", default=None,
              metavar="<DIR>"),
  make_option("--file-driver", dest="file_driver", help="Driver to use"
1008
1009
1010
1011
              " for image files", default="loop", metavar="<DRIVER>"),
  make_option("--iallocator", metavar="<NAME>",
              help="Select nodes for the instance automatically using the"
              " <NAME> iallocator plugin", default=None, type="string"),
1012
1013
1014
1015
  ikv_option("-H", "--hypervisor", dest="hypervisor",
              help="Hypervisor and hypervisor options, in the format"
              " hypervisor:option=value,option=value,...", default=None,
              type="identkeyval"),
1016
  SUBMIT_OPT,
Iustin Pop's avatar
Iustin Pop committed
1017
1018
1019
1020
  ]

commands = {
  'add': (AddInstance, ARGS_ONE, add_opts,
1021
          "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
Iustin Pop's avatar
Iustin Pop committed
1022
          "Creates and adds a new instance to the cluster"),
1023
1024
1025
1026
  'batch-create': (BatchCreate, ARGS_ONE,
                   [DEBUG_OPT],
                   "<instances_file.json>",
                   "Create a bunch of instances based on specs in the file."),
1027
1028
1029
1030
1031
  'console': (ConnectToInstanceConsole, ARGS_ONE,
              [DEBUG_OPT,
               make_option("--show-cmd", dest="show_command",
                           action="store_true", default=False,
                           help=("Show command instead of executing it"))],
1032
              "[--show-cmd] <instance>",
Iustin Pop's avatar
Iustin Pop committed
1033
              "Opens a console on the specified instance"),
1034
  'failover': (FailoverInstance, ARGS_ONE,
1035
               [DEBUG_OPT, FORCE_OPT,
Iustin Pop's avatar
Iustin Pop committed
1036
1037
1038
1039
                make_option("--ignore-consistency", dest="ignore_consistency",
                            action="store_true", default=False,
                            help="Ignore the consistency of the disks on"
                            " the secondary"),
1040
                SUBMIT_OPT,
Iustin Pop's avatar
Iustin Pop committed
1041
                ],
1042
               "[-f] <instance>",
Iustin Pop's avatar
Iustin Pop committed
1043
               "Stops the instance and starts it on the backup node, using"
1044
               " the remote mirror (only for instances of type drbd)"),
1045
1046
1047
1048
1049
1050
1051
  'info': (ShowInstanceConfig, ARGS_ANY,
           [DEBUG_OPT,
            make_option("-s", "--static", dest="static",
                        action="store_true", default=False,
                        help="Only show configuration data, not runtime data"),
            ], "[-s] [<instance>...]",
           "Show information on the specified instance(s)"),
Iustin Pop's avatar
Iustin Pop committed
1052
  'list': (ListInstances, ARGS_NONE,
1053
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
1054
1055
           "Lists the instances and their status. The available fields are"
           " (see the man page for details): status, oper_state, oper_ram,"
1056
           " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
1057
1058
           " ip, mac, bridge, sda_size, sdb_size, vcpus, serial_no,"
           " hypervisor."
1059
           " The default field"
1060
1061
           " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
           ),
1062
1063
1064
1065
1066
  'reinstall': (ReinstallInstance, ARGS_ONE,
                [DEBUG_OPT, FORCE_OPT, os_opt,
                 make_option("--select-os", dest="select_os",
                             action="store_true", default=False,
                             help="Interactive OS reinstall, lists available"
1067
1068
1069
                             " OS templates for selection"),
                 SUBMIT_OPT,
                 ],
1070
                "[-f] <instance>", "Reinstall a stopped instance"),
Iustin Pop's avatar
Iustin Pop committed
1071
1072
1073
1074
1075
1076
1077
  'remove': (RemoveInstance, ARGS_ONE,
             [DEBUG_OPT, FORCE_OPT,
              make_option("--ignore-failures", dest="ignore_failures",
                          action="store_true", default=False,
                          help=("Remove the instance from the cluster even"
                                " if there are failures during the removal"
                                " process (shutdown, disk removal, etc.)")),
1078
              SUBMIT_OPT,
Iustin Pop's avatar
Iustin Pop committed
1079
              ],
1080
             "[-f] <instance>", "Shuts down the instance and removes it"),
1081
1082
1083
1084
1085
1086
  'rename': (RenameInstance, ARGS_FIXED(2),
             [DEBUG_OPT,
              make_option("--no-ip-check", dest="ignore_ip",
                          help="Do not check that the IP of the new name"
                          " is alive",
                          default=False, action="store_true"),
1087
              SUBMIT_OPT,
1088
              ],
1089
             "<instance> <new_name>", "Rename the instance"),
Iustin Pop's avatar
Iustin Pop committed
1090
1091
1092
  'replace-disks': (ReplaceDisks, ARGS_ONE,
                    [DEBUG_OPT,
                     make_option("-n", "--new-secondary", dest="new_secondary",
1093
1094
1095
1096
1097
                                 help=("New secondary node (for secondary"
                                       " node change)"), metavar="NODE"),
                     make_option("-p", "--on-primary", dest="on_primary",
                                 default=False, action="store_true",
                                 help=("Replace the disk(s) on the primary"
1098
                                       " node (only for the drbd template)")),
1099
1100
1101
                     make_option("-s", "--on-secondary", dest="on_secondary",
                                 default=False, action="store_true",
                                 help=("Replace the disk(s) on the secondary"
1102
                                       " node (only for the drbd template)")),
1103
1104
1105
1106
                     make_option("--disks", dest="disks", default=None,
                                 help=("Comma-separated list of disks"
                                       " to replace (e.g. sda) (optional,"
                                       " defaults to all disks")),
1107
1108
1109
1110
1111
1112
                     make_option("--iallocator", metavar="<NAME>",
                                 help="Select new secondary for the instance"
                                 " automatically using the"
                                 " <NAME> iallocator plugin (enables"
                                 " secondary node replacement)",
                                 default=None, type="string"),
1113
                     SUBMIT_OPT,
1114
                     ],
1115
                    "[-s|-p|-n NODE] <instance>",
Iustin Pop's avatar
Iustin Pop committed
1116
                    "Replaces all disks for the instance"),
1117
  'modify': (SetInstanceParams, ARGS_ONE,
1118
             [DEBUG_OPT, FORCE_OPT,
Iustin Pop's avatar
Iustin Pop committed
1119
1120
1121
1122
1123
              make_option("-i", "--ip", dest="ip",
                          help="IP address ('none' or numeric IP)",
                          default=None, type="string", metavar="<ADDRESS>"),
              make_option("-b", "--bridge", dest="bridge",
                          help="Bridge to connect this instance to",
1124
                          default=None, type="string", metavar="<bridge>"),
1125
1126
1127
              make_option("--mac", dest="mac",
                          help="MAC address", default=None,
                          type="string", metavar="<MACADDRESS>"),
1128
1129
1130
              keyval_option("-H", "--hypervisor", type="keyval",
                            default={}, dest="hypervisor",
                            help="Change hypervisor parameters"),
1131
1132
1133
              keyval_option("-B", "--backend", type="keyval",
                            default={}, dest="beparams",
                            help="Change backend parameters"),
1134
              SUBMIT_OPT,
Iustin Pop's avatar
Iustin Pop committed
1135
              ],
1136
             "<instance>", "Alters the parameters of an instance"),
1137
1138
  'shutdown': (ShutdownInstance, ARGS_ANY,
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1139
1140
1141
                m_clust_opt, m_inst_opt, m_force_multi,
                SUBMIT_OPT,
                ],
1142
               "<instance>", "Stops an instance"),
1143
  'startup': (StartupInstance, ARGS_ANY,
1144
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
Iustin Pop's avatar
Iustin Pop committed
1145
1146
1147
               make_option("-e", "--extra", dest="extra_args",
                           help="Extra arguments for the instance's kernel",
                           default=None, type="string", metavar="<PARAMS>"),
1148
1149
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
               m_clust_opt, m_inst_opt,
1150
               SUBMIT_OPT,
Iustin Pop's avatar
Iustin Pop committed
1151
               ],
1152
            "<instance>", "Starts an instance"),
1153
1154
1155
1156
1157
1158
1159
1160

  'reboot': (RebootInstance, ARGS_ANY,
              [DEBUG_OPT, m_force_multi,
               make_option("-e", "--extra", dest="extra_args",
                           help="Extra arguments for the instance's kernel",
                           default=None, type="string", metavar="<PARAMS>"),
               make_option("-t", "--type", dest="reboot_type",
                           help="Type of reboot: soft/hard/full",
1161
                           default=constants.INSTANCE_REBOOT_HARD,
1162
1163
1164
1165
1166
1167
                           type="string", metavar="<REBOOT>"),
               make_option("--ignore-secondaries", dest="ignore_secondaries",
                           default=False, action="store_true",
                           help="Ignore errors from secondaries"),
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
               m_clust_opt, m_inst_opt,
1168
               SUBMIT_OPT,
1169
               ],
1170
            "<instance>", "Reboots an instance"),
1171
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1172
                     "<instance>",
Iustin Pop's avatar
Iustin Pop committed
1173
                     "Activate an instance's disks"),
1174
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1175
                       "<instance>",
Iustin Pop's avatar
Iustin Pop committed
1176
                       "Deactivate an instance's disks"),
1177
1178
1179
1180
1181
1182
1183
  'grow-disk': (GrowDisk, ARGS_FIXED(3),
                [DEBUG_OPT, SUBMIT_OPT,
                 make_option("--no-wait-for-sync",
                             dest="wait_for_sync", default=True,
                             action="store_false",
                             help="Don't wait for sync (DANGEROUS!)"),
                 ],
Iustin Pop's avatar
Iustin Pop committed
1184
                "<instance> <disk> <size>", "Grow an instance's disk"),
1185
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1186
                "<instance_name>", "List the tags of the given instance"),
1187
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1188
               "<instance_name> tag...", "Add tags to the given instance"),
1189
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1190
                  "<instance_name> tag...", "Remove tags from given instance"),
Iustin Pop's avatar
Iustin Pop committed
1191
1192
  }

Guido Trotter's avatar
Guido Trotter committed
1193
1194
aliases = {
  'activate_block_devs': 'activate-disks',
1195
  'replace_disks': 'replace-disks',
1196
1197
  'start': 'startup',
  'stop': 'shutdown',
Guido Trotter's avatar
Guido Trotter committed
1198
1199
  }

Iustin Pop's avatar
Iustin Pop committed
1200
if __name__ == '__main__':
Guido Trotter's avatar
Guido Trotter committed
1201
  sys.exit(GenericMain(commands, aliases=aliases,
1202
                       override={"tag_type": constants.TAG_INSTANCE}))