Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 6e6bb8d5

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

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

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

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

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

211
    @param instance_name: the instance name
212

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

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

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

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

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

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

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

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

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

    
259
    return data
260

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

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

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

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

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

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

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

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

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

    
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
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
362
          vnc_append = '%s,password' % vnc_append
363

    
364
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
365

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

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

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

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

    
388
    return (kvm_cmd, kvm_nics, hvparams)
389

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

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

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

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

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

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

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

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

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

433
    @type incoming: tuple of strings
434
    @param incoming: (target_host_ip, port)
435

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

    
443
    temp_files = []
444

    
445
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
446

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

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

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

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

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

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

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

    
490
    for filename in temp_files:
491
      utils.RemoveFile(filename)
492

    
493
  def StartInstance(self, instance, block_devices):
494
    """Start an instance.
495

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

    
502
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
503
    self._SaveKVMRuntime(instance, kvm_runtime)
504
    self._ExecuteKVMRuntime(instance, kvm_runtime)
505

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

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

    
522
    return result
523

    
524
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
525
    """Wait for an instance  to power down.
526

527
    """
528
    # Wait up to $timeout seconds
529
    end = time.time() + timeout
530
    wait = 1
531
    while time.time() < end and utils.IsProcessAlive(pid):
532
      self._CallMonitorCommand(instance.name, 'system_powerdown')
533
      time.sleep(wait)
534
      # Make wait time longer for next try
535
      if wait < 5:
536
        wait *= 1.3
537

    
538
  def StopInstance(self, instance, force=False):
539
    """Stop an instance.
540

541
    """
542
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
543
    if pid > 0 and alive:
544
      if force or not instance.hvparams[constants.HV_ACPI]:
545
        utils.KillProcess(pid)
546
      else:
547
        self._RetryInstancePowerdown(instance, pid)
548

    
549
    if not utils.IsProcessAlive(pid):
550
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
551
      return True
552
    else:
553
      return False
554

    
555
  def RebootInstance(self, instance):
556
    """Reboot an instance.
557

558
    """
559
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
560
    # socket the instance will stop, but now power up again. So we'll resort
561
    # to shutdown and restart.
562
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
563
    if not alive:
564
      raise errors.HypervisorError("Failed to reboot instance %s: not running" %
565
                                             (instance.name))
566
    # StopInstance will delete the saved KVM runtime so:
567
    # ...first load it...
568
    kvm_runtime = self._LoadKVMRuntime(instance)
569
    # ...now we can safely call StopInstance...
570
    if not self.StopInstance(instance):
571
      self.StopInstance(instance, force=True)
572
    # ...and finally we can save it again, and execute it...
573
    self._SaveKVMRuntime(instance, kvm_runtime)
574
    self._ExecuteKVMRuntime(instance, kvm_runtime)
575

    
576
  def MigrationInfo(self, instance):
577
    """Get instance information to perform a migration.
578

579
    @type instance: L{objects.Instance}
580
    @param instance: instance to be migrated
581
    @rtype: string
582
    @return: content of the KVM runtime file
583

584
    """
585
    return self._ReadKVMRuntime(instance.name)
586

    
587
  def AcceptInstance(self, instance, info, target):
588
    """Prepare to accept an instance.
589

590
    @type instance: L{objects.Instance}
591
    @param instance: instance to be accepted
592
    @type info: string
593
    @param info: content of the KVM runtime file on the source node
594
    @type target: string
595
    @param target: target host (usually ip), on this node
596

597
    """
598
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
599
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
600
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
601

    
602
  def FinalizeMigration(self, instance, info, success):
603
    """Finalize an instance migration.
604

605
    Stop the incoming mode KVM.
606

607
    @type instance: L{objects.Instance}
608
    @param instance: instance whose migration is being aborted
609

610
    """
611
    if success:
612
      self._WriteKVMRuntime(instance.name, info)
613
    else:
614
      self.StopInstance(instance, force=True)
615

    
616
  def MigrateInstance(self, instance_name, target, live):
617
    """Migrate an instance to a target node.
618

619
    The migration will not be attempted if the instance is not
620
    currently running.
