Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 00ce8b29

History | View | Annotate | Download (34.9 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 _TransformPath(user_input):
138
  """Transform a user path into a canonical value.
139

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

    
143
  """
144
  if user_input:
145
    if user_input.lower() == "default":
146
      result_path = constants.VALUE_DEFAULT
147
    elif user_input.lower() == "none":
148
      result_path = constants.VALUE_NONE
149
    else:
150
      if not os.path.isabs(user_input):
151
        raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
152
                                   user_input)
153
      result_path = user_input
154
  else:
155
    result_path = constants.VALUE_DEFAULT
156

    
157
  return result_path
158

    
159

    
160
def ListInstances(opts, args):
161
  """List instances and their properties.
162

    
163
  """
164
  if opts.output is None:
165
    selected_fields = ["name", "os", "pnode", "status", "oper_ram"]
166
  else:
167
    selected_fields = opts.output.split(",")
168

    
169
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
170
  output = SubmitOpCode(op)
171

    
172
  if not opts.no_headers:
173
    headers = {
174
      "name": "Instance", "os": "OS", "pnode": "Primary_node",
175
      "snodes": "Secondary_Nodes", "admin_state": "Autostart",
176
      "oper_state": "Running", "admin_ram": "Configured_memory",
177
      "oper_ram": "Memory", "disk_template": "Disk_template",
178
      "ip": "IP Address", "mac": "MAC Address",
179
      "bridge": "Bridge", "vcpus": "VCPUs",
180
      "sda_size": "Disk/0", "sdb_size": "Disk/1",
181
      "status": "Status",
182
      }
183
  else:
184
    headers = None
185

    
186
  if opts.human_readable:
187
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
188
  else:
189
    unitfields = None
190

    
191
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
192

    
193
  # change raw values to nicer strings
194
  for row in output:
195
    for idx, field in enumerate(selected_fields):
196
      val = row[idx]
197
      if field == "snodes":
198
        val = ",".join(val) or "-"
199
      elif field == "admin_state":
200
        if val:
201
          val = "yes"
202
        else:
203
          val = "no"
204
      elif field == "oper_state":
205
        if val is None:
206
          val = "(node down)"
207
        elif val: # True
208
          val = "running"
209
        else:
210
          val = "stopped"
211
      elif field == "oper_ram":
212
        if val is None:
213
          val = "(node down)"
214
      elif field == "sda_size" or field == "sdb_size":
215
        if val is None:
216
          val = "N/A"
217
      row[idx] = str(val)
218

    
219
  data = GenerateTable(separator=opts.separator, headers=headers,
220
                       fields=selected_fields, unitfields=unitfields,
221
                       numfields=numfields, data=output)
222

    
223
  for line in data:
224
    logger.ToStdout(line)
225

    
226
  return 0
227

    
228

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

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

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

    
244
  (pnode, snode) = SplitNodeOption(opts.node)
245

    
246
  kernel_path = _TransformPath(opts.kernel_path)
247
  initrd_path = _TransformPath(opts.initrd_path)
248

    
249
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
250
                                disk_size=opts.size, swap_size=opts.swap,
251
                                disk_template=opts.disk_template,
252
                                mode=constants.INSTANCE_CREATE,
253
                                os_type=opts.os, pnode=pnode,
254
                                snode=snode, vcpus=opts.vcpus,
255
                                ip=opts.ip, bridge=opts.bridge,
256
                                start=opts.start, ip_check=opts.ip_check,
257
                                wait_for_sync=opts.wait_for_sync,
258
                                mac=opts.mac,
259
                                kernel_path=kernel_path,
260
                                initrd_path=initrd_path,
261
                                hvm_boot_order=opts.hvm_boot_order)
262
  SubmitOpCode(op)
263
  return 0
264

    
265

    
266
def ReinstallInstance(opts, args):
267
  """Reinstall an instance.
268

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

    
273
  """
274
  instance_name = args[0]
275

    
276
  if not opts.force:
277
    usertext = ("This will reinstall the instance %s and remove"
278
                " all data. Continue?") % instance_name
279
    if not AskUser(usertext):
280
      return 1
281

    
282
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
283
                                   os_type=opts.os)
284
  SubmitOpCode(op)
285

    
286
  return 0
287

    
288

    
289
def RemoveInstance(opts, args):
290
  """Remove an instance.
291

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

    
296
  """
297
  instance_name = args[0]
298
  force = opts.force
299

    
300
  if not force:
