Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ ce0eb669

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
  @staticmethod
118
  def _SocatUnixConsoleParams():
119
    """Returns the correct parameters for socat
120

121
    If we have a new-enough socat we can use raw mode with an escape character.
122

123
    """
124
    if constants.SOCAT_ESCAPE:
125
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
126
    else:
127
      return "echo=0,icanon=0"
128

    
129
  @classmethod
130
  def _InstanceKVMRuntime(cls, instance_name):
131
    """Returns the instance KVM runtime filename
132

133
    """
134
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
135

    
136
  @classmethod
137
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
138
    """Removes an instance's rutime sockets/files.
139

140
    """
141
    utils.RemoveFile(pidfile)
142
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
143
    utils.RemoveFile(cls._InstanceSerial(instance_name))
144
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
145

    
146
  def _WriteNetScript(self, instance, seq, nic):
147
    """Write a script to connect a net interface to the proper bridge.
148

149
    This can be used by any qemu-type hypervisor.
150

151
    @param instance: instance we're acting on
152
    @type instance: instance object
153
    @param seq: nic sequence number
154
    @type seq: int
155
    @param nic: nic we're acting on
156
    @type nic: nic object
157
    @return: netscript file name
158
    @rtype: string
159

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

    
215
  def ListInstances(self):
216
    """Get the list of running instances.
217

218
    We can do this by listing our live instances directory and
219
    checking whether the associated kvm process is still alive.
220

221
    """
222
    result = []
223
    for name in os.listdir(self._PIDS_DIR):
224
      filename = "%s/%s" % (self._PIDS_DIR, name)
225
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
226
        result.append(name)
227
    return result
228

    
229
  def GetInstanceInfo(self, instance_name):
230
    """Get instance properties.
231

232
    @param instance_name: the instance name
233

234
    @return: tuple (name, id, memory, vcpus, stat, times)
235

236
    """
237
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
238
    if not alive:
239
      return None
240

    
241
    cmdline_file = "/proc/%s/cmdline" % pid
242
    try:
243
      cmdline = utils.ReadFile(cmdline_file)
244
    except EnvironmentError, err:
245
      raise errors.HypervisorError("Failed to list instance %s: %s" %
246
                                   (instance_name, err))
247

    
248
    memory = 0
249
    vcpus = 0
250
    stat = "---b-"
251
    times = "0"
252

    
253
    arg_list = cmdline.split('\x00')
254
    while arg_list:
255
      arg =  arg_list.pop(0)
256
      if arg == '-m':
257
        memory = int(arg_list.pop(0))
258
      elif arg == '-smp':
259
        vcpus = int(arg_list.pop(0))
260

    
261
    return (instance_name, pid, memory, vcpus, stat, times)
262

    
263
  def GetAllInstancesInfo(self):
264
    """Get properties of all instances.
265

266
    @return: list of tuples (name, id, memory, vcpus, stat, times)
267

268
    """
269
    data = []
270
    for name in os.listdir(self._PIDS_DIR):
271
      filename = "%s/%s" % (self._PIDS_DIR, name)
272
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
273
        try:
274
          info = self.GetInstanceInfo(name)
275
        except errors.HypervisorError, err:
276
          continue
277
        if info:
278
          data.append(info)
279

    
280
    return data
281

    
282
  def _GenerateKVMRuntime(self, instance, block_devices):
283
    """Generate KVM information to start an instance.
284

