Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 94c2ed34

History | View | Annotate | Download (27.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2008 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
"""KVM hypervisor
23

24
"""
25

    
26
import os
27
import os.path
28
import re
29
import tempfile
30
import time
31
import logging
32
from cStringIO import StringIO
33

    
34
from ganeti import utils
35
from ganeti import constants
36
from ganeti import errors
37
from ganeti import serializer
38
from ganeti import objects
39
from ganeti.hypervisor import hv_base
40

    
41

    
42
class KVMHypervisor(hv_base.BaseHypervisor):
43
  """KVM hypervisor interface"""
44

    
45
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
46
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
47
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
48
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
49
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
50

    
51
  PARAMETERS = {
52
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
53
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
54
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
55
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
56
    constants.HV_ACPI: hv_base.NO_CHECK,
57
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
58
    constants.HV_VNC_BIND_ADDRESS:
59
      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
60
       "the VNC bind address must be either a valid IP address or an absolute"
61
       " pathname", None, None),
62
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
63
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
64
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
65
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
66
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
67
    constants.HV_BOOT_ORDER:
68
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
69
    constants.HV_NIC_TYPE:
70
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
71
    constants.HV_DISK_TYPE:
72
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
73
    constants.HV_USB_MOUSE:
74
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
75
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
76
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
77
    }
78

    
79
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
80
                                    re.M | re.I)
81

    
82
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
83

    
84
  ANCILLARY_FILES = [
85
    _KVM_NETWORK_SCRIPT,
86
    ]
87

    
88
  def __init__(self):
89
    hv_base.BaseHypervisor.__init__(self)
90
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
91
    # in a tmpfs filesystem or has been otherwise wiped out.
92
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
93
    utils.EnsureDirs(dirs)
94

    
95
  def _InstancePidAlive(self, instance_name):
96
    """Returns the instance pid and pidfile
97

98
    """
99
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
100
    pid = utils.ReadPidFile(pidfile)
101
    alive = utils.IsProcessAlive(pid)
102

    
103
    return (pidfile, pid, alive)
104

    
105
  @classmethod
106
  def _InstanceMonitor(cls, instance_name):
107
    """Returns the instance monitor socket name
108

109
    """
110
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
111

    
112
  @classmethod
113
  def _InstanceSerial(cls, instance_name):
114
    """Returns the instance serial socket name
115

116
    """
117
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
118

    
119
  @staticmethod
120
  def _SocatUnixConsoleParams():
121
    """Returns the correct parameters for socat
122

123
    If we have a new-enough socat we can use raw mode with an escape character.
124

125
    """
126
    if constants.SOCAT_USE_ESCAPE:
127
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
128
    else:
129
      return "echo=0,icanon=0"
130

    
131
  @classmethod
132
  def _InstanceKVMRuntime(cls, instance_name):
133
    """Returns the instance KVM runtime filename
134

135
    """
136
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
137

    
138
  @classmethod
139
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
140
    """Removes an instance's rutime sockets/files.
141

142
    """
143
    utils.RemoveFile(pidfile)
144
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
145
    utils.RemoveFile(cls._InstanceSerial(instance_name))
146
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
147

    
148
  def _WriteNetScript(self, instance, seq, nic):
149
    """Write a script to connect a net interface to the proper bridge.
150

151
    This can be used by any qemu-type hypervisor.
152

153
    @param instance: instance we're acting on
154
    @type instance: instance object
155
    @param seq: nic sequence number
156
    @type seq: int
157
    @param nic: nic we're acting on
158
    @type nic: nic object
159
    @return: netscript file name
160
    @rtype: string
161

162
    """
163
    script = StringIO()
164
    script.write("#!/bin/sh\n")
165
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
166
    script.write("export INSTANCE=%s\n" % instance.name)
167
    script.write("export MAC=%s\n" % nic.mac)
168
    if nic.ip:
169
      script.write("export IP=%s\n" % nic.ip)
170
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
171
    if nic.nicparams[constants.NIC_LINK]:
