Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_xen.py @ 016d04b3

History | View | Annotate | Download (19.9 kB)

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

24 65a6f9b7 Michael Hanselmann
"""
25 65a6f9b7 Michael Hanselmann
26 65a6f9b7 Michael Hanselmann
import os
27 65a6f9b7 Michael Hanselmann
import os.path
28 65a6f9b7 Michael Hanselmann
import time
29 b48909c8 Iustin Pop
import logging
30 65a6f9b7 Michael Hanselmann
from cStringIO import StringIO
31 65a6f9b7 Michael Hanselmann
32 65a6f9b7 Michael Hanselmann
from ganeti import constants
33 65a6f9b7 Michael Hanselmann
from ganeti import errors
34 65a6f9b7 Michael Hanselmann
from ganeti import utils
35 a2d32034 Michael Hanselmann
from ganeti.hypervisor import hv_base
36 65a6f9b7 Michael Hanselmann
37 65a6f9b7 Michael Hanselmann
38 a2d32034 Michael Hanselmann
class XenHypervisor(hv_base.BaseHypervisor):
39 65a6f9b7 Michael Hanselmann
  """Xen generic hypervisor interface
40 65a6f9b7 Michael Hanselmann

41 65a6f9b7 Michael Hanselmann
  This is the Xen base class used for both Xen PVM and HVM. It contains
42 65a6f9b7 Michael Hanselmann
  all the functionality that is identical for both.
43 65a6f9b7 Michael Hanselmann

44 65a6f9b7 Michael Hanselmann
  """
45 7dd106d3 Iustin Pop
  REBOOT_RETRY_COUNT = 60
46 7dd106d3 Iustin Pop
  REBOOT_RETRY_INTERVAL = 10
47 65a6f9b7 Michael Hanselmann
48 3680f662 Guido Trotter
  ANCILLARY_FILES = [
49 3680f662 Guido Trotter
    '/etc/xen/xend-config.sxp',
50 3680f662 Guido Trotter
    '/etc/xen/scripts/vif-bridge',
51 3680f662 Guido Trotter
    ]
52 3680f662 Guido Trotter
53 5661b908 Iustin Pop
  @classmethod
54 07813a9e Iustin Pop
  def _WriteConfigFile(cls, instance, block_devices):
55 65a6f9b7 Michael Hanselmann
    """Write the Xen config file for the instance.
56 65a6f9b7 Michael Hanselmann

57 65a6f9b7 Michael Hanselmann
    """
58 65a6f9b7 Michael Hanselmann
    raise NotImplementedError
59 65a6f9b7 Michael Hanselmann
60 65a6f9b7 Michael Hanselmann
  @staticmethod
61 4390ccff Guido Trotter
  def _WriteConfigFileStatic(instance_name, data):
62 4390ccff Guido Trotter
    """Write the Xen config file for the instance.
63 4390ccff Guido Trotter

64 4390ccff Guido Trotter
    This version of the function just writes the config file from static data.
65 4390ccff Guido Trotter

66 4390ccff Guido Trotter
    """
67 4390ccff Guido Trotter
    utils.WriteFile("/etc/xen/%s" % instance_name, data=data)
68 4390ccff Guido Trotter
69 4390ccff Guido Trotter
  @staticmethod
70 4390ccff Guido Trotter
  def _ReadConfigFile(instance_name):
71 4390ccff Guido Trotter
    """Returns the contents of the instance config file.
72 4390ccff Guido Trotter

73 4390ccff Guido Trotter
    """
74 4390ccff Guido Trotter
    try:
75 4390ccff Guido Trotter
      file_content = utils.ReadFile("/etc/xen/%s" % instance_name)
76 4390ccff Guido Trotter
    except EnvironmentError, err:
77 4390ccff Guido Trotter
      raise errors.HypervisorError("Failed to load Xen config file: %s" % err)
78 4390ccff Guido Trotter
    return file_content
79 4390ccff Guido Trotter
80 4390ccff Guido Trotter
  @staticmethod
81 53c776b5 Iustin Pop
  def _RemoveConfigFile(instance_name):
82 65a6f9b7 Michael Hanselmann
    """Remove the xen configuration file.
83 65a6f9b7 Michael Hanselmann

84 65a6f9b7 Michael Hanselmann
    """
85 53c776b5 Iustin Pop
    utils.RemoveFile("/etc/xen/%s" % instance_name)
86 65a6f9b7 Michael Hanselmann
87 65a6f9b7 Michael Hanselmann
  @staticmethod
88 65a6f9b7 Michael Hanselmann
  def _GetXMList(include_node):
89 65a6f9b7 Michael Hanselmann
    """Return the list of running instances.
90 65a6f9b7 Michael Hanselmann

91 c41eea6e Iustin Pop
    If the include_node argument is True, then we return information
92 65a6f9b7 Michael Hanselmann
    for dom0 also, otherwise we filter that from the return value.
93 65a6f9b7 Michael Hanselmann

94 c41eea6e Iustin Pop
    @return: list of (name, id, memory, vcpus, state, time spent)
95 65a6f9b7 Michael Hanselmann

