Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 457697bc

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

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

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

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

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

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

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

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

    
100
  return inames
101

    
102

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

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

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

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

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

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

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

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

    
137

    
138
def _TransformPath(user_input):
139
  """Transform a user path into a canonical value.
140

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

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

    
158
  return result_path
159

    
160

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

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

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

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

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

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

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

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

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

    
227
  return 0
228

    
229

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

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

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

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

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

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

    
266

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

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

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

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

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

    
287
  return 0
288

    
289

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

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

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

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

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

    
313

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

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

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

    
327
  return 0
328

    
329

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

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

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

    
346

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

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

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

    
359

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

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

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

    
386

    
387
def RebootInstance(opts, args):
388
  """Reboot an instance
389

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

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

    
409
    SubmitOpCode(op)
410
  return 0
411

    
412

    
413
def ShutdownInstance(opts, args):
414
  """Shutdown an instance.
415

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

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

    
437

    
438
def AddMDDRBDComponent(opts, args):
439
  """Add a new component to a remote_raid1 disk.
440

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

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

    
452

    
453
def RemoveMDDRBDComponent(opts, args):
454
  """Remove a component from a remote_raid1 disk.
455

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

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

    
467

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

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

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

    
492
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
493
                              remote_node=new_2ndary, mode=mode)
494
  SubmitOpCode(op)
495
  return 0
496

    
497

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

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

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

    
510
  """
511
  instance_name = args[0]
512
  force = opts.force
513

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

    
521
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
522
                                  ignore_consistency=opts.ignore_consistency)
523
  SubmitOpCode(op)
524
  return 0
525

    
526

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

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

    
534
  """
535
  instance_name = args[0]
536

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

    
548

    
549
def _FormatBlockDevInfo(buf, dev, indent_level):
550
  """Show block device information.
551

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

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

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

    
602
  if dev["sstatus"]:
603
    buf.write("%*s    secondary: " % (2*indent_level, ""))
604
    helper(buf, dev["dev_type"], dev["sstatus"])
605

    
606
  if dev["children"]:
607
    for child in dev["children"]:
608
      _FormatBlockDevInfo(buf, child, indent_level+1)
609

    
610

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

    
614
  """
615
  retcode = 0
616
  op = opcodes.OpQueryInstanceData(instances=args)
617
  result = SubmitOpCode(op)
618

    
619
  if not result:
620
    logger.ToStdout("No instances.")
621
    return 1
622

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

    
657
    for device in instance["disks"]:
658
      _FormatBlockDevInfo(buf, device, 1)
659

    
660
  logger.ToStdout(buf.getvalue().rstrip('\n'))
661
  return retcode
662

    
663

    
664
def SetInstanceParms(opts, args):
665
  """Modifies an instance.
666

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

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

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

    
683
  kernel_path = _TransformPath(opts.kernel_path)
684
  initrd_path = _TransformPath(opts.initrd_path)
685
  if opts.hvm_boot_order == 'default':
686
    hvm_boot_order = constants.VALUE_DEFAULT
687
  else:
688
    hvm_boot_order = opts.hvm_boot_order
689

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

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

    
706

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

    
711
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
712
                    metavar="<os>")
713

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

    
720
m_pri_node_opt = make_option("--primary", dest="multi_mode",
721
                             help="Filter by nodes (primary only)",
722
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
723

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

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

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

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

    
740

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

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

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

    
951
aliases = {
952
  'activate_block_devs': 'activate-disks',
953
  'replace_disks': 'replace-disks',
954
  'start': 'startup',
955
  'stop': 'shutdown',
956
  }
957

    
958
if __name__ == '__main__':
959
  sys.exit(GenericMain(commands, aliases=aliases,
960
                       override={"tag_type": constants.TAG_INSTANCE}))