Revision 30e42c4e lib/hypervisor/hv_kvm.py
b/lib/hypervisor/hv_kvm.py | ||
---|---|---|
28 | 28 |
import re |
29 | 29 |
import tempfile |
30 | 30 |
import time |
31 |
import logging |
|
31 | 32 |
from cStringIO import StringIO |
32 | 33 |
|
33 | 34 |
from ganeti import utils |
... | ... | |
53 | 54 |
constants.HV_ACPI, |
54 | 55 |
] |
55 | 56 |
|
57 |
_MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)', |
|
58 |
re.M | re.I) |
|
59 |
|
|
56 | 60 |
def __init__(self): |
57 | 61 |
hv_base.BaseHypervisor.__init__(self) |
58 | 62 |
# Let's make sure the directories we need exist, even if the RUN_DIR lives |
... | ... | |
284 | 288 |
serialized_form = serializer.Dump((kvm_cmd, serialized_nics)) |
285 | 289 |
self._WriteKVMRuntime(instance.name, serialized_form) |
286 | 290 |
|
287 |
def _LoadKVMRuntime(self, instance): |
|
291 |
def _LoadKVMRuntime(self, instance, serialized_runtime=None):
|
|
288 | 292 |
"""Load an instance's KVM runtime |
289 | 293 |
|
290 | 294 |
""" |
291 |
serialized_form = self._ReadKVMRuntime(instance.name) |
|
292 |
loaded_runtime = serializer.Load(serialized_form) |
|
295 |
if not serialized_runtime: |
|
296 |
serialized_runtime = self._ReadKVMRuntime(instance.name) |
|
297 |
loaded_runtime = serializer.Load(serialized_runtime) |
|
293 | 298 |
kvm_cmd, serialized_nics = loaded_runtime |
294 | 299 |
kvm_nics = [objects.NIC.FromDict(snic) for snic in serialized_nics] |
295 | 300 |
return (kvm_cmd, kvm_nics) |
296 | 301 |
|
297 |
def _ExecuteKVMRuntime(self, instance, kvm_runtime): |
|
302 |
def _ExecuteKVMRuntime(self, instance, kvm_runtime, incoming=None):
|
|
298 | 303 |
"""Execute a KVM cmd, after completing it with some last minute data |
299 | 304 |
|
305 |
@type incoming: tuple of strings |
|
306 |
@param incoming: (target_host_ip, port) |
|
307 |
|
|
300 | 308 |
""" |
301 | 309 |
pidfile, pid, alive = self._InstancePidAlive(instance.name) |
302 | 310 |
if alive: |
... | ... | |
317 | 325 |
kvm_cmd.extend(['-net', 'tap,script=%s' % script]) |
318 | 326 |
temp_files.append(script) |
319 | 327 |
|
328 |
if incoming: |
|
329 |
target, port = incoming |
|
330 |
kvm_cmd.extend(['-incoming', 'tcp:%s:%s' % (target, port)]) |
|
331 |
|
|
320 | 332 |
result = utils.RunCmd(kvm_cmd) |
321 | 333 |
if result.failed: |
322 | 334 |
raise errors.HypervisorError("Failed to start instance %s: %s (%s)" % |
... | ... | |
415 | 427 |
self._SaveKVMRuntime(instance, kvm_runtime) |
416 | 428 |
self._ExecuteKVMRuntime(instance, kvm_runtime) |
417 | 429 |
|
430 |
def MigrationInfo(self, instance): |
|
431 |
"""Get instance information to perform a migration. |
|
432 |
|
|
433 |
@type instance: L{objects.Instance} |
|
434 |
@param instance: instance to be migrated |
|
435 |
@rtype: string |
|
436 |
@return: content of the KVM runtime file |
|
437 |
|
|
438 |
""" |
|
439 |
return self._ReadKVMRuntime(instance.name) |
|
440 |
|
|
441 |
def AcceptInstance(self, instance, info, target): |
|
442 |
"""Prepare to accept an instance. |
|
443 |
|
|
444 |
@type instance: L{objects.Instance} |
|
445 |
@param instance: instance to be accepted |
|
446 |
@type info: string |
|
447 |
@param info: content of the KVM runtime file on the source node |
|
448 |
@type target: string |
|
449 |
@param target: target host (usually ip), on this node |
|
450 |
|
|
451 |
""" |
|
452 |
kvm_runtime = self._LoadKVMRuntime(instance, serialized_runtime=info) |
|
453 |
incoming_address = (target, constants.KVM_MIGRATION_PORT) |
|
454 |
self._ExecuteKVMRuntime(instance, kvm_runtime, incoming=incoming_address) |
|
455 |
|
|
456 |
def FinalizeMigration(self, instance, info, success): |
|
457 |
"""Finalize an instance migration. |
|
458 |
|
|
459 |
Stop the incoming mode KVM. |
|
460 |
|
|
461 |
@type instance: L{objects.Instance} |
|
462 |
@param instance: instance whose migration is being aborted |
|
463 |
|
|
464 |
""" |
|
465 |
if success: |
|
466 |
self._WriteKVMRuntime(instance.name, info) |
|
467 |
else: |
|
468 |
self.StopInstance(instance, force=True) |
|
469 |
|
|
470 |
def MigrateInstance(self, instance_name, target, live): |
|
471 |
"""Migrate an instance to a target node. |
|
472 |
|
|
473 |
The migration will not be attempted if the instance is not |
|
474 |
currently running. |
|
475 |
|
|
476 |
@type instance_name: string |
|
477 |
@param instance_name: name of the instance to be migrated |
|
478 |
@type target: string |
|
479 |
@param target: ip address of the target node |
|
480 |
@type live: boolean |
|
481 |
@param live: perform a live migration |
|
482 |
|
|
483 |
""" |
|
484 |
pidfile, pid, alive = self._InstancePidAlive(instance_name) |
|
485 |
if not alive: |
|
486 |
raise errors.HypervisorError("Instance not running, cannot migrate") |
|
487 |
|
|
488 |
if not live: |
|
489 |
self._CallMonitorCommand(instance_name, 'stop') |
|
490 |
|
|
491 |
migrate_command = ('migrate -d tcp:%s:%s' % |
|
492 |
(target, constants.KVM_MIGRATION_PORT)) |
|
493 |
self._CallMonitorCommand(instance_name, migrate_command) |
|
494 |
|
|
495 |
info_command = 'info migrate' |
|
496 |
done = False |
|
497 |
while not done: |
|
498 |
result = self._CallMonitorCommand(instance_name, info_command) |
|
499 |
match = self._MIGRATION_STATUS_RE.search(result.stdout) |
|
500 |
if not match: |
|
501 |
raise errors.HypervisorError("Unknown 'info migrate' result: %s" % |
|
502 |
result.stdout) |
|
503 |
else: |
|
504 |
status = match.group(1) |
|
505 |
if status == 'completed': |
|
506 |
done = True |
|
507 |
elif status == 'active': |
|
508 |
time.sleep(2) |
|
509 |
else: |
|
510 |
logging.info("KVM: unknown migration status '%s'" % status) |
|
511 |
time.sleep(2) |
|
512 |
|
|
513 |
utils.KillProcess(pid) |
|
514 |
utils.RemoveFile(pidfile) |
|
515 |
utils.RemoveFile(self._InstanceMonitor(instance_name)) |
|
516 |
utils.RemoveFile(self._InstanceSerial(instance_name)) |
|
517 |
utils.RemoveFile(self._InstanceKVMRuntime(instance_name)) |
|
518 |
|
|
418 | 519 |
def GetNodeInfo(self): |
419 | 520 |
"""Return information about the node. |
420 | 521 |
|
Also available in: Unified diff