96 65a6f9b7 Michael Hanselmann
    """
97 7c4d6c7b Michael Hanselmann
    for _ in range(5):
98 65a6f9b7 Michael Hanselmann
      result = utils.RunCmd(["xm", "list"])
99 65a6f9b7 Michael Hanselmann
      if not result.failed:
100 65a6f9b7 Michael Hanselmann
        break
101 b48909c8 Iustin Pop
      logging.error("xm list failed (%s): %s", result.fail_reason,
102 b48909c8 Iustin Pop
                    result.output)
103 65a6f9b7 Michael Hanselmann
      time.sleep(1)
104 65a6f9b7 Michael Hanselmann
105 65a6f9b7 Michael Hanselmann
    if result.failed:
106 65a6f9b7 Michael Hanselmann
      raise errors.HypervisorError("xm list failed, retries"
107 65a6f9b7 Michael Hanselmann
                                   " exceeded (%s): %s" %
108 3213d3c8 Iustin Pop
                                   (result.fail_reason, result.output))
109 65a6f9b7 Michael Hanselmann
110 65a6f9b7 Michael Hanselmann
    # skip over the heading
111 65a6f9b7 Michael Hanselmann
    lines = result.stdout.splitlines()[1:]
112 65a6f9b7 Michael Hanselmann
    result = []
113 65a6f9b7 Michael Hanselmann
    for line in lines:
114 65a6f9b7 Michael Hanselmann
      # The format of lines is:
115 65a6f9b7 Michael Hanselmann
      # Name      ID Mem(MiB) VCPUs State  Time(s)
116 65a6f9b7 Michael Hanselmann
      # Domain-0   0  3418     4 r-----    266.2
117 65a6f9b7 Michael Hanselmann
      data = line.split()
118 65a6f9b7 Michael Hanselmann
      if len(data) != 6:
119 65a6f9b7 Michael Hanselmann
        raise errors.HypervisorError("Can't parse output of xm list,"
120 65a6f9b7 Michael Hanselmann
                                     " line: %s" % line)
121 65a6f9b7 Michael Hanselmann
      try:
122 65a6f9b7 Michael Hanselmann
        data[1] = int(data[1])
123 65a6f9b7 Michael Hanselmann
        data[2] = int(data[2])
124 65a6f9b7 Michael Hanselmann
        data[3] = int(data[3])
125 65a6f9b7 Michael Hanselmann
        data[5] = float(data[5])
126 65a6f9b7 Michael Hanselmann
      except ValueError, err:
127 65a6f9b7 Michael Hanselmann
        raise errors.HypervisorError("Can't parse output of xm list,"
128 65a6f9b7 Michael Hanselmann
                                     " line: %s, error: %s" % (line, err))
129 65a6f9b7 Michael Hanselmann
130 65a6f9b7 Michael Hanselmann
      # skip the Domain-0 (optional)
131 65a6f9b7 Michael Hanselmann
      if include_node or data[0] != 'Domain-0':
132 65a6f9b7 Michael Hanselmann
        result.append(data)
133 65a6f9b7 Michael Hanselmann
134 65a6f9b7 Michael Hanselmann
    return result
135 65a6f9b7 Michael Hanselmann
136 65a6f9b7 Michael Hanselmann
  def ListInstances(self):
137 65a6f9b7 Michael Hanselmann
    """Get the list of running instances.
138 65a6f9b7 Michael Hanselmann

139 65a6f9b7 Michael Hanselmann
    """
140 65a6f9b7 Michael Hanselmann
    xm_list = self._GetXMList(False)
141 65a6f9b7 Michael Hanselmann
    names = [info[0] for info in xm_list]
142 65a6f9b7 Michael Hanselmann
    return names
143 65a6f9b7 Michael Hanselmann
144 65a6f9b7 Michael Hanselmann
  def GetInstanceInfo(self, instance_name):
145 65a6f9b7 Michael Hanselmann
    """Get instance properties.
146 65a6f9b7 Michael Hanselmann

147 c41eea6e Iustin Pop
    @param instance_name: the instance name
148 c41eea6e Iustin Pop

149 c41eea6e Iustin Pop
    @return: tuple (name, id, memory, vcpus, stat, times)
150 65a6f9b7 Michael Hanselmann

151 65a6f9b7 Michael Hanselmann
    """
152 65a6f9b7 Michael Hanselmann
    xm_list = self._GetXMList(instance_name=="Domain-0")
153 65a6f9b7 Michael Hanselmann
    result = None
154 65a6f9b7 Michael Hanselmann
    for data in xm_list:
155 65a6f9b7 Michael Hanselmann
      if data[0] == instance_name:
156 65a6f9b7 Michael Hanselmann
        result = data
157 65a6f9b7 Michael Hanselmann
        break
158 65a6f9b7 Michael Hanselmann
    return result
159 65a6f9b7 Michael Hanselmann
160 65a6f9b7 Michael Hanselmann
  def GetAllInstancesInfo(self):
161 65a6f9b7 Michael Hanselmann
    """Get properties of all instances.
162 65a6f9b7 Michael Hanselmann

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

165 65a6f9b7 Michael Hanselmann
    """
166 65a6f9b7 Michael Hanselmann
    xm_list = self._GetXMList(False)
167 65a6f9b7 Michael Hanselmann
    return xm_list
168 65a6f9b7 Michael Hanselmann
169 07813a9e Iustin Pop
  def StartInstance(self, instance, block_devices):
170 c41eea6e Iustin Pop
    """Start an instance.
171 c41eea6e Iustin Pop

172 c41eea6e Iustin Pop
    """
173 07813a9e Iustin Pop
    self._WriteConfigFile(instance, block_devices)
174 65a6f9b7 Michael Hanselmann
    result = utils.RunCmd(["xm", "create", instance.name])
175 65a6f9b7 Michael Hanselmann
176 65a6f9b7 Michael Hanselmann
    if result.failed:
177 65a6f9b7 Michael Hanselmann
      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
178 65a6f9b7 Michael Hanselmann
                                   (instance.name, result.fail_reason,
179 65a6f9b7 Michael Hanselmann
                                    result.output))
180 65a6f9b7 Michael Hanselmann
181 65a6f9b7 Michael Hanselmann
  def StopInstance(self, instance, force=False):
182 c41eea6e Iustin Pop
    """Stop an instance.
