Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_chroot.py @ fb62843c

History | View | Annotate | Download (10.7 kB)

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

24 48297fa2 Iustin Pop
"""
25 48297fa2 Iustin Pop
26 48297fa2 Iustin Pop
import os
27 48297fa2 Iustin Pop
import os.path
28 48297fa2 Iustin Pop
import time
29 48297fa2 Iustin Pop
import logging
30 48297fa2 Iustin Pop
31 48297fa2 Iustin Pop
from ganeti import constants
32 b459a848 Andrea Spadaccini
from ganeti import errors # pylint: disable=W0611
33 48297fa2 Iustin Pop
from ganeti import utils
34 55cc0a44 Michael Hanselmann
from ganeti import objects
35 9d9bded1 Michael Hanselmann
from ganeti import pathutils
36 48297fa2 Iustin Pop
from ganeti.hypervisor import hv_base
37 48297fa2 Iustin Pop
from ganeti.errors import HypervisorError
38 48297fa2 Iustin Pop
39 48297fa2 Iustin Pop
40 48297fa2 Iustin Pop
class ChrootManager(hv_base.BaseHypervisor):
41 48297fa2 Iustin Pop
  """Chroot manager.
42 48297fa2 Iustin Pop

43 48297fa2 Iustin Pop
  This not-really hypervisor allows ganeti to manage chroots. It has
44 48297fa2 Iustin Pop
  special behaviour and requirements on the OS definition and the node
45 48297fa2 Iustin Pop
  environemnt:
46 48297fa2 Iustin Pop
    - the start and stop of the chroot environment are done via a
47 48297fa2 Iustin Pop
      script called ganeti-chroot located in the root directory of the
48 48297fa2 Iustin Pop
      first drive, which should be created by the OS definition
49 48297fa2 Iustin Pop
    - this script must accept the start and stop argument and, on
50 48297fa2 Iustin Pop
      shutdown, it should cleanly shutdown the daemons/processes
51 48297fa2 Iustin Pop
      using the chroot
52 48297fa2 Iustin Pop
    - the daemons run in chroot should only bind to the instance IP
53 48297fa2 Iustin Pop
      (to which the OS create script has access via the instance name)
54 48297fa2 Iustin Pop
    - since some daemons in the node could be listening on the wildcard
55 48297fa2 Iustin Pop
      address, some ports might be unavailable
56 48297fa2 Iustin Pop
    - the instance listing will show no memory usage
57 48297fa2 Iustin Pop
    - on shutdown, the chroot manager will try to find all mountpoints
58 48297fa2 Iustin Pop
      under the root dir of the instance and unmount them
59 48297fa2 Iustin Pop
    - instance alive check is based on whether any process is using the chroot
60 48297fa2 Iustin Pop

61 48297fa2 Iustin Pop
  """
62 9d9bded1 Michael Hanselmann
  _ROOT_DIR = pathutils.RUN_DIR + "/chroot-hypervisor"
63 48297fa2 Iustin Pop
64 46952329 Guido Trotter
  PARAMETERS = {
65 46952329 Guido Trotter
    constants.HV_INIT_SCRIPT: (True, utils.IsNormAbsPath,
66 46952329 Guido Trotter
                               "must be an absolute normalized path",
67 d73ef63f Michael Hanselmann
                               None, None),
68 46952329 Guido Trotter
    }
69 48297fa2 Iustin Pop
70 48297fa2 Iustin Pop
  def __init__(self):
71 48297fa2 Iustin Pop
    hv_base.BaseHypervisor.__init__(self)
72 98c98ab9 Guido Trotter
    utils.EnsureDirs([(self._ROOT_DIR, constants.RUN_DIRS_MODE)])
73 48297fa2 Iustin Pop
74 48297fa2 Iustin Pop
  @staticmethod
75 48297fa2 Iustin Pop
  def _IsDirLive(path):
76 48297fa2 Iustin Pop
    """Check if a directory looks like a live chroot.
77 48297fa2 Iustin Pop

78 48297fa2 Iustin Pop
    """
79 48297fa2 Iustin Pop
    if not os.path.ismount(path):
80 48297fa2 Iustin Pop
      return False
81 48297fa2 Iustin Pop
    result = utils.RunCmd(["fuser", "-m", path])
82 48297fa2 Iustin Pop
    return not result.failed
83 48297fa2 Iustin Pop
84 48297fa2 Iustin Pop
  @staticmethod
85 48297fa2 Iustin Pop
  def _GetMountSubdirs(path):
86 48297fa2 Iustin Pop
    """Return the list of mountpoints under a given path.