172
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
173
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
174
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
175
    script.write("export INTERFACE=$1\n")
176
    # TODO: make this configurable at ./configure time
177
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
178
    script.write("  # Execute the user-specific vif file\n")
179
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
180
    script.write("else\n")
181
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
182
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
183
      script.write("  # Connect the interface to the bridge\n")
184
      script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
185
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
186
      if not nic.ip:
187
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
188
      script.write("  # Route traffic targeted at the IP to the interface\n")
189
      if nic.nicparams[constants.NIC_LINK]:
190
        script.write("  while /sbin/ip rule del dev $INTERFACE; do :; done\n")
191
        script.write("  /sbin/ip rule add dev $INTERFACE table $LINK\n")
192
        script.write("  /sbin/ip route replace $IP table $LINK proto static"
193
                     " dev $INTERFACE\n")
194
      else:
195
        script.write("  /sbin/ip route replace $IP proto static"
196
                     " dev $INTERFACE\n")
197
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
198
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
199
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
200
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
201
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
202
      script.write("  fi\n")
203
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
204
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
205
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
206
      script.write("  fi\n")
207
    script.write("fi\n\n")
208
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
209
    # mounted noexec sometimes, so we'll have to find another place.
210
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
211
    tmpfile = os.fdopen(tmpfd, 'w')
212
    try:
213
      tmpfile.write(script.getvalue())
214
    finally:
215
      tmpfile.close()
216
    os.chmod(tmpfile_name, 0755)
217
    return tmpfile_name
218

    
219
  def ListInstances(self):
220
    """Get the list of running instances.
221

222
    We can do this by listing our live instances directory and
223
    checking whether the associated kvm process is still alive.
224

225
    """
226
    result = []
227
    for name in os.listdir(self._PIDS_DIR):
228
      filename = "%s/%s" % (self._PIDS_DIR, name)
229
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
230
        result.append(name)
231
    return result
232

    
233
  def GetInstanceInfo(self, instance_name):
234
    """Get instance properties.
235

236
    @param instance_name: the instance name
237

238
    @return: tuple (name, id, memory, vcpus, stat, times)
239

240
    """
241
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
242
    if not alive:
243
      return None
244

    
245
    cmdline_file = "/proc/%s/cmdline" % pid
246
    try:
247
      cmdline = utils.ReadFile(cmdline_file)
248
    except EnvironmentError, err:
249
      raise errors.HypervisorError("Failed to list instance %s: %s" %
250
                                   (instance_name, err))
251

    
252
    memory = 0
253
    vcpus = 0
254
    stat = "---b-"
255
    times = "0"
256

    
257
    arg_list = cmdline.split('\x00')
258
    while arg_list:
259
      arg =  arg_list.pop(0)
260
      if arg == '-m':
261
        memory = int(arg_list.pop(0))
262
      elif arg == '-smp':
263
        vcpus = int(arg_list.pop(0))
264

    
265
    return (instance_name, pid, memory, vcpus, stat, times)
266

    
267
  def GetAllInstancesInfo(self):
268
    """Get properties of all instances.
269

270
    @return: list of tuples (name, id, memory, vcpus, stat, times)
271

272
    """
273
    data = []
274
    for name in os.listdir(self._PIDS_DIR):
275
      filename = "%s/%s" % (self._PIDS_DIR, name)
276
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
277
        try:
278
          info = self.GetInstanceInfo(name)
279
        except errors.HypervisorError, err:
280
          continue
281
        if info:
282
          data.append(info)
283

    
284
    return data
285

    
286
  def _GenerateKVMRuntime(self, instance, block_devices):
287
    """Generate KVM information to start an instance.
288

289
    """
290
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
291
    kvm = constants.KVM_PATH
292
    kvm_cmd = [kvm]
293
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
294
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
295
    kvm_cmd.extend(['-pidfile', pidfile])
296
    # used just by the vnc server, if enabled
297
    kvm_cmd.extend(['-name', instance.name])
