Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 4fbb3c60

History | View | Annotate | Download (29.5 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
import pwd
33
from cStringIO import StringIO
34

    
35
from ganeti import utils
36
from ganeti import constants
37
from ganeti import errors
38
from ganeti import serializer
39
from ganeti import objects
40
from ganeti.hypervisor import hv_base
41

    
42

    
43
class KVMHypervisor(hv_base.BaseHypervisor):
44
  """KVM hypervisor interface"""
45

    
46
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
47
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
48
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
49
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
50
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
51

    
52
  PARAMETERS = {
53
    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
54
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
55
    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
56
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
57
    constants.HV_ACPI: hv_base.NO_CHECK,
58
    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
59
    constants.HV_VNC_BIND_ADDRESS:
60
      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
61
       "the VNC bind address must be either a valid IP address or an absolute"
62
       " pathname", None, None),
63
    constants.HV_VNC_TLS: hv_base.NO_CHECK,
64
    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
65
    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
66
    constants.HV_VNC_PASSWORD_FILE: hv_base.OPT_FILE_CHECK,
67
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
68
    constants.HV_BOOT_ORDER:
69
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
70
    constants.HV_NIC_TYPE:
71
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
72
    constants.HV_DISK_TYPE:
73
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
74
    constants.HV_USB_MOUSE:
75
      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
76
    constants.HV_MIGRATION_PORT: hv_base.NET_PORT_CHECK,
77
    constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
78
    constants.HV_DISK_CACHE:
79
      hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
80
    constants.HV_SECURITY_MODEL:
81
      hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
82
    constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
83
    }
84

    
85
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
86
                                    re.M | re.I)
87
  _MIGRATION_INFO_MAX_BAD_ANSWERS = 5
88
  _MIGRATION_INFO_RETRY_DELAY = 2
89

    
90
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
91

    
92
  ANCILLARY_FILES = [
93
    _KVM_NETWORK_SCRIPT,
94
    ]
95

    
96
  def __init__(self):
97
    hv_base.BaseHypervisor.__init__(self)
98
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
99
    # in a tmpfs filesystem or has been otherwise wiped out.
100
    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
101
    utils.EnsureDirs(dirs)
102

    
103
  @classmethod
104
  def _InstancePidFile(cls, instance_name):
105
    """Returns the instance pidfile.
106

107
    """
108
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
109

    
110
  def _InstancePidAlive(self, instance_name):
111
    """Returns the instance pid and pidfile
112

113
    """
114
    pidfile = self._InstancePidFile(instance_name)
115
    pid = utils.ReadPidFile(pidfile)
116
    alive = utils.IsProcessAlive(pid)
117

    
118
    return (pidfile, pid, alive)
119

    
120
  def _CheckDown(self, instance_name):
121
    """Raises an error unless the given instance is down.
122

123
    """
124
    alive = self._InstancePidAlive(instance_name)[2]
125
    if alive:
126
      raise errors.HypervisorError("Failed to start instance %s: %s" %
127
                                   (instance_name, "already running"))
128

    
129
  @classmethod
130
  def _InstanceMonitor(cls, instance_name):
131
    """Returns the instance monitor socket name
132

133
    """
134
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
135

    
136
  @classmethod
137
  def _InstanceSerial(cls, instance_name):
138
    """Returns the instance serial socket name
139

140
    """
141
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
142

    
143
  @staticmethod
144
  def _SocatUnixConsoleParams():
145
    """Returns the correct parameters for socat
146

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

149
    """
150
    if constants.SOCAT_USE_ESCAPE:
151
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
152
    else:
153
      return "echo=0,icanon=0"
154

    
155
  @classmethod
156
  def _InstanceKVMRuntime(cls, instance_name):
157
    """Returns the instance KVM runtime filename
158

159
    """
160
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
161

    
162
  @classmethod
163
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
164
    """Removes an instance's rutime sockets/files.
165

166
    """
167
    utils.RemoveFile(pidfile)
168
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
169
    utils.RemoveFile(cls._InstanceSerial(instance_name))
170
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
171

    
172
  def _WriteNetScript(self, instance, seq, nic):
173
    """Write a script to connect a net interface to the proper bridge.
174

175
    This can be used by any qemu-type hypervisor.
176

177
    @param instance: instance we're acting on
178
    @type instance: instance object
179
    @param seq: nic sequence number
180
    @type seq: int
181
    @param nic: nic we're acting on
182
    @type nic: nic object
183
    @return: netscript file name
184
    @rtype: string
185

186
    """