183 c41eea6e Iustin Pop

184 c41eea6e Iustin Pop
    """
185 53c776b5 Iustin Pop
    self._RemoveConfigFile(instance.name)
186 65a6f9b7 Michael Hanselmann
    if force:
187 65a6f9b7 Michael Hanselmann
      command = ["xm", "destroy", instance.name]
188 65a6f9b7 Michael Hanselmann
    else:
189 65a6f9b7 Michael Hanselmann
      command = ["xm", "shutdown", instance.name]
190 65a6f9b7 Michael Hanselmann
    result = utils.RunCmd(command)
191 65a6f9b7 Michael Hanselmann
192 65a6f9b7 Michael Hanselmann
    if result.failed:
193 3213d3c8 Iustin Pop
      raise errors.HypervisorError("Failed to stop instance %s: %s, %s" %
194 3213d3c8 Iustin Pop
                                   (instance.name, result.fail_reason,
195 3213d3c8 Iustin Pop
                                    result.output))
196 65a6f9b7 Michael Hanselmann
197 65a6f9b7 Michael Hanselmann
  def RebootInstance(self, instance):
198 c41eea6e Iustin Pop
    """Reboot an instance.
199 c41eea6e Iustin Pop

200 c41eea6e Iustin Pop
    """
201 7dd106d3 Iustin Pop
    ini_info = self.GetInstanceInfo(instance.name)
202 65a6f9b7 Michael Hanselmann
    result = utils.RunCmd(["xm", "reboot", instance.name])
203 65a6f9b7 Michael Hanselmann
204 65a6f9b7 Michael Hanselmann
    if result.failed:
205 3213d3c8 Iustin Pop
      raise errors.HypervisorError("Failed to reboot instance %s: %s, %s" %
206 3213d3c8 Iustin Pop
                                   (instance.name, result.fail_reason,
207 3213d3c8 Iustin Pop
                                    result.output))
208 7dd106d3 Iustin Pop
    done = False
209 7dd106d3 Iustin Pop
    retries = self.REBOOT_RETRY_COUNT
210 7dd106d3 Iustin Pop
    while retries > 0:
211 7dd106d3 Iustin Pop
      new_info = self.GetInstanceInfo(instance.name)
212 7dd106d3 Iustin Pop
      # check if the domain ID has changed or the run time has
213 7dd106d3 Iustin Pop
      # decreased
214 7dd106d3 Iustin Pop
      if new_info[1] != ini_info[1] or new_info[5] < ini_info[5]:
215 7dd106d3 Iustin Pop
        done = True
216 7dd106d3 Iustin Pop
        break
217 7dd106d3 Iustin Pop
      time.sleep(self.REBOOT_RETRY_INTERVAL)
218 7dd106d3 Iustin Pop
      retries -= 1
219 7dd106d3 Iustin Pop
220 7dd106d3 Iustin Pop
    if not done:
221 7dd106d3 Iustin Pop
      raise errors.HypervisorError("Failed to reboot instance %s: instance"
222 7dd106d3 Iustin Pop
                                   " did not reboot in the expected interval" %
223 7dd106d3 Iustin Pop
                                   (instance.name, ))
224 65a6f9b7 Michael Hanselmann
225 65a6f9b7 Michael Hanselmann
  def GetNodeInfo(self):
226 65a6f9b7 Michael Hanselmann
    """Return information about the node.
227 65a6f9b7 Michael Hanselmann

228 0105bad3 Iustin Pop
    @return: a dict with the following keys (memory values in MiB):
229 c41eea6e Iustin Pop
          - memory_total: the total memory size on the node
230 c41eea6e Iustin Pop
          - memory_free: the available memory on the node for instances
231 c41eea6e Iustin Pop
          - memory_dom0: the memory used by the node itself, if available
232 0105bad3 Iustin Pop
          - nr_cpus: total number of CPUs
233 0105bad3 Iustin Pop
          - nr_nodes: in a NUMA system, the number of domains
234 0105bad3 Iustin Pop
          - nr_sockets: the number of physical CPU sockets in the node
235 65a6f9b7 Michael Hanselmann

