Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ bbcf7ad0

History | View | Annotate | Download (30.7 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
  @classmethod
111
  def _InstancePidInfo(cls, pid):
112
    """Check pid file for instance information.
113

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

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

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

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

    
135
    instance = None
136
    memory = 0
137
    vcpus = 0
138

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

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

    
153
    return (instance, memory, vcpus)
154

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

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

163
    """
164
    pidfile = self._InstancePidFile(instance_name)
165
    pid = utils.ReadPidFile(pidfile)
166

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

    
174
    return (pidfile, pid, alive)
175

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

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

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

189
    """
190
    return utils.PathJoin(cls._CTRL_DIR, "%s.monitor" % instance_name)
191

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

196
    """
197
    return utils.PathJoin(cls._CTRL_DIR, "%s.serial" % instance_name)
198

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

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

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

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

215
    """
216
    return utils.PathJoin(cls._CONF_DIR, "%s.runtime" % instance_name)
217

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

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

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

231
    This can be used by any qemu-type hypervisor.
232

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

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

    
300
  def ListInstances(self):
301
    """Get the list of running instances.
302

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

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

    
313
  def GetInstanceInfo(self, instance_name):
314
    """Get instance properties.
315

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

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

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

    
330
    return (instance_name, pid, memory, vcpus, stat, times)
331

    
332
  def GetAllInstancesInfo(self):
333
    """Get properties of all instances.
334

335
    @return: list of tuples (name, id, memory, vcpus, stat, times)
336

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

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

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

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

    
369
    security_model = hvp[constants.HV_SECURITY_MODEL]
370
    if security_model == constants.HT_SM_USER:
371
      kvm_cmd.extend(['-runas', hvp[constants.HV_SECURITY_DOMAIN]])
372

    
373
    if boot_network:
374
      kvm_cmd.extend(['-boot', 'n'])
375

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

    
400
      drive_val = 'file=%s,format=raw%s%s%s' % (dev_path, if_val, boot_val,
401
                                                cache_val)
402
      kvm_cmd.extend(['-drive', drive_val])
403

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

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

    
427
    mouse_type = hvp[constants.HV_USB_MOUSE]
428
    if mouse_type:
429
      kvm_cmd.extend(['-usb'])
430
      kvm_cmd.extend(['-usbdevice', mouse_type])
431

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

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

    
461
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
462

    
463
      else:
464
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
465

    
466
      kvm_cmd.extend(['-vnc', vnc_arg])
467
    else:
468
      kvm_cmd.extend(['-nographic'])
469

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

    
480
    if hvp[constants.HV_USE_LOCALTIME]:
481
      kvm_cmd.extend(['-localtime'])
482

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

    
488
    return (kvm_cmd, kvm_nics, hvparams)
489

    
490
  def _WriteKVMRuntime(self, instance_name, data):
491
    """Write an instance's KVM runtime
492

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

    
500
  def _ReadKVMRuntime(self, instance_name):
501
    """Read an instance's KVM runtime
502

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

    
510
  def _SaveKVMRuntime(self, instance, kvm_runtime):
511
    """Save an instance's KVM runtime
512

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

    
519
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
520
    """Load an instance's KVM runtime
521

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

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

533
    @type incoming: tuple of strings
534
    @param incoming: (target_host_ip, port)
535

536
    """
537
    hvp = instance.hvparams
538
    name = instance.name
539
    self._CheckDown(name)
540

    
541
    temp_files = []
542

    
543
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
544

    
545
    if not kvm_nics:
546
      kvm_cmd.extend(['-net', 'none'])
547
    else:
548
      nic_type = hvparams[constants.HV_NIC_TYPE]
549
      if nic_type == constants.HT_NIC_PARAVIRTUAL:
550
        nic_model = "model=virtio"
551
      else:
552
        nic_model = "model=%s" % nic_type
553

    
554
      for nic_seq, nic in enumerate(kvm_nics):
555
        nic_val = "nic,vlan=%s,macaddr=%s,%s" % (nic_seq, nic.mac, nic_model)
556
        script = self._WriteNetScript(instance, nic_seq, nic)
557
        kvm_cmd.extend(['-net', nic_val])
558
        kvm_cmd.extend(['-net', 'tap,vlan=%s,script=%s' % (nic_seq, script)])
559
        temp_files.append(script)
560

    
561
    if incoming:
562
      target, port = incoming
563
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
564

    
565
    vnc_pwd_file = hvp[constants.HV_VNC_PASSWORD_FILE]
566
    vnc_pwd = None
567
    if vnc_pwd_file:
568
      try:
569
        vnc_pwd = utils.ReadFile(vnc_pwd_file)
570
      except EnvironmentError, err:
571
        raise errors.HypervisorError("Failed to open VNC password file %s: %s"
572
                                     % (vnc_pwd_file, err))
573

    
574
    result = utils.RunCmd(kvm_cmd)
575
    if result.failed:
576
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
577
                                   (name, result.fail_reason, result.output))
578

    
579
    if not self._InstancePidAlive(name)[2]:
580
      raise errors.HypervisorError("Failed to start instance %s" % name)
581

    
582
    if vnc_pwd:
583
      change_cmd = 'change vnc password %s' % vnc_pwd
584
      self._CallMonitorCommand(instance.name, change_cmd)
585

    
586
    for filename in temp_files:
587
      utils.RemoveFile(filename)
588

    
589
  def StartInstance(self, instance, block_devices):
590
    """Start an instance.
591

592
    """
593
    self._CheckDown(instance.name)
594
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices)
595
    self._SaveKVMRuntime(instance, kvm_runtime)
596
    self._ExecuteKVMRuntime(instance, kvm_runtime)
597

    
598
  def _CallMonitorCommand(self, instance_name, command):
599
    """Invoke a command on the instance monitor.
600

601
    """
602
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
603
             (utils.ShellQuote(command),
604
              constants.SOCAT_PATH,
605
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
606
    result = utils.RunCmd(socat)
607
    if result.failed:
608
      msg = ("Failed to send command '%s' to instance %s."
609
             " output: %s, error: %s, fail_reason: %s" %
610
             (command, instance_name,
611
              result.stdout, result.stderr, result.fail_reason))
612
      raise errors.HypervisorError(msg)
613

    
614
    return result
615

    
616
  def StopInstance(self, instance, force=False, retry=False, name=None):
617
    """Stop an instance.
618

619
    """
620
    if name is not None and not force:
621
      raise errors.HypervisorError("Cannot shutdown cleanly by name only")
622
    if name is None:
623
      name = instance.name
624
      acpi = instance.hvparams[constants.HV_ACPI]
625
    else:
626
      acpi = False
627
    pidfile, pid, alive = self._InstancePidAlive(name)
628
    if pid > 0 and alive:
629
      if force or not acpi:
630
        utils.KillProcess(pid)
631
      else:
632
        self._CallMonitorCommand(name, 'system_powerdown')
633

    
634
    if not self._InstancePidAlive(name)[2]:
635
      self._RemoveInstanceRuntimeFiles(pidfile, name)
636
      return True
637
    else:
638
      return False
639

    
640
  def RebootInstance(self, instance):
641
    """Reboot an instance.
642

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

    
661
  def MigrationInfo(self, instance):
662
    """Get instance information to perform a migration.
663

664
    @type instance: L{objects.Instance}
665
    @param instance: instance to be migrated
666
    @rtype: string
667
    @return: content of the KVM runtime file
668

669
    """
670
    return self._ReadKVMRuntime(instance.name)
671

    
672
  def AcceptInstance(self, instance, info, target):
673
    """Prepare to accept an instance.
674

675
    @type instance: L{objects.Instance}
676
    @param instance: instance to be accepted
677
    @type info: string
678
    @param info: content of the KVM runtime file on the source node
679
    @type target: string
680
    @param target: target host (usually ip), on this node
681

682
    """
683
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
684
    incoming_address = (target, instance.hvparams[constants.HV_MIGRATION_PORT])
685
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
686

    
687
  def FinalizeMigration(self, instance, info, success):
688
    """Finalize an instance migration.
689

690
    Stop the incoming mode KVM.
691

692
    @type instance: L{objects.Instance}
693
    @param instance: instance whose migration is being aborted
694

695
    """
696
    if success:
697
      self._WriteKVMRuntime(instance.name, info)
698
    else:
699
      self.StopInstance(instance, force=True)
700

    
701
  def MigrateInstance(self, instance, target, live):
702
    """Migrate an instance to a target node.
703

704
    The migration will not be attempted if the instance is not
705
    currently running.
706

707
    @type instance: L{objects.Instance}
708
    @param instance: the instance to be migrated
709
    @type target: string
710
    @param target: ip address of the target node
711
    @type live: boolean
712
    @param live: perform a live migration
713

714
    """
715
    instance_name = instance.name
716
    port = instance.hvparams[constants.HV_MIGRATION_PORT]
717
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
718
    if not alive:
719
      raise errors.HypervisorError("Instance not running, cannot migrate")
720

    
721
    if not utils.TcpPing(target, port, live_port_needed=True):
722
      raise errors.HypervisorError("Remote host %s not listening on port"
723
                                   " %s, cannot migrate" % (target, port))
724

    
725
    if not live:
726
      self._CallMonitorCommand(instance_name, 'stop')
727

    
728
    migrate_command = 'migrate -d tcp:%s:%s' % (target, port)
729
    self._CallMonitorCommand(instance_name, migrate_command)
730

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

    
765
    utils.KillProcess(pid)
766
    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
767

    
768
  def GetNodeInfo(self):
769
    """Return information about the node.
770

771
    This is just a wrapper over the base GetLinuxNodeInfo method.
772

773
    @return: a dict with the following keys (values in MiB):
774
          - memory_total: the total memory size on the node
775
          - memory_free: the available memory on the node for instances
776
          - memory_dom0: the memory used by the node itself, if available
777

778
    """
779
    return self.GetLinuxNodeInfo()
780

    
781
  @classmethod
782
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
783
    """Return a command for connecting to the console of an instance.
784

785
    """
786
    if hvparams[constants.HV_SERIAL_CONSOLE]:
787
      shell_command = ("%s STDIO,%s UNIX-CONNECT:%s" %
788
                       (constants.SOCAT_PATH, cls._SocatUnixConsoleParams(),
789
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
790
    else:
791
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
792

    
793
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
794
    if vnc_bind_address:
795
      if instance.network_port > constants.VNC_BASE_PORT:
796
        display = instance.network_port - constants.VNC_BASE_PORT
797
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
798
                       " (display: %d)'" % (vnc_bind_address,
799
                                            instance.network_port,
800
                                            display))
801
        shell_command = "%s; %s" % (vnc_command, shell_command)
802

    
803
    return shell_command
804

    
805
  def Verify(self):
806
    """Verify the hypervisor.
807

808
    Check that the binary exists.
809

810
    """
811
    if not os.path.exists(constants.KVM_PATH):
812
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
813
    if not os.path.exists(constants.SOCAT_PATH):
814
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
815

    
816

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

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

825
    """
826
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
827

    
828
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
829
    if kernel_path:
830
      if not hvparams[constants.HV_ROOT_PATH]:
831
        raise errors.HypervisorError("Need a root partition for the instance,"
832
                                     " if a kernel is defined")
833

    
834
    if (hvparams[constants.HV_VNC_X509_VERIFY] and
835
        not hvparams[constants.HV_VNC_X509]):
836
      raise errors.HypervisorError("%s must be defined, if %s is" %
837
                                   (constants.HV_VNC_X509,
838
                                    constants.HV_VNC_X509_VERIFY))
839

    
840
    boot_order = hvparams[constants.HV_BOOT_ORDER]
841
    if (boot_order == constants.HT_BO_CDROM and
842
        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
843
      raise errors.HypervisorError("Cannot boot from cdrom without an"
844
                                   " ISO path")
845

    
846
    security_model = hvparams[constants.HV_SECURITY_MODEL]
847
    if security_model == constants.HT_SM_USER:
848
      if not hvparams[constants.HV_SECURITY_DOMAIN]:
849
        raise errors.HypervisorError("A security domain (user to run kvm as)"
850
                                     " must be specified")
851
    elif (security_model == constants.HT_SM_NONE or
852
          security_model == constants.HT_SM_POOL):
853
      if hvparams[constants.HV_SECURITY_DOMAIN]:
854
        raise errors.HypervisorError("Cannot have a security domain when the"
855
                                     " security model is 'none' or 'pool'")
856
    if security_model == constants.HT_SM_POOL:
857
      raise errors.HypervisorError("Security model pool is not supported yet")
858

    
859
  @classmethod
860
  def ValidateParameters(cls, hvparams):
861
    """Check the given parameters for validity.
862

863
    @type hvparams:  dict
864
    @param hvparams: dictionary with parameter names/value
865
    @raise errors.HypervisorError: when a parameter is not valid
866

867
    """
868
    super(KVMHypervisor, cls).ValidateParameters(hvparams)
869

    
870
    security_model = hvparams[constants.HV_SECURITY_MODEL]
871
    if security_model == constants.HT_SM_USER:
872
      username = hvparams[constants.HV_SECURITY_DOMAIN]
873
      try:
874
        pwd.getpwnam(username)
875
      except KeyError:
876
        raise errors.HypervisorError("Unknown security domain user %s"
877
                                     % username)
878

    
879
  @classmethod
880
  def PowercycleNode(cls):
881
    """KVM powercycle, just a wrapper over Linux powercycle.
882

883
    """
884
    cls.LinuxPowercycle()