Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 0a3c881e

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

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

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

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

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

209
    @param instance_name: the instance name
210

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

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

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

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

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

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

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

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

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

    
257
    return data
258

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
384
    return (kvm_cmd, kvm_nics, hvparams)
385

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

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

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

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

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

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

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

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

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

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

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

    
438
    temp_files = []
439

    
440
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
441

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

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

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

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

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

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

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

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

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

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

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

    
504
    return result
505

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

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

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

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

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

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

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

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

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

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

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

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

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

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

587
    Stop the incoming mode KVM.
588

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
691
    return shell_command
692

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

696
    Check that the binary exists.
697

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

    
704

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

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

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

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

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

    
728
    boot_order = hvparams[constants.HV_BOOT_ORDER]
729

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

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

743
    """
744
    cls.LinuxPowercycle()