236 65a6f9b7 Michael Hanselmann
    """
237 65a6f9b7 Michael Hanselmann
    # note: in xen 3, memory has changed to total_memory
238 65a6f9b7 Michael Hanselmann
    result = utils.RunCmd(["xm", "info"])
239 65a6f9b7 Michael Hanselmann
    if result.failed:
240 b48909c8 Iustin Pop
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
241 b48909c8 Iustin Pop
                    result.output)
242 65a6f9b7 Michael Hanselmann
      return None
243 65a6f9b7 Michael Hanselmann
244 65a6f9b7 Michael Hanselmann
    xmoutput = result.stdout.splitlines()
245 65a6f9b7 Michael Hanselmann
    result = {}
246 0105bad3 Iustin Pop
    cores_per_socket = threads_per_core = nr_cpus = None
247 65a6f9b7 Michael Hanselmann
    for line in xmoutput:
248 65a6f9b7 Michael Hanselmann
      splitfields = line.split(":", 1)
249 65a6f9b7 Michael Hanselmann
250 65a6f9b7 Michael Hanselmann
      if len(splitfields) > 1:
251 65a6f9b7 Michael Hanselmann
        key = splitfields[0].strip()
252 65a6f9b7 Michael Hanselmann
        val = splitfields[1].strip()
253 65a6f9b7 Michael Hanselmann
        if key == 'memory' or key == 'total_memory':
254 65a6f9b7 Michael Hanselmann
          result['memory_total'] = int(val)
255 65a6f9b7 Michael Hanselmann
        elif key == 'free_memory':
256 65a6f9b7 Michael Hanselmann
          result['memory_free'] = int(val)
257 e8a4c138 Iustin Pop
        elif key == 'nr_cpus':
258 0105bad3 Iustin Pop
          nr_cpus = result['cpu_total'] = int(val)
259 0105bad3 Iustin Pop
        elif key == 'nr_nodes':
260 0105bad3 Iustin Pop
          result['cpu_nodes'] = int(val)
261 0105bad3 Iustin Pop
        elif key == 'cores_per_socket':
262 0105bad3 Iustin Pop
          cores_per_socket = int(val)
263 0105bad3 Iustin Pop
        elif key == 'threads_per_core':
264 0105bad3 Iustin Pop
          threads_per_core = int(val)
265 0105bad3 Iustin Pop
266 0105bad3 Iustin Pop
    if (cores_per_socket is not None and
267 0105bad3 Iustin Pop
        threads_per_core is not None and nr_cpus is not None):
268 0105bad3 Iustin Pop
      result['cpu_sockets'] = nr_cpus / (cores_per_socket * threads_per_core)
269 0105bad3 Iustin Pop
270 65a6f9b7 Michael Hanselmann
    dom0_info = self.GetInstanceInfo("Domain-0")
271 65a6f9b7 Michael Hanselmann
    if dom0_info is not None:
272 65a6f9b7 Michael Hanselmann
      result['memory_dom0'] = dom0_info[2]
273 65a6f9b7 Michael Hanselmann
274 65a6f9b7 Michael Hanselmann
    return result
275 65a6f9b7 Michael Hanselmann
276 637ce7f9 Guido Trotter
  @classmethod
277 5431b2e4 Guido Trotter
  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
278 65a6f9b7 Michael Hanselmann
    """Return a command for connecting to the console of an instance.
279 65a6f9b7 Michael Hanselmann

280 65a6f9b7 Michael Hanselmann
    """
281 04c4330c Alexander Schreiber
    return "xm console %s" % instance.name
282 04c4330c Alexander Schreiber
283 65a6f9b7 Michael Hanselmann
284 65a6f9b7 Michael Hanselmann
  def Verify(self):
285 65a6f9b7 Michael Hanselmann
    """Verify the hypervisor.
286 65a6f9b7 Michael Hanselmann

287 65a6f9b7 Michael Hanselmann
    For Xen, this verifies that the xend process is running.
288 65a6f9b7 Michael Hanselmann

289 65a6f9b7 Michael Hanselmann
    """
290 e3e66f02 Michael Hanselmann
    result = utils.RunCmd(["xm", "info"])
291 e3e66f02 Michael Hanselmann
    if result.failed:
292 3213d3c8 Iustin Pop
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
293 65a6f9b7 Michael Hanselmann
294 65a6f9b7 Michael Hanselmann
  @staticmethod
295 65a6f9b7 Michael Hanselmann
  def _GetConfigFileDiskData(disk_template, block_devices):
296 65a6f9b7 Michael Hanselmann
    """Get disk directive for xen config file.
297 65a6f9b7 Michael Hanselmann

298 65a6f9b7 Michael Hanselmann
    This method builds the xen config disk directive according to the
299 65a6f9b7 Michael Hanselmann
    given disk_template and block_devices.
300 65a6f9b7 Michael Hanselmann

301 c41eea6e Iustin Pop
    @param disk_template: string containing instance disk template
302 c41eea6e Iustin Pop
    @param block_devices: list of tuples (cfdev, rldev):
303 c41eea6e Iustin Pop
        - cfdev: dict containing ganeti config disk part
304 c41eea6e Iustin Pop
        - rldev: ganeti.bdev.BlockDev object
305 65a6f9b7 Michael Hanselmann

306 c41eea6e Iustin Pop
    @return: string containing disk directive for xen instance config file
307 65a6f9b7 Michael Hanselmann

308 65a6f9b7 Michael Hanselmann
    """
309 65a6f9b7 Michael Hanselmann
    FILE_DRIVER_MAP = {
310 65a6f9b7 Michael Hanselmann
      constants.FD_LOOP: "file",
311 65a6f9b7 Michael Hanselmann
      constants.FD_BLKTAP: "tap:aio",
312 65a6f9b7 Michael Hanselmann
      }
313 65a6f9b7 Michael Hanselmann
    disk_data = []
314 2864f2d9 Iustin Pop
    if len(block_devices) > 24:
315 2864f2d9 Iustin Pop
      # 'z' - 'a' = 24
316 2864f2d9 Iustin Pop
      raise errors.HypervisorError("Too many disks")
317 2864f2d9 Iustin Pop
    # FIXME: instead of this hardcoding here, each of PVM/HVM should
318 2864f2d9 Iustin Pop
    # directly export their info (currently HVM will just sed this info)
319 2864f2d9 Iustin Pop
    namespace = ["sd" + chr(i + ord('a')) for i in range(24)]
320 069cfbf1 Iustin Pop
    for sd_name, (cfdev, dev_path) in zip(namespace, block_devices):
321 d34b16d7 Iustin Pop
      if cfdev.mode == constants.DISK_RDWR:
322 d34b16d7 Iustin Pop
        mode = "w"
323 d34b16d7 Iustin Pop
      else:
324 d34b16d7 Iustin Pop
        mode = "r"
325 65a6f9b7 Michael Hanselmann
      if cfdev.dev_type == constants.LD_FILE:
326 d34b16d7 Iustin Pop
        line = "'%s:%s,%s,%s'" % (FILE_DRIVER_MAP[cfdev.physical_id[0]],
327 d34b16d7 Iustin Pop
                                  dev_path, sd_name, mode)
328 65a6f9b7 Michael Hanselmann
      else:
329 d34b16d7 Iustin Pop
        line = "'phy:%s,%s,%s'" % (dev_path, sd_name, mode)
330 65a6f9b7 Michael Hanselmann
      disk_data.append(line)
331 65a6f9b7 Michael Hanselmann
332 65a6f9b7 Michael Hanselmann
    return disk_data
333 65a6f9b7 Michael Hanselmann
334 4390ccff Guido Trotter
  def MigrationInfo(self, instance):
335 4390ccff Guido Trotter
    """Get instance information to perform a migration.