87 48297fa2 Iustin Pop

88 48297fa2 Iustin Pop
    """
89 e8cd390d Balazs Lecz
    result = []
90 e8cd390d Balazs Lecz
    for _, mountpoint, _, _ in utils.GetMounts():
91 e8cd390d Balazs Lecz
      if (mountpoint.startswith(path) and
92 e8cd390d Balazs Lecz
          mountpoint != path):
93 e8cd390d Balazs Lecz
        result.append(mountpoint)
94 e8cd390d Balazs Lecz
95 e8cd390d Balazs Lecz
    result.sort(key=lambda x: x.count("/"), reverse=True)
96 e8cd390d Balazs Lecz
    return result
97 48297fa2 Iustin Pop
98 6b0391b3 Iustin Pop
  @classmethod
99 6b0391b3 Iustin Pop
  def _InstanceDir(cls, instance_name):
100 6b0391b3 Iustin Pop
    """Return the root directory for an instance.
101 6b0391b3 Iustin Pop

102 6b0391b3 Iustin Pop
    """
103 6b0391b3 Iustin Pop
    return utils.PathJoin(cls._ROOT_DIR, instance_name)
104 6b0391b3 Iustin Pop
105 58e356a9 Helga Velroyen
  def ListInstances(self, hvparams=None):
106 48297fa2 Iustin Pop
    """Get the list of running instances.
107 48297fa2 Iustin Pop

108 48297fa2 Iustin Pop
    """
109 48297fa2 Iustin Pop
    return [name for name in os.listdir(self._ROOT_DIR)
110 c4feafe8 Iustin Pop
            if self._IsDirLive(utils.PathJoin(self._ROOT_DIR, name))]
111 48297fa2 Iustin Pop
112 0bbec3af Helga Velroyen
  def GetInstanceInfo(self, instance_name, hvparams=None):
113 48297fa2 Iustin Pop
    """Get instance properties.
114 48297fa2 Iustin Pop

115 d73ef63f Michael Hanselmann
    @type instance_name: string
116 d73ef63f Michael Hanselmann
    @param instance_name: the instance name
117 0bbec3af Helga Velroyen
    @type hvparams: dict of strings
118 0bbec3af Helga Velroyen
    @param hvparams: hvparams to be used with this instance
119 d73ef63f Michael Hanselmann

120 d73ef63f Michael Hanselmann
    @return: (name, id, memory, vcpus, stat, times)
121 48297fa2 Iustin Pop

122 48297fa2 Iustin Pop
    """
123 6b0391b3 Iustin Pop
    dir_name = self._InstanceDir(instance_name)
124 48297fa2 Iustin Pop
    if not self._IsDirLive(dir_name):
125 48297fa2 Iustin Pop
      raise HypervisorError("Instance %s is not running" % instance_name)
126 48297fa2 Iustin Pop
    return (instance_name, 0, 0, 0, 0, 0)
127 48297fa2 Iustin Pop
128 0200a1af Helga Velroyen
  def GetAllInstancesInfo(self, hvparams=None):
129 48297fa2 Iustin Pop
    """Get properties of all instances.
130 48297fa2 Iustin Pop

131 0200a1af Helga Velroyen
    @type hvparams: dict of strings
132 0200a1af Helga Velroyen
    @param hvparams: hypervisor parameter
133 d73ef63f Michael Hanselmann
    @return: [(name, id, memory, vcpus, stat, times),...]
134 d73ef63f Michael Hanselmann

135 48297fa2 Iustin Pop
    """
136 48297fa2 Iustin Pop
    data = []
137 48297fa2 Iustin Pop
    for file_name in os.listdir(self._ROOT_DIR):
138 c4feafe8 Iustin Pop
      path = utils.PathJoin(self._ROOT_DIR, file_name)
139 48297fa2 Iustin Pop
      if self._IsDirLive(path):
140 48297fa2 Iustin Pop
        data.append((file_name, 0, 0, 0, 0, 0))
141 48297fa2 Iustin Pop
    return data
142 48297fa2 Iustin Pop
143 323f9095 Stephen Shirley
  def StartInstance(self, instance, block_devices, startup_paused):
144 48297fa2 Iustin Pop
    """Start an instance.
145 48297fa2 Iustin Pop

146 48297fa2 Iustin Pop
    For the chroot manager, we try to mount the block device and
147 48297fa2 Iustin Pop
    execute '/ganeti-chroot start'.
