Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 40cece88

History | View | Annotate | Download (34.6 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2006, 2007 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
import sys
23
import os
24
import itertools
25
from optparse import make_option
26
from cStringIO import StringIO
27

    
28
from ganeti.cli import *
29
from ganeti import opcodes
30
from ganeti import logger
31
from ganeti import constants
32
from ganeti import utils
33
from ganeti import errors
34

    
35

    
36
_SHUTDOWN_CLUSTER = "cluster"
37
_SHUTDOWN_NODES_BOTH = "nodes"
38
_SHUTDOWN_NODES_PRI = "nodes-pri"
39
_SHUTDOWN_NODES_SEC = "nodes-sec"
40
_SHUTDOWN_INSTANCES = "instances"
41

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

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

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

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

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

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

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

    
99
  return inames
100

    
101

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

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

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

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

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

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

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

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

    
136

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

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

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

    
157
  return result_path
158

    
159

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

    
163
  """
164
  if opts.output is None:
165
    selected_fields = ["name", "os", "pnode", "admin_state",
166
                       "oper_state", "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 = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
175
               "snodes": "Secondary_Nodes", "admin_state": "Autostart",
176
               "oper_state": "Status", "admin_ram": "Configured_memory",
177
               "oper_ram": "Memory", "disk_template": "Disk_template",
178
               "ip": "IP Address", "mac": "MAC Address",
179
               "bridge": "Bridge",
180
               "sda_size": "Disk/0", "sdb_size": "Disk/1"}
181
  else:
182
    headers = None
183

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

    
189
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
190

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

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

    
221
  for line in data:
222
    logger.ToStdout(line)
223

    
224
  return 0
225

    
226

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

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

    
239
  """
240
  instance = args[0]
241

    
242
  (pnode, snode) = SplitNodeOption(opts.node)
243

    
244
  kernel_path = _TransformPath(opts.kernel_path)
245
  initrd_path = _TransformPath(opts.initrd_path)
246

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

    
263

    
264
def ReinstallInstance(opts, args):
265
  """Reinstall an instance.
266

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

    
271
  """
272
  instance_name = args[0]
273

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

    
280
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
281
                                   os_type=opts.os)
282
  SubmitOpCode(op)
283

    
284
  return 0
285

    
286

    
287
def RemoveInstance(opts, args):
288
  """Remove an instance.
289

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

    
294
  """
295
  instance_name = args[0]
296
  force = opts.force
297

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

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

    
310

    
311
def RenameInstance(opts, args):
312
  """Rename an instance.
313

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

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

    
324
  return 0
325

    
326

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

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

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

    
343

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

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

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

    
356

    
357
def StartupInstance(opts, args):
358
  """Startup an instance.
359

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

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

    
383
def RebootInstance(opts, args):
384
  """Reboot an instance
385

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

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

    
405
    SubmitOpCode(op)
406
  return 0
407

    
408
def ShutdownInstance(opts, args):
409
  """Shutdown an instance.
410

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

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

    
432

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

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

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

    
447

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

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

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

    
462

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

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

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

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

    
492

    
493
def FailoverInstance(opts, args):
494
  """Failover an instance.
495

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

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

    
505
  """
506
  instance_name = args[0]
507
  force = opts.force
508

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

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

    
521

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

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

    
529
  """
530
  instance_name = args[0]
531

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

    
543

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

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

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

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

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

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

    
605

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

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

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

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

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

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

    
658

    
659
def SetInstanceParms(opts, args):
660
  """Modifies an instance.
661

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

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

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

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

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

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

    
701

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

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

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

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

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

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

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

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

    
735

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

    
786
commands = {
787
  'add': (AddInstance, ARGS_ONE, add_opts,
788
          "[opts...] <name>",
789
          "Creates and adds a new instance to the cluster"),
790
  'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
791
                [DEBUG_OPT, node_opt,
792
                 make_option("-b", "--disk", dest="disk", metavar="sdX",
793
                             help=("The name of the instance disk for which to"
794
                                   " add the mirror"))],
795
                "-n node -b disk <instance>",
796
                "Creates a new mirror for the instance"),
797
  'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
798
              "<instance>",
799
              "Opens a console on the specified instance"),