298
    kvm_cmd.extend(['-daemonize'])
299
    if not instance.hvparams[constants.HV_ACPI]:
300
      kvm_cmd.extend(['-no-acpi'])
301

    
302
    hvp = instance.hvparams
303
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
304
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
305
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
306

    
307
    if boot_network:
308
      kvm_cmd.extend(['-boot', 'n'])
309

    
310
    disk_type = hvp[constants.HV_DISK_TYPE]
311
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
312
      if_val = ',if=virtio'
313
    else:
314
      if_val = ',if=%s' % disk_type
315
    for cfdev, dev_path in block_devices:
316
      if cfdev.mode != constants.DISK_RDWR:
317
        raise errors.HypervisorError("Instance has read-only disks which"
318
                                     " are not supported by KVM")
319
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
320
      if boot_disk:
321
        kvm_cmd.extend(['-boot', 'c'])
322
        boot_val = ',boot=on'
323
        # We only boot from the first disk
324
        boot_disk = False
325
      else:
326
        boot_val = ''
327

    
328
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
329
      kvm_cmd.extend(['-drive', drive_val])
330

    
331
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
332
    if iso_image:
333
      options = ',format=raw,media=cdrom'
334
      if boot_cdrom:
335
        kvm_cmd.extend(['-boot', 'd'])
336
        options = '%s,boot=on' % options
337
      else:
338
        options = '%s,if=virtio' % options
339
      drive_val = 'file=%s%s' % (iso_image, options)
340
      kvm_cmd.extend(['-drive', drive_val])
341

    
342
    kernel_path = hvp[constants.HV_KERNEL_PATH]
343
    if kernel_path:
344
      kvm_cmd.extend(['-kernel', kernel_path])
345
      initrd_path = hvp[constants.HV_INITRD_PATH]
346
      if initrd_path:
347
        kvm_cmd.extend(['-initrd', initrd_path])
348
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
349
                     hvp[constants.HV_KERNEL_ARGS]]
350
      if hvp[constants.HV_SERIAL_CONSOLE]:
351
        root_append.append('console=ttyS0,38400')
352
      kvm_cmd.extend(['-append', ' '.join(root_append)])
353

    
354
    mouse_type = hvp[constants.HV_USB_MOUSE]
355
    if mouse_type:
356
      kvm_cmd.extend(['-usb'])
357
      kvm_cmd.extend(['-usbdevice', mouse_type])
358

    
359
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
360
    if vnc_bind_address:
361
      if utils.IsValidIP(vnc_bind_address):
362
        if instance.network_port > constants.VNC_BASE_PORT:
363
          display = instance.network_port - constants.VNC_BASE_PORT
364
          if vnc_bind_address == '0.0.0.0':
365
            vnc_arg = ':%d' % (display)
366
          else:
367
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
368
        else:
369
          logging.error("Network port is not a valid VNC display (%d < %d)."
370
                        " Not starting VNC" %
371
                        (instance.network_port,
372
                         constants.VNC_BASE_PORT))
373
          vnc_arg = 'none'
374

    
375
        # Only allow tls and other option when not binding to a file, for now.
376
        # kvm/qemu gets confused otherwise about the filename to use.
377
        vnc_append = ''
378
        if hvp[constants.HV_VNC_TLS]:
379
          vnc_append = '%s,tls' % vnc_append
380
          if hvp[constants.HV_VNC_X509_VERIFY]:
381
            vnc_append = '%s,x509verify=%s' % (vnc_append,
382
                                               hvp[constants.HV_VNC_X509])
383
          elif hvp[constants.HV_VNC_X509]:
384
            vnc_append = '%s,x509=%s' % (vnc_append,
385
                                         hvp[constants.HV_VNC_X509])
386
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
387
          vnc_append = '%s,password' % vnc_append
388

    
389
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
390

    
391
      else:
392
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
393

    
394
      kvm_cmd.extend(['-vnc', vnc_arg])
395
    else:
396
      kvm_cmd.extend(['-nographic'])
