Package ganeti :: Package hypervisor :: Module hv_chroot
[hide private]
[frames] | no frames]

Source Code for Module ganeti.hypervisor.hv_chroot

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2013 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30   
 31  """Chroot manager hypervisor 
 32   
 33  """ 
 34   
 35  import os 
 36  import os.path 
 37  import time 
 38  import logging 
 39   
 40  from ganeti import constants 
 41  from ganeti import errors # pylint: disable=W0611 
 42  from ganeti import utils 
 43  from ganeti import objects 
 44  from ganeti import pathutils 
 45  from ganeti.hypervisor import hv_base 
 46  from ganeti.errors import HypervisorError 
47 48 49 -class ChrootManager(hv_base.BaseHypervisor):
50 """Chroot manager. 51 52 This not-really hypervisor allows ganeti to manage chroots. It has 53 special behaviour and requirements on the OS definition and the node 54 environemnt: 55 - the start and stop of the chroot environment are done via a 56 script called ganeti-chroot located in the root directory of the 57 first drive, which should be created by the OS definition 58 - this script must accept the start and stop argument and, on 59 shutdown, it should cleanly shutdown the daemons/processes 60 using the chroot 61 - the daemons run in chroot should only bind to the instance IP 62 (to which the OS create script has access via the instance name) 63 - since some daemons in the node could be listening on the wildcard 64 address, some ports might be unavailable 65 - the instance listing will show no memory usage 66 - on shutdown, the chroot manager will try to find all mountpoints 67 under the root dir of the instance and unmount them 68 - instance alive check is based on whether any process is using the chroot 69 70 """ 71 _ROOT_DIR = pathutils.RUN_DIR + "/chroot-hypervisor" 72 73 PARAMETERS = { 74 constants.HV_INIT_SCRIPT: (True, utils.IsNormAbsPath, 75 "must be an absolute normalized path", 76 None, None), 77 } 78
79 - def __init__(self):
82 83 @staticmethod
84 - def _IsDirLive(path):
85 """Check if a directory looks like a live chroot. 86 87 """ 88 if not os.path.ismount(path): 89 return False 90 result = utils.RunCmd(["fuser", "-m", path]) 91 return not result.failed
92 93 @staticmethod
94 - def _GetMountSubdirs(path):
95 """Return the list of mountpoints under a given path. 96 97 """ 98 result = [] 99 for _, mountpoint, _, _ in utils.GetMounts(): 100 if (mountpoint.startswith(path) and 101 mountpoint != path): 102 result.append(mountpoint) 103 104 result.sort(key=lambda x: x.count("/"), reverse=True) 105 return result
106 107 @classmethod
108 - def _InstanceDir(cls, instance_name):
109 """Return the root directory for an instance. 110 111 """ 112 return utils.PathJoin(cls._ROOT_DIR, instance_name)
113
114 - def ListInstances(self, hvparams=None):
115 """Get the list of running instances. 116 117 """ 118 return [name for name in os.listdir(self._ROOT_DIR) 119 if self._IsDirLive(utils.PathJoin(self._ROOT_DIR, name))]
120
121 - def GetInstanceInfo(self, instance_name, hvparams=None):
122 """Get instance properties. 123 124 @type instance_name: string 125 @param instance_name: the instance name 126 @type hvparams: dict of strings 127 @param hvparams: hvparams to be used with this instance 128 129 @return: (name, id, memory, vcpus, stat, times) 130 131 """ 132 dir_name = self._InstanceDir(instance_name) 133 if not self._IsDirLive(dir_name): 134 raise HypervisorError("Instance %s is not running" % instance_name) 135 return (instance_name, 0, 0, 0, hv_base.HvInstanceState.RUNNING, 0)
136
137 - def GetAllInstancesInfo(self, hvparams=None):
138 """Get properties of all instances. 139 140 @type hvparams: dict of strings 141 @param hvparams: hypervisor parameter 142 @return: [(name, id, memory, vcpus, stat, times),...] 143 144 """ 145 data = [] 146 for file_name in os.listdir(self._ROOT_DIR): 147 path = utils.PathJoin(self._ROOT_DIR, file_name) 148 if self._IsDirLive(path): 149 data.append((file_name, 0, 0, 0, 0, 0)) 150 return data
151
152 - def StartInstance(self, instance, block_devices, startup_paused):
153 """Start an instance. 154 155 For the chroot manager, we try to mount the block device and 156 execute '/ganeti-chroot start'. 157 158 """ 159 root_dir = self._InstanceDir(instance.name) 160 if not os.path.exists(root_dir): 161 try: 162 os.mkdir(root_dir) 163 except IOError, err: 164 raise HypervisorError("Failed to start instance %s: %s" % 165 (instance.name, err)) 166 if not os.path.isdir(root_dir): 167 raise HypervisorError("Needed path %s is not a directory" % root_dir) 168 169 if not os.path.ismount(root_dir): 170 if not block_devices: 171 raise HypervisorError("The chroot manager needs at least one disk") 172 173 sda_dev_path = block_devices[0][1] 174 result = utils.RunCmd(["mount", sda_dev_path, root_dir]) 175 if result.failed: 176 raise HypervisorError("Can't mount the chroot dir: %s" % result.output) 177 init_script = instance.hvparams[constants.HV_INIT_SCRIPT] 178 result = utils.RunCmd(["chroot", root_dir, init_script, "start"]) 179 if result.failed: 180 raise HypervisorError("Can't run the chroot start script: %s" % 181 result.output)
182
183 - def StopInstance(self, instance, force=False, retry=False, name=None, 184 timeout=None):
185 """Stop an instance. 186 187 This method has complicated cleanup tests, as we must: 188 - try to kill all leftover processes 189 - try to unmount any additional sub-mountpoints 190 - finally unmount the instance dir 191 192 """ 193 assert(timeout is None or force is not None) 194 195 if name is None: 196 name = instance.name 197 198 root_dir = self._InstanceDir(name) 199 if not os.path.exists(root_dir) or not self._IsDirLive(root_dir): 200 return 201 202 timeout_cmd = [] 203 if timeout is not None: 204 timeout_cmd.extend(["timeout", str(timeout)]) 205 206 # Run the chroot stop script only once 207 if not retry and not force: 208 result = utils.RunCmd(timeout_cmd.extend(["chroot", root_dir, 209 "/ganeti-chroot", "stop"])) 210 if result.failed: 211 raise HypervisorError("Can't run the chroot stop script: %s" % 212 result.output) 213 214 if not force: 215 utils.RunCmd(["fuser", "-k", "-TERM", "-m", root_dir]) 216 else: 217 utils.RunCmd(["fuser", "-k", "-KILL", "-m", root_dir]) 218 # 2 seconds at most should be enough for KILL to take action 219 time.sleep(2) 220 221 if self._IsDirLive(root_dir): 222 if force: 223 raise HypervisorError("Can't stop the processes using the chroot") 224 return
225
226 - def CleanupInstance(self, instance_name):
227 """Cleanup after a stopped instance 228 229 """ 230 root_dir = self._InstanceDir(instance_name) 231 232 if not os.path.exists(root_dir): 233 return 234 235 if self._IsDirLive(root_dir): 236 raise HypervisorError("Processes are still using the chroot") 237 238 for mpath in self._GetMountSubdirs(root_dir): 239 utils.RunCmd(["umount", mpath]) 240 241 result = utils.RunCmd(["umount", root_dir]) 242 if result.failed: 243 msg = ("Processes still alive in the chroot: %s" % 244 utils.RunCmd("fuser -vm %s" % root_dir).output) 245 logging.error(msg) 246 raise HypervisorError("Can't umount the chroot dir: %s (%s)" % 247 (result.output, msg))
248
249 - def RebootInstance(self, instance):
250 """Reboot an instance. 251 252 This is not (yet) implemented for the chroot manager. 253 254 """ 255 raise HypervisorError("The chroot manager doesn't implement the" 256 " reboot functionality")
257
258 - def BalloonInstanceMemory(self, instance, mem):
259 """Balloon an instance memory to a certain value. 260 261 @type instance: L{objects.Instance} 262 @param instance: instance to be accepted 263 @type mem: int 264 @param mem: actual memory size to use for instance runtime 265 266 """ 267 # Currently chroots don't have memory limits 268 pass
269
270 - def GetNodeInfo(self, hvparams=None):
271 """Return information about the node. 272 273 See L{BaseHypervisor.GetLinuxNodeInfo}. 274 275 """ 276 return self.GetLinuxNodeInfo()
277 278 @classmethod
279 - def GetInstanceConsole(cls, instance, primary_node, # pylint: disable=W0221 280 node_group, hvparams, beparams, root_dir=None):
281 """Return information for connecting to the console of an instance. 282 283 """ 284 if root_dir is None: 285 root_dir = cls._InstanceDir(instance.name) 286 if not os.path.ismount(root_dir): 287 raise HypervisorError("Instance %s is not running" % instance.name) 288 289 ndparams = node_group.FillND(primary_node) 290 return objects.InstanceConsole(instance=instance.name, 291 kind=constants.CONS_SSH, 292 host=primary_node.name, 293 port=ndparams.get(constants.ND_SSH_PORT), 294 user=constants.SSH_CONSOLE_USER, 295 command=["chroot", root_dir])
296
297 - def Verify(self, hvparams=None):
298 """Verify the hypervisor. 299 300 For the chroot manager, it just checks the existence of the base dir. 301 302 @type hvparams: dict of strings 303 @param hvparams: hypervisor parameters to be verified against, not used 304 in for chroot 305 306 @return: Problem description if something is wrong, C{None} otherwise 307 308 """ 309 if os.path.exists(self._ROOT_DIR): 310 return None 311 else: 312 return "The required directory '%s' does not exist" % self._ROOT_DIR
313 314 @classmethod
315 - def PowercycleNode(cls, hvparams=None):
316 """Chroot powercycle, just a wrapper over Linux powercycle. 317 318 @type hvparams: dict of strings 319 @param hvparams: hypervisor params to be used on this node 320 321 """ 322 cls.LinuxPowercycle()
323
324 - def MigrateInstance(self, cluster_name, instance, target, live):
325 """Migrate an instance. 326 327 @type cluster_name: string 328 @param cluster_name: name of the cluster 329 @type instance: L{objects.Instance} 330 @param instance: the instance to be migrated 331 @type target: string 332 @param target: hostname (usually ip) of the target node 333 @type live: boolean 334 @param live: whether to do a live or non-live migration 335 336 """ 337 raise HypervisorError("Migration not supported by the chroot hypervisor")
338
339 - def GetMigrationStatus(self, instance):
340 """Get the migration status 341 342 @type instance: L{objects.Instance} 343 @param instance: the instance that is being migrated 344 @rtype: L{objects.MigrationStatus} 345 @return: the status of the current migration (one of 346 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional 347 progress info that can be retrieved from the hypervisor 348 349 """ 350 raise HypervisorError("Migration not supported by the chroot hypervisor")
351