336 4390ccff Guido Trotter

337 4390ccff Guido Trotter
    @type instance: L{objects.Instance}
338 4390ccff Guido Trotter
    @param instance: instance to be migrated
339 4390ccff Guido Trotter
    @rtype: string
340 4390ccff Guido Trotter
    @return: content of the xen config file
341 4390ccff Guido Trotter

342 4390ccff Guido Trotter
    """
343 4390ccff Guido Trotter
    return self._ReadConfigFile(instance.name)
344 4390ccff Guido Trotter
345 4390ccff Guido Trotter
  def AcceptInstance(self, instance, info, target):
346 4390ccff Guido Trotter
    """Prepare to accept an instance.
347 4390ccff Guido Trotter

348 4390ccff Guido Trotter
    @type instance: L{objects.Instance}
349 4390ccff Guido Trotter
    @param instance: instance to be accepted
350 4390ccff Guido Trotter
    @type info: string
351 4390ccff Guido Trotter
    @param info: content of the xen config file on the source node
352 4390ccff Guido Trotter
    @type target: string
353 4390ccff Guido Trotter
    @param target: target host (usually ip), on this node
354 4390ccff Guido Trotter

355 4390ccff Guido Trotter
    """
356 4390ccff Guido Trotter
    pass
357 4390ccff Guido Trotter
358 4390ccff Guido Trotter
  def FinalizeMigration(self, instance, info, success):
359 4390ccff Guido Trotter
    """Finalize an instance migration.
360 4390ccff Guido Trotter

361 4390ccff Guido Trotter
    After a successful migration we write the xen config file.
362 4390ccff Guido Trotter
    We do nothing on a failure, as we did not change anything at accept time.
363 4390ccff Guido Trotter

364 4390ccff Guido Trotter
    @type instance: L{objects.Instance}
365 4390ccff Guido Trotter
    @param instance: instance whose migration is being aborted
366 4390ccff Guido Trotter
    @type info: string
367 4390ccff Guido Trotter
    @param info: content of the xen config file on the source node
368 4390ccff Guido Trotter
    @type success: boolean
369 4390ccff Guido Trotter
    @param success: whether the migration was a success or a failure
370 4390ccff Guido Trotter

371 4390ccff Guido Trotter
    """
372 4390ccff Guido Trotter
    if success:
373 4390ccff Guido Trotter
      self._WriteConfigFileStatic(instance.name, info)
374 4390ccff Guido Trotter
375 6e7275c0 Iustin Pop
  def MigrateInstance(self, instance, target, live):
376 6e7275c0 Iustin Pop
    """Migrate an instance to a target node.
377 6e7275c0 Iustin Pop

378 6e7275c0 Iustin Pop
    The migration will not be attempted if the instance is not
379 6e7275c0 Iustin Pop
    currently running.
380 6e7275c0 Iustin Pop

381 fdf7f055 Guido Trotter
    @type instance: string
382 fdf7f055 Guido Trotter
    @param instance: instance name
383 fdf7f055 Guido Trotter
    @type target: string
384 fdf7f055 Guido Trotter
    @param target: ip address of the target node
385 fdf7f055 Guido Trotter
    @type live: boolean
386 fdf7f055 Guido Trotter
    @param live: perform a live migration
387 fdf7f055 Guido Trotter

388 6e7275c0 Iustin Pop
    """
389 6e7275c0 Iustin Pop
    if self.GetInstanceInfo(instance) is None:
390 6e7275c0 Iustin Pop
      raise errors.HypervisorError("Instance not running, cannot migrate")
391 6e7275c0 Iustin Pop
    args = ["xm", "migrate"]
392 6e7275c0 Iustin Pop
    if live:
393 6e7275c0 Iustin Pop
      args.append("-l")
394 6e7275c0 Iustin Pop
    args.extend([instance, target])
395 6e7275c0 Iustin Pop
    result = utils.RunCmd(args)
396 6e7275c0 Iustin Pop
    if result.failed:
397 6e7275c0 Iustin Pop
      raise errors.HypervisorError("Failed to migrate instance %s: %s" %
398 6e7275c0 Iustin Pop
                                   (instance, result.output))
399 53c776b5 Iustin Pop
    # remove old xen file after migration succeeded
400 53c776b5 Iustin Pop
    try:
401 53c776b5 Iustin Pop
      self._RemoveConfigFile(instance)
402 c979d253 Iustin Pop
    except EnvironmentError:
403 c979d253 Iustin Pop
      logging.exception("Failure while removing instance config file")
404 6e7275c0 Iustin Pop
405 f5118ade Iustin Pop
  @classmethod
406 f5118ade Iustin Pop
  def PowercycleNode(cls):
407 f5118ade Iustin Pop
    """Xen-specific powercycle.
408 f5118ade Iustin Pop

409 f5118ade Iustin Pop
    This first does a Linux reboot (which triggers automatically a Xen
410 f5118ade Iustin Pop
    reboot), and if that fails it tries to do a Xen reboot. The reason