397

    
398
    monitor_dev = ("unix:%s,server,nowait" %
399
                   self._InstanceMonitor(instance.name))
400
    kvm_cmd.extend(['-monitor', monitor_dev])
401
    if hvp[constants.HV_SERIAL_CONSOLE]:
402
      serial_dev = ('unix:%s,server,nowait' %
403
                    self._InstanceSerial(instance.name))
404
      kvm_cmd.extend(['-serial', serial_dev])
405
    else:
406
      kvm_cmd.extend(['-serial', 'none'])
407

    
408
    if hvp[constants.HV_USE_LOCALTIME]:
409
      kvm_cmd.extend(['-localtime'])
410

    
411
    # Save the current instance nics, but defer their expansion as parameters,
412
    # as we'll need to generate executable temp files for them.
413
    kvm_nics = instance.nics
414
    hvparams = hvp
415

    
416
    return (kvm_cmd, kvm_nics, hvparams)
417

    
418
  def _WriteKVMRuntime(self, instance_name, data):
419
    """Write an instance's KVM runtime
420

421
    """
422
    try:
423
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
424
                      data=data)
425
    except EnvironmentError, err:
426
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
427

    
428
  def _ReadKVMRuntime(self, instance_name):
429
    """Read an instance's KVM runtime
430

431
    """
432
    try:
433
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
434
    except EnvironmentError, err:
435
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
436
    return file_content
437

    
438
  def _SaveKVMRuntime(self, instance, kvm_runtime):
439
    """Save an instance's KVM runtime
440

441
    """
442
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
443
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
444
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
445
    self._WriteKVMRuntime(instance.name, serialized_form)
446

    
447
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
448
    """Load an instance's KVM runtime
449

450
    """
451
    if not serialized_runtime:
452
      serialized_runtime = self._ReadKVMRuntime(instance.name)
453
    loaded_runtime = serializer.Load(serialized_runtime)
454
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
455
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
456
    return (kvm_cmd, kvm_nics, hvparams)
457

    
458
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
459
    """Execute a KVM cmd, after completing it with some last minute data
460

461
    @type incoming: tuple of strings
462
    @param incoming: (target_host_ip, port)
463

464
    """
465
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
466
    hvp = instance.hvparams
467
    if alive:
468
      raise errors.HypervisorError("Failed to start instance %s: %s" %
469
                                   (instance.name, "already running"))
470

    
471
    temp_files = []
472

    
473
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
474

    
475
    if not kvm_nics:
476
      kvm_cmd.extend(['-net', 'none'])
477
    else:
478
      nic_type = hvparams[constants.HV_NIC_TYPE]
479
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
480
        nic_model = "model=virtio"
481
      else:
482
        nic_model = "model=%s" % nic_type
483

    
484
      for nic_seq, nic in enumerate(kvm_nics):
485
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
486
        script = self._WriteNetScript(instance, nic_seq, nic)
487
        kvm_cmd.extend(['-net', nic_val])
488
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
489
        temp_files.append(script)
490

    
491
    if incoming:
492
      target, port = incoming
493
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
494

    
495
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
496
    vnc_pwd = None
497
    if vnc_pwd_file:
498
      try:
499
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
500
      except EnvironmentError, err:
501
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
502
                                     % (vnc_pwd_file, err))
503

    
504
    result = utils.RunCmd(kvm_cmd)
505
    if result.failed:
506
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
507
                                   (instance.name, result.fail_reason,
508
                                    result.output))
509

    
510
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
511
      raise errors.HypervisorError("Failed to start instance %s" %
512
                                   (instance.name))
513

    
514
    if vnc_pwd:
515
      change_cmd = 'change vnc password %s' % vnc_pwd
516
      self._CallMonitorCommand(instance.name, change_cmd)
517

    
518
    for filename in temp_files:
519
      utils.RemoveFile(filename)
520

    
521
  def StartInstance(self, instance, block_devices):
522
    """Start an instance.
523

524
    """
525
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
526
    if alive:
527
      raise errors.HypervisorError("Failed to start instance %s: %s" %
528
                                   (instance.name, "already running"))
529

    
530
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
531
    self._SaveKVMRuntime(instance, kvm_runtime)
532
    self._ExecuteKVMRuntime(instance, kvm_runtime)
533

    
534
  def _CallMonitorCommand(self, instance_name, command):
535
    """Invoke a command on the instance monitor.
536

537
    """
538
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
539
             (utils.ShellQuote(command),
540
              constants.SOCAT_PATH,
541
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
542
    result = utils.RunCmd(socat)
543
    if result.failed:
544
      msg = ("Failed to send command '%s' to instance %s."
545
             " output: %s, error: %s, fail_reason: %s" %
546
             (command, instance_name,
547
              result.stdout, result.stderr, result.fail_reason))
548
      raise errors.HypervisorError(msg)
549

    
550
    return result
551

    
552
  def StopInstance(self, instance, force=False, retry=False):
553
    """Stop an instance.
554

555
    """
556
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
557
    if pid > 0 and alive:
558
      if force or not instance.hvparams[constants.HV_ACPI]:
559
        utils.KillProcess(pid)
560
      else:
561
        self._CallMonitorCommand(instance.name, 'system_powerdown')
562

    
563
    if not utils.IsProcessAlive(pid):
564
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
565
      return True
566
    else:
567
      return False
568

    
569
  def RebootInstance(self, instance):
570
    """Reboot an instance.
571

572
    """
573
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
574
    # socket the instance will stop, but now power up again. So we'll resort
575
    # to shutdown and restart.
576
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
577
    if not alive:
578
      raise errors.HypervisorError("Failed to reboot instance %s:"
579
                                   " not running" % instance.name)
580
    # StopInstance will delete the saved KVM runtime so:
581
    # ...first load it...
582
    kvm_runtime = self._LoadKVMRuntime(instance)
583
    # ...now we can safely call StopInstance...
584
    if not self.StopInstance(instance):
585
      self.StopInstance(instance, force=True)
586
    # ...and finally we can save it again, and execute it...
587
    self._SaveKVMRuntime(instance, kvm_runtime)
588
    self._ExecuteKVMRuntime(instance, kvm_runtime)
589

    
590
  def MigrationInfo(self, instance):
591
    """Get instance information to perform a migration.
592

593
    @type instance: L{objects.Instance}
594
    @param instance: instance to be migrated
595
    @rtype: string
596
    @return: content of the KVM runtime file
597

598
    """
599
    return self._ReadKVMRuntime(instance.name)
600

    
601
  def AcceptInstance(self, instance, info, target):
602
    """Prepare to accept an instance.
603

604
    @type instance: L{objects.Instance}
605
    @param instance: instance to be accepted
606
    @type info: string
607
    @param info: content of the KVM runtime file on the source node
608
    @type target: string
609
    @param target: target host (usually ip), on this node
610

611
    """
612
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
613
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
614
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
615

    
616
  def FinalizeMigration(self, instance, info, success):
617
    """Finalize an instance migration.
618

619
    Stop the incoming mode KVM.
620

621
    @type instance: L{objects.Instance}
622
    @param instance: instance whose migration is being aborted
623

624
    """
625
    if success:
626
      self._WriteKVMRuntime(instance.name, info)
627
    else:
628
      self.StopInstance(instance, force=True)
629

    
630
  def MigrateInstance(self, instance, target, live):
631
    """Migrate an instance to a target node.
632

633
    The migration will not be attempted if the instance is not
634
    currently running.
635

636
    @type instance: L{objects.Instance}
637
    @param instance: the instance to be migrated
638
    @type target: string
639
    @param target: ip address of the target node
640
    @type live: boolean
641
    @param live: perform a live migration
642

643
    """
644
    instance_name = instance.name
645
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
646
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
647
    if not alive:
648
      raise errors.HypervisorError("Instance not running, cannot migrate")
649

    
650
    if not utils.TcpPing(target, port, live_port_needed=True):
651
      raise errors.HypervisorError("Remote host %s not listening on port"
652
                                   " %s, cannot migrate" % (target, port))
653

    
654
    if not live:
655
      self._CallMonitorCommand(instance_name, 'stop')
656

    
657
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
658
    self._CallMonitorCommand(instance_name, migrate_command)
659

    
660
    info_command = 'info migrate'
661
    done = False
662
    while not done:
663
      result = self._CallMonitorCommand(instance_name, info_command)
664
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
665
      if not match:
666
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
667
                                     result.stdout)
