Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ d63218ce

History | View | Annotate | Download (26 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
      if nic.nicparams[constants.NIC_LINK]:
173
        script.write("  /sbin/ip route replace $IP/32 table $LINK"
174
                     " dev $INTERFACE\n")
175
      else:
176
        script.write("  /sbin/ip route replace $IP/32 dev $INTERFACE\n")
177
      interface_proxy_arp = "/proc/sys/net/ipv4/conf/$INTERFACE/proxy_arp"
178
      interface_forwarding = "/proc/sys/net/ipv4/conf/$INTERFACE/forwarding"
179
      script.write("  /bin/echo 1 > %s\n" % interface_proxy_arp)
180
      script.write("  /bin/echo 1 > %s\n" % interface_forwarding)
181
    script.write("fi\n\n")
182
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
183
    # mounted noexec sometimes, so we'll have to find another place.
184
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
185
    tmpfile = os.fdopen(tmpfd, 'w')
186
    try:
187
      tmpfile.write(script.getvalue())
188
    finally:
189
      tmpfile.close()
190
    os.chmod(tmpfile_name, 0755)
191
    return tmpfile_name
192

    
193
  def ListInstances(self):
194
    """Get the list of running instances.
195

196
    We can do this by listing our live instances directory and
197
    checking whether the associated kvm process is still alive.
198

199
    """
200
    result = []
201
    for name in os.listdir(self._PIDS_DIR):
202
      filename = "%s/%s" % (self._PIDS_DIR, name)
203
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
204
        result.append(name)
205
    return result
206

    
207
  def GetInstanceInfo(self, instance_name):
208
    """Get instance properties.
209

210
    @param instance_name: the instance name
211

212
    @return: tuple (name, id, memory, vcpus, stat, times)
213

214
    """
215
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
216
    if not alive:
217
      return None
218

    
219
    cmdline_file = "/proc/%s/cmdline" % pid
220
    try:
221
      cmdline = utils.ReadFile(cmdline_file)
222
    except EnvironmentError, err:
223
      raise errors.HypervisorError("Failed to list instance %s: %s" %
224
                                   (instance_name, err))
225

    
226
    memory = 0
227
    vcpus = 0
228
    stat = "---b-"
229
    times = "0"
230

    
231
    arg_list = cmdline.split('\x00')
232
    while arg_list:
233
      arg =  arg_list.pop(0)
234
      if arg == '-m':
235
        memory = int(arg_list.pop(0))
236
      elif arg == '-smp':
237
        vcpus = int(arg_list.pop(0))
238

    
239
    return (instance_name, pid, memory, vcpus, stat, times)
240

    
241
  def GetAllInstancesInfo(self):
242
    """Get properties of all instances.
243

244
    @return: list of tuples (name, id, memory, vcpus, stat, times)
245

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

    
258
    return data
259

    
260
  def _GenerateKVMRuntime(self, instance, block_devices):
261
    """Generate KVM information to start an instance.
262

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

    
276
    hvp = instance.hvparams
277
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
278
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
279
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
280

    
281
    if boot_network:
282
      kvm_cmd.extend(['-boot', 'n'])
283

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

    
302
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
303
      kvm_cmd.extend(['-drive', drive_val])
304

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

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

    
328
    mouse_type = hvp[constants.HV_USB_MOUSE]
329
    if mouse_type:
330
      kvm_cmd.extend(['-usb'])
331
      kvm_cmd.extend(['-usbdevice', mouse_type])
332

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

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

    
363
      else:
364
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
365

    
366
      kvm_cmd.extend(['-vnc', vnc_arg])
367
    else:
368
      kvm_cmd.extend(['-nographic'])
369

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

    
380
    # Save the current instance nics, but defer their expansion as parameters,
381
    # as we'll need to generate executable temp files for them.
382
    kvm_nics = instance.nics
383
    hvparams = hvp
384

    
385
    return (kvm_cmd, kvm_nics, hvparams)
386

    
387
  def _WriteKVMRuntime(self, instance_name, data):
388
    """Write an instance's KVM runtime
389

390
    """
391
    try:
392
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
393
                      data=data)
394
    except EnvironmentError, err:
395
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
396

    
397
  def _ReadKVMRuntime(self, instance_name):
398
    """Read an instance's KVM runtime
399

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

    
407
  def _SaveKVMRuntime(self, instance, kvm_runtime):
408
    """Save an instance's KVM runtime
409

410
    """
411
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
412
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
413
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
414
    self._WriteKVMRuntime(instance.name, serialized_form)
415

    
416
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
417
    """Load an instance's KVM runtime
418

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

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

430
    @type incoming: tuple of strings
431
    @param incoming: (target_host_ip, port)
432

