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 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Chroot manager hypervisor 
 23   
 24  """ 
 25   
 26  import os 
 27  import os.path 
 28  import time 
 29  import logging 
 30   
 31  from ganeti import constants 
 32  from ganeti import errors # pylint: disable=W0611 
 33  from ganeti import utils 
 34  from ganeti import objects 
 35  from ganeti.hypervisor import hv_base 
 36  from ganeti.errors import HypervisorError 
37 38 39 -class ChrootManager(hv_base.BaseHypervisor):
40 """Chroot manager. 41 42 This not-really hypervisor allows ganeti to manage chroots. It has 43 special behaviour and requirements on the OS definition and the node 44 environemnt: 45 - the start and stop of the chroot environment are done via a 46 script called ganeti-chroot located in the root directory of the 47 first drive, which should be created by the OS definition 48 - this script must accept the start and stop argument and, on 49 shutdown, it should cleanly shutdown the daemons/processes 50 using the chroot 51 - the daemons run in chroot should only bind to the instance IP 52 (to which the OS create script has access via the instance name) 53 - since some daemons in the node could be listening on the wildcard 54 address, some ports might be unavailable 55 - the instance listing will show no memory usage 56 - on shutdown, the chroot manager will try to find all mountpoints 57 under the root dir of the instance and unmount them 58 - instance alive check is based on whether any process is using the chroot 59 60 """ 61 _ROOT_DIR = constants.RUN_GANETI_DIR + "/chroot-hypervisor" 62 63 PARAMETERS = { 64 constants.HV_INIT_SCRIPT: (True, utils.IsNormAbsPath, 65 "must be an absolute normalized path", 66 None, None), 67 } 68
69 - def __init__(self):
72 73 @staticmethod
74 - def _IsDirLive(path):
75 """Check if a directory looks like a live chroot. 76 77 """ 78 if not os.path.ismount(path): 79 return False 80 result = utils.RunCmd(["fuser", "-m", path]) 81 return not result.failed
82 83 @staticmethod
84 - def _GetMountSubdirs(path):
85 """Return the list of mountpoints under a given path. 86 87 """ 88 result = [] 89 for _, mountpoint, _, _ in utils.GetMounts(): 90 if (mountpoint.startswith(path) and 91 mountpoint != path): 92 result.append(mountpoint) 93 94 result.sort(key=lambda x: x.count("/"), reverse=True) 95 return result
96 97 @classmethod
98 - def _InstanceDir(cls, instance_name):
99 """Return the root directory for an instance. 100 101 """ 102 return utils.PathJoin(cls._ROOT_DIR, instance_name)
103
104 - def ListInstances(self):
105 """Get the list of running instances. 106 107 """ 108 return [name for name in os.listdir(self._ROOT_DIR) 109 if self._IsDirLive(utils.PathJoin(self._ROOT_DIR, name))]
110
111 - def GetInstanceInfo(self, instance_name):
112 """Get instance properties. 113 114 @type instance_name: string 115 @param instance_name: the instance name 116 117 @return: (name, id, memory, vcpus, stat, times) 118 119 """ 120 dir_name = self._InstanceDir(instance_name) 121 if not self._IsDirLive(dir_name): 122 raise HypervisorError("Instance %s is not running" % instance_name) 123 return (instance_name, 0, 0, 0, 0, 0)
124
125 - def GetAllInstancesInfo(self):
126 """Get properties of all instances. 127 128 @return: [(name, id, memory, vcpus, stat, times),...] 129 130 """ 131 data = [] 132 for file_name in os.listdir(self._ROOT_DIR): 133 path = utils.PathJoin(self._ROOT_DIR, file_name) 134 if self._IsDirLive(path): 135 data.append((file_name, 0, 0, 0, 0, 0)) 136 return data
137
138 - def StartInstance(self, instance, block_devices, startup_paused):
139 """Start an instance. 140 141 For the chroot manager, we try to mount the block device and 142 execute '/ganeti-chroot start'. 143 144 """ 145 root_dir = self._InstanceDir(instance.name) 146 if not os.path.exists(root_dir): 147 try: 148 os.mkdir(root_dir) 149 except IOError, err: 150 raise HypervisorError("Failed to start instance %s: %s" % 151 (instance.name, err)) 152 if not os.path.isdir(root_dir): 153 raise HypervisorError("Needed path %s is not a directory" % root_dir) 154 155 if not os.path.ismount(root_dir): 156 if not block_devices: 157 raise HypervisorError("The chroot manager needs at least one disk") 158 159 sda_dev_path = block_devices[0][1] 160 result = utils.RunCmd(["mount", sda_dev_path, root_dir]) 161 if result.failed: 162 raise HypervisorError("Can't mount the chroot dir: %s" % result.output) 163 init_script = instance.hvparams[constants.HV_INIT_SCRIPT] 164 result = utils.RunCmd(["chroot", root_dir, init_script, "start"]) 165 if result.failed: 166 raise HypervisorError("Can't run the chroot start script: %s" % 167 result.output)
168
169 - def StopInstance(self, instance, force=False, retry=False, name=None):
170 """Stop an instance. 171 172 This method has complicated cleanup tests, as we must: 173 - try to kill all leftover processes 174 - try to unmount any additional sub-mountpoints 175 - finally unmount the instance dir 176 177 """ 178 if name is None: 179 name = instance.name 180 181 root_dir = self._InstanceDir(name) 182 if not os.path.exists(root_dir) or not self._IsDirLive(root_dir): 183 return 184 185 # Run the chroot stop script only once 186 if not retry and not force: 187 result = utils.RunCmd(["chroot", root_dir, "/ganeti-chroot", "stop"]) 188 if result.failed: 189 raise HypervisorError("Can't run the chroot stop script: %s" % 190 result.output) 191 192 if not force: 193 utils.RunCmd(["fuser", "-k", "-TERM", "-m", root_dir]) 194 else: 195 utils.RunCmd(["fuser", "-k", "-KILL", "-m", root_dir]) 196 # 2 seconds at most should be enough for KILL to take action 197 time.sleep(2) 198 199 if self._IsDirLive(root_dir): 200 if force: 201 raise HypervisorError("Can't stop the processes using the chroot") 202 return
203
204 - def CleanupInstance(self, instance_name):
205 """Cleanup after a stopped instance 206 207 """ 208 root_dir = self._InstanceDir(instance_name) 209 210 if not os.path.exists(root_dir): 211 return 212 213 if self._IsDirLive(root_dir): 214 raise HypervisorError("Processes are still using the chroot") 215 216 for mpath in self._GetMountSubdirs(root_dir): 217 utils.RunCmd(["umount", mpath]) 218 219 result = utils.RunCmd(["umount", root_dir]) 220 if result.failed: 221 msg = ("Processes still alive in the chroot: %s" % 222 utils.RunCmd("fuser -vm %s" % root_dir).output) 223 logging.error(msg) 224 raise HypervisorError("Can't umount the chroot dir: %s (%s)" % 225 (result.output, msg))
226
227 - def RebootInstance(self, instance):
228 """Reboot an instance. 229 230 This is not (yet) implemented for the chroot manager. 231 232 """ 233 raise HypervisorError("The chroot manager doesn't implement the" 234 " reboot functionality")
235
236 - def GetNodeInfo(self):
237 """Return information about the node. 238 239 This is just a wrapper over the base GetLinuxNodeInfo method. 240 241 @return: a dict with the following keys (values in MiB): 242 - memory_total: the total memory size on the node 243 - memory_free: the available memory on the node for instances 244 - memory_dom0: the memory used by the node itself, if available 245 246 """ 247 return self.GetLinuxNodeInfo()
248 249 @classmethod
250 - def GetInstanceConsole(cls, instance, # pylint: disable=W0221 251 hvparams, beparams, root_dir=None):
252 """Return information for connecting to the console of an instance. 253 254 """ 255 if root_dir is None: 256 root_dir = cls._InstanceDir(instance.name) 257 if not os.path.ismount(root_dir): 258 raise HypervisorError("Instance %s is not running" % instance.name) 259 260 return objects.InstanceConsole(instance=instance.name, 261 kind=constants.CONS_SSH, 262 host=instance.primary_node, 263 user=constants.GANETI_RUNAS, 264 command=["chroot", root_dir])
265
266 - def Verify(self):
267 """Verify the hypervisor. 268 269 For the chroot manager, it just checks the existence of the base dir. 270 271 """ 272 if not os.path.exists(self._ROOT_DIR): 273 return "The required directory '%s' does not exist." % self._ROOT_DIR
274 275 @classmethod
276 - def PowercycleNode(cls):
277 """Chroot powercycle, just a wrapper over Linux powercycle. 278 279 """ 280 cls.LinuxPowercycle()
281
282 - def MigrateInstance(self, instance, target, live):
283 """Migrate an instance. 284 285 @type instance: L{objects.Instance} 286 @param instance: the instance to be migrated 287 @type target: string 288 @param target: hostname (usually ip) of the target node 289 @type live: boolean 290 @param live: whether to do a live or non-live migration 291 292 """ 293 raise HypervisorError("Migration not supported by the chroot hypervisor")
294