Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_kvm.py @ c2672466

History | View | Annotate | Download (28.3 kB)

1 eb58f9b1 Guido Trotter
#
2 eb58f9b1 Guido Trotter
#
3 eb58f9b1 Guido Trotter
4 eb58f9b1 Guido Trotter
# Copyright (C) 2008 Google Inc.
5 eb58f9b1 Guido Trotter
#
6 eb58f9b1 Guido Trotter
# This program is free software; you can redistribute it and/or modify
7 eb58f9b1 Guido Trotter
# it under the terms of the GNU General Public License as published by
8 eb58f9b1 Guido Trotter
# the Free Software Foundation; either version 2 of the License, or
9 eb58f9b1 Guido Trotter
# (at your option) any later version.
10 eb58f9b1 Guido Trotter
#
11 eb58f9b1 Guido Trotter
# This program is distributed in the hope that it will be useful, but
12 eb58f9b1 Guido Trotter
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 eb58f9b1 Guido Trotter
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 eb58f9b1 Guido Trotter
# General Public License for more details.
15 eb58f9b1 Guido Trotter
#
16 eb58f9b1 Guido Trotter
# You should have received a copy of the GNU General Public License
17 eb58f9b1 Guido Trotter
# along with this program; if not, write to the Free Software
18 eb58f9b1 Guido Trotter
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 eb58f9b1 Guido Trotter
# 02110-1301, USA.
20 eb58f9b1 Guido Trotter
21 eb58f9b1 Guido Trotter
22 eb58f9b1 Guido Trotter
"""KVM hypervisor
23 eb58f9b1 Guido Trotter

24 eb58f9b1 Guido Trotter
"""
25 eb58f9b1 Guido Trotter
26 eb58f9b1 Guido Trotter
import os
27 eb58f9b1 Guido Trotter
import os.path
28 eb58f9b1 Guido Trotter
import re
29 eb58f9b1 Guido Trotter
import tempfile
30 6567aff3 Guido Trotter
import time
31 30e42c4e Guido Trotter
import logging
32 eb58f9b1 Guido Trotter
from cStringIO import StringIO
33 eb58f9b1 Guido Trotter
34 eb58f9b1 Guido Trotter
from ganeti import utils
35 eb58f9b1 Guido Trotter
from ganeti import constants
36 eb58f9b1 Guido Trotter
from ganeti import errors
37 38e250ba Guido Trotter
from ganeti import serializer
38 38e250ba Guido Trotter
from ganeti import objects
39 eb58f9b1 Guido Trotter
from ganeti.hypervisor import hv_base
40 eb58f9b1 Guido Trotter
41 eb58f9b1 Guido Trotter
42 eb58f9b1 Guido Trotter
class KVMHypervisor(hv_base.BaseHypervisor):
43 c4469f75 Guido Trotter
  """KVM hypervisor interface"""
44 eb58f9b1 Guido Trotter
45 eb58f9b1 Guido Trotter
  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
46 a1d79fc6 Guido Trotter
  _PIDS_DIR = _ROOT_DIR + "/pid" # contains live instances pids
47 a1d79fc6 Guido Trotter
  _CTRL_DIR = _ROOT_DIR + "/ctrl" # contains instances control sockets
48 a1d79fc6 Guido Trotter
  _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
49 a1d79fc6 Guido Trotter
  _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
50 eb58f9b1 Guido Trotter
51 6b5605e8 Iustin Pop
  PARAMETERS = [
52 6b5605e8 Iustin Pop
    constants.HV_KERNEL_PATH,
53 6b5605e8 Iustin Pop
    constants.HV_INITRD_PATH,
54 074ca009 Guido Trotter
    constants.HV_ROOT_PATH,
55 6b5605e8 Iustin Pop
    constants.HV_ACPI,
56 a2faf9ee Guido Trotter
    constants.HV_SERIAL_CONSOLE,
57 56fee73b Guido Trotter
    constants.HV_VNC_BIND_ADDRESS,
58 8b2d1013 Guido Trotter
    constants.HV_VNC_TLS,
59 8b2d1013 Guido Trotter
    constants.HV_VNC_X509,
60 8b2d1013 Guido Trotter
    constants.HV_VNC_X509_VERIFY,
61 66d5dbef Guido Trotter
    constants.HV_CDROM_IMAGE_PATH,
62 66d5dbef Guido Trotter
    constants.HV_BOOT_ORDER,
63 43440815 Guido Trotter
    constants.HV_NIC_TYPE,
64 43440815 Guido Trotter
    constants.HV_DISK_TYPE,
65 6b5605e8 Iustin Pop
    ]
66 6b5605e8 Iustin Pop
67 30e42c4e Guido Trotter
  _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
68 30e42c4e Guido Trotter
                                    re.M | re.I)
69 30e42c4e Guido Trotter
70 eb58f9b1 Guido Trotter
  def __init__(self):
71 eb58f9b1 Guido Trotter
    hv_base.BaseHypervisor.__init__(self)
72 eb58f9b1 Guido Trotter
    # Let's make sure the directories we need exist, even if the RUN_DIR lives
73 eb58f9b1 Guido Trotter
    # in a tmpfs filesystem or has been otherwise wiped out.
74 08137f9e Iustin Pop
    for mydir in self._DIRS:
75 08137f9e Iustin Pop
      if not os.path.exists(mydir):
76 08137f9e Iustin Pop
        os.mkdir(mydir)
77 eb58f9b1 Guido Trotter
78 1f8b3a27 Guido Trotter
  def _InstancePidAlive(self, instance_name):
79 1f8b3a27 Guido Trotter
    """Returns the instance pid and pidfile
80 1f8b3a27 Guido Trotter

81 1f8b3a27 Guido Trotter
    """
82 1f8b3a27 Guido Trotter
    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
83 1f8b3a27 Guido Trotter
    pid = utils.ReadPidFile(pidfile)
84 1f8b3a27 Guido Trotter
    alive = utils.IsProcessAlive(pid)
85 1f8b3a27 Guido Trotter
86 1f8b3a27 Guido Trotter
    return (pidfile, pid, alive)
87 1f8b3a27 Guido Trotter
88 0df4d98a Guido Trotter
  @classmethod
89 0df4d98a Guido Trotter
  def _InstanceMonitor(cls, instance_name):
90 c4fbefc8 Guido Trotter
    """Returns the instance monitor socket name
91 c4fbefc8 Guido Trotter

92 c4fbefc8 Guido Trotter
    """
93 0df4d98a Guido Trotter
    return '%s/%s.monitor' % (cls._CTRL_DIR, instance_name)
94 c4fbefc8 Guido Trotter
95 0df4d98a Guido Trotter
  @classmethod
96 0df4d98a Guido Trotter
  def _InstanceSerial(cls, instance_name):
97 c4fbefc8 Guido Trotter
    """Returns the instance serial socket name
98 c4fbefc8 Guido Trotter

99 c4fbefc8 Guido Trotter
    """
100 0df4d98a Guido Trotter
    return '%s/%s.serial' % (cls._CTRL_DIR, instance_name)
101 c4fbefc8 Guido Trotter
102 0df4d98a Guido Trotter
  @classmethod
103 0df4d98a Guido Trotter
  def _InstanceKVMRuntime(cls, instance_name):
104 38e250ba Guido Trotter
    """Returns the instance KVM runtime filename
105 38e250ba Guido Trotter

106 38e250ba Guido Trotter
    """
107 0df4d98a Guido Trotter
    return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
108 38e250ba Guido Trotter
109 eb58f9b1 Guido Trotter
  def _WriteNetScript(self, instance, seq, nic):
