Statistics
| Branch: | Tag: | Revision:

root / lib / hypervisor / hv_chroot.py @ 55cc0a44

History | View | Annotate | Download (9.4 kB)

1 48297fa2 Iustin Pop
#
2 48297fa2 Iustin Pop
#
3 48297fa2 Iustin Pop
4 48297fa2 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009 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 30e4e741 Iustin Pop
from ganeti import errors # pylint: disable-msg=W0611
33 48297fa2 Iustin Pop
from ganeti import utils
34 55cc0a44 Michael Hanselmann
from ganeti import objects
35 48297fa2 Iustin Pop
from ganeti.hypervisor import hv_base
36 48297fa2 Iustin Pop
from ganeti.errors import HypervisorError
37 48297fa2 Iustin Pop
38 48297fa2 Iustin Pop
39 48297fa2 Iustin Pop
class ChrootManager(hv_base.BaseHypervisor):
40 48297fa2 Iustin Pop
  """Chroot manager.
41 48297fa2 Iustin Pop

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

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

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

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

101 6b0391b3 Iustin Pop
    """
102 6b0391b3 Iustin Pop
    return utils.PathJoin(cls._ROOT_DIR, instance_name)
103 6b0391b3 Iustin Pop
104 48297fa2 Iustin Pop
  def ListInstances(self):
105 48297fa2 Iustin Pop
    """Get the list of running instances.
106 48297fa2 Iustin Pop

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

114 d73ef63f Michael Hanselmann
    @type instance_name: string
115 d73ef63f Michael Hanselmann
    @param instance_name: the instance name
116 d73ef63f Michael Hanselmann

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

119 48297fa2 Iustin Pop
    """
120 6b0391b3 Iustin Pop
    dir_name = self._InstanceDir(instance_name)
121 48297fa2 Iustin Pop
    if not self._IsDirLive(dir_name):
122 48297fa2 Iustin Pop
      raise HypervisorError("Instance %s is not running" % instance_name)
123 48297fa2 Iustin Pop
    return (instance_name, 0, 0, 0, 0, 0)
124 48297fa2 Iustin Pop
125 48297fa2 Iustin Pop
  def GetAllInstancesInfo(self):
126 48297fa2 Iustin Pop
    """Get properties of all instances.
127 48297fa2 Iustin Pop

128 d73ef63f Michael Hanselmann
    @return: [(name, id, memory, vcpus, stat, times),...]
129 d73ef63f Michael Hanselmann

130 48297fa2 Iustin Pop
    """
131 48297fa2 Iustin Pop
    data = []
132 48297fa2 Iustin Pop
    for file_name in os.listdir(self._ROOT_DIR):
133 c4feafe8 Iustin Pop
      path = utils.PathJoin(self._ROOT_DIR, file_name)
134 48297fa2 Iustin Pop
      if self._IsDirLive(path):
135 48297fa2 Iustin Pop
        data.append((file_name, 0, 0, 0, 0, 0))
136 48297fa2 Iustin Pop
    return data
137 48297fa2 Iustin Pop
138 48297fa2 Iustin Pop
  def StartInstance(self, instance, block_devices):
139 48297fa2 Iustin Pop
    """Start an instance.
140 48297fa2 Iustin Pop

141 48297fa2 Iustin Pop
    For the chroot manager, we try to mount the block device and
142 48297fa2 Iustin Pop
    execute '/ganeti-chroot start'.
143 48297fa2 Iustin Pop

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

172 48297fa2 Iustin Pop
    This method has complicated cleanup tests, as we must:
173 48297fa2 Iustin Pop
      - try to kill all leftover processes
174 48297fa2 Iustin Pop
      - try to unmount any additional sub-mountpoints
175 48297fa2 Iustin Pop
      - finally unmount the instance dir
176 48297fa2 Iustin Pop

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

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

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

232 48297fa2 Iustin Pop
    """
233 48297fa2 Iustin Pop
    raise HypervisorError("The chroot manager doesn't implement the"
234 48297fa2 Iustin Pop
                          " reboot functionality")