187
    script = StringIO()
188
    script.write("#!/bin/sh\n")
189
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
190
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
191
    script.write("export INSTANCE=%s\n" % instance.name)
192
    script.write("export MAC=%s\n" % nic.mac)
193
    if nic.ip:
194
      script.write("export IP=%s\n" % nic.ip)
195
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
196
    if nic.nicparams[constants.NIC_LINK]:
197
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
198
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
199
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
200
    script.write("export INTERFACE=$1\n")
201
    # TODO: make this configurable at ./configure time
202
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
203
    script.write("  # Execute the user-specific vif file\n")
204
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
205
    script.write("else\n")
206
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
207
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
208
      script.write("  # Connect the interface to the bridge\n")
209
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
210
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
211
      if not nic.ip:
212
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
213
      script.write("  # Route traffic targeted at the IP to the interface\n")
214
      if nic.nicparams[constants.NIC_LINK]:
215
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
216
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
217
        script.write("  ip route replace $IP table $LINK proto static"
218
                     " dev $INTERFACE\n")
219
      else:
220
        script.write("  ip route replace $IP proto static"
221
                     " dev $INTERFACE\n")
222
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
223
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
224
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
225
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
226
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
227
      script.write("  fi\n")
228
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
229
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
230
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
231
      script.write("  fi\n")
232
    script.write("fi\n\n")
233
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
234
    # mounted noexec sometimes, so we'll have to find another place.
235
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
236
    tmpfile = os.fdopen(tmpfd, 'w')
237
    try:
238
      tmpfile.write(script.getvalue())
239
    finally:
240
      tmpfile.close()
241
    os.chmod(tmpfile_name, 0755)
242
    return tmpfile_name
243

    
244
  def ListInstances(self):
245
    """Get the list of running instances.
246

247
    We can do this by listing our live instances directory and
248
    checking whether the associated kvm process is still alive.
249

250
    """
251
    result = []
252
    for name in os.listdir(self._PIDS_DIR):
253
      filename = utils.PathJoin(self._PIDS_DIR, name)
254
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
255
        result.append(name)
256
    return result
257

    
258
  def GetInstanceInfo(self, instance_name):
259
    """Get instance properties.
260

261
    @type instance_name: string
262
    @param instance_name: the instance name
263
    @rtype: tuple of strings
264
    @return: (name, id, memory, vcpus, stat, times)
265

266
    """
267
    _, pid, alive = self._InstancePidAlive(instance_name)
268
    if not alive:
269
      return None
270

    
271
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
272
    try:
273
      cmdline = utils.ReadFile(cmdline_file)
274
    except EnvironmentError, err:
275
      raise errors.HypervisorError("Failed to list instance %s: %s" %
276
                                   (instance_name, err))
277

    
278
    memory = 0
279
    vcpus = 0
280
    stat = "---b-"
281
    times = "0"
282

    
283
    arg_list = cmdline.split('\x00')
284
    while arg_list:
285
      arg =  arg_list.pop(0)
286
      if arg == '-m':
287
        memory = int(arg_list.pop(0))
288
      elif arg == '-smp':
289
        vcpus = int(arg_list.pop(0))
290

    
291
    return (instance_name, pid, memory, vcpus, stat, times)
292

    
293
  def GetAllInstancesInfo(self):
294
    """Get properties of all instances.
295

296
    @return: list of tuples (name, id, memory, vcpus, stat, times)
297

298
    """
299
    data = []
300
    for name in os.listdir(self._PIDS_DIR):
301
      filename = utils.PathJoin(self._PIDS_DIR, name)
302
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
303
        try:
304
          info = self.GetInstanceInfo(name)
305
        except errors.HypervisorError:
306
          continue
307
        if info:
308
          data.append(info)
309

    
310
    return data
311

    
312
  def _GenerateKVMRuntime(self, instance, block_devices):
313
    """Generate KVM information to start an instance.
314

315
    """
316
    pidfile  = self._InstancePidFile(instance.name)
317
    kvm = constants.KVM_PATH
318
    kvm_cmd = [kvm]
319
    # used just by the vnc server, if enabled
320
    kvm_cmd.extend(['-name', instance.name])
321
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
322
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
323
    kvm_cmd.extend(['-pidfile', pidfile])
324
    kvm_cmd.extend(['-daemonize'])