301
    usertext = ("This will remove the volumes of the instance %s"
302
                " (including mirrors), thus removing all the data"
303
                " of the instance. Continue?") % instance_name
304
    if not AskUser(usertext):
305
      return 1
306

    
307
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
308
                                ignore_failures=opts.ignore_failures)
309
  SubmitOpCode(op)
310
  return 0
311

    
312

    
313
def RenameInstance(opts, args):
314
  """Rename an instance.
315

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

    
320
  """
321
  op = opcodes.OpRenameInstance(instance_name=args[0],
322
                                new_name=args[1],
323
                                ignore_ip=opts.ignore_ip)
324
  SubmitOpCode(op)
325

    
326
  return 0
327

    
328

    
329
def ActivateDisks(opts, args):
330
  """Activate an instance's disks.
331

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

    
337
  """
338
  instance_name = args[0]
339
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
340
  disks_info = SubmitOpCode(op)
341
  for host, iname, nname in disks_info:
342
    print "%s:%s:%s" % (host, iname, nname)
343
  return 0
344

    
345

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

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

    
352
  """
353
  instance_name = args[0]
354
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
355
  SubmitOpCode(op)
356
  return 0
357

    
358

    
359
def StartupInstance(opts, args):
360
  """Startup an instance.
361

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

    
366
  """
367
  if opts.multi_mode is None:
368
    opts.multi_mode = _SHUTDOWN_INSTANCES
369
  inames = _ExpandMultiNames(opts.multi_mode, args)
370
  if not inames:
371
    raise errors.OpPrereqError("Selection filter does not match any instances")
372
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
373
  if not (opts.force_multi or not multi_on
374
          or _ConfirmOperation(inames, "startup")):
375
    return 1
376
  for name in inames:
377
    op = opcodes.OpStartupInstance(instance_name=name,
378
                                   force=opts.force,
379
                                   extra_args=opts.extra_args)
380
    if multi_on:
381
      logger.ToStdout("Starting up %s" % name)
382
    SubmitOpCode(op)
383
  return 0
384

    
385
def RebootInstance(opts, args):
386
  """Reboot an instance
387

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

    
392
  """
393
  if opts.multi_mode is None:
394
    opts.multi_mode = _SHUTDOWN_INSTANCES
395
  inames = _ExpandMultiNames(opts.multi_mode, args)
396
  if not inames:
397
    raise errors.OpPrereqError("Selection filter does not match any instances")
398
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
399
  if not (opts.force_multi or not multi_on
400
          or _ConfirmOperation(inames, "reboot")):
401
    return 1
402
  for name in inames:
403
    op = opcodes.OpRebootInstance(instance_name=name,
404
                                  reboot_type=opts.reboot_type,
405
                                  ignore_secondaries=opts.ignore_secondaries)
406

    
407
    SubmitOpCode(op)
408
  return 0
409

    
410
def ShutdownInstance(opts, args):
411
  """Shutdown an instance.
412

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

    
417
  """
418
  if opts.multi_mode is None:
419
    opts.multi_mode = _SHUTDOWN_INSTANCES
420
  inames = _ExpandMultiNames(opts.multi_mode, args)
421
  if not inames:
422
    raise errors.OpPrereqError("Selection filter does not match any instances")
423
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
424
  if not (opts.force_multi or not multi_on
425
          or _ConfirmOperation(inames, "shutdown")):
426
    return 1
427
  for name in inames:
428
    op = opcodes.OpShutdownInstance(instance_name=name)
429
    if multi_on:
430
      logger.ToStdout("Shutting down %s" % name)
431
    SubmitOpCode(op)
432
  return 0
433

    
434

    
435
def AddMDDRBDComponent(opts, args):
436
  """Add a new component to a remote_raid1 disk.
437

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

    
442
  """
443
  op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
444
                                    disk_name=opts.disk,
445
                                    remote_node=opts.node)
446
  SubmitOpCode(op)
447
  return 0
448

    
449

    
450
def RemoveMDDRBDComponent(opts, args):
451
  """Remove a component from a remote_raid1 disk.
452

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

    
457
  """
458
  op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
459
                                       disk_name=opts.disk,
460
                                       disk_id=opts.port)
461
  SubmitOpCode(op)
462
  return 0
463

    
464

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

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

    
472
  """
473
  instance_name = args[0]
474
  new_2ndary = opts.new_secondary
475
  if opts.disks is None:
476
    disks = ["sda", "sdb"]
477
  else:
478
    disks = opts.disks.split(",")
479
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
480
    mode = constants.REPLACE_DISK_ALL
481
  elif opts.on_primary: # only on primary:
482
    mode = constants.REPLACE_DISK_PRI
483
    if new_2ndary is not None:
484
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
485
                                 " replacement")
486
  elif opts.on_secondary is not None: # only on secondary
487
    mode = constants.REPLACE_DISK_SEC
488

    
489
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
490
                              remote_node=new_2ndary, mode=mode)
491
  SubmitOpCode(op)
492
  return 0
493

    
494

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

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

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

    
507
  """
508
  instance_name = args[0]
509
  force = opts.force
510

    
511
  if not force:
512
    usertext = ("Failover will happen to image %s."
513
                " This requires a shutdown of the instance. Continue?" %
514
                (instance_name,))
515
    if not AskUser(usertext):
516
      return 1
517

    
518
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
519
                                  ignore_consistency=opts.ignore_consistency)
520
  SubmitOpCode(op)
521
  return 0
522

    
523

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

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

    
531
  """
532
  instance_name = args[0]
533

    
534
  op = opcodes.OpConnectConsole(instance_name=instance_name)
535
  cmd, argv = SubmitOpCode(op)
536
  # drop lock and exec so other commands can run while we have console
537
  utils.Unlock("cmd")
538
  try:
539
    os.execvp(cmd, argv)
540
  finally:
541
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
542
                     (cmd, " ".join(argv)))
543
    os._exit(1)
544

    
545

    
546
def _FormatBlockDevInfo(buf, dev, indent_level):
547
  """Show block device information.
548

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

    
552
  """
553
  def helper(buf, dtype, status):
554
    """Format one line for physical device status."""
555
    if not status:
556
      buf.write("not active\n")
557
    else:
558
      (path, major, minor, syncp, estt, degr, ldisk) = status
559
      buf.write("%s (%d:%d)" % (path, major, minor))
560
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
561
        if syncp is not None:
562
          sync_text = "*RECOVERING* %5.2f%%," % syncp
563
          if estt:
564
            sync_text += " ETA %ds" % estt
565
          else:
566
            sync_text += " ETA unknown"
567
        else:
568
          sync_text = "in sync"
569
        if degr:
570
          degr_text = "*DEGRADED*"
571
        else:
572
          degr_text = "ok"
573
        if ldisk:
574
          ldisk_text = " *MISSING DISK*"
575
        else:
576
          ldisk_text = ""
577
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
578
      elif dtype == constants.LD_LV:
579
        if ldisk:
580
          ldisk_text = " *FAILED* (failed drive?)"
581
        else:
582
          ldisk_text = ""
583
        buf.write(ldisk_text)
584
      buf.write("\n")
585

    
586
  if dev["iv_name"] is not None:
587
    data = "  - %s, " % dev["iv_name"]
588
  else:
589
    data = "  - "
590
  data += "type: %s" % dev["dev_type"]
591
  if dev["logical_id"] is not None:
592
    data += ", logical_id: %s" % (dev["logical_id"],)
593
  elif dev["physical_id"] is not None:
594
    data += ", physical_id: %s" % (dev["physical_id"],)
595
  buf.write("%*s%s\n" % (2*indent_level, "", data))
596
  buf.write("%*s    primary:   " % (2*indent_level, ""))
597
  helper(buf, dev["dev_type"], dev["pstatus"])
598

    
599
  if dev["sstatus"]:
600
    buf.write("%*s    secondary: " % (2*indent_level, ""))
601
    helper(buf, dev["dev_type"], dev["sstatus"])
602

    
603
  if dev["children"]:
604
    for child in dev["children"]:
605
      _FormatBlockDevInfo(buf, child, indent_level+1)
