Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 29921401

History | View | Annotate | Download (25.7 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_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
66
    constants.HV_BOOT_ORDER: \
67
    hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
68
    constants.HV_NIC_TYPE: \
69
    hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
70
    constants.HV_DISK_TYPE: \
71
    hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
72
    constants.HV_USB_MOUSE: \
73
    hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
74
    }
75

    
76
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
77
                                    re.M | re.I)
78

    
79
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
80

    
81
  ANCILLARY_FILES = [
82
    _KVM_NETWORK_SCRIPT,
83
    ]
84

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

    
92
  def _InstancePidAlive(self, instance_name):
93
    """Returns the instance pid and pidfile
94

95
    """
96
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
97
    pid = utils.ReadPidFile(pidfile)
98
    alive = utils.IsProcessAlive(pid)
99

    
100
    return (pidfile, pid, alive)
101

    
102
  @classmethod
103
  def _InstanceMonitor(cls, instance_name):
104
    """Returns the instance monitor socket name
105

106
    """
107
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
108

    
109
  @classmethod
110
  def _InstanceSerial(cls, instance_name):
111
    """Returns the instance serial socket name
112

113
    """
114
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
115

    
116
  @classmethod
117
  def _InstanceKVMRuntime(cls, instance_name):
118
    """Returns the instance KVM runtime filename
119

120
    """
121
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
122

    
123
  @classmethod
124
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
125
    """Removes an instance's rutime sockets/files.
126

127
    """
128
    utils.RemoveFile(pidfile)
129
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
130
    utils.RemoveFile(cls._InstanceSerial(instance_name))
131
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
132

    
133
  def _WriteNetScript(self, instance, seq, nic):
134
    """Write a script to connect a net interface to the proper bridge.
135

136
    This can be used by any qemu-type hypervisor.
137

138
    @param instance: instance we're acting on
139
    @type instance: instance object
140
    @param seq: nic sequence number
141
    @type seq: int
142
    @param nic: nic we're acting on
143
    @type nic: nic object
144
    @return: netscript file name
145
    @rtype: string
146

147
    """
148
    script = StringIO()
149
    script.write("#!/bin/sh\n")
150
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
151
    script.write("export INSTANCE=%s\n" % instance.name)
152
    script.write("export MAC=%s\n" % nic.mac)
153
    if nic.ip:
154
      script.write("export IP=%s\n" % nic.ip)
155
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
156
    if nic.nicparams[constants.NIC_LINK]:
157
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
158
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
159
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
160
    script.write("export INTERFACE=$1\n")
161
    # TODO: make this configurable at ./configure time
162
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
163
    script.write("  # Execute the user-specific vif file\n")
164
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
165
    script.write("else\n")
166
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
167
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
168
      script.write("  # Connect the interface to the bridge\n")
169
      script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
170
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
171
      script.write("  # Route traffic targeted at the IP to the interface\n")
172
      script.write("  /sbin/ip route add $IP/32 dev $INTERFACE\n")
173
      interface_proxy_arp = "/proc/sys/net/ipv4/conf/$INTERFACE/proxy_arp"
174
      script.write("  /bin/echo 1 > %s\n" % interface_proxy_arp)
175
    script.write("fi\n\n")
176
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
177
    # mounted noexec sometimes, so we'll have to find another place.
178
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
179
    tmpfile = os.fdopen(tmpfd, 'w')
180
    tmpfile.write(script.getvalue())
181
    tmpfile.close()
182
    os.chmod(tmpfile_name, 0755)
183
    return tmpfile_name
184

    
185
  def ListInstances(self):
186
    """Get the list of running instances.
187

188
    We can do this by listing our live instances directory and
189
    checking whether the associated kvm process is still alive.
190

191
    """
192
    result = []
193
    for name in os.listdir(self._PIDS_DIR):
194
      filename = "%s/%s" % (self._PIDS_DIR, name)
195
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
196
        result.append(name)
197
    return result
198

    
199
  def GetInstanceInfo(self, instance_name):
200
    """Get instance properties.
201

202
    @param instance_name: the instance name
203

204
    @return: tuple (name, id, memory, vcpus, stat, times)
205

206
    """
207
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
208
    if not alive:
209
      return None
210

    
211
    cmdline_file = "/proc/%s/cmdline" % pid
212
    try:
213
      fh = open(cmdline_file, 'r')
214
      try:
215
        cmdline = fh.read()
216
      finally:
217
        fh.close()
218
    except EnvironmentError, err:
219
      raise errors.HypervisorError("Failed to list instance %s: %s" %
220
                                   (instance_name, err))
221

    
222
    memory = 0
223
    vcpus = 0
224
    stat = "---b-"
225
    times = "0"
226

    
227
    arg_list = cmdline.split('\x00')
228
    while arg_list:
229
      arg =  arg_list.pop(0)
230
      if arg == '-m':
231
        memory = int(arg_list.pop(0))
232
      elif arg == '-smp':
233
        vcpus = int(arg_list.pop(0))
234

    
235
    return (instance_name, pid, memory, vcpus, stat, times)
236

    
237
  def GetAllInstancesInfo(self):
238
    """Get properties of all instances.
239

240
    @return: list of tuples (name, id, memory, vcpus, stat, times)
241

242
    """
243
    data = []
244
    for name in os.listdir(self._PIDS_DIR):
245
      filename = "%s/%s" % (self._PIDS_DIR, name)
246
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
247
        try:
248
          info = self.GetInstanceInfo(name)
249
        except errors.HypervisorError, err:
250
          continue
251
        if info:
252
          data.append(info)
253

    
254
    return data
255

    
256
  def _GenerateKVMRuntime(self, instance, block_devices):
257
    """Generate KVM information to start an instance.
258

259
    """
260
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
261
    kvm = constants.KVM_PATH
262
    kvm_cmd = [kvm]
263
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
264
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
265
    kvm_cmd.extend(['-pidfile', pidfile])
266
    # used just by the vnc server, if enabled
267
    kvm_cmd.extend(['-name', instance.name])
268
    kvm_cmd.extend(['-daemonize'])
269
    if not instance.hvparams[constants.HV_ACPI]:
270
      kvm_cmd.extend(['-no-acpi'])
271

    
272
    hvp = instance.hvparams
273
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
274
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
275
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
276

    
277
    if boot_network:
278
      kvm_cmd.extend(['-boot', 'n'])
279

    
280
    disk_type = hvp[constants.HV_DISK_TYPE]
281
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
282
      if_val = ',if=virtio'
283
    else:
284
      if_val = ',if=%s' % disk_type
285
    for cfdev, dev_path in block_devices:
286
      if cfdev.mode != constants.DISK_RDWR:
287
        raise errors.HypervisorError("Instance has read-only disks which"
288
                                     " are not supported by KVM")
289
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
290
      if boot_disk:
291
        kvm_cmd.extend(['-boot', 'c'])
292
        boot_val = ',boot=on'
293
        # We only boot from the first disk
294
        boot_disk = False
295
      else:
296
        boot_val = ''
297

    
298
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
299
      kvm_cmd.extend(['-drive', drive_val])
300

    
301
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
302
    if iso_image:
303
      options = ',format=raw,media=cdrom'
304
      if boot_cdrom:
305
        kvm_cmd.extend(['-boot', 'd'])
306
        options = '%s,boot=on' % options
307
      else:
308
        options = '%s,if=virtio' % options
309
      drive_val = 'file=%s%s' % (iso_image, options)
310
      kvm_cmd.extend(['-drive', drive_val])
311

    
312
    kernel_path = hvp[constants.HV_KERNEL_PATH]
313
    if kernel_path:
314
      kvm_cmd.extend(['-kernel', kernel_path])
315
      initrd_path = hvp[constants.HV_INITRD_PATH]
316
      if initrd_path:
317
        kvm_cmd.extend(['-initrd', initrd_path])
318
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
319
                     hvp[constants.HV_KERNEL_ARGS]]
320
      if hvp[constants.HV_SERIAL_CONSOLE]:
321
        root_append.append('console=ttyS0,38400')
322
      kvm_cmd.extend(['-append', ' '.join(root_append)])
323

    
324
    mouse_type = hvp[constants.HV_USB_MOUSE]
325
    if mouse_type:
326
      kvm_cmd.extend(['-usb'])
327
      kvm_cmd.extend(['-usbdevice', mouse_type])
328

    
329
    # FIXME: handle vnc password
330
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
331
    if vnc_bind_address:
332
      if utils.IsValidIP(vnc_bind_address):
333
        if instance.network_port > constants.VNC_BASE_PORT:
334
          display = instance.network_port - constants.VNC_BASE_PORT
335
          if vnc_bind_address == '0.0.0.0':
336
            vnc_arg = ':%d' % (display)