411 f5118ade Iustin Pop
    we don't try a Xen reboot first is that the xen reboot launches an
412 f5118ade Iustin Pop
    external command which connects to the Xen hypervisor, and that
413 f5118ade Iustin Pop
    won't work in case the root filesystem is broken and/or the xend
414 f5118ade Iustin Pop
    daemon is not working.
415 f5118ade Iustin Pop

416 f5118ade Iustin Pop
    """
417 f5118ade Iustin Pop
    try:
418 f5118ade Iustin Pop
      cls.LinuxPowercycle()
419 f5118ade Iustin Pop
    finally:
420 f5118ade Iustin Pop
      utils.RunCmd(["xm", "debug", "R"])
421 f5118ade Iustin Pop
422 65a6f9b7 Michael Hanselmann
423 65a6f9b7 Michael Hanselmann
class XenPvmHypervisor(XenHypervisor):
424 65a6f9b7 Michael Hanselmann
  """Xen PVM hypervisor interface"""
425 65a6f9b7 Michael Hanselmann
426 205ab586 Iustin Pop
  PARAMETERS = {
427 205ab586 Iustin Pop
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
428 205ab586 Iustin Pop
    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
429 205ab586 Iustin Pop
    constants.HV_ROOT_PATH: hv_base.REQUIRED_CHECK,
430 205ab586 Iustin Pop
    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
431 205ab586 Iustin Pop
    }
432 f48148c3 Iustin Pop
433 65a6f9b7 Michael Hanselmann
  @classmethod
434 07813a9e Iustin Pop
  def _WriteConfigFile(cls, instance, block_devices):
435 65a6f9b7 Michael Hanselmann
    """Write the Xen config file for the instance.
436 65a6f9b7 Michael Hanselmann

437 65a6f9b7 Michael Hanselmann
    """
438 a985b417 Iustin Pop
    hvp = instance.hvparams
439 65a6f9b7 Michael Hanselmann
    config = StringIO()
440 65a6f9b7 Michael Hanselmann
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
441 65a6f9b7 Michael Hanselmann
442 65a6f9b7 Michael Hanselmann
    # kernel handling
443 a985b417 Iustin Pop
    kpath = hvp[constants.HV_KERNEL_PATH]
444 65a6f9b7 Michael Hanselmann
    config.write("kernel = '%s'\n" % kpath)
445 65a6f9b7 Michael Hanselmann
446 65a6f9b7 Michael Hanselmann
    # initrd handling
447 a985b417 Iustin Pop
    initrd_path = hvp[constants.HV_INITRD_PATH]
448 65a6f9b7 Michael Hanselmann
    if initrd_path:
449 65a6f9b7 Michael Hanselmann
      config.write("ramdisk = '%s'\n" % initrd_path)
450 65a6f9b7 Michael Hanselmann
451 65a6f9b7 Michael Hanselmann
    # rest of the settings
452 8b3fd458 Iustin Pop
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
453 8b3fd458 Iustin Pop
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
454 65a6f9b7 Michael Hanselmann
    config.write("name = '%s'\n" % instance.name)
455 65a6f9b7 Michael Hanselmann
456 65a6f9b7 Michael Hanselmann
    vif_data = []
457 65a6f9b7 Michael Hanselmann
    for nic in instance.nics:
458 503b97a9 Guido Trotter
      nic_str = "mac=%s" % (nic.mac)
459 65a6f9b7 Michael Hanselmann
      ip = getattr(nic, "ip", None)
460 65a6f9b7 Michael Hanselmann
      if ip is not None:
461 65a6f9b7 Michael Hanselmann
        nic_str += ", ip=%s" % ip
462 65a6f9b7 Michael Hanselmann
      vif_data.append("'%s'" % nic_str)
463 503b97a9 Guido Trotter
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
464 503b97a9 Guido Trotter
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
465 65a6f9b7 Michael Hanselmann
466 65a6f9b7 Michael Hanselmann
    config.write("vif = [%s]\n" % ",".join(vif_data))
467 65a6f9b7 Michael Hanselmann
    config.write("disk = [%s]\n" % ",".join(
468 65a6f9b7 Michael Hanselmann
                 cls._GetConfigFileDiskData(instance.disk_template,
469 65a6f9b7 Michael Hanselmann
                                            block_devices)))
470 074ca009 Guido Trotter
471 07813a9e Iustin Pop
    config.write("root = '%s'\n" % hvp[constants.HV_ROOT_PATH])
472 65a6f9b7 Michael Hanselmann
    config.write("on_poweroff = 'destroy'\n")
473 65a6f9b7 Michael Hanselmann
    config.write("on_reboot = 'restart'\n")
474 65a6f9b7 Michael Hanselmann
    config.write("on_crash = 'restart'\n")
475 07813a9e Iustin Pop
    config.write("extra = '%s'\n" % hvp[constants.HV_KERNEL_ARGS])
476 65a6f9b7 Michael Hanselmann
    # just in case it exists
477 65a6f9b7 Michael Hanselmann
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
478 65a6f9b7 Michael Hanselmann
    try:
479 a985b417 Iustin Pop
      utils.WriteFile("/etc/xen/%s" % instance.name, data=config.getvalue())
480 73cd67f4 Guido Trotter
    except EnvironmentError, err:
481 73cd67f4 Guido Trotter
      raise errors.HypervisorError("Cannot write Xen instance confile"
482 73cd67f4 Guido Trotter
                                   " file /etc/xen/%s: %s" %
483 73cd67f4 Guido Trotter
                                   (instance.name, err))
484 73cd67f4 Guido Trotter
485 65a6f9b7 Michael Hanselmann
    return True
486 65a6f9b7 Michael Hanselmann
487 65a6f9b7 Michael Hanselmann
488 65a6f9b7 Michael Hanselmann
class XenHvmHypervisor(XenHypervisor):
489 65a6f9b7 Michael Hanselmann
  """Xen HVM hypervisor interface"""
490 65a6f9b7 Michael Hanselmann
491 3680f662 Guido Trotter
  ANCILLARY_FILES = XenHypervisor.ANCILLARY_FILES + \
492 3680f662 Guido Trotter
    [constants.VNC_PASSWORD_FILE]
493 3680f662 Guido Trotter
494 205ab586 Iustin Pop
  PARAMETERS = {
495 205ab586 Iustin Pop
    constants.HV_ACPI: hv_base.NO_CHECK,
496 016d04b3 Michael Hanselmann
    constants.HV_BOOT_ORDER: (True, ) +
497 016d04b3 Michael Hanselmann
      (lambda x: x and len(x.strip("acdn")) == 0,
498 016d04b3 Michael Hanselmann
       "Invalid boot order specified, must be one or more of [acdn]",
499 016d04b3 Michael Hanselmann
       None, None),
500 205ab586 Iustin Pop
    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
501 016d04b3 Michael Hanselmann
    constants.HV_DISK_TYPE:
502 016d04b3 Michael Hanselmann
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_DISK_TYPES),
503 016d04b3 Michael Hanselmann
    constants.HV_NIC_TYPE:
504 016d04b3 Michael Hanselmann
      hv_base.ParamInSet(True, constants.HT_HVM_VALID_NIC_TYPES),
505 205ab586 Iustin Pop
    constants.HV_PAE: hv_base.NO_CHECK,
506 016d04b3 Michael Hanselmann
    constants.HV_VNC_BIND_ADDRESS:
507 016d04b3 Michael Hanselmann
      (False, utils.IsValidIP,
508 016d04b3 Michael Hanselmann
       "VNC bind address is not a valid IP address", None, None),
509 205ab586 Iustin Pop
    constants.HV_KERNEL_PATH: hv_base.REQ_FILE_CHECK,
510 205ab586 Iustin Pop
    constants.HV_DEVICE_MODEL: hv_base.REQ_FILE_CHECK,
511 205ab586 Iustin Pop
    }
512 09ea8710 Iustin Pop
513 65a6f9b7 Michael Hanselmann
  @classmethod
514 07813a9e Iustin Pop
  def _WriteConfigFile(cls, instance, block_devices):
515 65a6f9b7 Michael Hanselmann
    """Create a Xen 3.1 HVM config file.
