Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ bdb7d4e8

History | View | Annotate | Download (35 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
_LIST_DEF_FIELDS = [
44
  "name", "os", "pnode", "status", "oper_ram",
45
  ]
46

    
47

    
48
def _ExpandMultiNames(mode, names):
49
  """Expand the given names using the passed mode.
50

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

    
59
  For _SHUTDOWN_CLUSTER, all instances will be returned. For
60
  _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
61
  primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
62
  instances having those nodes as either primary or secondary will be
63
  returned. For _SHUTDOWN_INSTANCES, the given instances will be
64
  returned.
65

    
66
  """
67
  if mode == _SHUTDOWN_CLUSTER:
68
    if names:
69
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
70
    op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
71
    idata = SubmitOpCode(op)
72
    inames = [row[0] for row in idata]
73

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

    
95
  elif mode == _SHUTDOWN_INSTANCES:
96
    if not names:
97
      raise errors.OpPrereqError("No instance names passed")
98
    op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
99
    idata = SubmitOpCode(op)
100
    inames = [row[0] for row in idata]
101

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

    
105
  return inames
106

    
107

    
108
def _ConfirmOperation(inames, text):
109
  """Ask the user to confirm an operation on a list of instances.
110

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

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

    
118
  Returns: boolean depending on user's confirmation.
119

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

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

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

    
136
  choice = AskUser(ask, choices)
137
  if choice == 'v':
138
    choices.pop(1)
139
    choice = AskUser(msg + affected, choices)
140
  return choice
141

    
142

    
143
def _TransformPath(user_input):
144
  """Transform a user path into a canonical value.
145

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

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

    
163
  return result_path
164

    
165

    
166
def ListInstances(opts, args):
167
  """List instances and their properties.
168

    
169
  """
170
  if opts.output is None:
171
    selected_fields = _LIST_DEF_FIELDS
172
  elif opts.output.startswith("+"):
173
    selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
174
  else:
175
    selected_fields = opts.output.split(",")
176

    
177
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
178
  output = SubmitOpCode(op)
179

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

    
194
  if opts.human_readable:
195
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
196
  else:
197
    unitfields = None
198

    
199
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
200

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

    
227
  data = GenerateTable(separator=opts.separator, headers=headers,
228
                       fields=selected_fields, unitfields=unitfields,
229
                       numfields=numfields, data=output)
230

    
231
  for line in data:
232
    logger.ToStdout(line)
233

    
234
  return 0
235

    
236

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

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

    
249
  """
250
  instance = args[0]
251

    
252
  (pnode, snode) = SplitNodeOption(opts.node)
253

    
254
  kernel_path = _TransformPath(opts.kernel_path)
255
  initrd_path = _TransformPath(opts.initrd_path)
256

    
257
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
258
                                disk_size=opts.size, swap_size=opts.swap,
259
                                disk_template=opts.disk_template,
260
                                mode=constants.INSTANCE_CREATE,
261
                                os_type=opts.os, pnode=pnode,
262
                                snode=snode, vcpus=opts.vcpus,
263
                                ip=opts.ip, bridge=opts.bridge,
264
                                start=opts.start, ip_check=opts.ip_check,
265
                                wait_for_sync=opts.wait_for_sync,
266
                                mac=opts.mac,
267
                                kernel_path=kernel_path,
268
                                initrd_path=initrd_path,
269
                                hvm_boot_order=opts.hvm_boot_order,
270
                                file_storage_dir=opts.file_storage_dir,
271
                                file_driver=opts.file_driver,
272
                                iallocator=opts.iallocator)
273
  SubmitOpCode(op)
274
  return 0
275

    
276

    
277
def ReinstallInstance(opts, args):
278
  """Reinstall an instance.
279

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

    
284
  """
285
  instance_name = args[0]
286

    
287
  if not opts.force:
288
    usertext = ("This will reinstall the instance %s and remove"
289
                " all data. Continue?") % instance_name
290
    if not AskUser(usertext):
291
      return 1
292

    
293
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
294
                                   os_type=opts.os)
295
  SubmitOpCode(op)
296

    
297
  return 0
298

    
299

    
300
def RemoveInstance(opts, args):
301
  """Remove an instance.
302

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

    
307
  """
308
  instance_name = args[0]
309
  force = opts.force
310

    
311
  if not force:
312
    usertext = ("This will remove the volumes of the instance %s"
313
                " (including mirrors), thus removing all the data"
314
                " of the instance. Continue?") % instance_name
315
    if not AskUser(usertext):
316
      return 1
317

    
318
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
319
                                ignore_failures=opts.ignore_failures)
320
  SubmitOpCode(op)
321
  return 0
322

    
323

    
324
def RenameInstance(opts, args):
325
  """Rename an instance.
326

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

    
331
  """
332
  op = opcodes.OpRenameInstance(instance_name=args[0],
333
                                new_name=args[1],
334
                                ignore_ip=opts.ignore_ip)
335
  SubmitOpCode(op)
336

    
337
  return 0
338

    
339

    
340
def ActivateDisks(opts, args):
341
  """Activate an instance's disks.
342

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

    
348
  """
349
  instance_name = args[0]
350
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
351
  disks_info = SubmitOpCode(op)
352
  for host, iname, nname in disks_info:
353
    print "%s:%s:%s" % (host, iname, nname)
354
  return 0
355

    
356

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

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

    
363
  """
364
  instance_name = args[0]
365
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
366
  SubmitOpCode(op)
367
  return 0
368

    
369

    
370
def StartupInstance(opts, args):
371
  """Startup an instance.
372

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

    
377
  """
378
  if opts.multi_mode is None:
379
    opts.multi_mode = _SHUTDOWN_INSTANCES
380
  inames = _ExpandMultiNames(opts.multi_mode, args)
381
  if not inames:
382
    raise errors.OpPrereqError("Selection filter does not match any instances")
383
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
384
  if not (opts.force_multi or not multi_on
385
          or _ConfirmOperation(inames, "startup")):
386
    return 1
387
  for name in inames:
388
    op = opcodes.OpStartupInstance(instance_name=name,
389
                                   force=opts.force,
390
                                   extra_args=opts.extra_args)
391
    if multi_on:
392
      logger.ToStdout("Starting up %s" % name)
393
    SubmitOpCode(op)
394
  return 0
395

    
396

    
397
def RebootInstance(opts, args):
398
  """Reboot an instance
399

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

    
404
  """
405
  if opts.multi_mode is None:
406
    opts.multi_mode = _SHUTDOWN_INSTANCES
407
  inames = _ExpandMultiNames(opts.multi_mode, args)
408
  if not inames:
409
    raise errors.OpPrereqError("Selection filter does not match any instances")
410
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
411
  if not (opts.force_multi or not multi_on
412
          or _ConfirmOperation(inames, "reboot")):
413
    return 1
414
  for name in inames:
415
    op = opcodes.OpRebootInstance(instance_name=name,
416
                                  reboot_type=opts.reboot_type,
417
                                  ignore_secondaries=opts.ignore_secondaries)
418

    
419
    SubmitOpCode(op)
420
  return 0
421

    
422

    
423
def ShutdownInstance(opts, args):
424
  """Shutdown an instance.
425

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

    
430
  """
431
  if opts.multi_mode is None:
432
    opts.multi_mode = _SHUTDOWN_INSTANCES
433
  inames = _ExpandMultiNames(opts.multi_mode, args)
434
  if not inames:
435
    raise errors.OpPrereqError("Selection filter does not match any instances")
436
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
437
  if not (opts.force_multi or not multi_on
438
          or _ConfirmOperation(inames, "shutdown")):
439
    return 1
440
  for name in inames:
441
    op = opcodes.OpShutdownInstance(instance_name=name)
442
    if multi_on:
443
      logger.ToStdout("Shutting down %s" % name)
444
    SubmitOpCode(op)
445
  return 0
446

    
447

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

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

    
455
  """
456
  instance_name = args[0]
457
  new_2ndary = opts.new_secondary
458
  iallocator = opts.iallocator
459
  if opts.disks is None:
460
    disks = ["sda", "sdb"]
461
  else:
462
    disks = opts.disks.split(",")
463
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
464
    mode = constants.REPLACE_DISK_ALL
465
  elif opts.on_primary: # only on primary:
466
    mode = constants.REPLACE_DISK_PRI
467
    if new_2ndary is not None or iallocator is not None:
468
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
469
                                 " replacement")
470
  elif opts.on_secondary is not None or iallocator is not None:
471
    # only on secondary
472
    mode = constants.REPLACE_DISK_SEC
473

    
474
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
475
                              remote_node=new_2ndary, mode=mode,
476
                              iallocator=iallocator)
477
  SubmitOpCode(op)
478
  return 0
479

    
480

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

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

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

    
493
  """
494
  instance_name = args[0]
495
  force = opts.force
496

    
497
  if not force:
498
    usertext = ("Failover will happen to image %s."
499
                " This requires a shutdown of the instance. Continue?" %
500
                (instance_name,))
501
    if not AskUser(usertext):
502
      return 1
503

    
504
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
505
                                  ignore_consistency=opts.ignore_consistency)