337
          else:
338
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
339
        else:
340
          logging.error("Network port is not a valid VNC display (%d < %d)."
341
                        " Not starting VNC" %
342
                        (instance.network_port,
343
                         constants.VNC_BASE_PORT))
344
          vnc_arg = 'none'
345

    
346
        # Only allow tls and other option when not binding to a file, for now.
347
        # kvm/qemu gets confused otherwise about the filename to use.
348
        vnc_append = ''
349
        if hvp[constants.HV_VNC_TLS]:
350
          vnc_append = '%s,tls' % vnc_append
351
          if hvp[constants.HV_VNC_X509_VERIFY]:
352
            vnc_append = '%s,x509verify=%s' % (vnc_append,
353
                                               hvp[constants.HV_VNC_X509])
354
          elif hvp[constants.HV_VNC_X509]:
355
            vnc_append = '%s,x509=%s' % (vnc_append,
356
                                         hvp[constants.HV_VNC_X509])
357
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
358

    
359
      else:
360
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
361

    
362
      kvm_cmd.extend(['-vnc', vnc_arg])
363
    else:
364
      kvm_cmd.extend(['-nographic'])
365

    
366
    monitor_dev = 'unix:%s,server,nowait' % \
367
      self._InstanceMonitor(instance.name)
368
    kvm_cmd.extend(['-monitor', monitor_dev])
369
    if hvp[constants.HV_SERIAL_CONSOLE]:
370
      serial_dev = ('unix:%s,server,nowait' %
371
                    self._InstanceSerial(instance.name))
372
      kvm_cmd.extend(['-serial', serial_dev])
373
    else:
374
      kvm_cmd.extend(['-serial', 'none'])
375

    
376
    # Save the current instance nics, but defer their expansion as parameters,
377
    # as we'll need to generate executable temp files for them.
378
    kvm_nics = instance.nics
379
    hvparams = hvp
380

    
381
    return (kvm_cmd, kvm_nics, hvparams)
382

    
383
  def _WriteKVMRuntime(self, instance_name, data):
384
    """Write an instance's KVM runtime
385

386
    """
387
    try:
388
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
389
                      data=data)
390
    except EnvironmentError, err:
391
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
392

    
393
  def _ReadKVMRuntime(self, instance_name):
394
    """Read an instance's KVM runtime
395

396
    """
397
    try:
398
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
399
    except EnvironmentError, err:
400
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
401
    return file_content
402

    
403
  def _SaveKVMRuntime(self, instance, kvm_runtime):
404
    """Save an instance's KVM runtime
405

406
    """
407
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
408
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
409
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
410
    self._WriteKVMRuntime(instance.name, serialized_form)
411

    
412
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
413
    """Load an instance's KVM runtime
414

415
    """
416
    if not serialized_runtime:
417
      serialized_runtime = self._ReadKVMRuntime(instance.name)
418
    loaded_runtime = serializer.Load(serialized_runtime)
419
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
420
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
421
    return (kvm_cmd, kvm_nics, hvparams)
422

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

426
    @type incoming: tuple of strings
427
    @param incoming: (target_host_ip, port)
428

429
    """
430
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
431
    if alive:
432
      raise errors.HypervisorError("Failed to start instance %s: %s" %
433
                                   (instance.name, "already running"))
434

    
435
    temp_files = []
436

    
437
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
438

    
439
    if not kvm_nics:
440
      kvm_cmd.extend(['-net', 'none'])
441
    else:
442
      nic_type = hvparams[constants.HV_NIC_TYPE]
443
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
444
        nic_model = "model=virtio"
445
      else:
446
        nic_model = "model=%s" % nic_type
447

    
448
      for nic_seq, nic in enumerate(kvm_nics):
449
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
450
        script = self._WriteNetScript(instance, nic_seq, nic)
451
        kvm_cmd.extend(['-net', nic_val])
452
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
453
        temp_files.append(script)
454

    
455
    if incoming:
456
      target, port = incoming
457
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
458

    
459
    result = utils.RunCmd(kvm_cmd)
460
    if result.failed:
461
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
462
                                   (instance.name, result.fail_reason,
463
                                    result.output))
464

    
465
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
466
      raise errors.HypervisorError("Failed to start instance %s: %s" %
467
                                   (instance.name))
468

    
469
    for filename in temp_files:
470
      utils.RemoveFile(filename)
471

    
472
  def StartInstance(self, instance, block_devices):
473
    """Start an instance.