606

    
607

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

    
611
  """
612
  retcode = 0
613
  op = opcodes.OpQueryInstanceData(instances=args)
614
  result = SubmitOpCode(op)
615

    
616
  if not result:
617
    logger.ToStdout("No instances.")
618
    return 1
619

    
620
  buf = StringIO()
621
  retcode = 0
622
  for instance_name in result:
623
    instance = result[instance_name]
624
    buf.write("Instance name: %s\n" % instance["name"])
625
    buf.write("State: configured to be %s, actual state is %s\n" %
626
              (instance["config_state"], instance["run_state"]))
627
    buf.write("  Nodes:\n")
628
    buf.write("    - primary: %s\n" % instance["pnode"])
629
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
630
    buf.write("  Operating system: %s\n" % instance["os"])
631
    buf.write("  Allocated network port: %s\n" % instance["network_port"])
632
    if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
633
      kpath = "(default: %s)" % constants.XEN_KERNEL
634
    else:
635
      kpath = instance["kernel_path"]
636
    buf.write("  Kernel path: %s\n" % kpath)
637
    if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
638
      initrd = "(default: %s)" % constants.XEN_INITRD
639
    elif instance["initrd_path"] == constants.VALUE_NONE:
640
      initrd = "(none)"
641
    else:
642
      initrd = instance["initrd_path"]
643
    buf.write("       initrd: %s\n" % initrd)
644
    buf.write("  HVM boot order: %s\n" % instance["hvm_boot_order"])
645
    buf.write("  Hardware:\n")
646
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
647
    buf.write("    - memory: %dMiB\n" % instance["memory"])
648
    buf.write("    - NICs: %s\n" %
649
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
650
                   (mac, ip, bridge)
651
                     for mac, ip, bridge in instance["nics"]]))
652
    buf.write("  Block devices:\n")
653

    
654
    for device in instance["disks"]:
655
      _FormatBlockDevInfo(buf, device, 1)
656

    
657
  logger.ToStdout(buf.getvalue().rstrip('\n'))
658
  return retcode
659

    
660

    
661
def SetInstanceParms(opts, args):
662
  """Modifies an instance.
663

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

    
666
  Args:
667
    opts - class with options as members
668
    args - list with a single element, the instance name
669
  Opts used:
670
    memory - the new memory size
671
    vcpus - the new number of cpus
672
    mac - the new MAC address of the instance