506
  SubmitOpCode(op)
507
  return 0
508

    
509

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

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

    
517
  """
518
  instance_name = args[0]
519

    
520
  op = opcodes.OpConnectConsole(instance_name=instance_name)
521
  cmd = SubmitOpCode(op)
522

    
523
  if opts.show_command:
524
    print utils.ShellQuoteArgs(cmd)
525
  else:
526
    # drop lock and exec so other commands can run while we have console
527
    utils.Unlock("cmd")
528
    try:
529
      os.execvp(cmd[0], cmd)
530
    finally:
531
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
532
                       (cmd, " ".join(argv)))
533
      os._exit(1)
534

    
535

    
536
def _FormatBlockDevInfo(buf, dev, indent_level):
537
  """Show block device information.
538

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

    
542
  """
543
  def helper(buf, dtype, status):
544
    """Format one line for physical device status."""
545
    if not status:
546
      buf.write("not active\n")
547
    else:
548
      (path, major, minor, syncp, estt, degr, ldisk) = status
549
      if major is None:
550
        major_string = "N/A"
551
      else:
552
        major_string = str(major)
553

    
554
      if minor is None:
555
        minor_string = "N/A"
556
      else:
557
        minor_string = str(minor)
558

    
559
      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
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 SetInstanceParams(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.OpSetInstanceParams(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, file, plain or drbd)",
757
              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
  make_option("--file-storage-dir", dest="file_storage_dir",
787
              help="Relative path under default cluster-wide file storage dir"
788
              " to store file-based disks", default=None,
789
              metavar="<DIR>"),
790
  make_option("--file-driver", dest="file_driver", help="Driver to use"
791
              " for image files", default="loop", metavar="<DRIVER>"),
792
  make_option("--iallocator", metavar="<NAME>",
793
              help="Select nodes for the instance automatically using the"
794
              " <NAME> iallocator plugin", default=None, type="string"),
795
  ]
796

    
797
commands = {
798
  'add': (AddInstance, ARGS_ONE, add_opts,
799
          "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
800
          "Creates and adds a new instance to the cluster"),
801
  'console': (ConnectToInstanceConsole, ARGS_ONE,
802
              [DEBUG_OPT,
803
               make_option("--show-cmd", dest="show_command",
804
                           action="store_true", default=False,
805
                           help=("Show command instead of executing it"))],
806
              "[--show-cmd] <instance>",
807
              "Opens a console on the specified instance"),
808
  'failover': (FailoverInstance, ARGS_ONE,
809
               [DEBUG_OPT, FORCE_OPT,
810
                make_option("--ignore-consistency", dest="ignore_consistency",
811
                            action="store_true", default=False,
812
                            help="Ignore the consistency of the disks on"
813
                            " the secondary"),
814
                ],
815
               "[-f] <instance>",
816
               "Stops the instance and starts it on the backup node, using"
817
               " the remote mirror (only for instances of type drbd)"),
818
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
819
           "Show information on the specified instance"),
820
  'list': (ListInstances, ARGS_NONE,
821
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
822
           "Lists the instances and their status. The available fields are"
823
           " (see the man page for details): status, oper_state, oper_ram,"
824
           " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
825
           " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
826
           " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
827
           ),
828
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
829
                "[-f] <instance>", "Reinstall a stopped instance"),
830
  'remove': (RemoveInstance, ARGS_ONE,
831
             [DEBUG_OPT, FORCE_OPT,
832
              make_option("--ignore-failures", dest="ignore_failures",
833
                          action="store_true", default=False,
834
                          help=("Remove the instance from the cluster even"
835
                                " if there are failures during the removal"
836
                                " process (shutdown, disk removal, etc.)")),
837
              ],
838
             "[-f] <instance>", "Shuts down the instance and removes it"),
839
  'rename': (RenameInstance, ARGS_FIXED(2),
840
             [DEBUG_OPT,
841
              make_option("--no-ip-check", dest="ignore_ip",
842
                          help="Do not check that the IP of the new name"
843
                          " is alive",
844
                          default=False, action="store_true"),
845
              ],
846
             "<instance> <new_name>", "Rename the instance"),
847
  'replace-disks': (ReplaceDisks, ARGS_ONE,
848
                    [DEBUG_OPT,
849
                     make_option("-n", "--new-secondary", dest="new_secondary",
850
                                 help=("New secondary node (for secondary"
851
                                       " node change)"), metavar="NODE"),
852
                     make_option("-p", "--on-primary", dest="on_primary",
853
                                 default=False, action="store_true",
854
                                 help=("Replace the disk(s) on the primary"
855
                                       " node (only for the drbd template)")),
856
                     make_option("-s", "--on-secondary", dest="on_secondary",
857
                                 default=False, action="store_true",
858
                                 help=("Replace the disk(s) on the secondary"
859
                                       " node (only for the drbd template)")),
860
                     make_option("--disks", dest="disks", default=None,
861
                                 help=("Comma-separated list of disks"
862
                                       " to replace (e.g. sda) (optional,"
863
                                       " defaults to all disks")),
864
                     make_option("--iallocator", metavar="<NAME>",
865
                                 help="Select new secondary for the instance"
866
                                 " automatically using the"
867
                                 " <NAME> iallocator plugin (enables"
868
                                 " secondary node replacement)",
869
                                 default=None, type="string"),
870
                     ],
871
                    "[-s|-p|-n NODE] <instance>",
872
                    "Replaces all disks for the instance"),
873
  'modify': (SetInstanceParams, 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}))