Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ 8904b35c

History | View | Annotate | Download (31.1 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
    constants.HV_KVM_FLAG:
84
      hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
85
    }
86

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

    
92
  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
93

    
94
  ANCILLARY_FILES = [
95
    _KVM_NETWORK_SCRIPT,
96
    ]
97

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

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

109
    """
110
    return utils.PathJoin(cls._PIDS_DIR, instance_name)
111

    
112
  @classmethod
113
  def _InstancePidInfo(cls, pid):
114
    """Check pid file for instance information.
115

116
    Check that a pid file is associated with an instance, and retrieve
117
    information from its command line.
118

119
    @type pid: string or int
120
    @param pid: process id of the instance to check
121
    @rtype: tuple
122
    @return: (instance_name, memory, vcpus)
123
    @raise errors.HypervisorError: when an instance cannot be found
124

125
    """
126
    alive = utils.IsProcessAlive(pid)
127
    if not alive:
128
      raise errors.HypervisorError("Cannot get info for pid %s" % pid)
129

    
130
    cmdline_file = utils.PathJoin("/proc", str(pid), "cmdline")
131
    try:
132
      cmdline = utils.ReadFile(cmdline_file)
133
    except EnvironmentError, err:
134
      raise errors.HypervisorError("Can't open cmdline file for pid %s: %s" %
135
                                   (pid, err))
136

    
137
    instance = None
138
    memory = 0
139
    vcpus = 0
140

    
141
    arg_list = cmdline.split('\x00')
142
    while arg_list:
143
      arg =  arg_list.pop(0)
144
      if arg == "-name":
145
        instance = arg_list.pop(0)
146
      elif arg == "-m":
147
        memory = int(arg_list.pop(0))
148
      elif arg == "-smp":
149
        vcpus = int(arg_list.pop(0))
150

    
151
    if instance is None:
152
      raise errors.HypervisorError("Pid %s doesn't contain a ganeti kvm"
153
                                   " instance" % pid)
154

    
155
    return (instance, memory, vcpus)
156

    
157
  def _InstancePidAlive(self, instance_name):
158
    """Returns the instance pidfile, pid, and liveness.
159

160
    @type instance_name: string
161
    @param instance_name: instance name
162
    @rtype: tuple
163
    @return: (pid file name, pid, liveness)
164

165
    """
166
    pidfile = self._InstancePidFile(instance_name)
167
    pid = utils.ReadPidFile(pidfile)
168

    
169
    alive = False
170
    try:
171
      cmd_instance = self._InstancePidInfo(pid)[0]
172
      alive = (cmd_instance == instance_name)
173
    except errors.HypervisorError:
174
      pass
175

    
176
    return (pidfile, pid, alive)
177

    
178
  def _CheckDown(self, instance_name):
179
    """Raises an error unless the given instance is down.
180

181
    """
182
    alive = self._InstancePidAlive(instance_name)[2]
183
    if alive:
184
      raise errors.HypervisorError("Failed to start instance %s: %s" %
185
                                   (instance_name, "already running"))
186

    
187
  @classmethod
188
  def _InstanceMonitor(cls, instance_name):
189
    """Returns the instance monitor socket name
190

191
    """
192
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
193

    
194
  @classmethod
195
  def _InstanceSerial(cls, instance_name):
196
    """Returns the instance serial socket name
197

198
    """
199
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
200

    
201
  @staticmethod
202
  def _SocatUnixConsoleParams():
203
    """Returns the correct parameters for socat
204

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

207
    """
208
    if constants.SOCAT_USE_ESCAPE:
209
      return "raw,echo=0,escape=%s" % constants.SOCAT_ESCAPE_CODE
210
    else:
211
      return "echo=0,icanon=0"
212

    
213
  @classmethod
214
  def _InstanceKVMRuntime(cls, instance_name):
215
    """Returns the instance KVM runtime filename
216

217
    """
218
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
219

    
220
  @classmethod
221
  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
222
    """Removes an instance's rutime sockets/files.
223

