Remove XEN_CMD from constants, adjust to PowercycleNode
[ganeti-local] / lib / hypervisor / hv_fake.py
1 #
2 #
3
4 # Copyright (C) 2006, 2007, 2008 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 """Fake hypervisor
23
24 """
25
26 import os
27 import os.path
28 import logging
29
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
36
37
38 class FakeHypervisor(hv_base.BaseHypervisor):
39   """Fake hypervisor interface.
40
41   This can be used for testing the ganeti code without having to have
42   a real virtualisation software installed.
43
44   """
45   CAN_MIGRATE = True
46
47   _ROOT_DIR = pathutils.RUN_DIR + "/fake-hypervisor"
48
49   def __init__(self):
50     hv_base.BaseHypervisor.__init__(self)
51     utils.EnsureDirs([(self._ROOT_DIR, constants.RUN_DIRS_MODE)])
52
53   def ListInstances(self, hvparams=None):
54     """Get the list of running instances.
55
56     """
57     return os.listdir(self._ROOT_DIR)
58
59   def GetInstanceInfo(self, instance_name, hvparams=None):
60     """Get instance properties.
61
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
66
67     @return: tuple of (name, id, memory, vcpus, stat, times)
68
69     """
70     file_name = self._InstanceFile(instance_name)
71     if not os.path.exists(file_name):
72       return None
73     try:
74       fh = open(file_name, "r")
75       try:
76         inst_id = fh.readline().strip()
77         memory = utils.TryConvert(int, fh.readline().strip())
78         vcpus = utils.TryConvert(int, fh.readline().strip())
79         stat = "---b-"
80         times = "0"
81         return (instance_name, inst_id, memory, vcpus, stat, times)
82       finally:
83         fh.close()
84     except IOError, err:
85       raise errors.HypervisorError("Failed to list instance %s: %s" %
86                                    (instance_name, err))
87
88   def GetAllInstancesInfo(self, hvparams=None):
89     """Get properties of all instances.
90
91     @type hvparams: dict of strings
92     @param hvparams: hypervisor parameter
93     @return: list of tuples (name, id, memory, vcpus, stat, times)
94
95     """
96     data = []
97     for file_name in os.listdir(self._ROOT_DIR):
98       try:
99         fh = open(utils.PathJoin(self._ROOT_DIR, file_name), "r")
100         inst_id = "-1"
101         memory = 0
102         vcpus = 1
103         stat = "-----"
104         times = "-1"
105         try:
106           inst_id = fh.readline().strip()
107           memory = utils.TryConvert(int, fh.readline().strip())
108           vcpus = utils.TryConvert(int, fh.readline().strip())
109           stat = "---b-"
110           times = "0"
111         finally:
112           fh.close()
113         data.append((file_name, inst_id, memory, vcpus, stat, times))
114       except IOError, err:
115         raise errors.HypervisorError("Failed to list instances: %s" % err)
116     return data
117
118   @classmethod
119   def _InstanceFile(cls, instance_name):
120     """Compute the instance file for an instance name.
121
122     """
123     return utils.PathJoin(cls._ROOT_DIR, instance_name)
124
125   def _IsAlive(self, instance_name):
126     """Checks if an instance is alive.
127
128     """
129     file_name = self._InstanceFile(instance_name)
130     return os.path.exists(file_name)
131
132   def _MarkUp(self, instance, memory):
133     """Mark the instance as running.
134
135     This does no checks, which should be done by its callers.
136
137     """
138     file_name = self._InstanceFile(instance.name)
139     fh = file(file_name, "w")
140     try:
141       fh.write("0\n%d\n%d\n" %
142                (memory,
143                 instance.beparams[constants.BE_VCPUS]))
144     finally:
145       fh.close()
146
147   def _MarkDown(self, instance_name):
148     """Mark the instance as running.
149
150     This does no checks, which should be done by its callers.
151
152     """
153     file_name = self._InstanceFile(instance_name)
154     utils.RemoveFile(file_name)
155
156   def StartInstance(self, instance, block_devices, startup_paused):
157     """Start an instance.
158
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.
162
163     """
164     if self._IsAlive(instance.name):
165       raise errors.HypervisorError("Failed to start instance %s: %s" %
166                                    (instance.name, "already running"))
167     try:
168       self._MarkUp(instance, self._InstanceStartupMemory(instance))
169     except IOError, err:
170       raise errors.HypervisorError("Failed to start instance %s: %s" %
171                                    (instance.name, err))
172
173   def StopInstance(self, instance, force=False, retry=False, name=None):
174     """Stop an instance.
175
176     For the fake hypervisor, this just removes the file in the base
177     dir, if it exist, otherwise we raise an exception.
178
179     """
180     if name is None:
181       name = instance.name
182     if not self._IsAlive(name):
183       raise errors.HypervisorError("Failed to stop instance %s: %s" %
184                                    (name, "not running"))
185     self._MarkDown(name)
186
187   def RebootInstance(self, instance):
188     """Reboot an instance.
189
190     For the fake hypervisor, this does nothing.
191
192     """
193     return
194
195   def BalloonInstanceMemory(self, instance, mem):
196     """Balloon an instance memory to a certain value.
197
198     @type instance: L{objects.Instance}
199     @param instance: instance to be accepted
200     @type mem: int
201     @param mem: actual memory size to use for instance runtime
202
203     """
204     if not self._IsAlive(instance.name):
205       raise errors.HypervisorError("Failed to balloon memory for %s: %s" %
206                                    (instance.name, "not running"))
207     try:
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)))
212
213   def GetNodeInfo(self, hvparams=None):
214     """Return information about the node.
215
216     This is just a wrapper over the base GetLinuxNodeInfo method.
217
218     @type hvparams: dict of strings
219     @param hvparams: hypervisor parameters, not used in this class
220
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
225
226     """
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]))
232     return result
233
234   @classmethod
235   def GetInstanceConsole(cls, instance, hvparams, beparams):
236     """Return information for connecting to the console of an instance.
237
238     """
239     return objects.InstanceConsole(instance=instance.name,
240                                    kind=constants.CONS_MESSAGE,
241                                    message=("Console not available for fake"
242                                             " hypervisor"))
243
244   def Verify(self, hvparams=None):
245     """Verify the hypervisor.
246
247     For the fake hypervisor, it just checks the existence of the base
248     dir.
249
250     @type hvparams: dict of strings
251     @param hvparams: hypervisor parameters to be verified against; not used
252       for fake hypervisors
253
254     @return: Problem description if something is wrong, C{None} otherwise
255
256     """
257     if os.path.exists(self._ROOT_DIR):
258       return None
259     else:
260       return "The required directory '%s' does not exist" % self._ROOT_DIR
261
262   @classmethod
263   def PowercycleNode(cls, hvparams=None):
264     """Fake hypervisor powercycle, just a wrapper over Linux powercycle.
265
266     @type hvparams: dict of strings
267     @param hvparams: hypervisor params to be used on this node
268
269     """
270     cls.LinuxPowercycle()
271
272   def AcceptInstance(self, instance, info, target):
273     """Prepare to accept an instance.
274
275     @type instance: L{objects.Instance}
276     @param instance: instance to be accepted
277     @type info: string
278     @param info: instance info, not used
279     @type target: string
280     @param target: target host (usually ip), on this node
281
282     """
283     if self._IsAlive(instance.name):
284       raise errors.HypervisorError("Can't accept instance, already running")
285
286   def MigrateInstance(self, instance, target, live):
287     """Migrate an instance.
288
289     @type instance: L{objects.Instance}
290     @param instance: the instance to be migrated
291     @type target: string
292     @param target: hostname (usually ip) of the target node
293     @type live: boolean
294     @param live: whether to do a live or non-live migration
295
296     """
297     logging.debug("Fake hypervisor migrating %s to %s (live=%s)",
298                   instance, target, live)
299
300   def FinalizeMigrationDst(self, instance, info, success):
301     """Finalize the instance migration on the target node.
302
303     For the fake hv, this just marks the instance up.
304
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
311
312     """
313     if success:
314       self._MarkUp(instance, self._InstanceStartupMemory(instance))
315     else:
316       # ensure it's down
317       self._MarkDown(instance.name)
318
319   def PostMigrationCleanup(self, instance):
320     """Clean-up after a migration.
321
322     To be executed on the source node.
323
324     @type instance: L{objects.Instance}
325     @param instance: the instance that was migrated
326
327     """
328     pass
329
330   def FinalizeMigrationSource(self, instance, success, live):
331     """Finalize the instance migration on the source node.
332
333     @type instance: L{objects.Instance}
334     @param instance: the instance that was migrated
335     @type success: bool
336     @param success: whether the migration succeeded or not
337     @type live: bool
338     @param live: whether the user requested a live migration or not
339
340     """
341     # pylint: disable=W0613
342     if success:
343       self._MarkDown(instance.name)
344
345   def GetMigrationStatus(self, instance):
346     """Get the migration status
347
348     The fake hypervisor migration always succeeds.
349
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
356
357     """
358     return objects.MigrationStatus(status=constants.HV_MIGRATION_COMPLETED)