110 eb58f9b1 Guido Trotter
    """Write a script to connect a net interface to the proper bridge.
111 eb58f9b1 Guido Trotter

112 eb58f9b1 Guido Trotter
    This can be used by any qemu-type hypervisor.
113 eb58f9b1 Guido Trotter

114 eb58f9b1 Guido Trotter
    @param instance: instance we're acting on
115 eb58f9b1 Guido Trotter
    @type instance: instance object
116 eb58f9b1 Guido Trotter
    @param seq: nic sequence number
117 eb58f9b1 Guido Trotter
    @type seq: int
118 eb58f9b1 Guido Trotter
    @param nic: nic we're acting on
119 eb58f9b1 Guido Trotter
    @type nic: nic object
120 eb58f9b1 Guido Trotter
    @return: netscript file name
121 eb58f9b1 Guido Trotter
    @rtype: string
122 eb58f9b1 Guido Trotter

123 eb58f9b1 Guido Trotter
    """
124 eb58f9b1 Guido Trotter
    script = StringIO()
125 eb58f9b1 Guido Trotter
    script.write("#!/bin/sh\n")
126 eb58f9b1 Guido Trotter
    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
127 eb58f9b1 Guido Trotter
    script.write("export INSTANCE=%s\n" % instance.name)
128 eb58f9b1 Guido Trotter
    script.write("export MAC=%s\n" % nic.mac)
129 eb58f9b1 Guido Trotter
    script.write("export IP=%s\n" % nic.ip)
130 eb58f9b1 Guido Trotter
    script.write("export BRIDGE=%s\n" % nic.bridge)
131 eb58f9b1 Guido Trotter
    script.write("export INTERFACE=$1\n")
132 eb58f9b1 Guido Trotter
    # TODO: make this configurable at ./configure time
133 eb58f9b1 Guido Trotter
    script.write("if [ -x /etc/ganeti/kvm-vif-bridge ]; then\n")
134 eb58f9b1 Guido Trotter
    script.write("  # Execute the user-specific vif file\n")
135 eb58f9b1 Guido Trotter
    script.write("  /etc/ganeti/kvm-vif-bridge\n")
136 eb58f9b1 Guido Trotter
    script.write("else\n")
137 eb58f9b1 Guido Trotter
    script.write("  # Connect the interface to the bridge\n")
138 eb58f9b1 Guido Trotter
    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
139 eb58f9b1 Guido Trotter
    script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
140 eb58f9b1 Guido Trotter
    script.write("fi\n\n")
141 eb58f9b1 Guido Trotter
    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
142 eb58f9b1 Guido Trotter
    # mounted noexec sometimes, so we'll have to find another place.
143 eb58f9b1 Guido Trotter
    (tmpfd, tmpfile_name) = tempfile.mkstemp()
144 eb58f9b1 Guido Trotter
    tmpfile = os.fdopen(tmpfd, 'w')
145 eb58f9b1 Guido Trotter
    tmpfile.write(script.getvalue())
146 eb58f9b1 Guido Trotter
    tmpfile.close()
147 eb58f9b1 Guido Trotter
    os.chmod(tmpfile_name, 0755)
148 eb58f9b1 Guido Trotter
    return tmpfile_name
149 eb58f9b1 Guido Trotter
150 eb58f9b1 Guido Trotter
  def ListInstances(self):
151 eb58f9b1 Guido Trotter
    """Get the list of running instances.
152 eb58f9b1 Guido Trotter

153 c41eea6e Iustin Pop
    We can do this by listing our live instances directory and
154 c41eea6e Iustin Pop
    checking whether the associated kvm process is still alive.
155 eb58f9b1 Guido Trotter

156 eb58f9b1 Guido Trotter
    """
157 eb58f9b1 Guido Trotter
    result = []
158 eb58f9b1 Guido Trotter
    for name in os.listdir(self._PIDS_DIR):
159 08137f9e Iustin Pop
      filename = "%s/%s" % (self._PIDS_DIR, name)
160 08137f9e Iustin Pop
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
161 eb58f9b1 Guido Trotter
        result.append(name)
162 eb58f9b1 Guido Trotter
    return result
163 eb58f9b1 Guido Trotter
164 eb58f9b1 Guido Trotter
  def GetInstanceInfo(self, instance_name):
165 eb58f9b1 Guido Trotter
    """Get instance properties.
166 eb58f9b1 Guido Trotter

167 c41eea6e Iustin Pop
    @param instance_name: the instance name
168 c41eea6e Iustin Pop

169 c41eea6e Iustin Pop
    @return: tuple (name, id, memory, vcpus, stat, times)
170 eb58f9b1 Guido Trotter

171 eb58f9b1 Guido Trotter
    """
172 1f8b3a27 Guido Trotter
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
173 1f8b3a27 Guido Trotter
    if not alive:
174 eb58f9b1 Guido Trotter
      return None
175 eb58f9b1 Guido Trotter
176 eb58f9b1 Guido Trotter
    cmdline_file = "/proc/%s/cmdline" % pid
177 eb58f9b1 Guido Trotter
    try:
178 eb58f9b1 Guido Trotter
      fh = open(cmdline_file, 'r')
179 eb58f9b1 Guido Trotter
      try:
180 eb58f9b1 Guido Trotter
        cmdline = fh.read()
181 eb58f9b1 Guido Trotter
      finally:
182 eb58f9b1 Guido Trotter
        fh.close()
183 90c024f6 Guido Trotter
    except EnvironmentError, err:
184 eb58f9b1 Guido Trotter
      raise errors.HypervisorError("Failed to list instance %s: %s" %
185 eb58f9b1 Guido Trotter
                                   (instance_name, err))
186 eb58f9b1 Guido Trotter
187 eb58f9b1 Guido Trotter
    memory = 0
188 eb58f9b1 Guido Trotter
    vcpus = 0
189 eb58f9b1 Guido Trotter
    stat = "---b-"
190 eb58f9b1 Guido Trotter
    times = "0"
191 eb58f9b1 Guido Trotter
192 eb58f9b1 Guido Trotter
    arg_list = cmdline.split('\x00')
193 eb58f9b1 Guido Trotter
    while arg_list:
194 eb58f9b1 Guido Trotter
      arg =  arg_list.pop(0)
195 eb58f9b1 Guido Trotter
      if arg == '-m':
196 eb58f9b1 Guido Trotter
        memory = arg_list.pop(0)
197 eb58f9b1 Guido Trotter
      elif arg == '-smp':
198 eb58f9b1 Guido Trotter
        vcpus = arg_list.pop(0)
199 eb58f9b1 Guido Trotter
200 eb58f9b1 Guido Trotter
    return (instance_name, pid, memory, vcpus, stat, times)
201 eb58f9b1 Guido Trotter
202 eb58f9b1 Guido Trotter
  def GetAllInstancesInfo(self):
203 eb58f9b1 Guido Trotter
    """Get properties of all instances.
204 eb58f9b1 Guido Trotter

205 c41eea6e Iustin Pop
    @return: list of tuples (name, id, memory, vcpus, stat, times)
206 c41eea6e Iustin Pop

207 eb58f9b1 Guido Trotter
    """
208 eb58f9b1 Guido Trotter
    data = []
209 eb58f9b1 Guido Trotter
    for name in os.listdir(self._PIDS_DIR):
210 08137f9e Iustin Pop
      filename = "%s/%s" % (self._PIDS_DIR, name)
211 08137f9e Iustin Pop
      if utils.IsProcessAlive(utils.ReadPidFile(filename)):
212 00ad5362 Guido Trotter
        try:
213 00ad5362 Guido Trotter
          info = self.GetInstanceInfo(name)