224
    """
225
    utils.RemoveFile(pidfile)
226
    utils.RemoveFile(cls._InstanceMonitor(instance_name))
227
    utils.RemoveFile(cls._InstanceSerial(instance_name))
228
    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
229

    
230
  def _WriteNetScript(self, instance, seq, nic):
231
    """Write a script to connect a net interface to the proper bridge.
232

233
    This can be used by any qemu-type hypervisor.
234

235
    @param instance: instance we're acting on
236
    @type instance: instance object
237
    @param seq: nic sequence number
238
    @type seq: int
239
    @param nic: nic we're acting on
240
    @type nic: nic object
241
    @return: netscript file name
242
    @rtype: string
243

244
    """
245
    script = StringIO()
246
    script.write("#!/bin/sh\n")
247
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
248
    script.write("PATH=$PATH:/sbin:/usr/sbin\n")
249
    script.write("export INSTANCE=%s\n" % instance.name)
250
    script.write("export MAC=%s\n" % nic.mac)
251
    if nic.ip:
252
      script.write("export IP=%s\n" % nic.ip)
253
    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
254
    if nic.nicparams[constants.NIC_LINK]:
255
      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
256
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
257
      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
258
    script.write("export INTERFACE=$1\n")
259
    # TODO: make this configurable at ./configure time
260
    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
261
    script.write("  # Execute the user-specific vif file\n")
262
    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
263
    script.write("else\n")
264
    script.write("  ifconfig $INTERFACE 0.0.0.0 up\n")
265
    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
266
      script.write("  # Connect the interface to the bridge\n")
267
      script.write("  brctl addif $BRIDGE $INTERFACE\n")
268
    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
269
      if not nic.ip:
270
        raise errors.HypervisorError("nic/%d is routed, but has no ip." % seq)
271
      script.write("  # Route traffic targeted at the IP to the interface\n")
272
      if nic.nicparams[constants.NIC_LINK]:
273
        script.write("  while ip rule del dev $INTERFACE; do :; done\n")
274
        script.write("  ip rule add dev $INTERFACE table $LINK\n")
275
        script.write("  ip route replace $IP table $LINK proto static"
276
                     " dev $INTERFACE\n")
277
      else:
278
        script.write("  ip route replace $IP proto static"
279
                     " dev $INTERFACE\n")
280
      interface_v4_conf = "/proc/sys/net/ipv4/conf/$INTERFACE"
281
      interface_v6_conf = "/proc/sys/net/ipv6/conf/$INTERFACE"
282
      script.write("  if [ -d %s ]; then\n" % interface_v4_conf)
283
      script.write("    echo 1 > %s/proxy_arp\n" % interface_v4_conf)
284
      script.write("    echo 1 > %s/forwarding\n" % interface_v4_conf)
285
      script.write("  fi\n")
286
      script.write("  if [ -d %s ]; then\n" % interface_v6_conf)
287
      script.write("    echo 1 > %s/proxy_ndp\n" % interface_v6_conf)
288
      script.write("    echo 1 > %s/forwarding\n" % interface_v6_conf)
289
      script.write("  fi\n")
290
    script.write("fi\n\n")
291
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
292
    # mounted noexec sometimes, so we'll have to find another place.
293
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
294
    tmpfile = os.fdopen(tmpfd, 'w')
295
    try:
296
      tmpfile.write(script.getvalue())
297
    finally:
298
      tmpfile.close()
299
    os.chmod(tmpfile_name, 0755)
300
    return tmpfile_name
301

    
302
  def ListInstances(self):
303
    """Get the list of running instances.
304

305
    We can do this by listing our live instances directory and
306
    checking whether the associated kvm process is still alive.
307

308
    """
309
    result = []
310
    for name in os.listdir(self._PIDS_DIR):
311
      if self._InstancePidAlive(name)[2]:
312
        result.append(name)
313
    return result
314

    
315
  def GetInstanceInfo(self, instance_name):
316
    """Get instance properties.
317

318
    @type instance_name: string
319
    @param instance_name: the instance name
320
    @rtype: tuple of strings
