1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
33 from ganeti import utils
34 from ganeti import objects
35 from ganeti import pathutils
36 from ganeti.hypervisor import hv_base
37 from ganeti.errors import HypervisorError
41 """Chroot manager.
42
43 This not-really hypervisor allows ganeti to manage chroots. It has
44 special behaviour and requirements on the OS definition and the node
45 environemnt:
46 - the start and stop of the chroot environment are done via a
47 script called ganeti-chroot located in the root directory of the
48 first drive, which should be created by the OS definition
49 - this script must accept the start and stop argument and, on
50 shutdown, it should cleanly shutdown the daemons/processes
51 using the chroot
52 - the daemons run in chroot should only bind to the instance IP
53 (to which the OS create script has access via the instance name)
54 - since some daemons in the node could be listening on the wildcard
55 address, some ports might be unavailable
56 - the instance listing will show no memory usage
57 - on shutdown, the chroot manager will try to find all mountpoints
58 under the root dir of the instance and unmount them
59 - instance alive check is based on whether any process is using the chroot
60
61 """
62 _ROOT_DIR = pathutils.RUN_DIR + "/chroot-hypervisor"
63
64 PARAMETERS = {
65 constants.HV_INIT_SCRIPT: (True, utils.IsNormAbsPath,
66 "must be an absolute normalized path",
67 None, None),
68 }
69
73
74 @staticmethod
76 """Check if a directory looks like a live chroot.
77
78 """
79 if not os.path.ismount(path):
80 return False
81 result = utils.RunCmd(["fuser", "-m", path])
82 return not result.failed
83
84 @staticmethod
86 """Return the list of mountpoints under a given path.
87
88 """
89 result = []
90 for _, mountpoint, _, _ in utils.GetMounts():
91 if (mountpoint.startswith(path) and
92 mountpoint != path):
93 result.append(mountpoint)
94
95 result.sort(key=lambda x: x.count("/"), reverse=True)
96 return result
97
98 @classmethod
100 """Return the root directory for an instance.
101
102 """
103 return utils.PathJoin(cls._ROOT_DIR, instance_name)
104
111
113 """Get instance properties.
114
115 @type instance_name: string
116 @param instance_name: the instance name
117
118 @return: (name, id, memory, vcpus, stat, times)
119
120 """
121 dir_name = self._InstanceDir(instance_name)
122 if not self._IsDirLive(dir_name):
123 raise HypervisorError("Instance %s is not running" % instance_name)
124 return (instance_name, 0, 0, 0, 0, 0)
125
127 """Get properties of all instances.
128
129 @return: [(name, id, memory, vcpus, stat, times),...]
130
131 """
132 data = []
133 for file_name in os.listdir(self._ROOT_DIR):
134 path = utils.PathJoin(self._ROOT_DIR, file_name)
135 if self._IsDirLive(path):
136 data.append((file_name, 0, 0, 0, 0, 0))
137 return data
138
139 - def StartInstance(self, instance, block_devices, startup_paused):
140 """Start an instance.
141
142 For the chroot manager, we try to mount the block device and
143 execute '/ganeti-chroot start'.
144
145 """
146 root_dir = self._InstanceDir(instance.name)
147 if not os.path.exists(root_dir):
148 try:
149 os.mkdir(root_dir)
150 except IOError, err:
151 raise HypervisorError("Failed to start instance %s: %s" %
152 (instance.name, err))
153 if not os.path.isdir(root_dir):
154 raise HypervisorError("Needed path %s is not a directory" % root_dir)
155
156 if not os.path.ismount(root_dir):
157 if not block_devices:
158 raise HypervisorError("The chroot manager needs at least one disk")
159
160 sda_dev_path = block_devices[0][1]
161 result = utils.RunCmd(["mount", sda_dev_path, root_dir])
162 if result.failed:
163 raise HypervisorError("Can't mount the chroot dir: %s" % result.output)
164 init_script = instance.hvparams[constants.HV_INIT_SCRIPT]
165 result = utils.RunCmd(["chroot", root_dir, init_script, "start"])
166 if result.failed:
167 raise HypervisorError("Can't run the chroot start script: %s" %
168 result.output)
169
170 - def StopInstance(self, instance, force=False, retry=False, name=None,
171 timeout=None):
172 """Stop an instance.
173
174 This method has complicated cleanup tests, as we must:
175 - try to kill all leftover processes
176 - try to unmount any additional sub-mountpoints
177 - finally unmount the instance dir
178
179 """
180 assert(timeout is None or force is not None)
181
182 if name is None:
183 name = instance.name
184
185 root_dir = self._InstanceDir(name)
186 if not os.path.exists(root_dir) or not self._IsDirLive(root_dir):
187 return
188
189 timeout_cmd = []
190 if timeout is not None:
191 timeout_cmd.extend(["timeout", str(timeout)])
192
193
194 if not retry and not force:
195 result = utils.RunCmd(timeout_cmd.extend(["chroot", root_dir,
196 "/ganeti-chroot", "stop"]))
197 if result.failed:
198 raise HypervisorError("Can't run the chroot stop script: %s" %
199 result.output)
200
201 if not force:
202 utils.RunCmd(["fuser", "-k", "-TERM", "-m", root_dir])
203 else:
204 utils.RunCmd(["fuser", "-k", "-KILL", "-m", root_dir])
205
206 time.sleep(2)
207
208 if self._IsDirLive(root_dir):
209 if force:
210 raise HypervisorError("Can't stop the processes using the chroot")
211 return
212
214 """Cleanup after a stopped instance
215
216 """
217 root_dir = self._InstanceDir(instance_name)
218
219 if not os.path.exists(root_dir):
220 return
221
222 if self._IsDirLive(root_dir):
223 raise HypervisorError("Processes are still using the chroot")
224
225 for mpath in self._GetMountSubdirs(root_dir):
226 utils.RunCmd(["umount", mpath])
227
228 result = utils.RunCmd(["umount", root_dir])
229 if result.failed:
230 msg = ("Processes still alive in the chroot: %s" %
231 utils.RunCmd("fuser -vm %s" % root_dir).output)
232 logging.error(msg)
233 raise HypervisorError("Can't umount the chroot dir: %s (%s)" %
234 (result.output, msg))
235
237 """Reboot an instance.
238
239 This is not (yet) implemented for the chroot manager.
240
241 """
242 raise HypervisorError("The chroot manager doesn't implement the"
243 " reboot functionality")
244
246 """Balloon an instance memory to a certain value.
247
248 @type instance: L{objects.Instance}
249 @param instance: instance to be accepted
250 @type mem: int
251 @param mem: actual memory size to use for instance runtime
252
253 """
254
255 pass
256
258 """Return information about the node.
259
260 This is just a wrapper over the base GetLinuxNodeInfo method.
261
262 @return: a dict with the following keys (values in MiB):
263 - memory_total: the total memory size on the node
264 - memory_free: the available memory on the node for instances
265 - memory_dom0: the memory used by the node itself, if available
266
267 """
268 return self.GetLinuxNodeInfo()
269
270 @classmethod
286
288 """Verify the hypervisor.
289
290 For the chroot manager, it just checks the existence of the base dir.
291
292 @return: Problem description if something is wrong, C{None} otherwise
293
294 """
295 if os.path.exists(self._ROOT_DIR):
296 return None
297 else:
298 return "The required directory '%s' does not exist" % self._ROOT_DIR
299
300 @classmethod
302 """Chroot powercycle, just a wrapper over Linux powercycle.
303
304 """
305 cls.LinuxPowercycle()
306
308 """Migrate an instance.
309
310 @type instance: L{objects.Instance}
311 @param instance: the instance to be migrated
312 @type target: string
313 @param target: hostname (usually ip) of the target node
314 @type live: boolean
315 @param live: whether to do a live or non-live migration
316
317 """
318 raise HypervisorError("Migration not supported by the chroot hypervisor")
319
321 """Get the migration status
322
323 @type instance: L{objects.Instance}
324 @param instance: the instance that is being migrated
325 @rtype: L{objects.MigrationStatus}
326 @return: the status of the current migration (one of
327 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
328 progress info that can be retrieved from the hypervisor
329
330 """
331 raise HypervisorError("Migration not supported by the chroot hypervisor")
332