800
  'failover': (FailoverInstance, ARGS_ONE,
801
               [DEBUG_OPT, FORCE_OPT,
802
                make_option("--ignore-consistency", dest="ignore_consistency",
803
                            action="store_true", default=False,
804
                            help="Ignore the consistency of the disks on"
805
                            " the secondary"),
806
                ],
807
               "[-f] <instance>",
808
               "Stops the instance and starts it on the backup node, using"
809
               " the remote mirror (only for instances of type remote_raid1)"),
810
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
811
           "Show information on the specified instance"),
812
  'list': (ListInstances, ARGS_NONE,
813
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
814
           "", "Lists the instances and their status"),
815
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
816
                "[-f] <instance>", "Reinstall the instance"),
817
  'remove': (RemoveInstance, ARGS_ONE,
818
             [DEBUG_OPT, FORCE_OPT,
819
              make_option("--ignore-failures", dest="ignore_failures",
820
                          action="store_true", default=False,
821
                          help=("Remove the instance from the cluster even"
822
                                " if there are failures during the removal"
823
                                " process (shutdown, disk removal, etc.)")),
824
              ],
825
             "[-f] <instance>", "Shuts down the instance and removes it"),
826
  'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
827
                   [DEBUG_OPT, node_opt,
828
                    make_option("-b", "--disk", dest="disk", metavar="sdX",
829
                                help=("The name of the instance disk"
830
                                      " for which to add the mirror")),
831
                    make_option("-p", "--port", dest="port", metavar="PORT",
832
                                help=("The port of the drbd device"
833
                                      " which to remove from the mirror"),
834
                                type="int"),
835
                    ],
836
                   "-b disk -p port <instance>",
837
                   "Removes a mirror from the instance"),
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
                     ],
864
                    "[-n NODE] <instance>",
865
                    "Replaces all disks for the instance"),
866
  'modify': (SetInstanceParms, ARGS_ONE,
867
             [DEBUG_OPT, FORCE_OPT,
868
              cli_option("-m", "--memory", dest="mem",
869
                         help="Memory size",
870
                         default=None, type="unit", metavar="<mem>"),
871
              make_option("-p", "--cpu", dest="vcpus",
872
                          help="Number of virtual CPUs",
873
                          default=None, type="int", metavar="<PROC>"),
874
              make_option("-i", "--ip", dest="ip",
875
                          help="IP address ('none' or numeric IP)",
876
                          default=None, type="string", metavar="<ADDRESS>"),
877
              make_option("-b", "--bridge", dest="bridge",
878
                          help="Bridge to connect this instance to",
879
                          default=None, type="string", metavar="<bridge>"),
880
              make_option("--mac", dest="mac",
881
                          help="MAC address", default=None,
882
                          type="string", metavar="<MACADDRESS>"),
883
              make_option("--kernel", dest="kernel_path",
884
                          help="Path to the instances' kernel (or"
885
                          " 'default')", default=None,
886
                          type="string", metavar="<FILENAME>"),
887
              make_option("--initrd", dest="initrd_path",
888
                          help="Path to the instances' initrd (or 'none', or"
889
                          " 'default')", default=None,
890
                          type="string", metavar="<FILENAME>"),
891
              make_option("--hvm-boot-order", dest="hvm_boot_order",
892
                          help="boot device order for HVM"
893
                          "(either one or more of [acdn] or 'default')",
894
                          default=None, type="string", metavar="<BOOTORDER>"),
895
              ],
896
             "<instance>", "Alters the parameters of an instance"),
897
  'shutdown': (ShutdownInstance, ARGS_ANY,
898
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
899
                m_clust_opt, m_inst_opt, m_force_multi],
900
               "<instance>", "Stops an instance"),
901
  'startup': (StartupInstance, ARGS_ANY,
902
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
903
               make_option("-e", "--extra", dest="extra_args",
904
                           help="Extra arguments for the instance's kernel",
905
                           default=None, type="string", metavar="<PARAMS>"),
906
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
907
               m_clust_opt, m_inst_opt,
908
               ],
909
            "<instance>", "Starts an instance"),
910

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

    
941
aliases = {
942
  'activate_block_devs': 'activate-disks',
943
  'start': 'startup',
944
  'stop': 'shutdown',
945
  }
946

    
947
if __name__ == '__main__':
948
  sys.exit(GenericMain(commands, aliases=aliases,
949
                       override={"tag_type": constants.TAG_INSTANCE}))