321
    @return: (name, id, memory, vcpus, stat, times)
322

323
    """
324
    _, pid, alive = self._InstancePidAlive(instance_name)
325
    if not alive:
326
      return None
327

    
328
    _, memory, vcpus = self._InstancePidInfo(pid)
329
    stat = "---b-"
330
    times = "0"
331

    
332
    return (instance_name, pid, memory, vcpus, stat, times)
333

    
334
  def GetAllInstancesInfo(self):
335
    """Get properties of all instances.
336

337
    @return: list of tuples (name, id, memory, vcpus, stat, times)
338

339
    """
340
    data = []
341
    for name in os.listdir(self._PIDS_DIR):
342
      try:
343
        info = self.GetInstanceInfo(name)
344
      except errors.HypervisorError:
345
        continue
346
      if info:
347
        data.append(info)
348
    return data
349

    
350
  def _GenerateKVMRuntime(self, instance, block_devices):
351
    """Generate KVM information to start an instance.
352

353
    """
354
    pidfile  = self._InstancePidFile(instance.name)
355
    kvm = constants.KVM_PATH
356
    kvm_cmd = [kvm]
357
    # used just by the vnc server, if enabled
358
    kvm_cmd.extend(['-name', instance.name])
359
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
360
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
361
    kvm_cmd.extend(['-pidfile', pidfile])
362
    kvm_cmd.extend(['-daemonize'])
363
    if not instance.hvparams[constants.HV_ACPI]:
364
      kvm_cmd.extend(['-no-acpi'])
365

    
366
    hvp = instance.hvparams
367
    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
368
    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
369
    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
370

    
371
    if hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_ENABLED:
372
      kvm_cmd.extend(["-enable-kvm"])
373
    elif hvp[constants.HV_KVM_FLAG] == constants.HT_KVM_DISABLED:
374
      kvm_cmd.extend(["-disable-kvm"])
375

    
376
    if boot_network:
377
      kvm_cmd.extend(['-boot', 'n'])
378

    
379
    disk_type = hvp[constants.HV_DISK_TYPE]
380
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
381
      if_val = ',if=virtio'
382
    else:
383
      if_val = ',if=%s' % disk_type
384
    # Cache mode
385
    disk_cache = hvp[constants.HV_DISK_CACHE]
386
    if disk_cache != constants.HT_CACHE_DEFAULT:
387
      cache_val = ",cache=%s" % disk_cache
388
    else:
389
      cache_val = ""
390
    for cfdev, dev_path in block_devices:
391
      if cfdev.mode != constants.DISK_RDWR:
392
        raise errors.HypervisorError("Instance has read-only disks which"
393
                                     " are not supported by KVM")
394
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
395
      if boot_disk:
396
        kvm_cmd.extend(['-boot', 'c'])
397
        boot_val = ',boot=on'
398
        # We only boot from the first disk
399
        boot_disk = False
400
      else:
401
        boot_val = ''
402

    
403
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
404
                                                cache_val)
405
      kvm_cmd.extend(['-drive', drive_val])
406

    
407
    iso_image = hvp[constants.HV_CDROM_IMAGE_PATH]
408
    if iso_image:
409
      options = ',format=raw,media=cdrom'
410
      if boot_cdrom:
411
        kvm_cmd.extend(['-boot', 'd'])
412
        options = '%s,boot=on' % options
413
      else:
414
        options = '%s,if=virtio' % options
415
      drive_val = 'file=%s%s' % (iso_image, options)
416
      kvm_cmd.extend(['-drive', drive_val])
417

    
418
    kernel_path = hvp[constants.HV_KERNEL_PATH]
419
    if kernel_path:
420
      kvm_cmd.extend(['-kernel', kernel_path])
421
      initrd_path = hvp[constants.HV_INITRD_PATH]
422
      if initrd_path:
423
        kvm_cmd.extend(['-initrd', initrd_path])
424
      root_append = ['root=%s' % hvp[constants.HV_ROOT_PATH],
425
                     hvp[constants.HV_KERNEL_ARGS]]
426
      if hvp[constants.HV_SERIAL_CONSOLE]:
427
        root_append.append('console=ttyS0,38400')
428
      kvm_cmd.extend(['-append', ' '.join(root_append)])
429

    
430
    mouse_type = hvp[constants.HV_USB_MOUSE]
431
    if mouse_type:
432
      kvm_cmd.extend(['-usb'])
433
      kvm_cmd.extend(['-usbdevice', mouse_type])
434

    
435
    vnc_bind_address = hvp[constants.HV_VNC_BIND_ADDRESS]
436
    if vnc_bind_address:
437
      if utils.IsValidIP(vnc_bind_address):
438
        if instance.network_port > constants.VNC_BASE_PORT:
439
          display = instance.network_port - constants.VNC_BASE_PORT
440
          if vnc_bind_address == '0.0.0.0':
441
            vnc_arg = ':%d' % (display)
442
          else:
443
            vnc_arg = '%s:%d' % (vnc_bind_address, display)
444
        else:
445
          logging.error("Network port is not a valid VNC display (%d < %d)."
446
                        " Not starting VNC", instance.network_port,
447
                        constants.VNC_BASE_PORT)
448
          vnc_arg = 'none'
449

    
450
        # Only allow tls and other option when not binding to a file, for now.
451
        # kvm/qemu gets confused otherwise about the filename to use.
452
        vnc_append = ''
453
        if hvp[constants.HV_VNC_TLS]:
454
          vnc_append = '%s,tls' % vnc_append
455
          if hvp[constants.HV_VNC_X509_VERIFY]:
456
            vnc_append = '%s,x509verify=%s' % (vnc_append,
457
                                               hvp[constants.HV_VNC_X509])
458
          elif hvp[constants.HV_VNC_X509]:
459
            vnc_append = '%s,x509=%s' % (vnc_append,
460
                                         hvp[constants.HV_VNC_X509])
461
        if hvp[constants.HV_VNC_PASSWORD_FILE]:
462
          vnc_append = '%s,password' % vnc_append
463

    
464
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
465

    
466
      else:
467
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
468

    
469
      kvm_cmd.extend(['-vnc', vnc_arg])
470
    else:
471
      kvm_cmd.extend(['-nographic'])
472

    
473
    monitor_dev = ("unix:%s,server,nowait" %
474
                   self._InstanceMonitor(instance.name))
475
    kvm_cmd.extend(['-monitor', monitor_dev])
476
    if hvp[constants.HV_SERIAL_CONSOLE]:
477
      serial_dev = ('unix:%s,server,nowait' %
478
                    self._InstanceSerial(instance.name))
479
      kvm_cmd.extend(['-serial', serial_dev])
480
    else:
481
      kvm_cmd.extend(['-serial', 'none'])
482

    
483
    if hvp[constants.HV_USE_LOCALTIME]:
484
      kvm_cmd.extend(['-localtime'])
485

    
486
    # Save the current instance nics, but defer their expansion as parameters,
487
    # as we'll need to generate executable temp files for them.
488
    kvm_nics = instance.nics
489
    hvparams = hvp
490

    
491
    return (kvm_cmd, kvm_nics, hvparams)
492

    
493
  def _WriteKVMRuntime(self, instance_name, data):
494
    """Write an instance's KVM runtime