214 00ad5362 Guido Trotter
        except errors.HypervisorError, err:
215 00ad5362 Guido Trotter
          continue
216 00ad5362 Guido Trotter
        if info:
217 00ad5362 Guido Trotter
          data.append(info)
218 eb58f9b1 Guido Trotter
219 eb58f9b1 Guido Trotter
    return data
220 eb58f9b1 Guido Trotter
221 ee5f20b0 Guido Trotter
  def _GenerateKVMRuntime(self, instance, block_devices, extra_args):
222 ee5f20b0 Guido Trotter
    """Generate KVM information to start an instance.
223 eb58f9b1 Guido Trotter

224 eb58f9b1 Guido Trotter
    """
225 1f8b3a27 Guido Trotter
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
226 eb58f9b1 Guido Trotter
    kvm = constants.KVM_PATH
227 eb58f9b1 Guido Trotter
    kvm_cmd = [kvm]
228 8b3fd458 Iustin Pop
    kvm_cmd.extend(['-m', instance.beparams[constants.BE_MEMORY]])
229 8b3fd458 Iustin Pop
    kvm_cmd.extend(['-smp', instance.beparams[constants.BE_VCPUS]])
230 eb58f9b1 Guido Trotter
    kvm_cmd.extend(['-pidfile', pidfile])
231 eb58f9b1 Guido Trotter
    # used just by the vnc server, if enabled
232 eb58f9b1 Guido Trotter
    kvm_cmd.extend(['-name', instance.name])
233 eb58f9b1 Guido Trotter
    kvm_cmd.extend(['-daemonize'])
234 6b5605e8 Iustin Pop
    if not instance.hvparams[constants.HV_ACPI]:
235 eb58f9b1 Guido Trotter
      kvm_cmd.extend(['-no-acpi'])
236 eb58f9b1 Guido Trotter
237 66d5dbef Guido Trotter
    boot_disk = (instance.hvparams[constants.HV_BOOT_ORDER] == "disk")
238 66d5dbef Guido Trotter
    boot_cdrom = (instance.hvparams[constants.HV_BOOT_ORDER] == "cdrom")
239 1213604d Guido Trotter
240 1213604d Guido Trotter
    disk_type = instance.hvparams[constants.HV_DISK_TYPE]
241 1213604d Guido Trotter
    if disk_type == constants.HT_DISK_PARAVIRTUAL:
242 1213604d Guido Trotter
      if_val = ',if=virtio'
243 1213604d Guido Trotter
    else:
244 1213604d Guido Trotter
      if_val = ',if=%s' % disk_type
245 069cfbf1 Iustin Pop
    for cfdev, dev_path in block_devices:
246 d34b16d7 Iustin Pop
      if cfdev.mode != constants.DISK_RDWR:
247 d34b16d7 Iustin Pop
        raise errors.HypervisorError("Instance has read-only disks which"
248 d34b16d7 Iustin Pop
                                     " are not supported by KVM")
249 eb58f9b1 Guido Trotter
      # TODO: handle FD_LOOP and FD_BLKTAP (?)
250 66d5dbef Guido Trotter
      if boot_disk:
251 9dd363eb Guido Trotter
        kvm_cmd.extend(['-boot', 'c'])
252 eb58f9b1 Guido Trotter
        boot_val = ',boot=on'
253 66d5dbef Guido Trotter
        boot_disk = False
254 eb58f9b1 Guido Trotter
      else:
255 eb58f9b1 Guido Trotter
        boot_val = ''
256 eb58f9b1 Guido Trotter
257 069cfbf1 Iustin Pop
      drive_val = 'file=%s,format=raw%s%s' % (dev_path, if_val, boot_val)
258 eb58f9b1 Guido Trotter
      kvm_cmd.extend(['-drive', drive_val])
259 eb58f9b1 Guido Trotter
260 66d5dbef Guido Trotter
    iso_image = instance.hvparams[constants.HV_CDROM_IMAGE_PATH]
261 66d5dbef Guido Trotter
    if iso_image:
262 9dd363eb Guido Trotter
      options = ',format=raw,media=cdrom'
263 66d5dbef Guido Trotter
      if boot_cdrom:
264 9dd363eb Guido Trotter
        kvm_cmd.extend(['-boot', 'd'])
265 66d5dbef Guido Trotter
        options = '%s,boot=on' % options
266 9dd363eb Guido Trotter
      else:
267 9dd363eb Guido Trotter
        options = '%s,if=virtio' % options
268 66d5dbef Guido Trotter
      drive_val = 'file=%s%s' % (iso_image, options)
269 66d5dbef Guido Trotter
      kvm_cmd.extend(['-drive', drive_val])
270 66d5dbef Guido Trotter
271 df5ab9f0 Guido Trotter
    kernel_path = instance.hvparams[constants.HV_KERNEL_PATH]
272 df5ab9f0 Guido Trotter
    if kernel_path:
273 df5ab9f0 Guido Trotter
      kvm_cmd.extend(['-kernel', kernel_path])
274 df5ab9f0 Guido Trotter
      initrd_path = instance.hvparams[constants.HV_INITRD_PATH]
275 df5ab9f0 Guido Trotter
      if initrd_path:
276 df5ab9f0 Guido Trotter
        kvm_cmd.extend(['-initrd', initrd_path])
277 df5ab9f0 Guido Trotter
      root_append = 'root=%s ro' % instance.hvparams[constants.HV_ROOT_PATH]
278 df5ab9f0 Guido Trotter
      if instance.hvparams[constants.HV_SERIAL_CONSOLE]:
279 df5ab9f0 Guido Trotter
        kvm_cmd.extend(['-append', 'console=ttyS0,38400 %s' % root_append])
280 df5ab9f0 Guido Trotter
      else:
281 df5ab9f0 Guido Trotter
        kvm_cmd.extend(['-append', root_append])
282 eb58f9b1 Guido Trotter
283 8470c8db Guido Trotter
    # FIXME: handle vnc password
284 8470c8db Guido Trotter
    vnc_bind_address = instance.hvparams[constants.HV_VNC_BIND_ADDRESS]
285 8470c8db Guido Trotter
    if vnc_bind_address:
286 8470c8db Guido Trotter
      kvm_cmd.extend(['-usbdevice', 'tablet'])
287 8447f52b Guido Trotter
      if utils.IsValidIP(vnc_bind_address):
288 377d74c9 Guido Trotter
        if instance.network_port > constants.VNC_BASE_PORT:
289 377d74c9 Guido Trotter
          display = instance.network_port - constants.VNC_BASE_PORT
290 8447f52b Guido Trotter
          if vnc_bind_address == '0.0.0.0':
291 8447f52b Guido Trotter
            vnc_arg = ':%d' % (display)
292 8447f52b Guido Trotter
          else:
293 8447f52b Guido Trotter
            vnc_arg = '%s:%d' % (constants.HV_VNC_BIND_ADDRESS, display)
294 8470c8db Guido Trotter
        else:
295 8447f52b Guido Trotter
          logging.error("Network port is not a valid VNC display (%d < %d)."
296 8447f52b Guido Trotter
                        " Not starting VNC" %
297 8447f52b Guido Trotter
                        (instance.network_port,
298 377d74c9 Guido Trotter
                         constants.VNC_BASE_PORT))
299 8447f52b Guido Trotter
          vnc_arg = 'none'
300 8b2d1013 Guido Trotter
301 8b2d1013 Guido Trotter
        # Only allow tls and other option when not binding to a file, for now.
302 8b2d1013 Guido Trotter
        # kvm/qemu gets confused otherwise about the filename to use.
