Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ bcee9cb4

History | View | Annotate | Download (30.8 kB)

1
#!/usr/bin/python
2
#
3

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

    
21

    
22
import sys
23
import os
24
import itertools
25
from optparse import make_option
26
from cStringIO import StringIO
27

    
28
from ganeti.cli import *
29
from ganeti import opcodes
30
from ganeti import logger
31
from ganeti import constants
32
from ganeti import utils
33
from ganeti import errors
34

    
35

    
36
_SHUTDOWN_CLUSTER = "cluster"
37
_SHUTDOWN_NODES_BOTH = "nodes"
38
_SHUTDOWN_NODES_PRI = "nodes-pri"
39
_SHUTDOWN_NODES_SEC = "nodes-sec"
40
_SHUTDOWN_INSTANCES = "instances"
41

    
42
def _ExpandMultiNames(mode, names):
43
  """Expand the given names using the passed mode.
44

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

    
53
  For _SHUTDOWN_CLUSTER, all instances will be returned. For
54
  _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
55
  primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
56
  instances having those nodes as either primary or secondary will be
57
  returned. For _SHUTDOWN_INSTANCES, the given instances will be
58
  returned.
59

    
60
  """
61
  if mode == _SHUTDOWN_CLUSTER:
62
    if names:
63
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
64
    op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
65
    idata = SubmitOpCode(op)
66
    inames = [row[0] for row in idata]
67

    
68
  elif mode in (_SHUTDOWN_NODES_BOTH,
69
                _SHUTDOWN_NODES_PRI,
70
                _SHUTDOWN_NODES_SEC):
71
    if not names:
72
      raise errors.OpPrereqError("No node names passed")
73
    op = opcodes.OpQueryNodes(output_fields=["name", "pinst_list",
74
                                             "sinst_list"], names=names)
75
    ndata = SubmitOpCode(op)
76
    ipri = [row[1] for row in ndata]
77
    pri_names = list(itertools.chain(*ipri))
78
    isec = [row[2] for row in ndata]
79
    sec_names = list(itertools.chain(*isec))
80
    if mode == _SHUTDOWN_NODES_BOTH:
81
      inames = pri_names + sec_names
82
    elif mode == _SHUTDOWN_NODES_PRI:
83
      inames = pri_names
84
    elif mode == _SHUTDOWN_NODES_SEC:
85
      inames = sec_names
86
    else:
87
      raise errors.ProgrammerError("Unhandled shutdown type")
88

    
89
  elif mode == _SHUTDOWN_INSTANCES:
90
    if not names:
91
      raise errors.OpPrereqError("No instance names passed")
92
    op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
93
    idata = SubmitOpCode(op)
94
    inames = [row[0] for row in idata]
95

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

    
99
  return inames
100

    
101

    
102
def _ConfirmOperation(inames, text):
103
  """Ask the user to confirm an operation on a list of instances.
104

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

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

    
112
  Returns: boolean depending on user's confirmation.
113

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

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

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

    
130
  choice = AskUser(ask, choices)
131
  if choice == 'v':
132
    choices.pop(1)
133
    choice = AskUser(choices, msg + affected)
134
  return choice
135

    
136

    
137
def ListInstances(opts, args):
138
  """List instances and their properties.
139

    
140
  """
141
  if opts.output is None:
142
    selected_fields = ["name", "os", "pnode", "admin_state",
143
                       "oper_state", "oper_ram"]
144
  else:
145
    selected_fields = opts.output.split(",")
146

    
147
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
148
  output = SubmitOpCode(op)
149

    
150
  if not opts.no_headers:
151
    headers = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
152
               "snodes": "Secondary_Nodes", "admin_state": "Autostart",
153
               "oper_state": "Status", "admin_ram": "Configured_memory",
154
               "oper_ram": "Memory", "disk_template": "Disk_template",
155
               "ip": "IP Address", "mac": "MAC Address",
156
               "bridge": "Bridge",
157
               "sda_size": "Disk/0", "sdb_size": "Disk/1"}
158
  else:
159
    headers = None
160

    
161
  if opts.human_readable:
162
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
163
  else:
164
    unitfields = None
165

    
166
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
167

    
168
  # change raw values to nicer strings
169
  for row in output:
170
    for idx, field in enumerate(selected_fields):
171
      val = row[idx]
172
      if field == "snodes":
173
        val = ",".join(val) or "-"
174
      elif field == "admin_state":
175
        if val:
176
          val = "yes"
177
        else:
178
          val = "no"
179
      elif field == "oper_state":
180
        if val is None:
181
          val = "(node down)"
182
        elif val: # True
183
          val = "running"
184
        else:
185
          val = "stopped"
186
      elif field == "oper_ram":
187
        if val is None:
188
          val = "(node down)"
189
      elif field == "sda_size" or field == "sdb_size":
190
        if val is None:
191
          val = "N/A"
192
      row[idx] = str(val)
193

    
194
  data = GenerateTable(separator=opts.separator, headers=headers,
195
                       fields=selected_fields, unitfields=unitfields,
196
                       numfields=numfields, data=output)
197

    
198
  for line in data:
199
    logger.ToStdout(line)
200

    
201
  return 0
202

    
203

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

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

    
216
  """
