1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Base class for all hypervisors
23
24 The syntax for the _CHECK variables and the contents of the PARAMETERS
25 dict is the same, see the docstring for L{BaseHypervisor.PARAMETERS}.
26
27 @var _FILE_CHECK: stub for file checks, without the required flag
28 @var _DIR_CHECK: stub for directory checks, without the required flag
29 @var REQ_FILE_CHECK: mandatory file parameter
30 @var OPT_FILE_CHECK: optional file parameter
31 @var REQ_DIR_CHECK: mandatory directory parametr
32 @var OPT_DIR_CHECK: optional directory parameter
33 @var NO_CHECK: parameter without any checks at all
34 @var REQUIRED_CHECK: parameter required to exist (and non-false), but
35 without other checks; beware that this can't be used for boolean
36 parameters, where you should use NO_CHECK or a custom checker
37
38 """
39
40 import os
41 import re
42 import logging
43
44
45 from ganeti import errors
46 from ganeti import utils
47 from ganeti import constants
62
77
78
79
80
81
82
83 _FILE_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
84 os.path.isfile, "not found or not a file")
85
86
87 _DIR_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
88 os.path.isdir, "not found or not a directory")
89
90
91
92 _CPU_MASK_CHECK = (_IsCpuMaskWellFormed,
93 "CPU mask definition is not well-formed",
94 None, None)
95
96
97 _MULTI_CPU_MASK_CHECK = (_IsMultiCpuMaskWellFormed,
98 "Multiple CPU mask definition is not well-formed",
99 None, None)
100
101
102 _NET_PORT_CHECK = (lambda x: 0 < x < 65535, "invalid port number",
103 None, None)
104
105
106 _NONNEGATIVE_INT_CHECK = (lambda x: x >= 0, "cannot be negative", None, None)
107
108
109 REQ_FILE_CHECK = (True, ) + _FILE_CHECK
110 OPT_FILE_CHECK = (False, ) + _FILE_CHECK
111 REQ_DIR_CHECK = (True, ) + _DIR_CHECK
112 OPT_DIR_CHECK = (False, ) + _DIR_CHECK
113 REQ_NET_PORT_CHECK = (True, ) + _NET_PORT_CHECK
114 OPT_NET_PORT_CHECK = (False, ) + _NET_PORT_CHECK
115 REQ_CPU_MASK_CHECK = (True, ) + _CPU_MASK_CHECK
116 OPT_CPU_MASK_CHECK = (False, ) + _CPU_MASK_CHECK
117 REQ_MULTI_CPU_MASK_CHECK = (True, ) + _MULTI_CPU_MASK_CHECK
118 OPT_MULTI_CPU_MASK_CHECK = (False, ) + _MULTI_CPU_MASK_CHECK
119 REQ_NONNEGATIVE_INT_CHECK = (True, ) + _NONNEGATIVE_INT_CHECK
120 OPT_NONNEGATIVE_INT_CHECK = (False, ) + _NONNEGATIVE_INT_CHECK
121
122
123 NO_CHECK = (False, None, None, None, None)
124
125
126 REQUIRED_CHECK = (True, None, None, None, None)
127
128
129 MIGRATION_MODE_CHECK = (True, lambda x: x in constants.HT_MIGRATION_MODES,
130 "invalid migration mode", None, None)
134 """Builds parameter checker for set membership.
135
136 @type required: boolean
137 @param required: whether this is a required parameter
138 @type my_set: tuple, list or set
139 @param my_set: allowed values set
140
141 """
142 fn = lambda x: x in my_set
143 err = ("The value must be one of: %s" % utils.CommaJoin(my_set))
144 return (required, fn, err, None, None)
145
148 """Abstract virtualisation technology interface
149
150 The goal is that all aspects of the virtualisation technology are
151 abstracted away from the rest of code.
152
153 @cvar PARAMETERS: a dict of parameter name: check type; the check type is
154 a five-tuple containing:
155 - the required flag (boolean)
156 - a function to check for syntax, that will be used in
157 L{CheckParameterSyntax}, in the master daemon process
158 - an error message for the above function
159 - a function to check for parameter validity on the remote node,
160 in the L{ValidateParameters} function
161 - an error message for the above function
162 @type CAN_MIGRATE: boolean
163 @cvar CAN_MIGRATE: whether this hypervisor can do migration (either
164 live or non-live)
165
166 """
167 PARAMETERS = {}
168 ANCILLARY_FILES = []
169 ANCILLARY_FILES_OPT = []
170 CAN_MIGRATE = False
171
172 - def StartInstance(self, instance, block_devices, startup_paused):
173 """Start an instance."""
174 raise NotImplementedError
175
176 - def StopInstance(self, instance, force=False, retry=False, name=None,
177 timeout=None):
178 """Stop an instance
179
180 @type instance: L{objects.Instance}
181 @param instance: instance to stop
182 @type force: boolean
183 @param force: whether to do a "hard" stop (destroy)
184 @type retry: boolean
185 @param retry: whether this is just a retry call
186 @type name: string or None
187 @param name: if this parameter is passed, the the instance object
188 should not be used (will be passed as None), and the shutdown
189 must be done by name only
190 @type timeout: int or None
191 @param timeout: if the parameter is not None, a soft shutdown operation will
192 be killed after the specified number of seconds. A hard (forced)
193 shutdown cannot have a timeout
194
195 """
196 raise NotImplementedError
197
199 """Cleanup after a stopped instance
200
201 This is an optional method, used by hypervisors that need to cleanup after
202 an instance has been stopped.
203
204 @type instance_name: string
205 @param instance_name: instance name to cleanup after
206
207 """
208 pass
209
211 """Reboot an instance."""
212 raise NotImplementedError
213
215 """Get the list of running instances."""
216 raise NotImplementedError
217
219 """Get instance properties.
220
221 @type instance_name: string
222 @param instance_name: the instance name
223 @type hvparams: dict of strings
224 @param hvparams: hvparams to be used with this instance
225
226 @return: tuple (name, id, memory, vcpus, state, times)
227
228 """
229 raise NotImplementedError
230
232 """Get properties of all instances.
233
234 @type hvparams: dict of strings
235 @param hvparams: hypervisor parameter
236 @return: list of tuples (name, id, memory, vcpus, stat, times)
237
238 """
239 raise NotImplementedError
240
242 """Return information about the node.
243
244 @type hvparams: dict of strings
245 @param hvparams: hypervisor parameters
246
247 @return: a dict with at least the following keys (memory values in MiB):
248 - memory_total: the total memory size on the node
249 - memory_free: the available memory on the node for instances
250 - memory_dom0: the memory used by the node itself, if available
251 - cpu_total: total number of CPUs
252 - cpu_dom0: number of CPUs used by the node OS
253 - cpu_nodes: number of NUMA domains
254 - cpu_sockets: number of physical CPU sockets
255
256 """
257 raise NotImplementedError
258
259 @classmethod
261 """Return information for connecting to the console of an instance.
262
263 """
264 raise NotImplementedError
265
266 @classmethod
268 """Return a list of ancillary files to be copied to all nodes as ancillary
269 configuration files.
270
271 @rtype: (list of absolute paths, list of absolute paths)
272 @return: (all files, optional files)
273
274 """
275
276
277 assert set(cls.ANCILLARY_FILES).issuperset(cls.ANCILLARY_FILES_OPT), \
278 "Optional ancillary files must be a subset of ancillary files"
279
280 return (cls.ANCILLARY_FILES, cls.ANCILLARY_FILES_OPT)
281
282 - def Verify(self, hvparams=None):
283 """Verify the hypervisor.
284
285 @type hvparams: dict of strings
286 @param hvparams: hypervisor parameters to be verified against
287
288 @return: Problem description if something is wrong, C{None} otherwise
289
290 """
291 raise NotImplementedError
292
294 """Get instance information to perform a migration.
295
296 By default assume no information is needed.
297
298 @type instance: L{objects.Instance}
299 @param instance: instance to be migrated
300 @rtype: string/data (opaque)
301 @return: instance migration information - serialized form
302
303 """
304 return ""
305
307 """Prepare to accept an instance.
308
309 By default assume no preparation is needed.
310
311 @type instance: L{objects.Instance}
312 @param instance: instance to be accepted
313 @type info: string/data (opaque)
314 @param info: migration information, from the source node
315 @type target: string
316 @param target: target host (usually ip), on this node
317
318 """
319 pass
320
322 """Balloon an instance memory to a certain value.
323
324 @type instance: L{objects.Instance}
325 @param instance: instance to be accepted
326 @type mem: int
327 @param mem: actual memory size to use for instance runtime
328
329 """
330 raise NotImplementedError
331
333 """Finalize the instance migration on the target node.
334
335 Should finalize or revert any preparation done to accept the instance.
336 Since by default we do no preparation, we also don't have anything to do
337
338 @type instance: L{objects.Instance}
339 @param instance: instance whose migration is being finalized
340 @type info: string/data (opaque)
341 @param info: migration information, from the source node
342 @type success: boolean
343 @param success: whether the migration was a success or a failure
344
345 """
346 pass
347
349 """Migrate an instance.
350
351 @type cluster_name: string
352 @param cluster_name: name of the cluster
353 @type instance: L{objects.Instance}
354 @param instance: the instance to be migrated
355 @type target: string
356 @param target: hostname (usually ip) of the target node
357 @type live: boolean
358 @param live: whether to do a live or non-live migration
359
360 """
361 raise NotImplementedError
362
364 """Finalize the instance migration on the source node.
365
366 @type instance: L{objects.Instance}
367 @param instance: the instance that was migrated
368 @type success: bool
369 @param success: whether the migration succeeded or not
370 @type live: bool
371 @param live: whether the user requested a live migration or not
372
373 """
374 pass
375
377 """Get the migration status
378
379 @type instance: L{objects.Instance}
380 @param instance: the instance that is being migrated
381 @rtype: L{objects.MigrationStatus}
382 @return: the status of the current migration (one of
383 L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
384 progress info that can be retrieved from the hypervisor
385
386 """
387 raise NotImplementedError
388
390 """Get the correct startup memory for an instance
391
392 This function calculates how much memory an instance should be started
393 with, making sure it's a value between the minimum and the maximum memory,
394 but also trying to use no more than the current free memory on the node.
395
396 @type instance: L{objects.Instance}
397 @param instance: the instance that is being started
398 @rtype: integer
399 @return: memory the instance should be started with
400
401 """
402 free_memory = self.GetNodeInfo(hvparams=instance.hvparams)["memory_free"]
403 max_start_mem = min(instance.beparams[constants.BE_MAXMEM], free_memory)
404 start_mem = max(instance.beparams[constants.BE_MINMEM], max_start_mem)
405 return start_mem
406
407 @classmethod
409 """Check the given parameters for validity.
410
411 This should check the passed set of parameters for
412 validity. Classes should extend, not replace, this function.
413
414 @type hvparams: dict
415 @param hvparams: dictionary with parameter names/value
416 @raise errors.HypervisorError: when a parameter is not valid
417
418 """
419 for key in hvparams:
420 if key not in cls.PARAMETERS:
421 raise errors.HypervisorError("Parameter '%s' is not supported" % key)
422
423
424 for name, (required, check_fn, errstr, _, _) in cls.PARAMETERS.items():
425 if name not in hvparams:
426 raise errors.HypervisorError("Parameter '%s' is missing" % name)
427 value = hvparams[name]
428 if not required and not value:
429 continue
430 if not value:
431 raise errors.HypervisorError("Parameter '%s' is required but"
432 " is currently not defined" % (name, ))
433 if check_fn is not None and not check_fn(value):
434 raise errors.HypervisorError("Parameter '%s' fails syntax"
435 " check: %s (current value: '%s')" %
436 (name, errstr, value))
437
438 @classmethod
440 """Check the given parameters for validity.
441
442 This should check the passed set of parameters for
443 validity. Classes should extend, not replace, this function.
444
445 @type hvparams: dict
446 @param hvparams: dictionary with parameter names/value
447 @raise errors.HypervisorError: when a parameter is not valid
448
449 """
450 for name, (required, _, _, check_fn, errstr) in cls.PARAMETERS.items():
451 value = hvparams[name]
452 if not required and not value:
453 continue
454 if check_fn is not None and not check_fn(value):
455 raise errors.HypervisorError("Parameter '%s' fails"
456 " validation: %s (current value: '%s')" %
457 (name, errstr, value))
458
459 @classmethod
461 """Hard powercycle a node using hypervisor specific methods.
462
463 This method should hard powercycle the node, using whatever
464 methods the hypervisor provides. Note that this means that all
465 instances running on the node must be stopped too.
466
467 @type hvparams: dict of strings
468 @param hvparams: hypervisor params to be used on this node
469
470 """
471 raise NotImplementedError
472
473 @staticmethod
475 """For linux systems, return actual OS information.
476
477 This is an abstraction for all non-hypervisor-based classes, where
478 the node actually sees all the memory and CPUs via the /proc
479 interface and standard commands. The other case if for example
480 xen, where you only see the hardware resources via xen-specific
481 tools.
482
483 @param meminfo: name of the file containing meminfo
484 @type meminfo: string
485 @param cpuinfo: name of the file containing cpuinfo
486 @type cpuinfo: string
487 @return: a dict with the following keys (values in MiB):
488 - memory_total: the total memory size on the node
489 - memory_free: the available memory on the node for instances
490 - memory_dom0: the memory used by the node itself, if available
491 - cpu_total: total number of CPUs
492 - cpu_dom0: number of CPUs used by the node OS
493 - cpu_nodes: number of NUMA domains
494 - cpu_sockets: number of physical CPU sockets
495
496 """
497 try:
498 data = utils.ReadFile(meminfo).splitlines()
499 except EnvironmentError, err:
500 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
501
502 result = {}
503 sum_free = 0
504 try:
505 for line in data:
506 splitfields = line.split(":", 1)
507
508 if len(splitfields) > 1:
509 key = splitfields[0].strip()
510 val = splitfields[1].strip()
511 if key == "MemTotal":
512 result["memory_total"] = int(val.split()[0]) / 1024
513 elif key in ("MemFree", "Buffers", "Cached"):
514 sum_free += int(val.split()[0]) / 1024
515 elif key == "Active":
516 result["memory_dom0"] = int(val.split()[0]) / 1024
517 except (ValueError, TypeError), err:
518 raise errors.HypervisorError("Failed to compute memory usage: %s" %
519 (err,))
520 result["memory_free"] = sum_free
521
522 cpu_total = 0
523 try:
524 fh = open(cpuinfo)
525 try:
526 cpu_total = len(re.findall(r"(?m)^processor\s*:\s*[0-9]+\s*$",
527 fh.read()))
528 finally:
529 fh.close()
530 except EnvironmentError, err:
531 raise errors.HypervisorError("Failed to list node info: %s" % (err,))
532 result["cpu_total"] = cpu_total
533
534 result["cpu_dom0"] = cpu_total
535
536 result["cpu_nodes"] = 1
537 result["cpu_sockets"] = 1
538
539 return result
540
541 @classmethod
543 """Linux-specific powercycle method.
544
545 """
546 try:
547 fd = os.open("/proc/sysrq-trigger", os.O_WRONLY)
548 try:
549 os.write(fd, "b")
550 finally:
551 fd.close()
552 except OSError:
553 logging.exception("Can't open the sysrq-trigger file")
554 result = utils.RunCmd(["reboot", "-n", "-f"])
555 if not result:
556 logging.error("Can't run shutdown: %s", result.output)
557
558 @staticmethod
571