495

496
    """
497
    try:
498
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
499
                      data=data)
500
    except EnvironmentError, err:
501
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
502

    
503
  def _ReadKVMRuntime(self, instance_name):
504
    """Read an instance's KVM runtime
505

506
    """
507
    try:
508
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
509
    except EnvironmentError, err:
510
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
511
    return file_content
512

    
513
  def _SaveKVMRuntime(self, instance, kvm_runtime):
514
    """Save an instance's KVM runtime
515

516
    """
517
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
518
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
519
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
520
    self._WriteKVMRuntime(instance.name, serialized_form)
521

    
522
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
523
    """Load an instance's KVM runtime
524

525
    """
526
    if not serialized_runtime:
527
      serialized_runtime = self._ReadKVMRuntime(instance.name)
528
    loaded_runtime = serializer.Load(serialized_runtime)
529
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
530
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
531
    return (kvm_cmd, kvm_nics, hvparams)
532

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

536
    @type incoming: tuple of strings
537
    @param incoming: (target_host_ip, port)
538

539
    """
540
    hvp = instance.hvparams
541
    name = instance.name
542
    self._CheckDown(name)
543

    
544
    temp_files = []
545

    
546
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
547

    
548
    security_model = hvp[constants.HV_SECURITY_MODEL]
549
    if security_model == constants.HT_SM_USER:
550
      kvm_cmd.extend(["-runas", hvp[constants.HV_SECURITY_DOMAIN]])
551

    
552
    if not kvm_nics:
553
      kvm_cmd.extend(['-net', 'none'])
554
    else:
555
      nic_type = hvparams[constants.HV_NIC_TYPE]
556
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
557
        nic_model = "model=virtio"
558
      else:
559
        nic_model = "model=%s" % nic_type
560

    
561
      for nic_seq, nic in enumerate(kvm_nics):
562
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
563
        script = self._WriteNetScript(instance, nic_seq, nic)
564
        kvm_cmd.extend(['-net', nic_val])
565
        kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
566
        temp_files.append(script)
567

    
568
    if incoming:
569
      target, port = incoming
570
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
571

    
572
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
573
    vnc_pwd = None
574
    if vnc_pwd_file:
575
      try:
576
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
577
      except EnvironmentError, err:
578
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
579
                                     % (vnc_pwd_file, err))
580

    
581
    result = utils.RunCmd(kvm_cmd)
582
    if result.failed:
583
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
584
                                   (name, result.fail_reason, result.output))
585

    
586
    if not self._InstancePidAlive(name)[2]:
587
      raise errors.HypervisorError("Failed to start instance %s" % name)
588

    
589
    if vnc_pwd:
590
      change_cmd = 'change vnc password %s' % vnc_pwd
591
      self._CallMonitorCommand(instance.name, change_cmd)
592

    
593
    for filename in temp_files:
594
      utils.RemoveFile(filename)
595

    
596
  def StartInstance(self, instance, block_devices):
597
    """Start an instance.
598

599
    """
600
    self._CheckDown(instance.name)
601
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
602
    self._SaveKVMRuntime(instance, kvm_runtime)
603
    self._ExecuteKVMRuntime(instance, kvm_runtime)
604

    
605
  def _CallMonitorCommand(self, instance_name, command):
606
    """Invoke a command on the instance monitor.
607

608
    """