325
    if not instance.hvparams[constants.HV_ACPI]:
326
      kvm_cmd.extend(['-no-acpi'])
327

    
328
    hvp = instance.hvparams
329
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
330
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
331
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
332

    
333
    security_model = hvp[constants.HV_SECURITY_MODEL]
334
    if security_model == constants.HT_SM_USER:
335
      kvm_cmd.extend(['-runas', hvp[constants.HV_SECURITY_DOMAIN]])
336

    
337
    if boot_network:
338
      kvm_cmd.extend(['-boot', 'n'])
339

    
340
    disk_type = hvp[constants.HV_DISK_TYPE]
341
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
342
      if_val = ',if=virtio'
343
    else:
344
      if_val = ',if=%s' % disk_type
345
    # Cache mode
346
    disk_cache = hvp[constants.HV_DISK_CACHE]
347
    if disk_cache != constants.HT_CACHE_DEFAULT:
348
      cache_val = ",cache=%s" % disk_cache
349
    else:
350
      cache_val = ""
351
    for cfdev, dev_path in block_devices:
352
      if cfdev.mode != constants.DISK_RDWR:
353
        raise errors.HypervisorError("Instance has read-only disks which"
354
                                     " are not supported by KVM")
355
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
356
      if boot_disk:
357
        kvm_cmd.extend(['-boot', 'c'])
358
        boot_val = ',boot=on'
359
        # We only boot from the first disk
360
        boot_disk = False
361
      else:
362
        boot_val = ''
363

    
364
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
365
                                                cache_val)
366
      kvm_cmd.extend(['-drive', drive_val])
367

    
368
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
369
    if iso_image:
370
      options = ',format=raw,media=cdrom'
371
      if boot_cdrom:
372
        kvm_cmd.extend(['-boot', 'd'])
373
        options = '%s,boot=on' % options
374
      else:
375
        options = '%s,if=virtio' % options
376
      drive_val = 'file=%s%s' % (iso_image, options)
377
      kvm_cmd.extend(['-drive', drive_val])
378

    
379
    kernel_path = hvp[constants.HV_KERNEL_PATH]
380
    if kernel_path:
381
      kvm_cmd.extend(['-kernel', kernel_path])
382
      initrd_path = hvp[constants.HV_INITRD_PATH]
383
      if initrd_path:
384
        kvm_cmd.extend(['-initrd', initrd_path])
385
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
386
                     hvp[constants.HV_KERNEL_ARGS]]
387
      if hvp[constants.HV_SERIAL_CONSOLE]:
388
        root_append.append('console=ttyS0,38400')
389
      kvm_cmd.extend(['-append', ' '.join(root_append)])
390

    
391
    mouse_type = hvp[constants.HV_USB_MOUSE]
392
    if mouse_type:
393
      kvm_cmd.extend(['-usb'])
394
      kvm_cmd.extend(['-usbdevice', mouse_type])
395

    
396
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
397
    if vnc_bind_address:
398
      if utils.IsValidIP(vnc_bind_address):
399
        if instance.network_port > constants.VNC_BASE_PORT:
400
          display = instance.network_port - constants.VNC_BASE_PORT
401
          if vnc_bind_address == '0.0.0.0':
402
            vnc_arg = ':%d' % (display)
403
          else:
404
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
405
        else:
406
          logging.error("Network port is not a valid VNC display (%d < %d)."
407
                        " Not starting VNC", instance.network_port,
408
                        constants.VNC_BASE_PORT)
409
          vnc_arg = 'none'
410

    
411
        # Only allow tls and other option when not binding to a file, for now.
412
        # kvm/qemu gets confused otherwise about the filename to use.
413
        vnc_append = ''
414
        if hvp[constants.HV_VNC_TLS]:
415
          vnc_append = '%s,tls' % vnc_append
416
          if hvp[constants.HV_VNC_X509_VERIFY]:
417
            vnc_append = '%s,x509verify=%s' % (vnc_append,
418
                                               hvp[constants.HV_VNC_X509])
419
          elif hvp[constants.HV_VNC_X509]:
420
            vnc_append = '%s,x509=%s' % (vnc_append,
421
                                         hvp[constants.HV_VNC_X509])
422
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
423
          vnc_append = '%s,password' % vnc_append
424

    
425
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
426

    
427
      else:
428
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
429

    
430
      kvm_cmd.extend(['-vnc', vnc_arg])
431
    else:
432
      kvm_cmd.extend(['-nographic'])
