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, hvparams=None):
89 """Get properties of all instances.
91 @type hvparams: dict of strings
92 @param hvparams: hypervisor parameter
93 @return: list of tuples (name, id, memory, vcpus, stat, times)
97 for file_name in os.listdir(self._ROOT_DIR):
99 fh = open(utils.PathJoin(self._ROOT_DIR, file_name), "r")
106 inst_id = fh.readline().strip()
107 memory = utils.TryConvert(int, fh.readline().strip())
108 vcpus = utils.TryConvert(int, fh.readline().strip())
113 data.append((file_name, inst_id, memory, vcpus, stat, times))
115 raise errors.HypervisorError("Failed to list instances: %s" % err)
119 def _InstanceFile(cls, instance_name):
120 """Compute the instance file for an instance name.
123 return utils.PathJoin(cls._ROOT_DIR, instance_name)
125 def _IsAlive(self, instance_name):
126 """Checks if an instance is alive.
129 file_name = self._InstanceFile(instance_name)
130 return os.path.exists(file_name)
132 def _MarkUp(self, instance, memory):
133 """Mark the instance as running.
135 This does no checks, which should be done by its callers.
138 file_name = self._InstanceFile(instance.name)
139 fh = file(file_name, "w")
141 fh.write("0\n%d\n%d\n" %
143 instance.beparams[constants.BE_VCPUS]))
147 def _MarkDown(self, instance_name):
148 """Mark the instance as running.
150 This does no checks, which should be done by its callers.
153 file_name = self._InstanceFile(instance_name)
154 utils.RemoveFile(file_name)
156 def StartInstance(self, instance, block_devices, startup_paused):
157 """Start an instance.
159 For the fake hypervisor, it just creates a file in the base dir,
160 creating an exception if it already exists. We don't actually
161 handle race conditions properly, since these are *FAKE* instances.
164 if self._IsAlive(instance.name):
165 raise errors.HypervisorError("Failed to start instance %s: %s" %
166 (instance.name, "already running"))
168 self._MarkUp(instance, self._InstanceStartupMemory(instance))
170 raise errors.HypervisorError("Failed to start instance %s: %s" %
171 (instance.name, err))
173 def StopInstance(self, instance, force=False, retry=False, name=None):
176 For the fake hypervisor, this just removes the file in the base
177 dir, if it exist, otherwise we raise an exception.
182 if not self._IsAlive(name):
183 raise errors.HypervisorError("Failed to stop instance %s: %s" %
184 (name, "not running"))
187 def RebootInstance(self, instance):
188 """Reboot an instance.
190 For the fake hypervisor, this does nothing.
195 def BalloonInstanceMemory(self, instance, mem):
196 """Balloon an instance memory to a certain value.
198 @type instance: L{objects.Instance}
199 @param instance: instance to be accepted
201 @param mem: actual memory size to use for instance runtime
204 if not self._IsAlive(instance.name):
205 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
206 (instance.name, "not running"))
208 self._MarkUp(instance, mem)
209 except EnvironmentError, err:
210 raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
211 (instance.name, utils.ErrnoOrStr(err)))
213 def GetNodeInfo(self, hvparams=None):
214 """Return information about the node.
216 This is just a wrapper over the base GetLinuxNodeInfo method.
218 @type hvparams: dict of strings
219 @param hvparams: hypervisor parameters, not used in this class
221 @return: a dict with the following keys (values in MiB):
222 - memory_total: the total memory size on the node
223 - memory_free: the available memory on the node for instances
224 - memory_dom0: the memory used by the node itself, if available
227 result = self.GetLinuxNodeInfo()
228 # substract running instances
229 all_instances = self.GetAllInstancesInfo()
230 result["memory_free"] -= min(result["memory_free"],
231 sum([row[2] for row in all_instances]))
235 def GetInstanceConsole(cls, instance, hvparams, beparams):
236 """Return information for connecting to the console of an instance.
239 return objects.InstanceConsole(instance=instance.name,
240 kind=constants.CONS_MESSAGE,
241 message=("Console not available for fake"
244 def Verify(self, hvparams=None):
245 """Verify the hypervisor.
247 For the fake hypervisor, it just checks the existence of the base
250 @type hvparams: dict of strings
251 @param hvparams: hypervisor parameters to be verified against; not used
254 @return: Problem description if something is wrong, C{None} otherwise
257 if os.path.exists(self._ROOT_DIR):
260 return "The required directory '%s' does not exist" % self._ROOT_DIR
263 def PowercycleNode(cls, hvparams=None):
264 """Fake hypervisor powercycle, just a wrapper over Linux powercycle.
266 @type hvparams: dict of strings
267 @param hvparams: hypervisor params to be used on this node
270 cls.LinuxPowercycle()
272 def AcceptInstance(self, instance, info, target):
273 """Prepare to accept an instance.
275 @type instance: L{objects.Instance}
276 @param instance: instance to be accepted
278 @param info: instance info, not used
280 @param target: target host (usually ip), on this node
283 if self._IsAlive(instance.name):
284 raise errors.HypervisorError("Can't accept instance, already running")
286 def MigrateInstance(self, instance, target, live):
287 """Migrate an instance.
289 @type instance: L{objects.Instance}
290 @param instance: the instance to be migrated
292 @param target: hostname (usually ip) of the target node
294 @param live: whether to do a live or non-live migration
297 logging.debug("Fake hypervisor migrating %s to %s (live=%s)",
298 instance, target, live)
300 def FinalizeMigrationDst(self, instance, info, success):
301 """Finalize the instance migration on the target node.
303 For the fake hv, this just marks the instance up.
305 @type instance: L{objects.Instance}
306 @param instance: instance whose migration is being finalized
307 @type info: string/data (opaque)
308 @param info: migration information, from the source node
309 @type success: boolean
310 @param success: whether the migration was a success or a failure
314 self._MarkUp(instance, self._InstanceStartupMemory(instance))
317 self._MarkDown(instance.name)
319 def PostMigrationCleanup(self, instance):
320 """Clean-up after a migration.
322 To be executed on the source node.
324 @type instance: L{objects.Instance}
325 @param instance: the instance that was migrated
330 def FinalizeMigrationSource(self, instance, success, live):
331 """Finalize the instance migration on the source node.
333 @type instance: L{objects.Instance}
334 @param instance: the instance that was migrated
336 @param success: whether the migration succeeded or not
338 @param live: whether the user requested a live migration or not
341 # pylint: disable=W0613
343 self._MarkDown(instance.name)
345 def GetMigrationStatus(self, instance):
346 """Get the migration status
348 The fake hypervisor migration always succeeds.
350 @type instance: L{objects.Instance}
351 @param instance: the instance that is being migrated
352 @rtype: L{objects.MigrationStatus}
353 @return: the status of the current migration (one of
354 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
355 progress info that can be retrieved from the hypervisor
358 return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)