609
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
610
             (utils.ShellQuote(command),
611
              constants.SOCAT_PATH,
612
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
613
    result = utils.RunCmd(socat)
614
    if result.failed:
615
      msg = ("Failed to send command '%s' to instance %s."
616
             " output: %s, error: %s, fail_reason: %s" %
617
             (command, instance_name,
618
              result.stdout, result.stderr, result.fail_reason))
619
      raise errors.HypervisorError(msg)
620

    
621
    return result
622

    
623
  def StopInstance(self, instance, force=False, retry=False, name=None):
624
    """Stop an instance.
625

626
    """
627
    if name is not None and not force:
628
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
629
    if name is None:
630
      name = instance.name
631
      acpi = instance.hvparams[constants.HV_ACPI]
632
    else:
633
      acpi = False
634
    _, pid, alive = self._InstancePidAlive(name)
635
    if pid > 0 and alive:
636
      if force or not acpi:
637
        utils.KillProcess(pid)
638
      else:
639
        self._CallMonitorCommand(name, 'system_powerdown')
640

    
641
  def CleanupInstance(self, instance_name):
642
    """Cleanup after a stopped instance
643

644
    """
645
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
646
    if pid > 0 and alive:
647
      raise errors.HypervisorError("Cannot cleanup a live instance")
648
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
649

    
650
  def RebootInstance(self, instance):
651
    """Reboot an instance.
652

653
    """
654
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
655
    # socket the instance will stop, but now power up again. So we'll resort
656
    # to shutdown and restart.
657
    _, _, alive = self._InstancePidAlive(instance.name)
658
    if not alive:
659
      raise errors.HypervisorError("Failed to reboot instance %s:"
660
                                   " not running" % instance.name)
661
    # StopInstance will delete the saved KVM runtime so:
662
    # ...first load it...
663
    kvm_runtime = self._LoadKVMRuntime(instance)
664
    # ...now we can safely call StopInstance...
665
    if not self.StopInstance(instance):
666
      self.StopInstance(instance, force=True)
667
    # ...and finally we can save it again, and execute it...
668
    self._SaveKVMRuntime(instance, kvm_runtime)
669
    self._ExecuteKVMRuntime(instance, kvm_runtime)
670

    
671
  def MigrationInfo(self, instance):
672
    """Get instance information to perform a migration.
673

674
    @type instance: L{objects.Instance}
675
    @param instance: instance to be migrated
676
    @rtype: string
677
    @return: content of the KVM runtime file
678

679
    """
680
    return self._ReadKVMRuntime(instance.name)
681

    
682
  def AcceptInstance(self, instance, info, target):
683
    """Prepare to accept an instance.
684

685
    @type instance: L{objects.Instance}
686
    @param instance: instance to be accepted
687
    @type info: string
688
    @param info: content of the KVM runtime file on the source node
689
    @type target: string
690
    @param target: target host (usually ip), on this node
691

692
    """
693
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
694
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
695
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
696

    
697
  def FinalizeMigration(self, instance, info, success):
698
    """Finalize an instance migration.
699

700
    Stop the incoming mode KVM.
701

702
    @type instance: L{objects.Instance}
703
    @param instance: instance whose migration is being aborted
704

705
    """
706
    if success:
707
      self._WriteKVMRuntime(instance.name, info)
708
    else:
709
      self.StopInstance(instance, force=True)
710

    
711
  def MigrateInstance(self, instance, target, live):
712
    """Migrate an instance to a target node.
713

714
    The migration will not be attempted if the instance is not
715
    currently running.
716

717
    @type instance: L{objects.Instance}
718
    @param instance: the instance to be migrated
719
    @type target: string
720
    @param target: ip address of the target node
721
    @type live: boolean
722
    @param live: perform a live migration
723

724
    """
725
    instance_name = instance.name
726
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
727
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
728
    if not alive:
729
      raise errors.HypervisorError("Instance not running, cannot migrate")
730

    
731
    if not utils.TcpPing(target, port, live_port_needed=True):
732
      raise errors.HypervisorError("Remote host %s not listening on port"
733
                                   " %s, cannot migrate" % (target, port))
734

    
735
    if not live:
736
      self._CallMonitorCommand(instance_name, 'stop')
737

    
738
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
739
    self._CallMonitorCommand(instance_name, migrate_command)
740

    
741
    info_command = 'info migrate'
742
    done = False
743
    broken_answers = 0
744
    while not done:
745
      result = self._CallMonitorCommand(instance_name, info_command)
746
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
747
      if not match:
748
        broken_answers += 1
749
        if not result.stdout:
750
          logging.info("KVM: empty 'info migrate' result")
751
        else:
752
          logging.warning("KVM: unknown 'info migrate' result: %s",
753
                          result.stdout)
754
        time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
755
      else:
756
        status = match.group(1)
757
        if status == 'completed':
758
          done = True
759
        elif status == 'active':
760
          # reset the broken answers count
761
          broken_answers = 0
762
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
763
        elif status == 'failed' or status == 'cancelled':
764
          if not live:
765
            self._CallMonitorCommand(instance_name, 'cont')
766
          raise errors.HypervisorError("Migration %s at the kvm level" %
767
                                       status)
768
        else:
769
          logging.warning("KVM: unknown migration status '%s'", status)
770
          broken_answers += 1
771
          time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
772
      if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
773
        raise errors.HypervisorError("Too many 'info migrate' broken answers")
774

    
775
    utils.KillProcess(pid)
776
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
777

    
778
  def GetNodeInfo(self):
779
    """Return information about the node.
780

781
    This is just a wrapper over the base GetLinuxNodeInfo method.
782

783
    @return: a dict with the following keys (values in MiB):
784
          - memory_total: the total memory size on the node
785
          - memory_free: the available memory on the node for instances
786
          - memory_dom0: the memory used by the node itself, if available
787

788
    """
789
    return self.GetLinuxNodeInfo()
790

    
791
  @classmethod
792
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
793
    """Return a command for connecting to the console of an instance.
794

795
    """
796
    if hvparams[constants.HV_SERIAL_CONSOLE]:
797
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
798
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
799
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
800
    else:
801
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
802

    
803
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
804
    if vnc_bind_address:
805
      if instance.network_port > constants.VNC_BASE_PORT:
806
        display = instance.network_port - constants.VNC_BASE_PORT
807
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
808
                       " (display: %d)'" % (vnc_bind_address,
809
                                            instance.network_port,
810
                                            display))