285
    """
286
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
287
    kvm = constants.KVM_PATH
288
    kvm_cmd = [kvm]
289
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
290
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
291
    kvm_cmd.extend(['-pidfile', pidfile])
292
    # used just by the vnc server, if enabled
293
    kvm_cmd.extend(['-name', instance.name])
294
    kvm_cmd.extend(['-daemonize'])
295
    if not instance.hvparams[constants.HV_ACPI]:
296
      kvm_cmd.extend(['-no-acpi'])
297

    
298
    hvp = instance.hvparams
299
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
300
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
301
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
302

    
303
    if boot_network:
304
      kvm_cmd.extend(['-boot', 'n'])
305

    
306
    disk_type = hvp[constants.HV_DISK_TYPE]
307
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
308
      if_val = ',if=virtio'
309
    else:
310
      if_val = ',if=%s' % disk_type
311
    for cfdev, dev_path in block_devices:
312
      if cfdev.mode != constants.DISK_RDWR:
313
        raise errors.HypervisorError("Instance has read-only disks which"
314
                                     " are not supported by KVM")
315
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
316
      if boot_disk:
317
        kvm_cmd.extend(['-boot', 'c'])
318
        boot_val = ',boot=on'
319
        # We only boot from the first disk
320
        boot_disk = False
321
      else:
322
        boot_val = ''
323

    
324
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
325
      kvm_cmd.extend(['-drive', drive_val])
326

    
327
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
328
    if iso_image:
329
      options = ',format=raw,media=cdrom'
330
      if boot_cdrom:
331
        kvm_cmd.extend(['-boot', 'd'])
332
        options = '%s,boot=on' % options
333
      else:
334
        options = '%s,if=virtio' % options
335
      drive_val = 'file=%s%s' % (iso_image, options)
336
      kvm_cmd.extend(['-drive', drive_val])
337

    
338
    kernel_path = hvp[constants.HV_KERNEL_PATH]
339
    if kernel_path:
340
      kvm_cmd.extend(['-kernel', kernel_path])
341
      initrd_path = hvp[constants.HV_INITRD_PATH]
342
      if initrd_path:
343
        kvm_cmd.extend(['-initrd', initrd_path])
344
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
345
                     hvp[constants.HV_KERNEL_ARGS]]
346
      if hvp[constants.HV_SERIAL_CONSOLE]:
347
        root_append.append('console=ttyS0,38400')
348
      kvm_cmd.extend(['-append', ' '.join(root_append)])
349

    
350
    mouse_type = hvp[constants.HV_USB_MOUSE]
351
    if mouse_type:
352
      kvm_cmd.extend(['-usb'])
353
      kvm_cmd.extend(['-usbdevice', mouse_type])
354

    
355
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
356
    if vnc_bind_address:
357
      if utils.IsValidIP(vnc_bind_address):
358
        if instance.network_port > constants.VNC_BASE_PORT:
359
          display = instance.network_port - constants.VNC_BASE_PORT
360
          if vnc_bind_address == '0.0.0.0':
361
            vnc_arg = ':%d' % (display)
362
          else:
363
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
364
        else:
365
          logging.error("Network port is not a valid VNC display (%d < %d)."
366
                        " Not starting VNC" %
367
                        (instance.network_port,
368
                         constants.VNC_BASE_PORT))
369
          vnc_arg = 'none'
370

    
371
        # Only allow tls and other option when not binding to a file, for now.
372
        # kvm/qemu gets confused otherwise about the filename to use.
373
        vnc_append = ''
374
        if hvp[constants.HV_VNC_TLS]:
375
          vnc_append = '%s,tls' % vnc_append
376
          if hvp[constants.HV_VNC_X509_VERIFY]:
377
            vnc_append = '%s,x509verify=%s' % (vnc_append,
378
                                               hvp[constants.HV_VNC_X509])
379
          elif hvp[constants.HV_VNC_X509]:
380
            vnc_append = '%s,x509=%s' % (vnc_append,
381
                                         hvp[constants.HV_VNC_X509])
382
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
383
          vnc_append = '%s,password' % vnc_append
384

    
385
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
386

    
387
      else:
388
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
389

    
390
      kvm_cmd.extend(['-vnc', vnc_arg])
391
    else:
392
      kvm_cmd.extend(['-nographic'])
393

    
394
    monitor_dev = ("unix:%s,server,nowait" %
395
                   self._InstanceMonitor(instance.name))
396
    kvm_cmd.extend(['-monitor', monitor_dev])
397
    if hvp[constants.HV_SERIAL_CONSOLE]:
398
      serial_dev = ('unix:%s,server,nowait' %
399
                    self._InstanceSerial(instance.name))
400
      kvm_cmd.extend(['-serial', serial_dev])
401
    else:
402
      kvm_cmd.extend(['-serial', 'none'])
403

    
404
    # Save the current instance nics, but defer their expansion as parameters,
405
    # as we'll need to generate executable temp files for them.
406
    kvm_nics = instance.nics
407
    hvparams = hvp
408

    
409
    return (kvm_cmd, kvm_nics, hvparams)
410

    
411
  def _WriteKVMRuntime(self, instance_name, data):
412
    """Write an instance's KVM runtime