516 65a6f9b7 Michael Hanselmann

517 65a6f9b7 Michael Hanselmann
    """
518 a985b417 Iustin Pop
    hvp = instance.hvparams
519 a985b417 Iustin Pop
520 65a6f9b7 Michael Hanselmann
    config = StringIO()
521 65a6f9b7 Michael Hanselmann
    config.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
522 e2ee1cea Iustin Pop
523 e2ee1cea Iustin Pop
    # kernel handling
524 e2ee1cea Iustin Pop
    kpath = hvp[constants.HV_KERNEL_PATH]
525 e2ee1cea Iustin Pop
    config.write("kernel = '%s'\n" % kpath)
526 e2ee1cea Iustin Pop
527 65a6f9b7 Michael Hanselmann
    config.write("builder = 'hvm'\n")
528 8b3fd458 Iustin Pop
    config.write("memory = %d\n" % instance.beparams[constants.BE_MEMORY])
529 8b3fd458 Iustin Pop
    config.write("vcpus = %d\n" % instance.beparams[constants.BE_VCPUS])
530 65a6f9b7 Michael Hanselmann
    config.write("name = '%s'\n" % instance.name)
531 09ea8710 Iustin Pop
    if hvp[constants.HV_PAE]:
532 a21dda8b Iustin Pop
      config.write("pae = 1\n")
533 a21dda8b Iustin Pop
    else:
534 a21dda8b Iustin Pop
      config.write("pae = 0\n")
535 09ea8710 Iustin Pop
    if hvp[constants.HV_ACPI]:
536 a21dda8b Iustin Pop
      config.write("acpi = 1\n")
537 a21dda8b Iustin Pop
    else:
538 a21dda8b Iustin Pop
      config.write("acpi = 0\n")
539 65a6f9b7 Michael Hanselmann
    config.write("apic = 1\n")
540 09ea8710 Iustin Pop
    config.write("device_model = '%s'\n" % hvp[constants.HV_DEVICE_MODEL])
541 a985b417 Iustin Pop
    config.write("boot = '%s'\n" % hvp[constants.HV_BOOT_ORDER])
542 65a6f9b7 Michael Hanselmann
    config.write("sdl = 0\n")
543 97efde45 Guido Trotter
    config.write("usb = 1\n")
544 97efde45 Guido Trotter
    config.write("usbdevice = 'tablet'\n")
545 65a6f9b7 Michael Hanselmann
    config.write("vnc = 1\n")
546 a985b417 Iustin Pop
    if hvp[constants.HV_VNC_BIND_ADDRESS] is None:
547 d0c11cf7 Alexander Schreiber
      config.write("vnclisten = '%s'\n" % constants.VNC_DEFAULT_BIND_ADDRESS)
548 d0c11cf7 Alexander Schreiber
    else:
549 6b405598 Guido Trotter
      config.write("vnclisten = '%s'\n" % hvp[constants.HV_VNC_BIND_ADDRESS])
550 65a6f9b7 Michael Hanselmann
551 377d74c9 Guido Trotter
    if instance.network_port > constants.VNC_BASE_PORT:
552 377d74c9 Guido Trotter
      display = instance.network_port - constants.VNC_BASE_PORT
553 65a6f9b7 Michael Hanselmann
      config.write("vncdisplay = %s\n" % display)
554 65a6f9b7 Michael Hanselmann
      config.write("vncunused = 0\n")
555 65a6f9b7 Michael Hanselmann
    else:
556 65a6f9b7 Michael Hanselmann
      config.write("# vncdisplay = 1\n")
557 65a6f9b7 Michael Hanselmann
      config.write("vncunused = 1\n")
558 65a6f9b7 Michael Hanselmann
559 65a6f9b7 Michael Hanselmann
    try:
560 78f66a17 Guido Trotter
      password = utils.ReadFile(constants.VNC_PASSWORD_FILE)
561 78f66a17 Guido Trotter
    except EnvironmentError, err:
562 78f66a17 Guido Trotter
      raise errors.HypervisorError("Failed to open VNC password file %s: %s" %
563 78f66a17 Guido Trotter
                                   (constants.VNC_PASSWORD_FILE, err))
564 65a6f9b7 Michael Hanselmann
565 65a6f9b7 Michael Hanselmann
    config.write("vncpasswd = '%s'\n" % password.rstrip())
566 65a6f9b7 Michael Hanselmann
567 65a6f9b7 Michael Hanselmann
    config.write("serial = 'pty'\n")
568 65a6f9b7 Michael Hanselmann
    config.write("localtime = 1\n")
569 65a6f9b7 Michael Hanselmann
570 65a6f9b7 Michael Hanselmann
    vif_data = []
571 a985b417 Iustin Pop
    nic_type = hvp[constants.HV_NIC_TYPE]
572 f48148c3 Iustin Pop
    if nic_type is None:
573 f48148c3 Iustin Pop
      # ensure old instances don't change
574 f48148c3 Iustin Pop
      nic_type_str = ", type=ioemu"
575 d08f6067 Guido Trotter
    elif nic_type == constants.HT_NIC_PARAVIRTUAL:
576 f48148c3 Iustin Pop
      nic_type_str = ", type=paravirtualized"
577 f48148c3 Iustin Pop
    else:
578 f48148c3 Iustin Pop
      nic_type_str = ", model=%s, type=ioemu" % nic_type
579 65a6f9b7 Michael Hanselmann
    for nic in instance.nics:
580 503b97a9 Guido Trotter
      nic_str = "mac=%s%s" % (nic.mac, nic_type_str)
581 65a6f9b7 Michael Hanselmann
      ip = getattr(nic, "ip", None)
582 65a6f9b7 Michael Hanselmann
      if ip is not None:
583 65a6f9b7 Michael Hanselmann
        nic_str += ", ip=%s" % ip
584 65a6f9b7 Michael Hanselmann
      vif_data.append("'%s'" % nic_str)
585 503b97a9 Guido Trotter
      if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
586 503b97a9 Guido Trotter
        nic_str += ", bridge=%s" % nic.nicparams[constants.NIC_LINK]
587 65a6f9b7 Michael Hanselmann
588 65a6f9b7 Michael Hanselmann
    config.write("vif = [%s]\n" % ",".join(vif_data))
589 a21dda8b Iustin Pop
    disk_data = cls._GetConfigFileDiskData(instance.disk_template,
590 a21dda8b Iustin Pop
                                            block_devices)
591 a985b417 Iustin Pop
    disk_type = hvp[constants.HV_DISK_TYPE]
592 d08f6067 Guido Trotter
    if disk_type in (None, constants.HT_DISK_IOEMU):
593 5397e0b7 Alexander Schreiber
      replacement = ",ioemu:hd"
594 5397e0b7 Alexander Schreiber
    else:
595 5397e0b7 Alexander Schreiber
      replacement = ",hd"
596 5397e0b7 Alexander Schreiber
    disk_data = [line.replace(",sd", replacement) for line in disk_data]
597 a985b417 Iustin Pop
    iso_path = hvp[constants.HV_CDROM_IMAGE_PATH]
598 f48148c3 Iustin Pop
    if iso_path:
599 f48148c3 Iustin Pop
      iso = "'file:%s,hdc:cdrom,r'" % iso_path
600 a21dda8b Iustin Pop
      disk_data.append(iso)
601 a21dda8b Iustin Pop
602 a21dda8b Iustin Pop
    config.write("disk = [%s]\n" % (",".join(disk_data)))
603 a21dda8b Iustin Pop
604 65a6f9b7 Michael Hanselmann
    config.write("on_poweroff = 'destroy'\n")
605 65a6f9b7 Michael Hanselmann
    config.write("on_reboot = 'restart'\n")
606 65a6f9b7 Michael Hanselmann
    config.write("on_crash = 'restart'\n")
607 65a6f9b7 Michael Hanselmann
    # just in case it exists
608 65a6f9b7 Michael Hanselmann
    utils.RemoveFile("/etc/xen/auto/%s" % instance.name)
609 65a6f9b7 Michael Hanselmann
    try:
610 73cd67f4 Guido Trotter
      utils.WriteFile("/etc/xen/%s" % instance.name,
611 73cd67f4 Guido Trotter
                      data=config.getvalue())
612 73cd67f4 Guido Trotter
    except EnvironmentError, err:
613 73cd67f4 Guido Trotter
      raise errors.HypervisorError("Cannot write Xen instance confile"
614 73cd67f4 Guido Trotter
                                   " file /etc/xen/%s: %s" %
615 73cd67f4 Guido Trotter
                                   (instance.name, err))
616 73cd67f4 Guido Trotter
617 65a6f9b7 Michael Hanselmann
    return True