235 48297fa2 Iustin Pop
236 48297fa2 Iustin Pop
  def GetNodeInfo(self):
237 48297fa2 Iustin Pop
    """Return information about the node.
238 48297fa2 Iustin Pop

239 48297fa2 Iustin Pop
    This is just a wrapper over the base GetLinuxNodeInfo method.
240 48297fa2 Iustin Pop

241 48297fa2 Iustin Pop
    @return: a dict with the following keys (values in MiB):
242 48297fa2 Iustin Pop
          - memory_total: the total memory size on the node
243 48297fa2 Iustin Pop
          - memory_free: the available memory on the node for instances
244 48297fa2 Iustin Pop
          - memory_dom0: the memory used by the node itself, if available
245 48297fa2 Iustin Pop

246 48297fa2 Iustin Pop
    """
247 48297fa2 Iustin Pop
    return self.GetLinuxNodeInfo()
248 48297fa2 Iustin Pop
249 48297fa2 Iustin Pop
  @classmethod
250 55cc0a44 Michael Hanselmann
  def GetInstanceConsole(cls, instance, # pylint: disable-msg=W0221
251 55cc0a44 Michael Hanselmann
                         hvparams, beparams, root_dir=None):
252 55cc0a44 Michael Hanselmann
    """Return information for connecting to the console of an instance.
253 48297fa2 Iustin Pop

254 48297fa2 Iustin Pop
    """
255 55cc0a44 Michael Hanselmann
    if root_dir is None:
256 55cc0a44 Michael Hanselmann
      root_dir = cls._InstanceDir(instance.name)
257 55cc0a44 Michael Hanselmann
      if not os.path.ismount(root_dir):
258 55cc0a44 Michael Hanselmann
        raise HypervisorError("Instance %s is not running" % instance.name)
259 55cc0a44 Michael Hanselmann
260 55cc0a44 Michael Hanselmann
    return objects.InstanceConsole(instance=instance.name,
261 55cc0a44 Michael Hanselmann
                                   kind=constants.CONS_SSH,
262 55cc0a44 Michael Hanselmann
                                   host=instance.primary_node,
263 55cc0a44 Michael Hanselmann
                                   user=constants.GANETI_RUNAS,
264 55cc0a44 Michael Hanselmann
                                   command=["chroot", root_dir])
265 48297fa2 Iustin Pop
266 48297fa2 Iustin Pop
  def Verify(self):
267 48297fa2 Iustin Pop
    """Verify the hypervisor.
268 48297fa2 Iustin Pop

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

271 48297fa2 Iustin Pop
    """
272 48297fa2 Iustin Pop
    if not os.path.exists(self._ROOT_DIR):
273 48297fa2 Iustin Pop
      return "The required directory '%s' does not exist." % self._ROOT_DIR
274 94fed7da Iustin Pop
275 94fed7da Iustin Pop
  @classmethod
276 94fed7da Iustin Pop
  def PowercycleNode(cls):
277 94fed7da Iustin Pop
    """Chroot powercycle, just a wrapper over Linux powercycle.
278 94fed7da Iustin Pop

279 94fed7da Iustin Pop
    """
280 94fed7da Iustin Pop
    cls.LinuxPowercycle()
281 94fed7da Iustin Pop
282 94fed7da Iustin Pop
  def MigrateInstance(self, instance, target, live):
283 94fed7da Iustin Pop
    """Migrate an instance.
284 94fed7da Iustin Pop

285 3a488770 Iustin Pop
    @type instance: L{objects.Instance}
286 94fed7da Iustin Pop
    @param instance: the instance to be migrated
287 94fed7da Iustin Pop
    @type target: string
288 94fed7da Iustin Pop
    @param target: hostname (usually ip) of the target node
289 94fed7da Iustin Pop
    @type live: boolean
290 94fed7da Iustin Pop
    @param live: whether to do a live or non-live migration
291 94fed7da Iustin Pop

292 94fed7da Iustin Pop
    """
293 94fed7da Iustin Pop
    raise HypervisorError("Migration not supported by the chroot hypervisor")