433

    
434
    monitor_dev = ("unix:%s,server,nowait" %
435
                   self._InstanceMonitor(instance.name))
436
    kvm_cmd.extend(['-monitor', monitor_dev])
437
    if hvp[constants.HV_SERIAL_CONSOLE]:
438
      serial_dev = ('unix:%s,server,nowait' %
439
                    self._InstanceSerial(instance.name))
440
      kvm_cmd.extend(['-serial', serial_dev])
441
    else:
442
      kvm_cmd.extend(['-serial', 'none'])
443

    
444
    if hvp[constants.HV_USE_LOCALTIME]:
445
      kvm_cmd.extend(['-localtime'])
446

    
447
    # Save the current instance nics, but defer their expansion as parameters,
448
    # as we'll need to generate executable temp files for them.
449
    kvm_nics = instance.nics
450
    hvparams = hvp
451

    
452
    return (kvm_cmd, kvm_nics, hvparams)
453

    
454
  def _WriteKVMRuntime(self, instance_name, data):
455
    """Write an instance's KVM runtime
456

457
    """
458
    try:
459
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
460
                      data=data)
461
    except EnvironmentError, err:
462
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
463

    
464
  def _ReadKVMRuntime(self, instance_name):
465
    """Read an instance's KVM runtime
466

467
    """
468
    try:
469
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
470
    except EnvironmentError, err:
471
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
472
    return file_content
473

    
474
  def _SaveKVMRuntime(self, instance, kvm_runtime):
475
    """Save an instance's KVM runtime
476

477
    """
478
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
479
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
480
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
481
    self._WriteKVMRuntime(instance.name, serialized_form)
482

    
483
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
484
    """Load an instance's KVM runtime
485

486
    """
487
    if not serialized_runtime:
488
      serialized_runtime = self._ReadKVMRuntime(instance.name)
489
    loaded_runtime = serializer.Load(serialized_runtime)
490
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
491
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
492
    return (kvm_cmd, kvm_nics, hvparams)
493

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

497
    @type incoming: tuple of strings
498
    @param incoming: (target_host_ip, port)
499

500
    """
501
    hvp = instance.hvparams
502
    name = instance.name
503
    self._CheckDown(name)
504

    
505
    temp_files = []
506

    
507
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
508

    
509
    if not kvm_nics:
510
      kvm_cmd.extend(['-net', 'none'])
511
    else:
512
      nic_type = hvparams[constants.HV_NIC_TYPE]
513
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
514
        nic_model = "model=virtio"
515
      else:
516
        nic_model = "model=%s" % nic_type
517

    
518
      for nic_seq, nic in enumerate(kvm_nics):
519
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
520
        script = self._WriteNetScript(instance, nic_seq, nic)
521
        kvm_cmd.extend(['-net', nic_val])
522
        kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
523
        temp_files.append(script)
524

    
525
    if incoming:
526
      target, port = incoming
527
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
528

    
529
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
530
    vnc_pwd = None
531
    if vnc_pwd_file:
532
      try:
533
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
534
      except EnvironmentError, err:
535
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
536
                                     % (vnc_pwd_file, err))
537

    
538
    result = utils.RunCmd(kvm_cmd)
539
    if result.failed:
540
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
541
                                   (name, result.fail_reason, result.output))
542

    
543
    if not self._InstancePidAlive(name)[2]:
544
      raise errors.HypervisorError("Failed to start instance %s" % name)
545

    
546
    if vnc_pwd:
547
      change_cmd = 'change vnc password %s' % vnc_pwd
548
      self._CallMonitorCommand(instance.name, change_cmd)
549

    
550
    for filename in temp_files:
551
      utils.RemoveFile(filename)
552

    
553
  def StartInstance(self, instance, block_devices):
554
    """Start an instance.
555

556
    """
557
    self._CheckDown(instance.name)
558
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
559
    self._SaveKVMRuntime(instance, kvm_runtime)
560
    self._ExecuteKVMRuntime(instance, kvm_runtime)
561

    
562
  def _CallMonitorCommand(self, instance_name, command):
563
    """Invoke a command on the instance monitor.
564

565
    """
566
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
567
             (utils.ShellQuote(command),
568
              constants.SOCAT_PATH,
569
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
570
    result = utils.RunCmd(socat)
571
    if result.failed:
572
      msg = ("Failed to send command '%s' to instance %s."
573
             " output: %s, error: %s, fail_reason: %s" %
574
             (command, instance_name,
575
              result.stdout, result.stderr, result.fail_reason))
576
      raise errors.HypervisorError(msg)
577

    
578
    return result
579

    
580
  def StopInstance(self, instance, force=False, retry=False):
581
    """Stop an instance.