811
        shell_command = "%s; %s" % (vnc_command, shell_command)
812

    
813
    return shell_command
814

    
815
  def Verify(self):
816
    """Verify the hypervisor.
817

818
    Check that the binary exists.
819

820
    """
821
    if not os.path.exists(constants.KVM_PATH):
822
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
823
    if not os.path.exists(constants.SOCAT_PATH):
824
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
825

    
826

    
827
  @classmethod
828
  def CheckParameterSyntax(cls, hvparams):
829
    """Check the given parameters for validity.
830

831
    @type hvparams:  dict
832
    @param hvparams: dictionary with parameter names/value
833
    @raise errors.HypervisorError: when a parameter is not valid
834

835
    """
836
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
837

    
838
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
839
    if kernel_path:
840
      if not hvparams[constants.HV_ROOT_PATH]:
841
        raise errors.HypervisorError("Need a root partition for the instance,"
842
                                     " if a kernel is defined")
843

    
844
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
845
        not hvparams[constants.HV_VNC_X509]):
846
      raise errors.HypervisorError("%s must be defined, if %s is" %
847
                                   (constants.HV_VNC_X509,
848
                                    constants.HV_VNC_X509_VERIFY))
849

    
850
    boot_order = hvparams[constants.HV_BOOT_ORDER]
851
    if (boot_order == constants.HT_BO_CDROM and
852
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
853
      raise errors.HypervisorError("Cannot boot from cdrom without an"
854
                                   " ISO path")
855

    
856
    security_model = hvparams[constants.HV_SECURITY_MODEL]
857
    if security_model == constants.HT_SM_USER:
858
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
859
        raise errors.HypervisorError("A security domain (user to run kvm as)"
860
                                     " must be specified")
861
    elif (security_model == constants.HT_SM_NONE or
862
          security_model == constants.HT_SM_POOL):
863
      if hvparams[constants.HV_SECURITY_DOMAIN]:
864
        raise errors.HypervisorError("Cannot have a security domain when the"
865
                                     " security model is 'none' or 'pool'")
866
    if security_model == constants.HT_SM_POOL:
867
      raise errors.HypervisorError("Security model pool is not supported yet")
868

    
869
  @classmethod
870
  def ValidateParameters(cls, hvparams):
871
    """Check the given parameters for validity.
872

873
    @type hvparams:  dict
874
    @param hvparams: dictionary with parameter names/value
875
    @raise errors.HypervisorError: when a parameter is not valid
876

877
    """
878
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
879

    
880
    security_model = hvparams[constants.HV_SECURITY_MODEL]
881
    if security_model == constants.HT_SM_USER:
882
      username = hvparams[constants.HV_SECURITY_DOMAIN]
883
      try:
884
        pwd.getpwnam(username)
885
      except KeyError:
886
        raise errors.HypervisorError("Unknown security domain user %s"
887
                                     % username)
888

    
889
  @classmethod
890
  def PowercycleNode(cls):
891
    """KVM powercycle, just a wrapper over Linux powercycle.
892

893
    """
894
    cls.LinuxPowercycle()