Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 2c5afffb

History | View | Annotate | Download (26.4 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
    }
76

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

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

    
82
  ANCILLARY_FILES = [
83
    _KVM_NETWORK_SCRIPT,
84
    ]
85

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

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

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

    
101
    return (pidfile, pid, alive)
102

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

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

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

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

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

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

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

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

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

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

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

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

    
196
  def ListInstances(self):
197
    """Get the list of running instances.
198

199
    We can do this by listing our live instances directory and
200
    checking whether the associated kvm process is still alive.
201

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

    
210
  def GetInstanceInfo(self, instance_name):
211
    """Get instance properties.
212

213
    @param instance_name: the instance name
214

215
    @return: tuple (name, id, memory, vcpus, stat, times)
216

217
    """
218
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
219
    if not alive:
220
      return None
221

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

    
229
    memory = 0
230
    vcpus = 0
231
    stat = "---b-"
232
    times = "0"
233

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

    
242
    return (instance_name, pid, memory, vcpus, stat, times)
243

    
244
  def GetAllInstancesInfo(self):
245
    """Get properties of all instances.
246

247
    @return: list of tuples (name, id, memory, vcpus, stat, times)
248

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

    
261
    return data
262

    
263
  def _GenerateKVMRuntime(self, instance, block_devices):
264
    """Generate KVM information to start an instance.
265

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

    
279
    hvp = instance.hvparams
280
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
281
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
282
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
283

    
284
    if boot_network:
285
      kvm_cmd.extend(['-boot', 'n'])
286

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

    
305
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
306
      kvm_cmd.extend(['-drive', drive_val])
307

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

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

    
331
    mouse_type = hvp[constants.HV_USB_MOUSE]
332
    if mouse_type:
333
      kvm_cmd.extend(['-usb'])
334
      kvm_cmd.extend(['-usbdevice', mouse_type])
335

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

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

    
366
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
367

    
368
      else:
369
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
370

    
371
      kvm_cmd.extend(['-vnc', vnc_arg])
372
    else:
373
      kvm_cmd.extend(['-nographic'])
374

    
375
    monitor_dev = ("unix:%s,server,nowait" %
376
                   self._InstanceMonitor(instance.name))
377
    kvm_cmd.extend(['-monitor', monitor_dev])
378
    if hvp[constants.HV_SERIAL_CONSOLE]:
379
      serial_dev = ('unix:%s,server,nowait' %
380
                    self._InstanceSerial(instance.name))
381
      kvm_cmd.extend(['-serial', serial_dev])
382
    else:
383
      kvm_cmd.extend(['-serial', 'none'])
384

    
385
    # Save the current instance nics, but defer their expansion as parameters,
386
    # as we'll need to generate executable temp files for them.
387
    kvm_nics = instance.nics
388
    hvparams = hvp
389

    
390
    return (kvm_cmd, kvm_nics, hvparams)
391

    
392
  def _WriteKVMRuntime(self, instance_name, data):
393
    """Write an instance's KVM runtime
394

395
    """
396
    try:
397
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
398
                      data=data)
399
    except EnvironmentError, err:
400
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
401

    
402
  def _ReadKVMRuntime(self, instance_name):
403
    """Read an instance's KVM runtime
404

405
    """
406
    try:
407
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
408
    except EnvironmentError, err:
409
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
410
    return file_content
411

    
412
  def _SaveKVMRuntime(self, instance, kvm_runtime):
413
    """Save an instance's KVM runtime
414

415
    """
416
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
417
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
418
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
419
    self._WriteKVMRuntime(instance.name, serialized_form)
420

    
421
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
422
    """Load an instance's KVM runtime
423

424
    """
425
    if not serialized_runtime:
426
      serialized_runtime = self._ReadKVMRuntime(instance.name)
427
    loaded_runtime = serializer.Load(serialized_runtime)
428
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
429
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
430
    return (kvm_cmd, kvm_nics, hvparams)
431

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

435
    @type incoming: tuple of strings
436
    @param incoming: (target_host_ip, port)
437

438
    """
