Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 60d49723

History | View | Annotate | Download (30.5 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
  (pnode, snode) = SplitNodeOption(opts.node)
220

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

    
233

    
234
def ReinstallInstance(opts, args):
235
  """Reinstall an instance.
236

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

    
241
  """
242
  instance_name = args[0]
243

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

    
250
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
251
                                   os_type=opts.os)
252
  SubmitOpCode(op)
253

    
254
  return 0
255

    
256

    
257
def RemoveInstance(opts, args):
258
  """Remove an instance.
259

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

    
264
  """
265
  instance_name = args[0]
266
  force = opts.force
267

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

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

    
280

    
281
def RenameInstance(opts, args):
282
  """Rename an instance.
283

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

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

    
294
  return 0
295

    
296

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

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

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

    
313

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

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

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

    
326

    
327
def StartupInstance(opts, args):
328
  """Startup an instance.
329

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

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

    
353
def RebootInstance(opts, args):
354
  """Reboot an instance
355

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

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

    
375
    SubmitOpCode(op)
376
  return 0
377

    
378
def ShutdownInstance(opts, args):
379
  """Shutdown an instance.
380

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

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

    
402

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

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

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

    
417

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

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

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

    
432

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

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

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

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

    
462

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

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

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

    
475
  """
476
  instance_name = args[0]
477
  force = opts.force
478

    
479
  if not force:
480
    usertext = ("Failover will happen to image %s."
481
                " This requires a shutdown of the instance. Continue?" %
482
                (instance_name,))
483
    if not AskUser(usertext):
484
      return 1
485

    
486
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
487
                                  ignore_consistency=opts.ignore_consistency)
488
  SubmitOpCode(op)
489
  return 0
490

    
491

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

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

    
499
  """
500
  instance_name = args[0]
501

    
502
  op = opcodes.OpConnectConsole(instance_name=instance_name)
503
  cmd, argv = SubmitOpCode(op)
504
  # drop lock and exec so other commands can run while we have console
505
  utils.Unlock("cmd")
506
  try:
507
    os.execvp(cmd, argv)
508
  finally:
509
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
510
                     (cmd, " ".join(argv)))
511
    os._exit(1)
512

    
513

    
514
def _FormatBlockDevInfo(buf, dev, indent_level):
515
  """Show block device information.
516

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

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

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

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

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

    
571

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

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

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

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

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

    
607
  logger.ToStdout(buf.getvalue().rstrip('\n'))
608
  return retcode
609

    
610

    
611
def SetInstanceParms(opts, args):
612
  """Modifies an instance.
613

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

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

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

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

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

    
641

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

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

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

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

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

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

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

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

    
675

    
676
# this is defined separately due to readability only
677
add_opts = [
678
  DEBUG_OPT,
679
  make_option("-n", "--node", dest="node",
680
              help="Target node and optional secondary node",
681
              metavar="<pnode>[:<snode>]"),
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("-b", "--bridge", dest="bridge",
702
              help="Bridge to connect this instance to",
703
              default=None, metavar="<bridge>"),
704
  make_option("--no-start", dest="start", default=True,
705
              action="store_false", help="Don't start the instance after"
706
              " creation"),
707
  make_option("--no-ip-check", dest="ip_check", default=True,
708
              action="store_false", help="Don't check that the instance's IP"
709
              " is alive (only valid with --no-start)"),
710
  ]
711

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

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

    
852
if __name__ == '__main__':
853
  sys.exit(GenericMain(commands,
854
                       override={"tag_type": constants.TAG_INSTANCE}))