303 8b2d1013 Guido Trotter
        vnc_append = ''
304 8b2d1013 Guido Trotter
        if instance.hvparams[constants.HV_VNC_TLS]:
305 8b2d1013 Guido Trotter
          vnc_append = '%s,tls' % vnc_append
306 8b2d1013 Guido Trotter
          if instance.hvparams[constants.HV_VNC_X509_VERIFY]:
307 8b2d1013 Guido Trotter
            vnc_append = '%s,x509verify=%s' % (vnc_append,
308 8b2d1013 Guido Trotter
              instance.hvparams[constants.HV_VNC_X509])
309 8b2d1013 Guido Trotter
          elif instance.hvparams[constants.HV_VNC_X509]:
310 8b2d1013 Guido Trotter
            vnc_append = '%s,x509=%s' % (vnc_append,
311 8b2d1013 Guido Trotter
              instance.hvparams[constants.HV_VNC_X509])
312 8b2d1013 Guido Trotter
        vnc_arg = '%s%s' % (vnc_arg, vnc_append)
313 8b2d1013 Guido Trotter
314 8470c8db Guido Trotter
      else:
315 8b2d1013 Guido Trotter
        vnc_arg = 'unix:%s/%s.vnc' % (vnc_bind_address, instance.name)
316 8b2d1013 Guido Trotter
317 8447f52b Guido Trotter
      kvm_cmd.extend(['-vnc', vnc_arg])
318 8470c8db Guido Trotter
    else:
319 8470c8db Guido Trotter
      kvm_cmd.extend(['-nographic'])
320 8470c8db Guido Trotter
321 c4fbefc8 Guido Trotter
    monitor_dev = 'unix:%s,server,nowait' % \
322 c4fbefc8 Guido Trotter
      self._InstanceMonitor(instance.name)
323 eb58f9b1 Guido Trotter
    kvm_cmd.extend(['-monitor', monitor_dev])
324 a2faf9ee Guido Trotter
    if instance.hvparams[constants.HV_SERIAL_CONSOLE]:
325 a2faf9ee Guido Trotter
      serial_dev = 'unix:%s,server,nowait' % self._InstanceSerial(instance.name)
326 a2faf9ee Guido Trotter
      kvm_cmd.extend(['-serial', serial_dev])
327 a2faf9ee Guido Trotter
    else:
328 a2faf9ee Guido Trotter
      kvm_cmd.extend(['-serial', 'none'])
329 eb58f9b1 Guido Trotter
330 ee5f20b0 Guido Trotter
    # Save the current instance nics, but defer their expansion as parameters,
331 ee5f20b0 Guido Trotter
    # as we'll need to generate executable temp files for them.
332 ee5f20b0 Guido Trotter
    kvm_nics = instance.nics
333 c2672466 Guido Trotter
    hvparams = instance.hvparams
334 ee5f20b0 Guido Trotter
335 c2672466 Guido Trotter
    return (kvm_cmd, kvm_nics, hvparams)
336 ee5f20b0 Guido Trotter
337 38e250ba Guido Trotter
  def _WriteKVMRuntime(self, instance_name, data):
338 38e250ba Guido Trotter
    """Write an instance's KVM runtime
339 38e250ba Guido Trotter

340 38e250ba Guido Trotter
    """
341 38e250ba Guido Trotter
    try:
342 38e250ba Guido Trotter
      utils.WriteFile(self._InstanceKVMRuntime(instance_name),
343 38e250ba Guido Trotter
                      data=data)
344 90c024f6 Guido Trotter
    except EnvironmentError, err:
345 38e250ba Guido Trotter
      raise errors.HypervisorError("Failed to save KVM runtime file: %s" % err)
346 38e250ba Guido Trotter
347 38e250ba Guido Trotter
  def _ReadKVMRuntime(self, instance_name):
348 38e250ba Guido Trotter
    """Read an instance's KVM runtime
349 38e250ba Guido Trotter

350 38e250ba Guido Trotter
    """
351 38e250ba Guido Trotter
    try:
352 38e250ba Guido Trotter
      file_content = utils.ReadFile(self._InstanceKVMRuntime(instance_name))
353 90c024f6 Guido Trotter
    except EnvironmentError, err:
354 38e250ba Guido Trotter
      raise errors.HypervisorError("Failed to load KVM runtime file: %s" % err)
355 38e250ba Guido Trotter
    return file_content
356 38e250ba Guido Trotter
357 38e250ba Guido Trotter
  def _SaveKVMRuntime(self, instance, kvm_runtime):
358 38e250ba Guido Trotter
    """Save an instance's KVM runtime
359 38e250ba Guido Trotter

360 38e250ba Guido Trotter
    """
361 c2672466 Guido Trotter
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
362 38e250ba Guido Trotter
    serialized_nics = [nic.ToDict() for nic in kvm_nics]
363 c2672466 Guido Trotter
    serialized_form = serializer.Dump((kvm_cmd, serialized_nics, hvparams))
364 38e250ba Guido Trotter
    self._WriteKVMRuntime(instance.name, serialized_form)
365 38e250ba Guido Trotter
366 30e42c4e Guido Trotter
  def _LoadKVMRuntime(self, instance, serialized_runtime=None):
367 38e250ba Guido Trotter
    """Load an instance's KVM runtime
368 38e250ba Guido Trotter

369 38e250ba Guido Trotter
    """
370 30e42c4e Guido Trotter
    if not serialized_runtime:
371 30e42c4e Guido Trotter
      serialized_runtime = self._ReadKVMRuntime(instance.name)
372 30e42c4e Guido Trotter
    loaded_runtime = serializer.Load(serialized_runtime)
373 c2672466 Guido Trotter
    kvm_cmd, serialized_nics, hvparams = loaded_runtime
374 38e250ba Guido Trotter
    kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics]
375 c2672466 Guido Trotter
    return (kvm_cmd, kvm_nics, hvparams)
376 38e250ba Guido Trotter
377 30e42c4e Guido Trotter
  def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
378 ee5f20b0 Guido Trotter
    """Execute a KVM cmd, after completing it with some last minute data
379 ee5f20b0 Guido Trotter

380 30e42c4e Guido Trotter
    @type incoming: tuple of strings
381 30e42c4e Guido Trotter
    @param incoming: (target_host_ip, port)
382 30e42c4e Guido Trotter

383 ee5f20b0 Guido Trotter
    """
384 1f8b3a27 Guido Trotter
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
385 1f8b3a27 Guido Trotter
    if alive:
386 ee5f20b0 Guido Trotter
      raise errors.HypervisorError("Failed to start instance %s: %s" %
387 ee5f20b0 Guido Trotter
                                   (instance.name, "already running"))
388 ee5f20b0 Guido Trotter
389 ee5f20b0 Guido Trotter
    temp_files = []
390 ee5f20b0 Guido Trotter
391 c2672466 Guido Trotter
    kvm_cmd, kvm_nics, hvparams = kvm_runtime
392 ee5f20b0 Guido Trotter
393 ee5f20b0 Guido Trotter
    if not kvm_nics:
394 ee5f20b0 Guido Trotter
      kvm_cmd.extend(['-net', 'none'])
395 ee5f20b0 Guido Trotter
    else:
396 ee5f20b0 Guido Trotter
      for nic_seq, nic in enumerate(kvm_nics):
397 ee5f20b0 Guido Trotter
        nic_val = "nic,macaddr=%s,model=virtio" % nic.mac
398 ee5f20b0 Guido Trotter
        script = self._WriteNetScript(instance, nic_seq, nic)
399 ee5f20b0 Guido Trotter
        kvm_cmd.extend(['-net', nic_val])
