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