413

414
    """
415
    try:
416
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
417
                      data=data)
418
    except EnvironmentError, err:
419
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
420

    
421
  def _ReadKVMRuntime(self, instance_name):
422
    """Read an instance's KVM runtime
423

424
    """
425
    try:
426
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
427
    except EnvironmentError, err:
428
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
429
    return file_content
430

    
431
  def _SaveKVMRuntime(self, instance, kvm_runtime):
432
    """Save an instance's KVM runtime
433

434
    """
435
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
436
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
437
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
438
    self._WriteKVMRuntime(instance.name, serialized_form)
439

    
440
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
441
    """Load an instance's KVM runtime
442

443
    """
444
    if not serialized_runtime:
445
      serialized_runtime = self._ReadKVMRuntime(instance.name)
446
    loaded_runtime = serializer.Load(serialized_runtime)
447
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
448
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
449
    return (kvm_cmd, kvm_nics, hvparams)
450

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

454
    @type incoming: tuple of strings
455
    @param incoming: (target_host_ip, port)
456

457
    """
458
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
459
    hvp = instance.hvparams
460
    if alive:
461
      raise errors.HypervisorError("Failed to start instance %s: %s" %
462
                                   (instance.name, "already running"))
463

    
464
    temp_files = []
465

    
466
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
467

    
468
    if not kvm_nics:
469
      kvm_cmd.extend(['-net', 'none'])
470
    else:
471
      nic_type = hvparams[constants.HV_NIC_TYPE]
472
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
473
        nic_model = "model=virtio"
474
      else:
475
        nic_model = "model=%s" % nic_type
476

    
477
      for nic_seq, nic in enumerate(kvm_nics):
478
        nic_val = "nic,macaddr=%s,%s" % (nic.mac, nic_model)
479
        script = self._WriteNetScript(instance, nic_seq, nic)
480
        kvm_cmd.extend(['-net', nic_val])
481
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
482
        temp_files.append(script)
483

    
484
    if incoming:
485
      target, port = incoming
486
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
487

    
488
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
489
    vnc_pwd = None
490
    if vnc_pwd_file:
491
      try:
492
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
493
      except EnvironmentError, err:
494
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
495
                                     % (vnc_pwd_file, err))
496

    
497
    result = utils.RunCmd(kvm_cmd)
498
    if result.failed:
499
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
500
                                   (instance.name, result.fail_reason,
501
                                    result.output))
502

    
503
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
504
      raise errors.HypervisorError("Failed to start instance %s: %s" %
505
                                   (instance.name))
506

    
507
    if vnc_pwd:
508
      change_cmd = 'change vnc password %s' % vnc_pwd
509
      self._CallMonitorCommand(instance.name, change_cmd)
510

    
511
    for filename in temp_files:
512
      utils.RemoveFile(filename)
513

    
514
  def StartInstance(self, instance, block_devices):
515
    """Start an instance.
516

517
    """
518
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
519
    if alive:
520
      raise errors.HypervisorError("Failed to start instance %s: %s" %
521
                                   (instance.name, "already running"))
522

    
523
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
524
    self._SaveKVMRuntime(instance, kvm_runtime)