439
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
440
    hvp = instance.hvparams
441
    if alive:
442
      raise errors.HypervisorError("Failed to start instance %s: %s" %
443
                                   (instance.name, "already running"))
444

    
445
    temp_files = []
446

    
447
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
448

    
449
    if not kvm_nics:
450
      kvm_cmd.extend(['-net', 'none'])
451
    else:
452
      nic_type = hvparams[constants.HV_NIC_TYPE]
453
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
454
        nic_model = "model=virtio"
455
      else:
456
        nic_model = "model=%s" % nic_type
457

    
458
      for nic_seq, nic in enumerate(kvm_nics):
459
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
460
        script = self._WriteNetScript(instance, nic_seq, nic)
461
        kvm_cmd.extend(['-net', nic_val])
462
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
463
        temp_files.append(script)
464

    
465
    if incoming:
466
      target, port = incoming
467
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
468

    
469
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
470
    vnc_pwd = None
471
    if vnc_pwd_file:
472
      try:
473
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
474
      except EnvironmentError, err:
475
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
476
                                     % (vnc_pwd_file, err))
477

    
478
    result = utils.RunCmd(kvm_cmd)
479
    if result.failed:
480
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
481
                                   (instance.name, result.fail_reason,
482
                                    result.output))
483

    
484
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
485
      raise errors.HypervisorError("Failed to start instance %s: %s" %
486
                                   (instance.name))
487

    
488
    if vnc_pwd:
489
      change_cmd = 'change vnc password %s' % vnc_pwd
490
      self._CallMonitorCommand(instance.name, change_cmd)
491

    
492
    for filename in temp_files:
493
      utils.RemoveFile(filename)
494

    
495
  def StartInstance(self, instance, block_devices):
496
    """Start an instance.
497

498
    """
499
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
500
    if alive:
501
      raise errors.HypervisorError("Failed to start instance %s: %s" %
502
                                   (instance.name, "already running"))
503

    
504
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
505
    self._SaveKVMRuntime(instance, kvm_runtime)
506
    self._ExecuteKVMRuntime(instance, kvm_runtime)
507

    
508
  def _CallMonitorCommand(self, instance_name, command):
509
    """Invoke a command on the instance monitor.
510

511
    """