474

475
    """
476
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
477
    if alive:
478
      raise errors.HypervisorError("Failed to start instance %s: %s" %
479
                                   (instance.name, "already running"))
480

    
481
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
482
    self._SaveKVMRuntime(instance, kvm_runtime)
483
    self._ExecuteKVMRuntime(instance, kvm_runtime)
484

    
485
  def _CallMonitorCommand(self, instance_name, command):
486
    """Invoke a command on the instance monitor.
487

488
    """
489
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
490
             (utils.ShellQuote(command),
491
              constants.SOCAT_PATH,
492
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
493
    result = utils.RunCmd(socat)
494
    if result.failed:
495
      msg = ("Failed to send command '%s' to instance %s."
496
             " output: %s, error: %s, fail_reason: %s" %
497
             (command, instance_name,
498
              result.stdout, result.stderr, result.fail_reason))
499
      raise errors.HypervisorError(msg)
500

    
501
    return result
502

    
503
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
504
    """Wait for an instance  to power down.
505

506
    """
507
    # Wait up to $timeout seconds
508
    end = time.time() + timeout
509
    wait = 1
510
    while time.time() < end and utils.IsProcessAlive(pid):
511
      self._CallMonitorCommand(instance.name, 'system_powerdown')
512
      time.sleep(wait)
513
      # Make wait time longer for next try
514
      if wait < 5:
515
        wait *= 1.3
516

    
517
  def StopInstance(self, instance, force=False):
518
    """Stop an instance.
519

520
    """
521
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
522
    if pid > 0 and alive:
523
      if force or not instance.hvparams[constants.HV_ACPI]:
524
        utils.KillProcess(pid)
525
      else:
526
        self._RetryInstancePowerdown(instance, pid)
527

    
528
    if not utils.IsProcessAlive(pid):
529
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
530
      return True
531
    else:
532
      return False
533

    
534
  def RebootInstance(self, instance):
535
    """Reboot an instance.
536

537
    """
538
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
539
    # socket the instance will stop, but now power up again. So we'll resort
540
    # to shutdown and restart.
541
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
542
    if not alive:
543
      raise errors.HypervisorError("Failed to reboot instance %s: not running" %
544
                                             (instance.name))
545
    # StopInstance will delete the saved KVM runtime so:
546
    # ...first load it...
547
    kvm_runtime = self._LoadKVMRuntime(instance)
548
    # ...now we can safely call StopInstance...
549
    if not self.StopInstance(instance):
550
      self.StopInstance(instance, force=True)
551
    # ...and finally we can save it again, and execute it...
552
    self._SaveKVMRuntime(instance, kvm_runtime)
553
    self._ExecuteKVMRuntime(instance, kvm_runtime)
554

    
555
  def MigrationInfo(self, instance):
556
    """Get instance information to perform a migration.
557

558
    @type instance: L{objects.Instance}
559
    @param instance: instance to be migrated
560
    @rtype: string
561
    @return: content of the KVM runtime file
562

563
    """
564
    return self._ReadKVMRuntime(instance.name)
565

    
566
  def AcceptInstance(self, instance, info, target):
567
    """Prepare to accept an instance.
568

569
    @type instance: L{objects.Instance}
570
    @param instance: instance to be accepted
571
    @type info: string
572
    @param info: content of the KVM runtime file on the source node
573
    @type target: string
574
    @param target: target host (usually ip), on this node
575

576
    """
577
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
578
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
579
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
580

    
581
  def FinalizeMigration(self, instance, info, success):
582
    """Finalize an instance migration.
583

584
    Stop the incoming mode KVM.
585

586
    @type instance: L{objects.Instance}
587
    @param instance: instance whose migration is being aborted
588

589
    """
590
    if success:
591
      self._WriteKVMRuntime(instance.name, info)
592
    else:
593
      self.StopInstance(instance, force=True)
594

    
595
  def MigrateInstance(self, instance_name, target, live):
596
    """Migrate an instance to a target node.
597

598
    The migration will not be attempted if the instance is not
599
    currently running.
600

601
    @type instance_name: string
602
    @param instance_name: name of the instance to be migrated
603
    @type target: string
604
    @param target: ip address of the target node
605
    @type live: boolean
606
    @param live: perform a live migration
607