148 48297fa2 Iustin Pop

149 48297fa2 Iustin Pop
    """
150 6b0391b3 Iustin Pop
    root_dir = self._InstanceDir(instance.name)
151 48297fa2 Iustin Pop
    if not os.path.exists(root_dir):
152 48297fa2 Iustin Pop
      try:
153 48297fa2 Iustin Pop
        os.mkdir(root_dir)
154 48297fa2 Iustin Pop
      except IOError, err:
155 48297fa2 Iustin Pop
        raise HypervisorError("Failed to start instance %s: %s" %
156 48297fa2 Iustin Pop
                              (instance.name, err))
157 48297fa2 Iustin Pop
      if not os.path.isdir(root_dir):
158 48297fa2 Iustin Pop
        raise HypervisorError("Needed path %s is not a directory" % root_dir)
159 48297fa2 Iustin Pop
160 48297fa2 Iustin Pop
    if not os.path.ismount(root_dir):
161 48297fa2 Iustin Pop
      if not block_devices:
162 48297fa2 Iustin Pop
        raise HypervisorError("The chroot manager needs at least one disk")
163 48297fa2 Iustin Pop
164 48297fa2 Iustin Pop
      sda_dev_path = block_devices[0][1]
165 48297fa2 Iustin Pop
      result = utils.RunCmd(["mount", sda_dev_path, root_dir])
166 48297fa2 Iustin Pop
      if result.failed:
167 48297fa2 Iustin Pop
        raise HypervisorError("Can't mount the chroot dir: %s" % result.output)
168 48297fa2 Iustin Pop
    init_script = instance.hvparams[constants.HV_INIT_SCRIPT]
169 48297fa2 Iustin Pop
    result = utils.RunCmd(["chroot", root_dir, init_script, "start"])
170 48297fa2 Iustin Pop
    if result.failed:
171 48297fa2 Iustin Pop
      raise HypervisorError("Can't run the chroot start script: %s" %
172 48297fa2 Iustin Pop
                            result.output)
173 48297fa2 Iustin Pop
174 bbcf7ad0 Iustin Pop
  def StopInstance(self, instance, force=False, retry=False, name=None):
175 48297fa2 Iustin Pop
    """Stop an instance.
176 48297fa2 Iustin Pop

177 48297fa2 Iustin Pop
    This method has complicated cleanup tests, as we must:
178 48297fa2 Iustin Pop
      - try to kill all leftover processes
179 48297fa2 Iustin Pop
      - try to unmount any additional sub-mountpoints
180 48297fa2 Iustin Pop
      - finally unmount the instance dir
181 48297fa2 Iustin Pop

182 48297fa2 Iustin Pop
    """
183 bbcf7ad0 Iustin Pop
    if name is None:
184 bbcf7ad0 Iustin Pop
      name = instance.name
185 bbcf7ad0 Iustin Pop
186 bbcf7ad0 Iustin Pop
    root_dir = self._InstanceDir(name)
187 a2771c83 Guido Trotter
    if not os.path.exists(root_dir) or not self._IsDirLive(root_dir):
188 48297fa2 Iustin Pop
      return
189 48297fa2 Iustin Pop
190 a2771c83 Guido Trotter
    # Run the chroot stop script only once
191 a2771c83 Guido Trotter
    if not retry and not force:
192 48297fa2 Iustin Pop
      result = utils.RunCmd(["chroot", root_dir, "/ganeti-chroot", "stop"])
193 48297fa2 Iustin Pop
      if result.failed:
194 48297fa2 Iustin Pop
        raise HypervisorError("Can't run the chroot stop script: %s" %
195 48297fa2 Iustin Pop
                              result.output)
196 a2771c83 Guido Trotter
197 a2771c83 Guido Trotter
    if not force:
198 a2771c83 Guido Trotter
      utils.RunCmd(["fuser", "-k", "-TERM", "-m", root_dir])
199 a2771c83 Guido Trotter
    else:
200 a2771c83 Guido Trotter
      utils.RunCmd(["fuser", "-k", "-KILL", "-m", root_dir])
201 a2771c83 Guido Trotter
      # 2 seconds at most should be enough for KILL to take action
202 a2771c83 Guido Trotter
      time.sleep(2)
203 a2771c83 Guido Trotter
204 a2771c83 Guido Trotter
    if self._IsDirLive(root_dir):
205 a2771c83 Guido Trotter
      if force:
206 48297fa2 Iustin Pop
        raise HypervisorError("Can't stop the processes using the chroot")
207 a2771c83 Guido Trotter
      return
208 a2771c83 Guido Trotter
209 9e302a8c Iustin Pop
  def CleanupInstance(self, instance_name):
210 9e302a8c Iustin Pop
    """Cleanup after a stopped instance