400 ee5f20b0 Guido Trotter
        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
401 ee5f20b0 Guido Trotter
        temp_files.append(script)
402 ee5f20b0 Guido Trotter
403 30e42c4e Guido Trotter
    if incoming:
404 30e42c4e Guido Trotter
      target, port = incoming
405 30e42c4e Guido Trotter
      kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)])
406 30e42c4e Guido Trotter
407 eb58f9b1 Guido Trotter
    result = utils.RunCmd(kvm_cmd)
408 eb58f9b1 Guido Trotter
    if result.failed:
409 eb58f9b1 Guido Trotter
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
410 eb58f9b1 Guido Trotter
                                   (instance.name, result.fail_reason,
411 eb58f9b1 Guido Trotter
                                    result.output))
412 eb58f9b1 Guido Trotter
413 eb58f9b1 Guido Trotter
    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
414 eb58f9b1 Guido Trotter
      raise errors.HypervisorError("Failed to start instance %s: %s" %
415 eb58f9b1 Guido Trotter
                                   (instance.name))
416 eb58f9b1 Guido Trotter
417 08137f9e Iustin Pop
    for filename in temp_files:
418 08137f9e Iustin Pop
      utils.RemoveFile(filename)
419 eb58f9b1 Guido Trotter
420 ee5f20b0 Guido Trotter
  def StartInstance(self, instance, block_devices, extra_args):
421 ee5f20b0 Guido Trotter
    """Start an instance.
422 ee5f20b0 Guido Trotter

423 ee5f20b0 Guido Trotter
    """
424 1f8b3a27 Guido Trotter
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
425 1f8b3a27 Guido Trotter
    if alive:
426 ee5f20b0 Guido Trotter
      raise errors.HypervisorError("Failed to start instance %s: %s" %
427 ee5f20b0 Guido Trotter
                                   (instance.name, "already running"))
428 ee5f20b0 Guido Trotter
429 ee5f20b0 Guido Trotter
    kvm_runtime = self._GenerateKVMRuntime(instance, block_devices, extra_args)
430 38e250ba Guido Trotter
    self._SaveKVMRuntime(instance, kvm_runtime)
431 ee5f20b0 Guido Trotter
    self._ExecuteKVMRuntime(instance, kvm_runtime)
432 ee5f20b0 Guido Trotter
433 6567aff3 Guido Trotter
  def _CallMonitorCommand(self, instance_name, command):
434 6567aff3 Guido Trotter
    """Invoke a command on the instance monitor.
435 6567aff3 Guido Trotter

436 6567aff3 Guido Trotter
    """
437 6567aff3 Guido Trotter
    socat = ("echo %s | %s STDIO UNIX-CONNECT:%s" %
438 6567aff3 Guido Trotter
             (utils.ShellQuote(command),
439 6567aff3 Guido Trotter
              constants.SOCAT_PATH,
440 6567aff3 Guido Trotter
              utils.ShellQuote(self._InstanceMonitor(instance_name))))
441 6567aff3 Guido Trotter
    result = utils.RunCmd(socat)
442 6567aff3 Guido Trotter
    if result.failed:
443 6567aff3 Guido Trotter
      msg = ("Failed to send command '%s' to instance %s."
444 6567aff3 Guido Trotter
             " output: %s, error: %s, fail_reason: %s" %
445 6567aff3 Guido Trotter
             (instance.name, result.stdout, result.stderr, result.fail_reason))
446 6567aff3 Guido Trotter
      raise errors.HypervisorError(msg)
447 6567aff3 Guido Trotter
448 6567aff3 Guido Trotter
    return result
449 6567aff3 Guido Trotter
450 6567aff3 Guido Trotter
  def _RetryInstancePowerdown(self, instance, pid, timeout=30):
451 6567aff3 Guido Trotter
    """Wait for an instance  to power down.
452 6567aff3 Guido Trotter

453 6567aff3 Guido Trotter
    """
454 6567aff3 Guido Trotter
    # Wait up to $timeout seconds
455 6567aff3 Guido Trotter
    end = time.time() + timeout
456 6567aff3 Guido Trotter
    wait = 1
457 6567aff3 Guido Trotter
    while time.time() < end and utils.IsProcessAlive(pid):
458 6567aff3 Guido Trotter
      self._CallMonitorCommand(instance.name, 'system_powerdown')
459 6567aff3 Guido Trotter
      time.sleep(wait)
460 6567aff3 Guido Trotter
      # Make wait time longer for next try
461 6567aff3 Guido Trotter
      if wait < 5:
462 6567aff3 Guido Trotter
        wait *= 1.3
463 6567aff3 Guido Trotter
464 eb58f9b1 Guido Trotter
  def StopInstance(self, instance, force=False):
465 eb58f9b1 Guido Trotter
    """Stop an instance.
466 eb58f9b1 Guido Trotter

467 eb58f9b1 Guido Trotter
    """
468 1f8b3a27 Guido Trotter
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
469 1f8b3a27 Guido Trotter
    if pid > 0 and alive:
470 6b5605e8 Iustin Pop
      if force or not instance.hvparams[constants.HV_ACPI]:
471 eb58f9b1 Guido Trotter
        utils.KillProcess(pid)
472 eb58f9b1 Guido Trotter
      else:
473 6567aff3 Guido Trotter
        self._RetryInstancePowerdown(instance, pid)
474 eb58f9b1 Guido Trotter
475 eb58f9b1 Guido Trotter
    if not utils.IsProcessAlive(pid):
476 1f8b3a27 Guido Trotter
      utils.RemoveFile(pidfile)
477 c4fbefc8 Guido Trotter
      utils.RemoveFile(self._InstanceMonitor(instance.name))
478 c4fbefc8 Guido Trotter
      utils.RemoveFile(self._InstanceSerial(instance.name))
479 38e250ba Guido Trotter
      utils.RemoveFile(self._InstanceKVMRuntime(instance.name))
480 6567aff3 Guido Trotter
      return True
481 6567aff3 Guido Trotter
    else:
482 6567aff3 Guido Trotter
      return False
483 eb58f9b1 Guido Trotter
484 eb58f9b1 Guido Trotter
  def RebootInstance(self, instance):
485 eb58f9b1 Guido Trotter
    """Reboot an instance.
486 eb58f9b1 Guido Trotter

487 eb58f9b1 Guido Trotter
    """
488 eb58f9b1 Guido Trotter
    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
489 eb58f9b1 Guido Trotter
    # socket the instance will stop, but now power up again. So we'll resort
490 eb58f9b1 Guido Trotter
    # to shutdown and restart.
491 1f8b3a27 Guido Trotter
    pidfile, pid, alive = self._InstancePidAlive(instance.name)
492 1f8b3a27 Guido Trotter
    if not alive:
493 1f8b3a27 Guido Trotter
      raise errors.HypervisorError("Failed to reboot instance %s: not running" %
494 1f8b3a27 Guido Trotter
                                             (instance.name))
495 f02881e0 Guido Trotter
    # StopInstance will delete the saved KVM runtime so:
496 f02881e0 Guido Trotter
    # ...first load it...
497 f02881e0 Guido Trotter
    kvm_runtime = self._LoadKVMRuntime(instance)
498 f02881e0 Guido Trotter
    # ...now we can safely call StopInstance...
499 f02881e0 Guido Trotter
    if not self.StopInstance(instance):
500 f02881e0 Guido Trotter
      self.StopInstance(instance, force=True)
501 f02881e0 Guido Trotter
    # ...and finally we can save it again, and execute it...
502 f02881e0 Guido Trotter
    self._SaveKVMRuntime(instance, kvm_runtime)