608
    """
609
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
610
    if not alive:
611
      raise errors.HypervisorError("Instance not running, cannot migrate")
612

    
613
    if not live:
614
      self._CallMonitorCommand(instance_name, 'stop')
615

    
616
    migrate_command = ('migrate -d tcp:%s:%s' %
617
                       (target, constants.KVM_MIGRATION_PORT))
618
    self._CallMonitorCommand(instance_name, migrate_command)
619

    
620
    info_command = 'info migrate'
621
    done = False
622
    while not done:
623
      result = self._CallMonitorCommand(instance_name, info_command)
624
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
625
      if not match:
626
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
627
                                     result.stdout)
628
      else:
629
        status = match.group(1)
630
        if status == 'completed':
631
          done = True
632
        elif status == 'active':
633
          time.sleep(2)
634
        elif status == 'failed' or status == 'cancelled':
635
          if not live:
636
            self._CallMonitorCommand(instance_name, 'cont')
637
          raise errors.HypervisorError("Migration %s at the kvm level" %
638
                                       status)
639
        else:
640
          logging.info("KVM: unknown migration status '%s'" % status)
641
          time.sleep(2)
642

    
643
    utils.KillProcess(pid)
644
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
645

    
646
  def GetNodeInfo(self):
647
    """Return information about the node.
648

649
    This is just a wrapper over the base GetLinuxNodeInfo method.
650

651
    @return: a dict with the following keys (values in MiB):
652
          - memory_total: the total memory size on the node
653
          - memory_free: the available memory on the node for instances
654
          - memory_dom0: the memory used by the node itself, if available
655

656
    """
657
    return self.GetLinuxNodeInfo()
658

    
659
  @classmethod
660
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
661
    """Return a command for connecting to the console of an instance.
662

663
    """
664
    if hvparams[constants.HV_SERIAL_CONSOLE]:
665
      # FIXME: The socat shell is not perfect. In particular the way we start
666
      # it ctrl+c will close it, rather than being passed to the other end.
667
      # On the other hand if we pass the option 'raw' (or ignbrk=1) there
668
      # will be no way of exiting socat (except killing it from another shell)
669
      # and ctrl+c doesn't work anyway, printing ^C rather than being
670
      # interpreted by kvm. For now we'll leave it this way, which at least
671
      # allows a minimal interaction and changes on the machine.
672
      shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
673
                       (constants.SOCAT_PATH,
674
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
675
    else:
676
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
677

    
678
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
679
    if vnc_bind_address:
680
      if instance.network_port > constants.VNC_BASE_PORT:
681
        display = instance.network_port - constants.VNC_BASE_PORT
682
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
683
                       " (display: %d)'" % (vnc_bind_address,
684
                                            instance.network_port,
685
                                            display))
686
        shell_command = "%s; %s" % (vnc_command, shell_command)
687

    
688
    return shell_command
689

    
690
  def Verify(self):
691
    """Verify the hypervisor.
692

693
    Check that the binary exists.
694

695
    """
696
    if not os.path.exists(constants.KVM_PATH):
697
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
698
    if not os.path.exists(constants.SOCAT_PATH):
699
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
700

    
701

    
702
  @classmethod
703
  def CheckParameterSyntax(cls, hvparams):
704
    """Check the given parameters for validity.
705

706
    @type hvparams:  dict
707
    @param hvparams: dictionary with parameter names/value
708
    @raise errors.HypervisorError: when a parameter is not valid
709

710
    """
711
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
712

    
713
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
714
    if kernel_path:
715
      if not hvparams[constants.HV_ROOT_PATH]:
716
        raise errors.HypervisorError("Need a root partition for the instance,"
717
                                     " if a kernel is defined")
718

    
719
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
720
        not hvparams[constants.HV_VNC_X509]):
721
      raise errors.HypervisorError("%s must be defined, if %s is" %
722
                                   (constants.HV_VNC_X509,
723
                                    constants.HV_VNC_X509_VERIFY))
724

    
725
    boot_order = hvparams[constants.HV_BOOT_ORDER]
726

    
727
    if (boot_order == constants.HT_BO_CDROM and
728
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
729
      raise errors.HypervisorError("Cannot boot from cdrom without an"
730
                                   " ISO path")
731
    if (boot_order == constants.HT_BO_NETWORK and
732
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
733
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
734
                                   " change the NIC type.")
735

    
736
  @classmethod
737
  def PowercycleNode(cls):
738
    """KVM powercycle, just a wrapper over Linux powercycle.
739

740
    """
741
    cls.LinuxPowercycle()