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 |
|