217
  instance = args[0]
218

    
219
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
220
                                disk_size=opts.size, swap_size=opts.swap,
221
                                disk_template=opts.disk_template,
222
                                mode=constants.INSTANCE_CREATE,
223
                                os_type=opts.os, pnode=opts.node,
224
                                snode=opts.snode, vcpus=opts.vcpus,
225
                                ip=opts.ip, bridge=opts.bridge,
226
                                start=opts.start, ip_check=opts.ip_check,
227
                                wait_for_sync=opts.wait_for_sync)
228
  SubmitOpCode(op)
229
  return 0
230

    
231

    
232
def ReinstallInstance(opts, args):
233
  """Reinstall an instance.
234

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

    
239
  """
240
  instance_name = args[0]
241

    
242
  if not opts.force:
243
    usertext = ("This will reinstall the instance %s and remove "
244
                "all data. Continue?") % instance_name
245
    if not AskUser(usertext):
246
      return 1
247

    
248
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
249
                                   os_type=opts.os)
250
  SubmitOpCode(op)
251

    
252
  return 0
253

    
254

    
255
def RemoveInstance(opts, args):
256
  """Remove an instance.
257

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

    
262
  """
263
  instance_name = args[0]
264
  force = opts.force
265

    
266
  if not force:
267
    usertext = ("This will remove the volumes of the instance %s"
268
                " (including mirrors), thus removing all the data"
269
                " of the instance. Continue?") % instance_name
270
    if not AskUser(usertext):
271
      return 1
272

    
273
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
274
                                ignore_failures=opts.ignore_failures)
275
  SubmitOpCode(op)
276
  return 0
277

    
278

    
279
def RenameInstance(opts, args):
280
  """Rename an instance.
281

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

    
286
  """
287
  op = opcodes.OpRenameInstance(instance_name=args[0],
288
                                new_name=args[1],
289
                                ignore_ip=opts.ignore_ip)
290
  SubmitOpCode(op)
291

    
292
  return 0
293

    
294

    
295
def ActivateDisks(opts, args):
296
  """Activate an instance's disks.
297

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

    
303
  """
304
  instance_name = args[0]
305
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
306
  disks_info = SubmitOpCode(op)
307
  for host, iname, nname in disks_info:
308
    print "%s:%s:%s" % (host, iname, nname)
309
  return 0
310

    
311

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

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

    
318
  """
319
  instance_name = args[0]
320
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
321
  SubmitOpCode(op)
322
  return 0
323

    
324

    
325
def StartupInstance(opts, args):
326
  """Startup an instance.
327

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

    
332
  """
333
  if opts.multi_mode is None:
334
    opts.multi_mode = _SHUTDOWN_INSTANCES
335
  inames = _ExpandMultiNames(opts.multi_mode, args)
336
  if not inames:
337
    raise errors.OpPrereqError("Selection filter does not match any instances")
338
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
339
  if not (opts.force_multi or not multi_on
340
          or _ConfirmOperation(inames, "startup")):
341
    return 1
342
  for name in inames:
343
    op = opcodes.OpStartupInstance(instance_name=name,
344
                                   force=opts.force,
345
                                   extra_args=opts.extra_args)
346
    if multi_on:
347
      logger.ToStdout("Starting up %s" % name)
348
    SubmitOpCode(op)
349
  return 0
350

    
351
def RebootInstance(opts, args):
352
  """Reboot an instance