525
    self._ExecuteKVMRuntime(instance, kvm_runtime)
526

    
527
  def _CallMonitorCommand(self, instance_name, command):
528
    """Invoke a command on the instance monitor.
529

530
    """
531
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
532
             (utils.ShellQuote(command),
533
              constants.SOCAT_PATH,
534
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
535
    result = utils.RunCmd(socat)
536
    if result.failed:
537
      msg = ("Failed to send command '%s' to instance %s."
538
             " output: %s, error: %s, fail_reason: %s" %
539
             (command, instance_name,
540
              result.stdout, result.stderr, result.fail_reason))
541
      raise errors.HypervisorError(msg)
542

    
543
    return result
544

    
545
  def StopInstance(self, instance, force=False, retry=False):
546
    """Stop an instance.
547

548
    """
549
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
550
    if pid > 0 and alive:
551
      if force or not instance.hvparams[constants.HV_ACPI]:
552
        utils.KillProcess(pid)
553
      else:
554
        self._CallMonitorCommand(instance.name, 'system_powerdown')
555

    
556
    if not utils.IsProcessAlive(pid):
557
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
558
      return True
559
    else:
560
      return False
561

    
562
  def RebootInstance(self, instance):
563
    """Reboot an instance.
564

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

    
583
  def MigrationInfo(self, instance):
584
    """Get instance information to perform a migration.
585

586
    @type instance: L{objects.Instance}
587
    @param instance: instance to be migrated
588
    @rtype: string
589
    @return: content of the KVM runtime file
590

591
    """
592
    return self._ReadKVMRuntime(instance.name)
593

    
594
  def AcceptInstance(self, instance, info, target):
595
    """Prepare to accept an instance.
596

597
    @type instance: L{objects.Instance}
598
    @param instance: instance to be accepted
599
    @type info: string
600
    @param info: content of the KVM runtime file on the source node
601
    @type target: string
602
    @param target: target host (usually ip), on this node
603

604
    """
605
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
606
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
607
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
608

    
609
  def FinalizeMigration(self, instance, info, success):
610
    """Finalize an instance migration.
611

612
    Stop the incoming mode KVM.
613

614
    @type instance: L{objects.Instance}
615
    @param instance: instance whose migration is being aborted
616

617
    """
618
    if success:
619
      self._WriteKVMRuntime(instance.name, info)
620
    else:
621
      self.StopInstance(instance, force=True)
622

    
623
  def MigrateInstance(self, instance_name, target, live):
624
    """Migrate an instance to a target node.
625

626
    The migration will not be attempted if the instance is not
627
    currently running.
628

629
    @type instance_name: string
630
    @param instance_name: name of the instance to be migrated
631
    @type target: string
632
    @param target: ip address of the target node
633
    @type live: boolean
634
    @param live: perform a live migration
635

636
    """
637
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
638
    if not alive:
639
      raise errors.HypervisorError("Instance not running, cannot migrate")
640

    
641
    if not live:
642
      self._CallMonitorCommand(instance_name, 'stop')
643

    
644
    migrate_command = ('migrate -d tcp:%s:%s' %
645
                       (target, constants.KVM_MIGRATION_PORT))
646
    self._CallMonitorCommand(instance_name, migrate_command)
647

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

    
671
    utils.KillProcess(pid)
672
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
673

    
674
  def GetNodeInfo(self):
675
    """Return information about the node.
676

677
    This is just a wrapper over the base GetLinuxNodeInfo method.
678

679
    @return: a dict with the following keys (values in MiB):
680
          - memory_total: the total memory size on the node
681
          - memory_free: the available memory on the node for instances
682
          - memory_dom0: the memory used by the node itself, if available
683

684
    """
685
    return self.GetLinuxNodeInfo()
686

    
687
  @classmethod
688
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
689
    """Return a command for connecting to the console of an instance.
690

691
    """
692
    if hvparams[constants.HV_SERIAL_CONSOLE]:
693
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
694
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
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()