582

583
    """
584
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
585
    if pid > 0 and alive:
586
      if force or not instance.hvparams[constants.HV_ACPI]:
587
        utils.KillProcess(pid)
588
      else:
589
        self._CallMonitorCommand(instance.name, 'system_powerdown')
590

    
591
    if not utils.IsProcessAlive(pid):
592
      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
593
      return True
594
    else:
595
      return False
596

    
597
  def RebootInstance(self, instance):
598
    """Reboot an instance.
599

600
    """
601
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
602
    # socket the instance will stop, but now power up again. So we'll resort
603
    # to shutdown and restart.
604
    _, _, alive = self._InstancePidAlive(instance.name)
605
    if not alive:
606
      raise errors.HypervisorError("Failed to reboot instance %s:"
607
                                   " not running" % instance.name)
608
    # StopInstance will delete the saved KVM runtime so:
609
    # ...first load it...
610
    kvm_runtime = self._LoadKVMRuntime(instance)
611
    # ...now we can safely call StopInstance...
612
    if not self.StopInstance(instance):
613
      self.StopInstance(instance, force=True)
614
    # ...and finally we can save it again, and execute it...
615
    self._SaveKVMRuntime(instance, kvm_runtime)
616
    self._ExecuteKVMRuntime(instance, kvm_runtime)
617

    
618
  def MigrationInfo(self, instance):
619
    """Get instance information to perform a migration.
620

621
    @type instance: L{objects.Instance}
622
    @param instance: instance to be migrated
623
    @rtype: string
624
    @return: content of the KVM runtime file
625

626
    """
627
    return self._ReadKVMRuntime(instance.name)
628

    
629
  def AcceptInstance(self, instance, info, target):
630
    """Prepare to accept an instance.
631

632
    @type instance: L{objects.Instance}
633
    @param instance: instance to be accepted
634
    @type info: string
635
    @param info: content of the KVM runtime file on the source node
636
    @type target: string
637
    @param target: target host (usually ip), on this node
638

639
    """
640
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
641
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
642
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
643

    
644
  def FinalizeMigration(self, instance, info, success):
645
    """Finalize an instance migration.
646

647
    Stop the incoming mode KVM.
648

649
    @type instance: L{objects.Instance}
650
    @param instance: instance whose migration is being aborted
651

652
    """
653
    if success:
654
      self._WriteKVMRuntime(instance.name, info)
655
    else:
656
      self.StopInstance(instance, force=True)
657

    
658
  def MigrateInstance(self, instance, target, live):
659
    """Migrate an instance to a target node.
660

661
    The migration will not be attempted if the instance is not
662
    currently running.
663

664
    @type instance: L{objects.Instance}
665
    @param instance: the instance to be migrated
666
    @type target: string
667
    @param target: ip address of the target node
668
    @type live: boolean
669
    @param live: perform a live migration
670

671
    """
672
    instance_name = instance.name
673
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
674
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
675
    if not alive:
676
      raise errors.HypervisorError("Instance not running, cannot migrate")
677

    
678
    if not utils.TcpPing(target, port, live_port_needed=True):
679
      raise errors.HypervisorError("Remote host %s not listening on port"
680
                                   " %s, cannot migrate" % (target, port))
681

    
682
    if not live:
683
      self._CallMonitorCommand(instance_name, 'stop')
684

    
685
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
686
    self._CallMonitorCommand(instance_name, migrate_command)
687

    
688
    info_command = 'info migrate'
689
    done = False
690
    broken_answers = 0
691
    while not done:
692
      result = self._CallMonitorCommand(instance_name, info_command)
693
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
694
      if not match:
695
        broken_answers += 1
696
        if not result.stdout:
697
          logging.info("KVM: empty 'info migrate' result")
698
        else:
699
          logging.warning("KVM: unknown 'info migrate' result: %s",
700
                          result.stdout)
701
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
702
      else:
703
        status = match.group(1)
704
        if status == 'completed':
705
          done = True
706
        elif status == 'active':
707
          # reset the broken answers count
708
          broken_answers = 0
709
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
710
        elif status == 'failed' or status == 'cancelled':
711
          if not live:
712
            self._CallMonitorCommand(instance_name, 'cont')
713
          raise errors.HypervisorError("Migration %s at the kvm level" %
714
                                       status)
715
        else:
716
          logging.warning("KVM: unknown migration status '%s'", status)
717
          broken_answers += 1
718
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
719
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
720
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
721

    
722
    utils.KillProcess(pid)
723
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
724

    
725
  def GetNodeInfo(self):
726
    """Return information about the node.