353

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

    
358
  """
359
  if opts.multi_mode is None:
360
    opts.multi_mode = _SHUTDOWN_INSTANCES
361
  inames = _ExpandMultiNames(opts.multi_mode, args)
362
  if not inames:
363
    raise errors.OpPrereqError("Selection filter does not match any instances")
364
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
365
  if not (opts.force_multi or not multi_on
366
          or _ConfirmOperation(inames, "reboot")):
367
    return 1
368
  for name in inames:
369
    op = opcodes.OpRebootInstance(instance_name=name,
370
                                  reboot_type=opts.reboot_type,
371
                                  ignore_secondaries=opts.ignore_secondaries)
372

    
373
    SubmitOpCode(op)
374
  return 0
375

    
376
def ShutdownInstance(opts, args):
377
  """Shutdown an instance.
378

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

    
383
  """
384
  if opts.multi_mode is None:
385
    opts.multi_mode = _SHUTDOWN_INSTANCES
386
  inames = _ExpandMultiNames(opts.multi_mode, args)
387
  if not inames:
388
    raise errors.OpPrereqError("Selection filter does not match any instances")
389
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
390
  if not (opts.force_multi or not multi_on
391
          or _ConfirmOperation(inames, "shutdown")):
392
    return 1
393
  for name in inames:
394
    op = opcodes.OpShutdownInstance(instance_name=name)
395
    if multi_on:
396
      logger.ToStdout("Shutting down %s" % name)
397
    SubmitOpCode(op)
398
  return 0
399

    
400

    
401
def AddMDDRBDComponent(opts, args):
402
  """Add a new component to a remote_raid1 disk.
403

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

    
408
  """
409
  op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
410
                                    disk_name=opts.disk,
411
                                    remote_node=opts.node)
412
  SubmitOpCode(op)
413
  return 0
414

    
415

    
416
def RemoveMDDRBDComponent(opts, args):
417
  """Remove a component from a remote_raid1 disk.
418

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

    
423
  """
424
  op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
425
                                       disk_name=opts.disk,
426
                                       disk_id=opts.port)
427
  SubmitOpCode(op)
428
  return 0
429

    
430

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

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

    
438
  """
439
  instance_name = args[0]
440
  new_2ndary = opts.new_secondary
441
  if opts.disks is None:
442
    disks = ["sda", "sdb"]
443
  else:
444
    disks = opts.disks.split(",")
445
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
446
    mode = constants.REPLACE_DISK_ALL
447
  elif opts.on_primary: # only on primary:
448
    mode = constants.REPLACE_DISK_PRI
449
    if new_2ndary is not None:
450
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
451
                                 " replacement")
452
  elif opts.on_secondary is not None: # only on secondary
453
    mode = constants.REPLACE_DISK_SEC
454

    
455
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
456
                              remote_node=new_2ndary, mode=mode)
457
  SubmitOpCode(op)
458
  return 0
459

    
460

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

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

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

    
473
  """
474
  if opts.multi_mode is None:
475
    opts.multi_mode = _SHUTDOWN_INSTANCES
476
  inames = _ExpandMultiNames(opts.multi_mode, args)
477
  if not inames:
478
    raise errors.OpPrereqError("Selection filter does not match any instances")
479
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
480
  if not (opts.force_multi or not multi_on
481
          or _ConfirmOperation(inames, "failover (including shutdown)")):
482
    return 1
483

    
484
  result = 0
485
  for name in inames:
486
    op = opcodes.OpFailoverInstance(instance_name=name,
487
                                    ignore_consistency=opts.ignore_consistency)
488
    if multi_on:
489
      logger.ToStdout("Failing over instance %s" % name)
490
    try:
491
      SubmitOpCode(op)
492
    except errors.OpExecError, err:
493
      result = 1
494
      _, err_msg = FormatError(err)
495
      logger.ToStderr(err_msg)
496

    
497
  return result
498

    
499

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

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

    
507
  """
508
  instance_name = args[0]
509

    
510
  op = opcodes.OpConnectConsole(instance_name=instance_name)
511
  cmd, argv = SubmitOpCode(op)
512
  # drop lock and exec so other commands can run while we have console
513
  utils.Unlock("cmd")
514
  try:
515
    os.execvp(cmd, argv)
516
  finally:
517
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
518
                     (cmd, " ".join(argv)))