433
    """
434
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
435
    if alive:
436
      raise errors.HypervisorError("Failed to start instance %s: %s" %
437
                                   (instance.name, "already running"))
438

    
439
    temp_files = []
440

    
441
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
442

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

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

    
459
    if incoming:
460
      target, port = incoming
461
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
462

    
463
    result = utils.RunCmd(kvm_cmd)
464
    if result.failed:
465
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
466
                                   (instance.name, result.fail_reason,
467
                                    result.output))
468

    
469
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
470
      raise errors.HypervisorError("Failed to start instance %s: %s" %
471
                                   (instance.name))
472

    
473
    for filename in temp_files:
474
      utils.RemoveFile(filename)
475

    
476
  def StartInstance(self, instance, block_devices):
477
    """Start an instance.
478

479
    """
480
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
481
    if alive:
482
      raise errors.HypervisorError("Failed to start instance %s: %s" %
483
                                   (instance.name, "already running"))
484

    
485
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
486
    self._SaveKVMRuntime(instance, kvm_runtime)
487
    self._ExecuteKVMRuntime(instance, kvm_runtime)
488

    
489
  def _CallMonitorCommand(self, instance_name, command):
490
    """Invoke a command on the instance monitor.
491

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

    
505
    return result
506

    
507
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
508
    """Wait for an instance  to power down.
509

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

    
521
  def StopInstance(self, instance, force=False):
522
    """Stop an instance.
523

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

    
532
    if not utils.IsProcessAlive(pid):
533
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
534
      return True
535
    else:
536
      return False
537

    
538
  def RebootInstance(self, instance):
539
    """Reboot an instance.
540

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

    
559
  def MigrationInfo(self, instance):
560
    """Get instance information to perform a migration.
561

562
    @type instance: L{objects.Instance}
563
    @param instance: instance to be migrated
564
    @rtype: string
565
    @return: content of the KVM runtime file
566

567
    """
568
    return self._ReadKVMRuntime(instance.name)
569

    
570
  def AcceptInstance(self, instance, info, target):
571
    """Prepare to accept an instance.
572

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

580
    """
581
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
582
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
583
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
584

    
585
  def FinalizeMigration(self, instance, info, success):
586
    """Finalize an instance migration.
587

588
    Stop the incoming mode KVM.
589

590
    @type instance: L{objects.Instance}
591
    @param instance: instance whose migration is being aborted
592

593
    """
594
    if success:
595
      self._WriteKVMRuntime(instance.name, info)
596
    else:
597
      self.StopInstance(instance, force=True)
598

    
599
  def MigrateInstance(self, instance_name, target, live):
600
    """Migrate an instance to a target node.
601

602
    The migration will not be attempted if the instance is not
603
    currently running.
604

605
    @type instance_name: string
606
    @param instance_name: name of the instance to be migrated
607
    @type target: string
608
    @param target: ip address of the target node
609
    @type live: boolean
610
    @param live: perform a live migration
611

612
    """
613
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
614
    if not alive:
615
      raise errors.HypervisorError("Instance not running, cannot migrate")
616

    
617
    if not live:
618
      self._CallMonitorCommand(instance_name, 'stop')
619

    
620
    migrate_command = ('migrate -d tcp:%s:%s' %
621
                       (target, constants.KVM_MIGRATION_PORT))
622
    self._CallMonitorCommand(instance_name, migrate_command)
623

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

    
647
    utils.KillProcess(pid)
648
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
649

    
650
  def GetNodeInfo(self):
651
    """Return information about the node.
652

653
    This is just a wrapper over the base GetLinuxNodeInfo method.
654

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

660
    """
661
    return self.GetLinuxNodeInfo()
662

    
663
  @classmethod
664
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
665
    """Return a command for connecting to the console of an instance.
666

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

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

    
692
    return shell_command
693

    
694
  def Verify(self):
695
    """Verify the hypervisor.
696

697
    Check that the binary exists.
698

699
    """
700
    if not os.path.exists(constants.KVM_PATH):
701
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
702
    if not os.path.exists(constants.SOCAT_PATH):
703
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
704

    
705

    
706
  @classmethod
707
  def CheckParameterSyntax(cls, hvparams):
708
    """Check the given parameters for validity.
709

710
    @type hvparams:  dict
711
    @param hvparams: dictionary with parameter names/value
712
    @raise errors.HypervisorError: when a parameter is not valid
713

714
    """
715
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
716

    
717
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
718
    if kernel_path:
719
      if not hvparams[constants.HV_ROOT_PATH]:
720
        raise errors.HypervisorError("Need a root partition for the instance,"
721
                                     " if a kernel is defined")
722

    
723
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
724
        not hvparams[constants.HV_VNC_X509]):
725
      raise errors.HypervisorError("%s must be defined, if %s is" %
726
                                   (constants.HV_VNC_X509,
727
                                    constants.HV_VNC_X509_VERIFY))
728

    
729
    boot_order = hvparams[constants.HV_BOOT_ORDER]
730

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

    
740
  @classmethod
741
  def PowercycleNode(cls):
742
    """KVM powercycle, just a wrapper over Linux powercycle.
743

744
    """
745
    cls.LinuxPowercycle()