4 # Copyright (C) 2006, 2007, 2008 Google Inc.
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.
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.
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
30 from ganeti import utils
31 from ganeti import constants
32 from ganeti import errors
33 from ganeti import objects
34 from ganeti import pathutils
35 from ganeti.hypervisor import hv_base
38 class FakeHypervisor(hv_base.BaseHypervisor):
39 """Fake hypervisor interface.
41 This can be used for testing the ganeti code without having to have
42 a real virtualisation software installed.
47 _ROOT_DIR = pathutils.RUN_DIR + "/fake-hypervisor"
50 hv_base.BaseHypervisor.__init__(self)
51 utils.EnsureDirs([(self._ROOT_DIR, constants.RUN_DIRS_MODE)])
53 def ListInstances(self, hvparams=None):
54 """Get the list of running instances.
57 return os.listdir(self._ROOT_DIR)
59 def GetInstanceInfo(self, instance_name, hvparams=None):
60 """Get instance properties.
62 @type instance_name: string
63 @param instance_name: the instance name
64 @type hvparams: dict of strings
65 @param hvparams: hvparams to be used with this instance
67 @return: tuple of (name, id, memory, vcpus, stat, times)
70 file_name = self._InstanceFile(instance_name)
71 if not os.path.exists(file_name):
74 fh = open(file_name, "r")
76 inst_id = fh.readline().strip()
77 memory = utils.TryConvert(int, fh.readline().strip())
78 vcpus = utils.TryConvert(int, fh.readline().strip())
81 return (instance_name, inst_id, memory, vcpus, stat, times)
85 raise errors.HypervisorError("Failed to list instance %s: %s" %
88 def GetAllInstancesInfo(self):
89 """Get properties of all instances.
91 @return: list of tuples (name, id, memory, vcpus, stat, times)
95 for file_name in os.listdir(self._ROOT_DIR):
97 fh = open(utils.PathJoin(self._ROOT_DIR, file_name), "r")
104 inst_id = fh.readline().strip()
105 memory = utils.TryConvert(int, fh.readline().strip())
106 vcpus = utils.TryConvert(int, fh.readline().strip())
111 data.append((file_name, inst_id, memory, vcpus, stat, times))
113 raise errors.HypervisorError("Failed to list instances: %s" % err)
117 def _InstanceFile(cls, instance_name):
118 """Compute the instance file for an instance name.
121 return utils.PathJoin(cls._ROOT_DIR, instance_name)
123 def _IsAlive(self, instance_name):
124 """Checks if an instance is alive.
127 file_name = self._InstanceFile(instance_name)
128 return os.path.exists(file_name)
130 def _MarkUp(self, instance, memory):
131 """Mark the instance as running.
133 This does no checks, which should be done by its callers.
136 file_name = self._InstanceFile(instance.name)
137 fh = file(file_name, "w")
139 fh.write("0\n%d\n%d\n" %
141 instance.beparams[constants.BE_VCPUS]))
145 def _MarkDown(self, instance_name):
146 """Mark the instance as running.
148 This does no checks, which should be done by its callers.
151 file_name = self._InstanceFile(instance_name)
152 utils.RemoveFile(file_name)
154 def StartInstance(self, instance, block_devices, startup_paused):
155 """Start an instance.
157 For the fake hypervisor, it just creates a file in the base dir,
158 creating an exception if it already exists. We don't actually
159 handle race conditions properly, since these are *FAKE* instances.
162 if self._IsAlive(instance.name):
163 raise errors.HypervisorError("Failed to start instance %s: %s" %
164 (instance.name, "already running"))
166 self._MarkUp(instance, self._InstanceStartupMemory(instance))
168 raise errors.HypervisorError("Failed to start instance %s: %s" %
169 (instance.name, err))
171 def StopInstance(self, instance, force=False, retry=False, name=None):
174 For the fake hypervisor, this just removes the file in the base
175 dir, if it exist, otherwise we raise an exception.
180 if not self._IsAlive(name):
181 raise errors.HypervisorError("Failed to stop instance %s: %s" %
182 (name, "not running"))
185 def RebootInstance(self, instance):
186 """Reboot an instance.
188 For the fake hypervisor, this does nothing.
193 def BalloonInstanceMemory(self, instance, mem):
194 """Balloon an instance memory to a certain value.
196 @type instance: L{objects.Instance}
197 @param instance: instance to be accepted
199 @param mem: actual memory size to use for instance runtime
202 if not self._IsAlive(instance.name):
203 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
204 (instance.name, "not running"))
206 self._MarkUp(instance, mem)
207 except EnvironmentError, err:
208 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
209 (instance.name, utils.ErrnoOrStr(err)))
211 def GetNodeInfo(self, hvparams=None):
212 """Return information about the node.
214 This is just a wrapper over the base GetLinuxNodeInfo method.
216 @type hvparams: dict of strings
217 @param hvparams: hypervisor parameters, not used in this class
219 @return: a dict with the following keys (values in MiB):
220 - memory_total: the total memory size on the node
221 - memory_free: the available memory on the node for instances
222 - memory_dom0: the memory used by the node itself, if available
225 result = self.GetLinuxNodeInfo()
226 # substract running instances
227 all_instances = self.GetAllInstancesInfo()
228 result["memory_free"] -= min(result["memory_free"],
229 sum([row[2] for row in all_instances]))
233 def GetInstanceConsole(cls, instance, hvparams, beparams):
234 """Return information for connecting to the console of an instance.
237 return objects.InstanceConsole(instance=instance.name,
238 kind=constants.CONS_MESSAGE,
239 message=("Console not available for fake"
242 def Verify(self, hvparams=None):
243 """Verify the hypervisor.
245 For the fake hypervisor, it just checks the existence of the base
248 @type hvparams: dict of strings
249 @param hvparams: hypervisor parameters to be verified against; not used
252 @return: Problem description if something is wrong, C{None} otherwise
255 if os.path.exists(self._ROOT_DIR):
258 return "The required directory '%s' does not exist" % self._ROOT_DIR
261 def PowercycleNode(cls):
262 """Fake hypervisor powercycle, just a wrapper over Linux powercycle.
265 cls.LinuxPowercycle()
267 def AcceptInstance(self, instance, info, target):
268 """Prepare to accept an instance.
270 @type instance: L{objects.Instance}
271 @param instance: instance to be accepted
273 @param info: instance info, not used
275 @param target: target host (usually ip), on this node
278 if self._IsAlive(instance.name):
279 raise errors.HypervisorError("Can't accept instance, already running")
281 def MigrateInstance(self, instance, target, live):
282 """Migrate an instance.
284 @type instance: L{objects.Instance}
285 @param instance: the instance to be migrated
287 @param target: hostname (usually ip) of the target node
289 @param live: whether to do a live or non-live migration
292 logging.debug("Fake hypervisor migrating %s to %s (live=%s)",
293 instance, target, live)
295 def FinalizeMigrationDst(self, instance, info, success):
296 """Finalize the instance migration on the target node.
298 For the fake hv, this just marks the instance up.
300 @type instance: L{objects.Instance}
301 @param instance: instance whose migration is being finalized
302 @type info: string/data (opaque)
303 @param info: migration information, from the source node
304 @type success: boolean
305 @param success: whether the migration was a success or a failure
309 self._MarkUp(instance, self._InstanceStartupMemory(instance))
312 self._MarkDown(instance.name)
314 def PostMigrationCleanup(self, instance):
315 """Clean-up after a migration.
317 To be executed on the source node.
319 @type instance: L{objects.Instance}
320 @param instance: the instance that was migrated
325 def FinalizeMigrationSource(self, instance, success, live):
326 """Finalize the instance migration on the source node.
328 @type instance: L{objects.Instance}
329 @param instance: the instance that was migrated
331 @param success: whether the migration succeeded or not
333 @param live: whether the user requested a live migration or not
336 # pylint: disable=W0613
338 self._MarkDown(instance.name)
340 def GetMigrationStatus(self, instance):
341 """Get the migration status
343 The fake hypervisor migration always succeeds.
345 @type instance: L{objects.Instance}
346 @param instance: the instance that is being migrated
347 @rtype: L{objects.MigrationStatus}
348 @return: the status of the current migration (one of
349 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
350 progress info that can be retrieved from the hypervisor
353 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)