519
    os._exit(1)
520

    
521

    
522
def _FormatBlockDevInfo(buf, dev, indent_level):
523
  """Show block device information.
524

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

    
528
  """
529
  def helper(buf, dtype, status):
530
    """Format one line for phsyical device status."""
531
    if not status:
532
      buf.write("not active\n")
533
    else:
534
      (path, major, minor, syncp, estt, degr) = status
535
      buf.write("%s (%d:%d)" % (path, major, minor))
536
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
537
        if syncp is not None:
538
          sync_text = "*RECOVERING* %5.2f%%," % syncp
539
          if estt:
540
            sync_text += " ETA %ds" % estt
541
          else:
542
            sync_text += " ETA unknown"
543
        else:
544
          sync_text = "in sync"
545
        if degr:
546
          degr_text = "*DEGRADED*"
547
        else:
548
          degr_text = "ok"
549
        buf.write(" %s, status %s" % (sync_text, degr_text))
550
      buf.write("\n")
551

    
552
  if dev["iv_name"] is not None:
553
    data = "  - %s, " % dev["iv_name"]
554
  else:
555
    data = "  - "
556
  data += "type: %s" % dev["dev_type"]
557
  if dev["logical_id"] is not None:
558
    data += ", logical_id: %s" % (dev["logical_id"],)
559
  elif dev["physical_id"] is not None:
560
    data += ", physical_id: %s" % (dev["physical_id"],)
561
  buf.write("%*s%s\n" % (2*indent_level, "", data))
562
  buf.write("%*s    primary:   " % (2*indent_level, ""))
563
  helper(buf, dev["dev_type"], dev["pstatus"])
564

    
565
  if dev["sstatus"]:
566
    buf.write("%*s    secondary: " % (2*indent_level, ""))
567
    helper(buf, dev["dev_type"], dev["sstatus"])
568

    
569
  if dev["children"]:
570
    for child in dev["children"]:
571
      _FormatBlockDevInfo(buf, child, indent_level+1)
572

    
573

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

    
577
  """
578
  retcode = 0
579
  op = opcodes.OpQueryInstanceData(instances=args)
580
  result = SubmitOpCode(op)
581

    
582
  if not result:
583
    logger.ToStdout("No instances.")
584
    return 1
585

    
586
  buf = StringIO()
587
  retcode = 0
588
  for instance_name in result:
589
    instance = result[instance_name]
590
    buf.write("Instance name: %s\n" % instance["name"])
591
    buf.write("State: configured to be %s, actual state is %s\n" %
592
              (instance["config_state"], instance["run_state"]))
593
    buf.write("  Nodes:\n")
594
    buf.write("    - primary: %s\n" % instance["pnode"])
595
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
596
    buf.write("  Operating system: %s\n" % instance["os"])
597
    buf.write("  Hardware:\n")
598
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
599
    buf.write("    - memory: %dMiB\n" % instance["memory"])
600
    buf.write("    - NICs: %s\n" %
601
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
602
                   (mac, ip, bridge)
603
                     for mac, ip, bridge in instance["nics"]]))
604
    buf.write("  Block devices:\n")
605

    
606
    for device in instance["disks"]:
607
      _FormatBlockDevInfo(buf, device, 1)
608

    
609
  logger.ToStdout(buf.getvalue().rstrip('\n'))
610
  return retcode
611

    
612

    
613
def SetInstanceParms(opts, args):
614
  """Modifies an instance.
615

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

    
618
  Args:
619
    opts - class with options as members
620
    args - list with a single element, the instance name
621
  Opts used:
622
    memory - the new memory size
623
    vcpus - the new number of cpus