512
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
513
             (utils.ShellQuote(command),
514
              constants.SOCAT_PATH,
515
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
516
    result = utils.RunCmd(socat)
517
    if result.failed:
518
      msg = ("Failed to send command '%s' to instance %s."
519
             " output: %s, error: %s, fail_reason: %s" %
520
             (command, instance_name,
521
              result.stdout, result.stderr, result.fail_reason))
522
      raise errors.HypervisorError(msg)
523

    
524
    return result
525

    
526
  def StopInstance(self, instance, force=False, retry=False):
527
    """Stop an instance.
528

529
    """
530
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
531
    if pid > 0 and alive:
532
      if force or not instance.hvparams[constants.HV_ACPI]:
533
        utils.KillProcess(pid)
534
      else:
535
        self._CallMonitorCommand(instance.name, 'system_powerdown')
536

    
537
    if not utils.IsProcessAlive(pid):
538
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
539
      return True
540
    else:
541
      return False
542

    
543
  def RebootInstance(self, instance):
544
    """Reboot an instance.
545

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

    
564
  def MigrationInfo(self, instance):
565
    """Get instance information to perform a migration.
566

567
    @type instance: L{objects.Instance}
568
    @param instance: instance to be migrated
569
    @rtype: string
570
    @return: content of the KVM runtime file
571

572
    """
573
    return self._ReadKVMRuntime(instance.name)
574

    
575
  def AcceptInstance(self, instance, info, target):
576
    """Prepare to accept an instance.
577

578
    @type instance: L{objects.Instance}
579
    @param instance: instance to be accepted
580
    @type info: string
581
    @param info: content of the KVM runtime file on the source node
582
    @type target: string
583
    @param target: target host (usually ip), on this node
584

585
    """
586
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
587
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
588
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
589

    
590
  def FinalizeMigration(self, instance, info, success):
591
    """Finalize an instance migration.
592

593
    Stop the incoming mode KVM.
594

595
    @type instance: L{objects.Instance}
596
    @param instance: instance whose migration is being aborted
597

598
    """
599
    if success:
600
      self._WriteKVMRuntime(instance.name, info)
601
    else:
602
      self.StopInstance(instance, force=True)
603

    
604
  def MigrateInstance(self, instance_name, target, live):
605
    """Migrate an instance to a target node.
606

607
    The migration will not be attempted if the instance is not
608
    currently running.
609

610
    @type instance_name: string
611
    @param instance_name: name of the instance to be migrated
612
    @type target: string
613
    @param target: ip address of the target node
614
    @type live: boolean
615
    @param live: perform a live migration
616

617
    """
618
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
619
    if not alive:
620
      raise errors.HypervisorError("Instance not running, cannot migrate")
621

    
622
    if not live:
623
      self._CallMonitorCommand(instance_name, 'stop')
624

    
625
    migrate_command = ('migrate -d tcp:%s:%s' %
626
                       (target, constants.KVM_MIGRATION_PORT))
627
    self._CallMonitorCommand(instance_name, migrate_command)
628

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

    
652
    utils.KillProcess(pid)
653
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
654

    
655
  def GetNodeInfo(self):
656
    """Return information about the node.
657

658
    This is just a wrapper over the base GetLinuxNodeInfo method.
659

660
    @return: a dict with the following keys (values in MiB):
661
          - memory_total: the total memory size on the node
662
          - memory_free: the available memory on the node for instances
663
          - memory_dom0: the memory used by the node itself, if available
664

665
    """
666
    return self.GetLinuxNodeInfo()
667

    
668
  @classmethod
669
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
670
    """Return a command for connecting to the console of an instance.
671

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

    
687
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
688
    if vnc_bind_address:
689
      if instance.network_port > constants.VNC_BASE_PORT:
690
        display = instance.network_port - constants.VNC_BASE_PORT
691
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
692
                       " (display: %d)'" % (vnc_bind_address,
693
                                            instance.network_port,
694
                                            display))
695
        shell_command = "%s; %s" % (vnc_command, shell_command)
696

    
697
    return shell_command
698

    
699
  def Verify(self):
700
    """Verify the hypervisor.
701

702
    Check that the binary exists.
703

704
    """
705
    if not os.path.exists(constants.KVM_PATH):
706
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
707
    if not os.path.exists(constants.SOCAT_PATH):
708
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
709

    
710

    
711
  @classmethod
712
  def CheckParameterSyntax(cls, hvparams):
713
    """Check the given parameters for validity.
714

715
    @type hvparams:  dict
716
    @param hvparams: dictionary with parameter names/value
717
    @raise errors.HypervisorError: when a parameter is not valid
718

719
    """
720
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
721

    
722
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
723
    if kernel_path:
724
      if not hvparams[constants.HV_ROOT_PATH]:
725
        raise errors.HypervisorError("Need a root partition for the instance,"
726
                                     " if a kernel is defined")
727

    
728
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
729
        not hvparams[constants.HV_VNC_X509]):
730
      raise errors.HypervisorError("%s must be defined, if %s is" %
731
                                   (constants.HV_VNC_X509,
732
                                    constants.HV_VNC_X509_VERIFY))
733

    
734
    boot_order = hvparams[constants.HV_BOOT_ORDER]
735

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

    
745
  @classmethod
746
  def PowercycleNode(cls):
747
    """KVM powercycle, just a wrapper over Linux powercycle.
748

749
    """
750
    cls.LinuxPowercycle()