503 f02881e0 Guido Trotter
    self._ExecuteKVMRuntime(instance, kvm_runtime)
504 eb58f9b1 Guido Trotter
505 30e42c4e Guido Trotter
  def MigrationInfo(self, instance):
506 30e42c4e Guido Trotter
    """Get instance information to perform a migration.
507 30e42c4e Guido Trotter

508 30e42c4e Guido Trotter
    @type instance: L{objects.Instance}
509 30e42c4e Guido Trotter
    @param instance: instance to be migrated
510 30e42c4e Guido Trotter
    @rtype: string
511 30e42c4e Guido Trotter
    @return: content of the KVM runtime file
512 30e42c4e Guido Trotter

513 30e42c4e Guido Trotter
    """
514 30e42c4e Guido Trotter
    return self._ReadKVMRuntime(instance.name)
515 30e42c4e Guido Trotter
516 30e42c4e Guido Trotter
  def AcceptInstance(self, instance, info, target):
517 30e42c4e Guido Trotter
    """Prepare to accept an instance.
518 30e42c4e Guido Trotter

519 30e42c4e Guido Trotter
    @type instance: L{objects.Instance}
520 30e42c4e Guido Trotter
    @param instance: instance to be accepted
521 30e42c4e Guido Trotter
    @type info: string
522 30e42c4e Guido Trotter
    @param info: content of the KVM runtime file on the source node
523 30e42c4e Guido Trotter
    @type target: string
524 30e42c4e Guido Trotter
    @param target: target host (usually ip), on this node
525 30e42c4e Guido Trotter

526 30e42c4e Guido Trotter
    """
527 30e42c4e Guido Trotter
    kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info)
528 30e42c4e Guido Trotter
    incoming_address = (target, constants.KVM_MIGRATION_PORT)
529 30e42c4e Guido Trotter
    self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address)
530 30e42c4e Guido Trotter
531 30e42c4e Guido Trotter
  def FinalizeMigration(self, instance, info, success):
532 30e42c4e Guido Trotter
    """Finalize an instance migration.
533 30e42c4e Guido Trotter

534 30e42c4e Guido Trotter
    Stop the incoming mode KVM.
535 30e42c4e Guido Trotter

536 30e42c4e Guido Trotter
    @type instance: L{objects.Instance}
537 30e42c4e Guido Trotter
    @param instance: instance whose migration is being aborted
538 30e42c4e Guido Trotter

539 30e42c4e Guido Trotter
    """
540 30e42c4e Guido Trotter
    if success:
541 30e42c4e Guido Trotter
      self._WriteKVMRuntime(instance.name, info)
542 30e42c4e Guido Trotter
    else:
543 30e42c4e Guido Trotter
      self.StopInstance(instance, force=True)
544 30e42c4e Guido Trotter
545 30e42c4e Guido Trotter
  def MigrateInstance(self, instance_name, target, live):
546 30e42c4e Guido Trotter
    """Migrate an instance to a target node.
547 30e42c4e Guido Trotter

548 30e42c4e Guido Trotter
    The migration will not be attempted if the instance is not
549 30e42c4e Guido Trotter
    currently running.
550 30e42c4e Guido Trotter

551 30e42c4e Guido Trotter
    @type instance_name: string
552 30e42c4e Guido Trotter
    @param instance_name: name of the instance to be migrated
553 30e42c4e Guido Trotter
    @type target: string
554 30e42c4e Guido Trotter
    @param target: ip address of the target node
555 30e42c4e Guido Trotter
    @type live: boolean
556 30e42c4e Guido Trotter
    @param live: perform a live migration
557 30e42c4e Guido Trotter

558 30e42c4e Guido Trotter
    """
559 30e42c4e Guido Trotter
    pidfile, pid, alive = self._InstancePidAlive(instance_name)
560 30e42c4e Guido Trotter
    if not alive:
561 30e42c4e Guido Trotter
      raise errors.HypervisorError("Instance not running, cannot migrate")
562 30e42c4e Guido Trotter
563 30e42c4e Guido Trotter
    if not live:
564 30e42c4e Guido Trotter
      self._CallMonitorCommand(instance_name, 'stop')
565 30e42c4e Guido Trotter
566 30e42c4e Guido Trotter
    migrate_command = ('migrate -d tcp:%s:%s' %
567 30e42c4e Guido Trotter
                       (target, constants.KVM_MIGRATION_PORT))
568 30e42c4e Guido Trotter
    self._CallMonitorCommand(instance_name, migrate_command)
569 30e42c4e Guido Trotter
570 30e42c4e Guido Trotter
    info_command = 'info migrate'
571 30e42c4e Guido Trotter
    done = False
572 30e42c4e Guido Trotter
    while not done:
573 30e42c4e Guido Trotter
      result = self._CallMonitorCommand(instance_name, info_command)
574 30e42c4e Guido Trotter
      match = self._MIGRATION_STATUS_RE.search(result.stdout)
575 30e42c4e Guido Trotter
      if not match:
576 30e42c4e Guido Trotter
        raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
577 30e42c4e Guido Trotter
                                     result.stdout)
578 30e42c4e Guido Trotter
      else:
579 30e42c4e Guido Trotter
        status = match.group(1)
580 30e42c4e Guido Trotter
        if status == 'completed':
581 30e42c4e Guido Trotter
          done = True
582 30e42c4e Guido Trotter
        elif status == 'active':
583 30e42c4e Guido Trotter
          time.sleep(2)
584 c087266c Guido Trotter
        elif status == 'failed' or status == 'cancelled':
585 c087266c Guido Trotter
          if not live:
586 c087266c Guido Trotter
            self._CallMonitorCommand(instance_name, 'cont')
587 c087266c Guido Trotter
          raise errors.HypervisorError("Migration %s at the kvm level" %
588 c087266c Guido Trotter
                                       status)
589 30e42c4e Guido Trotter
        else:
590 30e42c4e Guido Trotter
          logging.info("KVM: unknown migration status '%s'" % status)
591 30e42c4e Guido Trotter
          time.sleep(2)
592 30e42c4e Guido Trotter
593 30e42c4e Guido Trotter
    utils.KillProcess(pid)
594 30e42c4e Guido Trotter
    utils.RemoveFile(pidfile)
595 30e42c4e Guido Trotter
    utils.RemoveFile(self._InstanceMonitor(instance_name))
596 30e42c4e Guido Trotter
    utils.RemoveFile(self._InstanceSerial(instance_name))
597 30e42c4e Guido Trotter
    utils.RemoveFile(self._InstanceKVMRuntime(instance_name))
598 30e42c4e Guido Trotter
599 eb58f9b1 Guido Trotter
  def GetNodeInfo(self):
600 eb58f9b1 Guido Trotter
    """Return information about the node.
601 eb58f9b1 Guido Trotter

602 c41eea6e Iustin Pop
    @return: a dict with the following keys (values in MiB):
603 c41eea6e Iustin Pop
          - memory_total: the total memory size on the node
604 c41eea6e Iustin Pop
          - memory_free: the available memory on the node for instances
605 c41eea6e Iustin Pop
          - memory_dom0: the memory used by the node itself, if available
606 eb58f9b1 Guido Trotter

607 eb58f9b1 Guido Trotter
    """
608 eb58f9b1 Guido Trotter
    # global ram usage from the xm info command
609 eb58f9b1 Guido Trotter
    # memory                 : 3583
610 eb58f9b1 Guido Trotter
    # free_memory            : 747
611 eb58f9b1 Guido Trotter
    # note: in xen 3, memory has changed to total_memory
612 eb58f9b1 Guido Trotter
    try:
613 eb58f9b1 Guido Trotter
      fh = file("/proc/meminfo")
614 eb58f9b1 Guido Trotter
      try:
615 eb58f9b1 Guido Trotter
        data = fh.readlines()
616 eb58f9b1 Guido Trotter
      finally:
617 eb58f9b1 Guido Trotter
        fh.close()
618 90c024f6 Guido Trotter
    except EnvironmentError, err:
619 eb58f9b1 Guido Trotter
      raise errors.HypervisorError("Failed to list node info: %s" % err)
620 eb58f9b1 Guido Trotter
621 eb58f9b1 Guido Trotter
    result = {}
622 eb58f9b1 Guido Trotter
    sum_free = 0
623 eb58f9b1 Guido Trotter
    for line in data:
624 eb58f9b1 Guido Trotter
      splitfields = line.split(":", 1)
625 eb58f9b1 Guido Trotter
626 eb58f9b1 Guido Trotter
      if len(splitfields) > 1:
627 eb58f9b1 Guido Trotter
        key = splitfields[0].strip()
628 eb58f9b1 Guido Trotter
        val = splitfields[1].strip()
629 eb58f9b1 Guido Trotter
        if key == 'MemTotal':
630 eb58f9b1 Guido Trotter
          result['memory_total'] = int(val.split()[0])/1024
631 eb58f9b1 Guido Trotter
        elif key in ('MemFree', 'Buffers', 'Cached'):
632 eb58f9b1 Guido Trotter
          sum_free += int(val.split()[0])/1024
633 eb58f9b1 Guido Trotter
        elif key == 'Active':
634 eb58f9b1 Guido Trotter
          result['memory_dom0'] = int(val.split()[0])/1024
635 eb58f9b1 Guido Trotter
    result['memory_free'] = sum_free
636 eb58f9b1 Guido Trotter
637 eb58f9b1 Guido Trotter
    cpu_total = 0
638 eb58f9b1 Guido Trotter
    try:
639 eb58f9b1 Guido Trotter
      fh = open("/proc/cpuinfo")
640 eb58f9b1 Guido Trotter
      try:
641 eb58f9b1 Guido Trotter
        cpu_total = len(re.findall("(?m)^processor\s*:\s*[0-9]+\s*$",
642 eb58f9b1 Guido Trotter
                                   fh.read()))
643 eb58f9b1 Guido Trotter
      finally:
644 eb58f9b1 Guido Trotter
        fh.close()
645 eb58f9b1 Guido Trotter
    except EnvironmentError, err:
646 eb58f9b1 Guido Trotter
      raise errors.HypervisorError("Failed to list node info: %s" % err)
647 eb58f9b1 Guido Trotter
    result['cpu_total'] = cpu_total
648 0105bad3 Iustin Pop
    # FIXME: export correct data here
649 0105bad3 Iustin Pop
    result['cpu_nodes'] = 1
650 0105bad3 Iustin Pop
    result['cpu_sockets'] = 1
651 eb58f9b1 Guido Trotter
652 eb58f9b1 Guido Trotter
    return result
653 eb58f9b1 Guido Trotter
654 637ce7f9 Guido Trotter
  @classmethod
655 5431b2e4 Guido Trotter
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
656 eb58f9b1 Guido Trotter
    """Return a command for connecting to the console of an instance.
657 eb58f9b1 Guido Trotter

658 eb58f9b1 Guido Trotter
    """
659 a2faf9ee Guido Trotter
    if hvparams[constants.HV_SERIAL_CONSOLE]:
660 a2faf9ee Guido Trotter
      # FIXME: The socat shell is not perfect. In particular the way we start
661 a2faf9ee Guido Trotter
      # it ctrl+c will close it, rather than being passed to the other end.
662 a2faf9ee Guido Trotter
      # On the other hand if we pass the option 'raw' (or ignbrk=1) there
663 a2faf9ee Guido Trotter
      # will be no way of exiting socat (except killing it from another shell)
664 a2faf9ee Guido Trotter
      # and ctrl+c doesn't work anyway, printing ^C rather than being
665 a2faf9ee Guido Trotter
      # interpreted by kvm. For now we'll leave it this way, which at least
666 a2faf9ee Guido Trotter
      # allows a minimal interaction and changes on the machine.
667 a2faf9ee Guido Trotter
      shell_command = ("%s STDIO,echo=0,icanon=0 UNIX-CONNECT:%s" %
668 a2faf9ee Guido Trotter
                       (constants.SOCAT_PATH,
669 a2faf9ee Guido Trotter
                        utils.ShellQuote(cls._InstanceSerial(instance.name))))
670 a2faf9ee Guido Trotter
    else:
671 a2faf9ee Guido Trotter
      shell_command = "echo 'No serial shell for instance %s'" % instance.name
672 3be34f57 Guido Trotter
673 3be34f57 Guido Trotter
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
674 3be34f57 Guido Trotter
    if vnc_bind_address:
675 377d74c9 Guido Trotter
      if instance.network_port > constants.VNC_BASE_PORT:
676 377d74c9 Guido Trotter
        display = instance.network_port - constants.VNC_BASE_PORT
677 3be34f57 Guido Trotter
        vnc_command = ("echo 'Instance has VNC listening on %s:%d"
678 3be34f57 Guido Trotter
                       " (display: %d)'" % (vnc_bind_address,
679 3be34f57 Guido Trotter
                                            instance.network_port,
680 3be34f57 Guido Trotter
                                            display))
681 3be34f57 Guido Trotter
        shell_command = "%s; %s" % (vnc_command, shell_command)
682 3be34f57 Guido Trotter
683 a2faf9ee Guido Trotter
    return shell_command
684 eb58f9b1 Guido Trotter
685 eb58f9b1 Guido Trotter
  def Verify(self):
686 eb58f9b1 Guido Trotter
    """Verify the hypervisor.
687 eb58f9b1 Guido Trotter

688 eb58f9b1 Guido Trotter
    Check that the binary exists.
689 eb58f9b1 Guido Trotter

690 eb58f9b1 Guido Trotter
    """
691 eb58f9b1 Guido Trotter
    if not os.path.exists(constants.KVM_PATH):
692 eb58f9b1 Guido Trotter
      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
693 14aa53cb Guido Trotter
    if not os.path.exists(constants.SOCAT_PATH):
694 14aa53cb Guido Trotter
      return "The socat binary ('%s') does not exist." % constants.SOCAT_PATH
695 14aa53cb Guido Trotter
696 6b5605e8 Iustin Pop
697 6b5605e8 Iustin Pop
  @classmethod
698 6b5605e8 Iustin Pop
  def CheckParameterSyntax(cls, hvparams):
699 6b5605e8 Iustin Pop
    """Check the given parameters for validity.
700 6b5605e8 Iustin Pop

701 6b5605e8 Iustin Pop
    For the KVM hypervisor, this only check the existence of the
702 6b5605e8 Iustin Pop
    kernel.
703 6b5605e8 Iustin Pop

704 6b5605e8 Iustin Pop
    @type hvparams:  dict
705 6b5605e8 Iustin Pop
    @param hvparams: dictionary with parameter names/value
706 6b5605e8 Iustin Pop
    @raise errors.HypervisorError: when a parameter is not valid
707 6b5605e8 Iustin Pop

708 6b5605e8 Iustin Pop
    """
709 47387b1e Guido Trotter
    super(KVMHypervisor, cls).CheckParameterSyntax(hvparams)
710 6b5605e8 Iustin Pop
711 df5ab9f0 Guido Trotter
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
712 df5ab9f0 Guido Trotter
    if kernel_path:
713 df5ab9f0 Guido Trotter
      if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
714 df5ab9f0 Guido Trotter
        raise errors.HypervisorError("The kernel path must be an absolute path"
715 df5ab9f0 Guido Trotter
                                     ", if defined")
716 6b5605e8 Iustin Pop
717 df5ab9f0 Guido Trotter
      if not hvparams[constants.HV_ROOT_PATH]:
718 df5ab9f0 Guido Trotter
        raise errors.HypervisorError("Need a root partition for the instance"
719 df5ab9f0 Guido Trotter
                                     ", if a kernel is defined")
720 074ca009 Guido Trotter
721 6b5605e8 Iustin Pop
    if hvparams[constants.HV_INITRD_PATH]:
722 6b5605e8 Iustin Pop
      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
723 df5ab9f0 Guido Trotter
        raise errors.HypervisorError("The initrd path must an absolute path"
724 6b5605e8 Iustin Pop
                                     ", if defined")
725 6b5605e8 Iustin Pop
726 56fee73b Guido Trotter
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
727 56fee73b Guido Trotter
    if vnc_bind_address:
728 56fee73b Guido Trotter
      if not utils.IsValidIP(vnc_bind_address):
729 8447f52b Guido Trotter
        if not os.path.isabs(vnc_bind_address):
730 8447f52b Guido Trotter
          raise errors.HypervisorError("The VNC bind address must be either"
731 8447f52b Guido Trotter
                                       " a valid IP address or an absolute"
732 8447f52b Guido Trotter
                                       " pathname. '%s' given" %
733 8447f52b Guido Trotter
                                       vnc_bind_address)
734 56fee73b Guido Trotter
735 8b2d1013 Guido Trotter
    if hvparams[constants.HV_VNC_X509_VERIFY] and \
736 8b2d1013 Guido Trotter
      not hvparams[constants.HV_VNC_X509]:
737 8b2d1013 Guido Trotter
        raise errors.HypervisorError("%s must be defined, if %s is" %
738 8b2d1013 Guido Trotter
                                     (constants.HV_VNC_X509,
739 8b2d1013 Guido Trotter
                                      constants.HV_VNC_X509_VERIFY))
740 8b2d1013 Guido Trotter
741 8b2d1013 Guido Trotter
    if hvparams[constants.HV_VNC_X509]:
742 8b2d1013 Guido Trotter
      if not os.path.isabs(hvparams[constants.HV_VNC_X509]):
743 8b2d1013 Guido Trotter
        raise errors.HypervisorError("The vnc x509 path must an absolute path"
744 8b2d1013 Guido Trotter
                                     ", if defined")
745 8b2d1013 Guido Trotter
746 66d5dbef Guido Trotter
    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
747 66d5dbef Guido Trotter
    if iso_path and not os.path.isabs(iso_path):
748 66d5dbef Guido Trotter
      raise errors.HypervisorError("The path to the CDROM image must be"
749 66d5dbef Guido Trotter
                                   " an absolute path, if defined")
750 66d5dbef Guido Trotter
751 66d5dbef Guido Trotter
    boot_order = hvparams[constants.HV_BOOT_ORDER]
752 66d5dbef Guido Trotter
    if boot_order not in ('cdrom', 'disk'):
753 66d5dbef Guido Trotter
      raise errors.HypervisorError("The boot order must be 'cdrom' or 'disk'")
754 66d5dbef Guido Trotter
755 ec91c05d Guido Trotter
    if boot_order == 'cdrom' and not iso_path:
756 ec91c05d Guido Trotter
      raise errors.HypervisorError("Cannot boot from cdrom without an ISO path")
757 ec91c05d Guido Trotter
758 43440815 Guido Trotter
    nic_type = hvparams[constants.HV_NIC_TYPE]
759 43440815 Guido Trotter
    if nic_type not in constants.HT_KVM_VALID_NIC_TYPES:
760 43440815 Guido Trotter
      raise errors.HypervisorError("Invalid NIC type %s specified for the KVM"
761 43440815 Guido Trotter
                                   " hypervisor. Please choose one of: %s" %
762 43440815 Guido Trotter
                                   (nic_type,
763 43440815 Guido Trotter
                                    constants.HT_KVM_VALID_NIC_TYPES))
764 43440815 Guido Trotter
765 43440815 Guido Trotter
    disk_type = hvparams[constants.HV_DISK_TYPE]
766 43440815 Guido Trotter
    if disk_type not in constants.HT_KVM_VALID_DISK_TYPES:
767 43440815 Guido Trotter
      raise errors.HypervisorError("Invalid disk type %s specified for the KVM"
768 43440815 Guido Trotter
                                   " hypervisor. Please choose one of: %s" %
769 43440815 Guido Trotter
                                   (disk_type,
770 43440815 Guido Trotter
                                    constants.HT_KVM_VALID_DISK_TYPES))
771 43440815 Guido Trotter
772 6b5605e8 Iustin Pop
  def ValidateParameters(self, hvparams):
773 6b5605e8 Iustin Pop
    """Check the given parameters for validity.
774 6b5605e8 Iustin Pop

775 6b5605e8 Iustin Pop
    For the KVM hypervisor, this checks the existence of the
776 6b5605e8 Iustin Pop
    kernel.
777 6b5605e8 Iustin Pop

778 6b5605e8 Iustin Pop
    """
779 47387b1e Guido Trotter
    super(KVMHypervisor, self).ValidateParameters(hvparams)
780 6b5605e8 Iustin Pop
781 6b5605e8 Iustin Pop
    kernel_path = hvparams[constants.HV_KERNEL_PATH]
782 df5ab9f0 Guido Trotter
    if kernel_path and not os.path.isfile(kernel_path):
783 6b5605e8 Iustin Pop
      raise errors.HypervisorError("Instance kernel '%s' not found or"
784 6b5605e8 Iustin Pop
                                   " not a file" % kernel_path)
785 6b5605e8 Iustin Pop
    initrd_path = hvparams[constants.HV_INITRD_PATH]
786 6b5605e8 Iustin Pop
    if initrd_path and not os.path.isfile(initrd_path):
787 6b5605e8 Iustin Pop
      raise errors.HypervisorError("Instance initrd '%s' not found or"
788 6b5605e8 Iustin Pop
                                   " not a file" % initrd_path)
789 8b2d1013 Guido Trotter
790 8b2d1013 Guido Trotter
    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
791 8b2d1013 Guido Trotter
    if vnc_bind_address and not utils.IsValidIP(vnc_bind_address) and \
792 8b2d1013 Guido Trotter
       not os.path.isdir(vnc_bind_address):
793 8b2d1013 Guido Trotter
       raise errors.HypervisorError("Instance vnc bind address must be either"
794 8b2d1013 Guido Trotter
                                    " an ip address or an existing directory")
795 8b2d1013 Guido Trotter
796 8b2d1013 Guido Trotter
    vnc_x509 = hvparams[constants.HV_VNC_X509]
797 8b2d1013 Guido Trotter
    if vnc_x509 and not os.path.isdir(vnc_x509):
798 8b2d1013 Guido Trotter
      raise errors.HypervisorError("Instance vnc x509 path '%s' not found"
799 8b2d1013 Guido Trotter
                                   " or not a directory" % vnc_x509)
800 8b2d1013 Guido Trotter
801 66d5dbef Guido Trotter
    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
802 66d5dbef Guido Trotter
    if iso_path and not os.path.isfile(iso_path):
803 66d5dbef Guido Trotter
      raise errors.HypervisorError("Instance cdrom image '%s' not found or"
804 66d5dbef Guido Trotter
                                   " not a file" % iso_path)