624

    
625
  """
626
  if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
627
    logger.ToStdout("Please give at least one of the parameters.")
628
    return 1
629

    
630
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
631
                                  vcpus=opts.vcpus, ip=opts.ip,
632
                                  bridge=opts.bridge)
633
  result = SubmitOpCode(op)
634

    
635
  if result:
636
    logger.ToStdout("Modified instance %s" % args[0])
637
    for param, data in result:
638
      logger.ToStdout(" - %-5s -> %s" % (param, data))
639
    logger.ToStdout("Please don't forget that these parameters take effect"
640
                    " only at the next start of the instance.")
641
  return 0
642

    
643

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

    
648
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
649
                    metavar="<os>")
650

    
651
# multi-instance selection options
652
m_force_multi = make_option("--force-multiple", dest="force_multi",
653
                            help="Do not ask for confirmation when more than"
654
                            " one instance is affected",
655
                            action="store_true", default=False)
656

    
657
m_pri_node_opt = make_option("--primary", dest="multi_mode",
658
                             help="Filter by nodes (primary only)",
659
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
660

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

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

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

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

    
677

    
678
# this is defined separately due to readability only
679
add_opts = [
680
  DEBUG_OPT,
681
  node_opt,
682
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
683
             " a suffix is used",
684
             default=20 * 1024, type="unit", metavar="<size>"),
685
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
686
             " suffix is used",
687
             default=4 * 1024, type="unit", metavar="<size>"),
688
  os_opt,
689
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
690
              default=128, type="unit", metavar="<mem>"),
691
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
692
              default=1, type="int", metavar="<PROC>"),
693
  make_option("-t", "--disk-template", dest="disk_template",
694
              help="Custom disk setup (diskless, plain, local_raid1,"
695
              " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
696
  make_option("-i", "--ip", dest="ip",
697
              help="IP address ('none' [default], 'auto', or specify address)",
698
              default='none', type="string", metavar="<ADDRESS>"),
699
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
700
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
701
  make_option("--secondary-node", dest="snode",
702
              help="Secondary node for remote_raid1 disk layout",
703
              metavar="<node>"),
704
  make_option("-b", "--bridge", dest="bridge",
705
              help="Bridge to connect this instance to",
706
              default=None, metavar="<bridge>"),
707
  make_option("--no-start", dest="start", default=True,
708
              action="store_false", help="Don't start the instance after"
709
              " creation"),
710
  make_option("--no-ip-check", dest="ip_check", default=True,
711
              action="store_false", help="Don't check that the instance's IP"
712
              " is alive (only valid with --no-start)"),
713
  ]
714

    
715
commands = {
716
  'add': (AddInstance, ARGS_ONE, add_opts,
717
          "[opts...] <name>",
718
          "Creates and adds a new instance to the cluster"),
719
  'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
720
                [DEBUG_OPT, node_opt,
721
                 make_option("-b", "--disk", dest="disk", metavar="sdX",
722
                             help=("The name of the instance disk for which to"
723
                                   " add the mirror"))],
724
                "-n node -b disk <instance>",
725
                "Creates a new mirror for the instance"),
726
  'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
727
              "<instance>",
728
              "Opens a console on the specified instance"),
729
  'failover': (FailoverInstance, ARGS_ATLEAST(1),
730
               [DEBUG_OPT, FORCE_OPT,
731
                make_option("--ignore-consistency", dest="ignore_consistency",
732
                            action="store_true", default=False,
733
                            help="Ignore the consistency of the disks on"
734
                            " the secondary"),
735
                m_pri_node_opt, m_sec_node_opt, m_inst_opt, m_force_multi,
736
                ],
737
               "[-f] <instance>",
738
               "Stops the instance and starts it on the backup node, using"
739
               " the remote mirror (only for instances of type remote_raid1)"),
740
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
741
           "Show information on the specified instance"),
742
  'list': (ListInstances, ARGS_NONE,
743
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
744
           "", "Lists the instances and their status"),
745
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
746
                "[-f] <instance>", "Reinstall the instance"),
747
  'remove': (RemoveInstance, ARGS_ONE,
748
             [DEBUG_OPT, FORCE_OPT,
749
              make_option("--ignore-failures", dest="ignore_failures",
750
                          action="store_true", default=False,
751
                          help=("Remove the instance from the cluster even"
752
                                " if there are failures during the removal"
753
                                " process (shutdown, disk removal, etc.)")),
754
              ],
755
             "[-f] <instance>", "Shuts down the instance and removes it"),
756
  'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
757
                   [DEBUG_OPT, node_opt,
758
                    make_option("-b", "--disk", dest="disk", metavar="sdX",
759
                                help=("The name of the instance disk"
760
                                      " for which to add the mirror")),
761
                    make_option("-p", "--port", dest="port", metavar="PORT",
762
                                help=("The port of the drbd device"
763
                                      " which to remove from the mirror"),
764
                                type="int"),
765
                    ],
766
                   "-b disk -p port <instance>",
767
                   "Removes a mirror from the instance"),
768
  'rename': (RenameInstance, ARGS_FIXED(2),
769
             [DEBUG_OPT,
770
              make_option("--no-ip-check", dest="ignore_ip",
771
                          help="Do not check that the IP of the new name"
772
                          " is alive",
773
                          default=False, action="store_true"),
774
              ],
775
             "<instance> <new_name>", "Rename the instance"),
776
  'replace-disks': (ReplaceDisks, ARGS_ONE,
777
                    [DEBUG_OPT,
778
                     make_option("-n", "--new-secondary", dest="new_secondary",
779
                                 help=("New secondary node (for secondary"
780
                                       " node change)"), metavar="NODE"),
781
                     make_option("-p", "--on-primary", dest="on_primary",
782
                                 default=False, action="store_true",
783
                                 help=("Replace the disk(s) on the primary"
784
                                       " node (only for the drbd8 template)")),
785
                     make_option("-s", "--on-secondary", dest="on_secondary",
786
                                 default=False, action="store_true",
787
                                 help=("Replace the disk(s) on the secondary"
788
                                       " node (only for the drbd8 template)")),
789
                     make_option("--disks", dest="disks", default=None,
790
                                 help=("Comma-separated list of disks"
791
                                       " to replace (e.g. sda) (optional,"
792
                                       " defaults to all disks")),
793
                     ],
794
                    "[-n NODE] <instance>",
795
                    "Replaces all disks for the instance"),
796
  'modify': (SetInstanceParms, ARGS_ONE,
797
             [DEBUG_OPT, FORCE_OPT,
798
              cli_option("-m", "--memory", dest="mem",
799
                         help="Memory size",
800
                         default=None, type="unit", metavar="<mem>"),
801
              make_option("-p", "--cpu", dest="vcpus",
802
                          help="Number of virtual CPUs",
803
                          default=None, type="int", metavar="<PROC>"),
804
              make_option("-i", "--ip", dest="ip",
805
                          help="IP address ('none' or numeric IP)",
806
                          default=None, type="string", metavar="<ADDRESS>"),
807
              make_option("-b", "--bridge", dest="bridge",
808
                          help="Bridge to connect this instance to",
809
                          default=None, type="string", metavar="<bridge>"),
810
              ],
811
             "<instance>", "Alters the parameters of an instance"),
812
  'shutdown': (ShutdownInstance, ARGS_ANY,
813
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
814
                m_clust_opt, m_inst_opt, m_force_multi],
815
               "<instance>", "Stops an instance"),
816
  'startup': (StartupInstance, ARGS_ANY,
817
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
818
               make_option("-e", "--extra", dest="extra_args",
819
                           help="Extra arguments for the instance's kernel",
820
                           default=None, type="string", metavar="<PARAMS>"),
821
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
822
               m_clust_opt, m_inst_opt,
823
               ],
824
            "<instance>", "Starts an instance"),
825

    
826
  'reboot': (RebootInstance, ARGS_ANY,
827
              [DEBUG_OPT, m_force_multi,
828
               make_option("-e", "--extra", dest="extra_args",
829
                           help="Extra arguments for the instance's kernel",
830
                           default=None, type="string", metavar="<PARAMS>"),
831
               make_option("-t", "--type", dest="reboot_type",
832
                           help="Type of reboot: soft/hard/full",
833
                           default=constants.INSTANCE_REBOOT_SOFT,
834
                           type="string", metavar="<REBOOT>"),
835
               make_option("--ignore-secondaries", dest="ignore_secondaries",
836
                           default=False, action="store_true",
837
                           help="Ignore errors from secondaries"),
838
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
839
               m_clust_opt, m_inst_opt,
840
               ],
841
            "<instance>", "Reboots an instance"),
842
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
843
                     "<instance>",
844
                     "Activate an instance's disks"),
845
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
846
                       "<instance>",
847
                       "Deactivate an instance's disks"),
848
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
849
                "<node_name>", "List the tags of the given instance"),
850
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
851
               "<node_name> tag...", "Add tags to the given instance"),
852
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
853
                  "<node_name> tag...", "Remove tags from given instance"),
854
  }
855

    
856
if __name__ == '__main__':
857
  sys.exit(GenericMain(commands,
858
                       override={"tag_type": constants.TAG_INSTANCE}))