727

728
    This is just a wrapper over the base GetLinuxNodeInfo method.
729

730
    @return: a dict with the following keys (values in MiB):
731
          - memory_total: the total memory size on the node
732
          - memory_free: the available memory on the node for instances
733
          - memory_dom0: the memory used by the node itself, if available
734

735
    """
736
    return self.GetLinuxNodeInfo()
737

    
738
  @classmethod
739
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
740
    """Return a command for connecting to the console of an instance.
741

742
    """
743
    if hvparams[constants.HV_SERIAL_CONSOLE]:
744
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
745
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
746
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
747
    else:
748
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
749

    
750
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
751
    if vnc_bind_address:
752
      if instance.network_port > constants.VNC_BASE_PORT:
753
        display = instance.network_port - constants.VNC_BASE_PORT
754
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
755
                       " (display: %d)'" % (vnc_bind_address,
756
                                            instance.network_port,
757
                                            display))
758
        shell_command = "%s; %s" % (vnc_command, shell_command)
759

    
760
    return shell_command
761

    
762
  def Verify(self):
763
    """Verify the hypervisor.
764

765
    Check that the binary exists.
766

767
    """
768
    if not os.path.exists(constants.KVM_PATH):
769
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
770
    if not os.path.exists(constants.SOCAT_PATH):
771
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
772

    
773

    
774
  @classmethod
775
  def CheckParameterSyntax(cls, hvparams):
776
    """Check the given parameters for validity.
777

778
    @type hvparams:  dict
779
    @param hvparams: dictionary with parameter names/value
780
    @raise errors.HypervisorError: when a parameter is not valid
781

782
    """
783
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
784

    
785
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
786
    if kernel_path:
787
      if not hvparams[constants.HV_ROOT_PATH]:
788
        raise errors.HypervisorError("Need a root partition for the instance,"
789
                                     " if a kernel is defined")
790

    
791
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
792
        not hvparams[constants.HV_VNC_X509]):
793
      raise errors.HypervisorError("%s must be defined, if %s is" %
794
                                   (constants.HV_VNC_X509,
795
                                    constants.HV_VNC_X509_VERIFY))
796

    
797
    boot_order = hvparams[constants.HV_BOOT_ORDER]
798
    if (boot_order == constants.HT_BO_CDROM and
799
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
800
      raise errors.HypervisorError("Cannot boot from cdrom without an"
801
                                   " ISO path")
802

    
803
    security_model = hvparams[constants.HV_SECURITY_MODEL]
804
    if security_model == constants.HT_SM_USER:
805
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
806
        raise errors.HypervisorError("A security domain (user to run kvm as)"
807
                                     " must be specified")
808
    elif (security_model == constants.HT_SM_NONE or
809
          security_model == constants.HT_SM_POOL):
810
      if hvparams[constants.HV_SECURITY_DOMAIN]:
811
        raise errors.HypervisorError("Cannot have a security domain when the"
812
                                     " security model is 'none' or 'pool'")
813
    if security_model == constants.HT_SM_POOL:
814
      raise errors.HypervisorError("Security model pool is not supported yet")
815

    
816
  @classmethod
817
  def ValidateParameters(cls, hvparams):
818
    """Check the given parameters for validity.
819

820
    @type hvparams:  dict
821
    @param hvparams: dictionary with parameter names/value
822
    @raise errors.HypervisorError: when a parameter is not valid
823

824
    """
825
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
826

    
827
    security_model = hvparams[constants.HV_SECURITY_MODEL]
828
    if security_model == constants.HT_SM_USER:
829
      username = hvparams[constants.HV_SECURITY_DOMAIN]
830
      try:
831
        pwd.getpwnam(username)
832
      except KeyError:
833
        raise errors.HypervisorError("Unknown security domain user %s"
834
                                     % username)
835

    
836
  @classmethod
837
  def PowercycleNode(cls):
838
    """KVM powercycle, just a wrapper over Linux powercycle.
839

840
    """
841
    cls.LinuxPowercycle()