211 9e302a8c Iustin Pop

212 9e302a8c Iustin Pop
    """
213 14b3f969 Iustin Pop
    root_dir = self._InstanceDir(instance_name)
214 14b3f969 Iustin Pop
215 14b3f969 Iustin Pop
    if not os.path.exists(root_dir):
216 14b3f969 Iustin Pop
      return
217 14b3f969 Iustin Pop
218 9e302a8c Iustin Pop
    if self._IsDirLive(root_dir):
219 9e302a8c Iustin Pop
      raise HypervisorError("Processes are still using the chroot")
220 9e302a8c Iustin Pop
221 48297fa2 Iustin Pop
    for mpath in self._GetMountSubdirs(root_dir):
222 48297fa2 Iustin Pop
      utils.RunCmd(["umount", mpath])
223 a2771c83 Guido Trotter
224 a2771c83 Guido Trotter
    result = utils.RunCmd(["umount", root_dir])
225 14b3f969 Iustin Pop
    if result.failed:
226 a2771c83 Guido Trotter
      msg = ("Processes still alive in the chroot: %s" %
227 a2771c83 Guido Trotter
             utils.RunCmd("fuser -vm %s" % root_dir).output)
228 a2771c83 Guido Trotter
      logging.error(msg)
229 a2771c83 Guido Trotter
      raise HypervisorError("Can't umount the chroot dir: %s (%s)" %
230 a2771c83 Guido Trotter
                            (result.output, msg))
231 48297fa2 Iustin Pop
232 48297fa2 Iustin Pop
  def RebootInstance(self, instance):
233 48297fa2 Iustin Pop
    """Reboot an instance.
234 48297fa2 Iustin Pop

235 48297fa2 Iustin Pop
    This is not (yet) implemented for the chroot manager.
236 48297fa2 Iustin Pop

237 48297fa2 Iustin Pop
    """
238 48297fa2 Iustin Pop
    raise HypervisorError("The chroot manager doesn't implement the"
239 48297fa2 Iustin Pop
                          " reboot functionality")
240 48297fa2 Iustin Pop
241 2e2fb795 Guido Trotter
  def BalloonInstanceMemory(self, instance, mem):
242 2e2fb795 Guido Trotter
    """Balloon an instance memory to a certain value.
243 2e2fb795 Guido Trotter

244 2e2fb795 Guido Trotter
    @type instance: L{objects.Instance}
245 2e2fb795 Guido Trotter
    @param instance: instance to be accepted
246 2e2fb795 Guido Trotter
    @type mem: int
247 2e2fb795 Guido Trotter
    @param mem: actual memory size to use for instance runtime
248 2e2fb795 Guido Trotter

249 2e2fb795 Guido Trotter
    """
250 2e2fb795 Guido Trotter
    # Currently chroots don't have memory limits
251 2e2fb795 Guido Trotter
    pass
252 2e2fb795 Guido Trotter
253 fac489a5 Helga Velroyen
  def GetNodeInfo(self, hvparams=None):
254 48297fa2 Iustin Pop
    """Return information about the node.
255 48297fa2 Iustin Pop

256 ef14e128 Bernardo Dal Seno
    See L{BaseHypervisor.GetLinuxNodeInfo}.
257 48297fa2 Iustin Pop

258 48297fa2 Iustin Pop
    """
259 48297fa2 Iustin Pop
    return self.GetLinuxNodeInfo()
260 48297fa2 Iustin Pop
261 48297fa2 Iustin Pop
  @classmethod
262 1c3231aa Thomas Thrainer
  def GetInstanceConsole(cls, instance, primary_node, # pylint: disable=W0221
263 55cc0a44 Michael Hanselmann
                         hvparams, beparams, root_dir=None):
264 55cc0a44 Michael Hanselmann
    """Return information for connecting to the console of an instance.
265 48297fa2 Iustin Pop

