Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 48c4dfa8

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
_LIST_DEF_FIELDS = [
44
  "name", "os", "pnode", "status", "oper_ram",
45
  ]
46

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

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

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

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

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

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

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

    
104
  return inames
105

    
106

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

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

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

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

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

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

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

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

    
141

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

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

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

    
162
  return result_path
163

    
164

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

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

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

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

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

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

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

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

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

    
233
  return 0
234

    
235

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

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

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

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

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

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

    
275

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

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

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

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

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

    
296
  return 0
297

    
298

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

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

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

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

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

    
322

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

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

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

    
336
  return 0
337

    
338

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

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

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

    
355

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

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

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

    
368

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

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

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

    
395

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

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

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

    
418
    SubmitOpCode(op)
419
  return 0
420

    
421

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

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

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

    
446

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

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

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

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

    
479

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

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

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

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

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

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

    
508

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

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

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

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

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

    
534

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

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

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

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

    
558
      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
559
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
560
        if syncp is not None:
561
          sync_text = "*RECOVERING* %5.2f%%," % syncp
562
          if estt:
563
            sync_text += " ETA %ds" % estt
564
          else:
565
            sync_text += " ETA unknown"
566
        else:
567
          sync_text = "in sync"
568
        if degr:
569
          degr_text = "*DEGRADED*"
570
        else:
571
          degr_text = "ok"
572
        if ldisk:
573
          ldisk_text = " *MISSING DISK*"
574
        else:
575
          ldisk_text = ""
576
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
577
      elif dtype == constants.LD_LV:
578
        if ldisk:
579
          ldisk_text = " *FAILED* (failed drive?)"
580
        else:
581
          ldisk_text = ""
582
        buf.write(ldisk_text)
583
      buf.write("\n")
584

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

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

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

    
606

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

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

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

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

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

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

    
659

    
660
def SetInstanceParams(opts, args):
661
  """Modifies an instance.
662

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

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

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

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

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

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

    
702

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

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

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

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

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

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

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

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

    
736

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

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

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

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

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