668
      else:
669
        status = match.group(1)
670
        if status == 'completed':
671
          done = True
672
        elif status == 'active':
673
          time.sleep(2)
674
        elif status == 'failed' or status == 'cancelled':
675
          if not live:
676
            self._CallMonitorCommand(instance_name, 'cont')
677
          raise errors.HypervisorError("Migration %s at the kvm level" %
678
                                       status)
679
        else:
680
          logging.info("KVM: unknown migration status '%s'" % status)
681
          time.sleep(2)
682

    
683
    utils.KillProcess(pid)
684
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
685

    
686
  def GetNodeInfo(self):
687
    """Return information about the node.
688

689
    This is just a wrapper over the base GetLinuxNodeInfo method.
690

691
    @return: a dict with the following keys (values in MiB):
692
          - memory_total: the total memory size on the node
693
          - memory_free: the available memory on the node for instances
694
          - memory_dom0: the memory used by the node itself, if available
695

696
    """
697
    return self.GetLinuxNodeInfo()
698

    
699
  @classmethod
700
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
701
    """Return a command for connecting to the console of an instance.
702

703
    """
704
    if hvparams[constants.HV_SERIAL_CONSOLE]:
705
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
706
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
707
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
708
    else:
709
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
710

    
711
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
712
    if vnc_bind_address:
713
      if instance.network_port > constants.VNC_BASE_PORT:
714
        display = instance.network_port - constants.VNC_BASE_PORT
715
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
716
                       " (display: %d)'" % (vnc_bind_address,
717
                                            instance.network_port,
718
                                            display))
719
        shell_command = "%s; %s" % (vnc_command, shell_command)
720

    
721
    return shell_command
722

    
723
  def Verify(self):
724
    """Verify the hypervisor.
725

726
    Check that the binary exists.
727

728
    """
729
    if not os.path.exists(constants.KVM_PATH):
730
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
731
    if not os.path.exists(constants.SOCAT_PATH):
732
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
733

    
734

    
735
  @classmethod
736
  def CheckParameterSyntax(cls, hvparams):
737
    """Check the given parameters for validity.
738

739
    @type hvparams:  dict
740
    @param hvparams: dictionary with parameter names/value
741
    @raise errors.HypervisorError: when a parameter is not valid
742

743
    """
744
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
745

    
746
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
747
    if kernel_path:
748
      if not hvparams[constants.HV_ROOT_PATH]:
749
        raise errors.HypervisorError("Need a root partition for the instance,"
750
                                     " if a kernel is defined")
751

    
752
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
753
        not hvparams[constants.HV_VNC_X509]):
754
      raise errors.HypervisorError("%s must be defined, if %s is" %
755
                                   (constants.HV_VNC_X509,
756
                                    constants.HV_VNC_X509_VERIFY))
757

    
758
    boot_order = hvparams[constants.HV_BOOT_ORDER]
759

    
760
    if (boot_order == constants.HT_BO_CDROM and
761
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
762
      raise errors.HypervisorError("Cannot boot from cdrom without an"
763
                                   " ISO path")
764
    if (boot_order == constants.HT_BO_NETWORK and
765
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
766
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
767
                                   " change the NIC type.")
768

    
769
  @classmethod
770
  def PowercycleNode(cls):
771
    """KVM powercycle, just a wrapper over Linux powercycle.
772

773
    """
774
    cls.LinuxPowercycle()