266 48297fa2 Iustin Pop
    """
267 55cc0a44 Michael Hanselmann
    if root_dir is None:
268 55cc0a44 Michael Hanselmann
      root_dir = cls._InstanceDir(instance.name)
269 55cc0a44 Michael Hanselmann
      if not os.path.ismount(root_dir):
270 55cc0a44 Michael Hanselmann
        raise HypervisorError("Instance %s is not running" % instance.name)
271 55cc0a44 Michael Hanselmann
272 55cc0a44 Michael Hanselmann
    return objects.InstanceConsole(instance=instance.name,
273 55cc0a44 Michael Hanselmann
                                   kind=constants.CONS_SSH,
274 1c3231aa Thomas Thrainer
                                   host=primary_node.name,
275 052783ff Michael Hanselmann
                                   user=constants.SSH_CONSOLE_USER,
276 55cc0a44 Michael Hanselmann
                                   command=["chroot", root_dir])
277 48297fa2 Iustin Pop
278 75bf3149 Helga Velroyen
  def Verify(self, hvparams=None):
279 48297fa2 Iustin Pop
    """Verify the hypervisor.
280 48297fa2 Iustin Pop

281 d73ef63f Michael Hanselmann
    For the chroot manager, it just checks the existence of the base dir.
282 48297fa2 Iustin Pop

283 75bf3149 Helga Velroyen
    @type hvparams: dict of strings
284 75bf3149 Helga Velroyen
    @param hvparams: hypervisor parameters to be verified against, not used
285 75bf3149 Helga Velroyen
      in for chroot
286 75bf3149 Helga Velroyen

287 cd04dfd2 Michael Hanselmann
    @return: Problem description if something is wrong, C{None} otherwise
288 cd04dfd2 Michael Hanselmann

289 48297fa2 Iustin Pop
    """
290 cd04dfd2 Michael Hanselmann
    if os.path.exists(self._ROOT_DIR):
291 cd04dfd2 Michael Hanselmann
      return None
292 cd04dfd2 Michael Hanselmann
    else:
293 cd04dfd2 Michael Hanselmann
      return "The required directory '%s' does not exist" % self._ROOT_DIR
294 94fed7da Iustin Pop
295 94fed7da Iustin Pop
  @classmethod
296 8ef418bb Helga Velroyen
  def PowercycleNode(cls, hvparams=None):
297 94fed7da Iustin Pop
    """Chroot powercycle, just a wrapper over Linux powercycle.
298 94fed7da Iustin Pop

299 8ef418bb Helga Velroyen
    @type hvparams: dict of strings
300 8ef418bb Helga Velroyen
    @param hvparams: hypervisor params to be used on this node
301 8ef418bb Helga Velroyen

302 94fed7da Iustin Pop
    """
303 94fed7da Iustin Pop
    cls.LinuxPowercycle()
304 94fed7da Iustin Pop
305 bc0a2284 Helga Velroyen
  def MigrateInstance(self, cluster_name, instance, target, live):
306 94fed7da Iustin Pop
    """Migrate an instance.
307 94fed7da Iustin Pop

308 bc0a2284 Helga Velroyen
    @type cluster_name: string
309 bc0a2284 Helga Velroyen
    @param cluster_name: name of the cluster
310 3a488770 Iustin Pop
    @type instance: L{objects.Instance}
311 94fed7da Iustin Pop
    @param instance: the instance to be migrated
312 94fed7da Iustin Pop
    @type target: string
313 94fed7da Iustin Pop
    @param target: hostname (usually ip) of the target node
314 94fed7da Iustin Pop
    @type live: boolean
315 94fed7da Iustin Pop
    @param live: whether to do a live or non-live migration
316 94fed7da Iustin Pop

317 94fed7da Iustin Pop
    """
318 94fed7da Iustin Pop
    raise HypervisorError("Migration not supported by the chroot hypervisor")
319 60af751d Andrea Spadaccini
320 60af751d Andrea Spadaccini
  def GetMigrationStatus(self, instance):
321 60af751d Andrea Spadaccini
    """Get the migration status
322 60af751d Andrea Spadaccini

323 60af751d Andrea Spadaccini
    @type instance: L{objects.Instance}
324 60af751d Andrea Spadaccini
    @param instance: the instance that is being migrated
325 60af751d Andrea Spadaccini
    @rtype: L{objects.MigrationStatus}
326 60af751d Andrea Spadaccini
    @return: the status of the current migration (one of
327 60af751d Andrea Spadaccini
             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
328 60af751d Andrea Spadaccini
             progress info that can be retrieved from the hypervisor
329 60af751d Andrea Spadaccini

330 60af751d Andrea Spadaccini
    """
331 60af751d Andrea Spadaccini
    raise HypervisorError("Migration not supported by the chroot hypervisor")