621

622
    @type instance_name: string
623
    @param instance_name: name of the instance to be migrated
624
    @type target: string
625
    @param target: ip address of the target node
626
    @type live: boolean
627
    @param live: perform a live migration
628

629
    """
630
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
631
    if not alive:
632
      raise errors.HypervisorError("Instance not running, cannot migrate")
633

    
634
    if not live:
635
      self._CallMonitorCommand(instance_name, 'stop')
636

    
637
    migrate_command = ('migrate -d tcp:%s:%s' %
638
                       (target, constants.KVM_MIGRATION_PORT))
639
    self._CallMonitorCommand(instance_name, migrate_command)
640

    
641
    info_command = 'info migrate'
642
    done = False
643
    while not done:
644
      result = self._CallMonitorCommand(instance_name, info_command)
645
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
646
      if not match:
647
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
648
                                     result.stdout)
649
      else:
650
        status = match.group(1)
651
        if status == 'completed':
652
          done = True
653
        elif status == 'active':
654
          time.sleep(2)
655
        elif status == 'failed' or status == 'cancelled':
656
          if not live:
657
            self._CallMonitorCommand(instance_name, 'cont')
658
          raise errors.HypervisorError("Migration %s at the kvm level" %
659
                                       status)
660
        else:
661
          logging.info("KVM: unknown migration status '%s'" % status)
662
          time.sleep(2)
663

    
664
    utils.KillProcess(pid)
665
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
666

    
667
  def GetNodeInfo(self):
668
    """Return information about the node.
669

670
    This is just a wrapper over the base GetLinuxNodeInfo method.
671

672
    @return: a dict with the following keys (values in MiB):
673
          - memory_total: the total memory size on the node
674
          - memory_free: the available memory on the node for instances
675
          - memory_dom0: the memory used by the node itself, if available
676

677
    """
678
    return self.GetLinuxNodeInfo()
679

    
680
  @classmethod
681
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
682
    """Return a command for connecting to the console of an instance.
683

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

    
699
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
700
    if vnc_bind_address:
701
      if instance.network_port > constants.VNC_BASE_PORT:
702
        display = instance.network_port - constants.VNC_BASE_PORT
703
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
704
                       " (display: %d)'" % (vnc_bind_address,
705
                                            instance.network_port,
706
                                            display))
707
        shell_command = "%s; %s" % (vnc_command, shell_command)
708

    
709
    return shell_command
710

    
711
  def Verify(self):
712
    """Verify the hypervisor.
713

714
    Check that the binary exists.
715

716
    """
717
    if not os.path.exists(constants.KVM_PATH):
718
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
719
    if not os.path.exists(constants.SOCAT_PATH):
720
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
721

    
722

    
723
  @classmethod
724
  def CheckParameterSyntax(cls, hvparams):
725
    """Check the given parameters for validity.
726

727
    @type hvparams:  dict
728
    @param hvparams: dictionary with parameter names/value
729
    @raise errors.HypervisorError: when a parameter is not valid
730

731
    """
732
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
733

    
734
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
735
    if kernel_path:
736
      if not hvparams[constants.HV_ROOT_PATH]:
737
        raise errors.HypervisorError("Need a root partition for the instance,"
738
                                     " if a kernel is defined")
739

    
740
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
741
        not hvparams[constants.HV_VNC_X509]):
742
      raise errors.HypervisorError("%s must be defined, if %s is" %
743
                                   (constants.HV_VNC_X509,
744
                                    constants.HV_VNC_X509_VERIFY))
745

    
746
    boot_order = hvparams[constants.HV_BOOT_ORDER]
747

    
748
    if (boot_order == constants.HT_BO_CDROM and
749
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
750
      raise errors.HypervisorError("Cannot boot from cdrom without an"
751
                                   " ISO path")
752
    if (boot_order == constants.HT_BO_NETWORK and
753
        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
754
      raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
755
                                   " change the NIC type.")
756

    
757
  @classmethod
758
  def PowercycleNode(cls):
759
    """KVM powercycle, just a wrapper over Linux powercycle.
760

761
    """
762
    cls.LinuxPowercycle()