673

    
674
  """
675
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
676
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order):
677
    logger.ToStdout("Please give at least one of the parameters.")
678
    return 1
679

    
680
  kernel_path = _TransformPath(opts.kernel_path)
681
  initrd_path = _TransformPath(opts.initrd_path)
682
  if opts.hvm_boot_order == 'default':
683
    hvm_boot_order = constants.VALUE_DEFAULT
684
  else:
685
    hvm_boot_order = opts.hvm_boot_order
686

    
687
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
688
                                  vcpus=opts.vcpus, ip=opts.ip,
689
                                  bridge=opts.bridge, mac=opts.mac,
690
                                  kernel_path=opts.kernel_path,
691
                                  initrd_path=opts.initrd_path,
692
                                  hvm_boot_order=hvm_boot_order)
693
  result = SubmitOpCode(op)
694

    
695
  if result:
696
    logger.ToStdout("Modified instance %s" % args[0])
697
    for param, data in result:
698
      logger.ToStdout(" - %-5s -> %s" % (param, data))
699
    logger.ToStdout("Please don't forget that these parameters take effect"
700
                    " only at the next start of the instance.")
701
  return 0
702

    
703

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

    
708
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
709
                    metavar="<os>")
710

    
711
# multi-instance selection options
712
m_force_multi = make_option("--force-multiple", dest="force_multi",
713
                            help="Do not ask for confirmation when more than"
714
                            " one instance is affected",
715
                            action="store_true", default=False)
716

    
717
m_pri_node_opt = make_option("--primary", dest="multi_mode",
718
                             help="Filter by nodes (primary only)",
719
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
720

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

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

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

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

    
737

    
738
# this is defined separately due to readability only
739
add_opts = [
740
  DEBUG_OPT,
741
  make_option("-n", "--node", dest="node",
742
              help="Target node and optional secondary node",
743
              metavar="<pnode>[:<snode>]"),
744
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
745
             " a suffix is used",
746
             default=20 * 1024, type="unit", metavar="<size>"),
747
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
748
             " suffix is used",
749
             default=4 * 1024, type="unit", metavar="<size>"),
750
  os_opt,
751
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
752
              default=128, type="unit", metavar="<mem>"),
753
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
754
              default=1, type="int", metavar="<PROC>"),
755
  make_option("-t", "--disk-template", dest="disk_template",
756
              help="Custom disk setup (diskless, plain, local_raid1,"
757
              " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
758
  make_option("-i", "--ip", dest="ip",
759
              help="IP address ('none' [default], 'auto', or specify address)",
760
              default='none', type="string", metavar="<ADDRESS>"),
761
  make_option("--mac", dest="mac",
762
              help="MAC address ('auto' [default], or specify address)",
763
              default='auto', type="string", metavar="<MACADDRESS>"),
764
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
765
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
766
  make_option("-b", "--bridge", dest="bridge",
767
              help="Bridge to connect this instance to",
768
              default=None, metavar="<bridge>"),
769
  make_option("--no-start", dest="start", default=True,
770
              action="store_false", help="Don't start the instance after"
771
              " creation"),
772
  make_option("--no-ip-check", dest="ip_check", default=True,
773
              action="store_false", help="Don't check that the instance's IP"
774
              " is alive (only valid with --no-start)"),
775
  make_option("--kernel", dest="kernel_path",
776
              help="Path to the instances' kernel (or 'default')",
777
              default=None,
778
              type="string", metavar="<FILENAME>"),
779
  make_option("--initrd", dest="initrd_path",
780
              help="Path to the instances' initrd (or 'none', or 'default')",
781
              default=None,
782
              type="string", metavar="<FILENAME>"),
783
  make_option("--hvm-boot-order", dest="hvm_boot_order",
784
              help="boot device order for HVM (one or more of [acdn])",
785
              default=None, type="string", metavar="<BOOTORDER>"),
786
  ]
787

    
788
commands = {
789
  'add': (AddInstance, ARGS_ONE, add_opts,
790
          "[opts...] <name>",
791
          "Creates and adds a new instance to the cluster"),
792
  'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
793
                [DEBUG_OPT, node_opt,
794
                 make_option("-b", "--disk", dest="disk", metavar="sdX",
795
                             help=("The name of the instance disk for which to"
796
                                   " add the mirror"))],
797
                "-n node -b disk <instance>",
798
                "Creates a new mirror for the instance"),
799
  'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
800
              "<instance>",
801
              "Opens a console on the specified instance"),
802
  'failover': (FailoverInstance, ARGS_ONE,
803
               [DEBUG_OPT, FORCE_OPT,
804
                make_option("--ignore-consistency", dest="ignore_consistency",
805
                            action="store_true", default=False,
806
                            help="Ignore the consistency of the disks on"
807
                            " the secondary"),
808
                ],
809
               "[-f] <instance>",
810
               "Stops the instance and starts it on the backup node, using"
811
               " the remote mirror (only for instances of type remote_raid1)"),
812
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
813
           "Show information on the specified instance"),
814
  'list': (ListInstances, ARGS_NONE,
815
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
816
           "Lists the instances and their status. The available fields are"
817
           " (see the man page for details): status, oper_state, oper_ram,"
818
           " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
819
           " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
820
           " list is (in order): name, os, pnode, status,"
821
           " oper_ram."),
822
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
823
                "[-f] <instance>", "Reinstall the instance"),
824
  'remove': (RemoveInstance, ARGS_ONE,
825
             [DEBUG_OPT, FORCE_OPT,
826
              make_option("--ignore-failures", dest="ignore_failures",
827
                          action="store_true", default=False,
828
                          help=("Remove the instance from the cluster even"
829
                                " if there are failures during the removal"
830
                                " process (shutdown, disk removal, etc.)")),
831
              ],
832
             "[-f] <instance>", "Shuts down the instance and removes it"),
833
  'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
834
                   [DEBUG_OPT, node_opt,
835
                    make_option("-b", "--disk", dest="disk", metavar="sdX",
836
                                help=("The name of the instance disk"
837
                                      " for which to add the mirror")),
838
                    make_option("-p", "--port", dest="port", metavar="PORT",
839
                                help=("The port of the drbd device"
840
                                      " which to remove from the mirror"),
841
                                type="int"),
842
                    ],
843
                   "-b disk -p port <instance>",
844
                   "Removes a mirror from the instance"),
845
  'rename': (RenameInstance, ARGS_FIXED(2),
846
             [DEBUG_OPT,
847
              make_option("--no-ip-check", dest="ignore_ip",
848
                          help="Do not check that the IP of the new name"
849
                          " is alive",
850
                          default=False, action="store_true"),
851
              ],
852
             "<instance> <new_name>", "Rename the instance"),
853
  'replace-disks': (ReplaceDisks, ARGS_ONE,
854
                    [DEBUG_OPT,
855
                     make_option("-n", "--new-secondary", dest="new_secondary",
856
                                 help=("New secondary node (for secondary"
857
                                       " node change)"), metavar="NODE"),
858
                     make_option("-p", "--on-primary", dest="on_primary",
859
                                 default=False, action="store_true",
860
                                 help=("Replace the disk(s) on the primary"
861
                                       " node (only for the drbd template)")),
862
                     make_option("-s", "--on-secondary", dest="on_secondary",
863
                                 default=False, action="store_true",
864
                                 help=("Replace the disk(s) on the secondary"
865
                                       " node (only for the drbd template)")),
866
                     make_option("--disks", dest="disks", default=None,
867
                                 help=("Comma-separated list of disks"
868
                                       " to replace (e.g. sda) (optional,"
869
                                       " defaults to all disks")),
870
                     ],
871
                    "[-n NODE] <instance>",
872
                    "Replaces all disks for the instance"),
873
  'modify': (SetInstanceParms, ARGS_ONE,
874
             [DEBUG_OPT, FORCE_OPT,
875
              cli_option("-m", "--memory", dest="mem",
876
                         help="Memory size",
877
                         default=None, type="unit", metavar="<mem>"),
878
              make_option("-p", "--cpu", dest="vcpus",
879
                          help="Number of virtual CPUs",
880
                          default=None, type="int", metavar="<PROC>"),
881
              make_option("-i", "--ip", dest="ip",
882
                          help="IP address ('none' or numeric IP)",
883
                          default=None, type="string", metavar="<ADDRESS>"),
884
              make_option("-b", "--bridge", dest="bridge",
885
                          help="Bridge to connect this instance to",
886
                          default=None, type="string", metavar="<bridge>"),
887
              make_option("--mac", dest="mac",
888
                          help="MAC address", default=None,
889
                          type="string", metavar="<MACADDRESS>"),
890
              make_option("--kernel", dest="kernel_path",
891
                          help="Path to the instances' kernel (or"
892
                          " 'default')", default=None,
893
                          type="string", metavar="<FILENAME>"),
894
              make_option("--initrd", dest="initrd_path",
895
                          help="Path to the instances' initrd (or 'none', or"
896
                          " 'default')", default=None,
897
                          type="string", metavar="<FILENAME>"),
898
              make_option("--hvm-boot-order", dest="hvm_boot_order",
899
                          help="boot device order for HVM"
900
                          "(either one or more of [acdn] or 'default')",
901
                          default=None, type="string", metavar="<BOOTORDER>"),
902
              ],
903
             "<instance>", "Alters the parameters of an instance"),
904
  'shutdown': (ShutdownInstance, ARGS_ANY,
905
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
906
                m_clust_opt, m_inst_opt, m_force_multi],
907
               "<instance>", "Stops an instance"),
908
  'startup': (StartupInstance, ARGS_ANY,
909
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
910
               make_option("-e", "--extra", dest="extra_args",
911
                           help="Extra arguments for the instance's kernel",
912
                           default=None, type="string", metavar="<PARAMS>"),
913
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
914
               m_clust_opt, m_inst_opt,
915
               ],
916
            "<instance>", "Starts an instance"),
917

    
918
  'reboot': (RebootInstance, ARGS_ANY,
919
              [DEBUG_OPT, m_force_multi,
920
               make_option("-e", "--extra", dest="extra_args",
921
                           help="Extra arguments for the instance's kernel",
922
                           default=None, type="string", metavar="<PARAMS>"),
923
               make_option("-t", "--type", dest="reboot_type",
924
                           help="Type of reboot: soft/hard/full",
925
                           default=constants.INSTANCE_REBOOT_SOFT,
926
                           type="string", metavar="<REBOOT>"),
927
               make_option("--ignore-secondaries", dest="ignore_secondaries",
928
                           default=False, action="store_true",
929
                           help="Ignore errors from secondaries"),
930
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
931
               m_clust_opt, m_inst_opt,
932
               ],
933
            "<instance>", "Reboots an instance"),
934
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
935
                     "<instance>",
936
                     "Activate an instance's disks"),
937
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
938
                       "<instance>",
939
                       "Deactivate an instance's disks"),
940
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
941
                "<node_name>", "List the tags of the given instance"),
942
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
943
               "<node_name> tag...", "Add tags to the given instance"),
944
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
945
                  "<node_name> tag...", "Remove tags from given instance"),
946
  }
947

    
948
aliases = {
949
  'activate_block_devs': 'activate-disks',
950
  'replace_disks': 'replace-disks',
951
  'start': 'startup',
952
  'stop': 'shutdown',
953
  }
954

    
955
if __name__ == '__main__':
956
  sys.exit(GenericMain(commands, aliases=aliases,
957
                       override={"tag_type": constants.TAG_INSTANCE}))