Revision 6906a9d8
b/daemons/ganeti-noded | ||
---|---|---|
403 | 403 |
return backend.StartInstance(instance, extra_args) |
404 | 404 |
|
405 | 405 |
@staticmethod |
406 |
def perspective_migration_info(params): |
|
407 |
"""Gather information about an instance to be migrated. |
|
408 |
|
|
409 |
""" |
|
410 |
instance = objects.Instance.FromDict(params[0]) |
|
411 |
return backend.MigrationInfo(instance) |
|
412 |
|
|
413 |
@staticmethod |
|
414 |
def perspective_accept_instance(params): |
|
415 |
"""Prepare the node to accept an instance. |
|
416 |
|
|
417 |
""" |
|
418 |
instance, info, target = params |
|
419 |
instance = objects.Instance.FromDict(instance) |
|
420 |
return backend.AcceptInstance(instance, info, target) |
|
421 |
|
|
422 |
@staticmethod |
|
423 |
def perspective_finalize_migration(params): |
|
424 |
"""Finalize the instance migration. |
|
425 |
|
|
426 |
""" |
|
427 |
instance, info, success = params |
|
428 |
instance = objects.Instance.FromDict(instance) |
|
429 |
return backend.FinalizeMigration(instance, info, success) |
|
430 |
|
|
431 |
@staticmethod |
|
406 | 432 |
def perspective_instance_migrate(params): |
407 | 433 |
"""Migrates an instance. |
408 | 434 |
|
b/lib/backend.py | ||
---|---|---|
973 | 973 |
return True |
974 | 974 |
|
975 | 975 |
|
976 |
def MigrationInfo(instance): |
|
977 |
"""Gather information about an instance to be migrated. |
|
978 |
|
|
979 |
@type instance: L{objects.Instance} |
|
980 |
@param instance: the instance definition |
|
981 |
|
|
982 |
""" |
|
983 |
return (True, '') |
|
984 |
|
|
985 |
|
|
986 |
def AcceptInstance(instance, info, target): |
|
987 |
"""Prepare the node to accept an instance. |
|
988 |
|
|
989 |
@type instance: L{objects.Instance} |
|
990 |
@param instance: the instance definition |
|
991 |
@type info: string/data (opaque) |
|
992 |
@param info: migration information, from the source node |
|
993 |
@type target: string |
|
994 |
@param target: target host (usually ip), on this node |
|
995 |
|
|
996 |
""" |
|
997 |
return (True, "Accept successfull") |
|
998 |
|
|
999 |
|
|
1000 |
def FinalizeMigration(instance, info, success): |
|
1001 |
"""Finalize any preparation to accept an instance. |
|
1002 |
|
|
1003 |
@type instance: L{objects.Instance} |
|
1004 |
@param instance: the instance definition |
|
1005 |
@type info: string/data (opaque) |
|
1006 |
@param info: migration information, from the source node |
|
1007 |
@type success: boolean |
|
1008 |
@param success: whether the migration was a success or a failure |
|
1009 |
|
|
1010 |
""" |
|
1011 |
return (True, "Migration Finalized") |
|
1012 |
|
|
1013 |
|
|
976 | 1014 |
def MigrateInstance(instance, target, live): |
977 | 1015 |
"""Migrates an instance to another node. |
978 | 1016 |
|
b/lib/cmdlib.py | ||
---|---|---|
3631 | 3631 |
|
3632 | 3632 |
self.feedback_fn("* done") |
3633 | 3633 |
|
3634 |
def _RevertDiskStatus(self): |
|
3635 |
"""Try to revert the disk status after a failed migration. |
|
3636 |
|
|
3637 |
""" |
|
3638 |
target_node = self.target_node |
|
3639 |
try: |
|
3640 |
self._EnsureSecondary(target_node) |
|
3641 |
self._GoStandalone() |
|
3642 |
self._GoReconnect(False) |
|
3643 |
self._WaitUntilSync() |
|
3644 |
except errors.OpExecError, err: |
|
3645 |
self.LogWarning("Migration failed and I can't reconnect the" |
|
3646 |
" drives: error '%s'\n" |
|
3647 |
"Please look and recover the instance status" % |
|
3648 |
str(err)) |
|
3649 |
|
|
3650 |
def _AbortMigration(self): |
|
3651 |
"""Call the hypervisor code to abort a started migration. |
|
3652 |
|
|
3653 |
""" |
|
3654 |
instance = self.instance |
|
3655 |
target_node = self.target_node |
|
3656 |
migration_info = self.migration_info |
|
3657 |
|
|
3658 |
abort_result = self.rpc.call_finalize_migration(target_node, |
|
3659 |
instance, |
|
3660 |
migration_info, |
|
3661 |
False) |
|
3662 |
abort_msg = abort_result.RemoteFailMsg() |
|
3663 |
if abort_msg: |
|
3664 |
logging.error("Aborting migration failed on target node %s: %s" % |
|
3665 |
(target_node, abort_msg)) |
|
3666 |
# Don't raise an exception here, as we stil have to try to revert the |
|
3667 |
# disk status, even if this step failed. |
|
3668 |
|
|
3634 | 3669 |
def _ExecMigration(self): |
3635 | 3670 |
"""Migrate an instance. |
3636 | 3671 |
|
... | ... | |
3654 | 3689 |
" synchronized on target node," |
3655 | 3690 |
" aborting migrate." % dev.iv_name) |
3656 | 3691 |
|
3692 |
# First get the migration information from the remote node |
|
3693 |
result = self.rpc.call_migration_info(source_node, instance) |
|
3694 |
msg = result.RemoteFailMsg() |
|
3695 |
if msg: |
|
3696 |
log_err = ("Failed fetching source migration information from %s: %s" % |
|
3697 |
(source_node, msg)) |
|
3698 |
logging.error(log_err) |
|
3699 |
raise errors.OpExecError(log_err) |
|
3700 |
|
|
3701 |
self.migration_info = migration_info = result.data[1] |
|
3702 |
|
|
3703 |
# Then switch the disks to master/master mode |
|
3657 | 3704 |
self._EnsureSecondary(target_node) |
3658 | 3705 |
self._GoStandalone() |
3659 | 3706 |
self._GoReconnect(True) |
3660 | 3707 |
self._WaitUntilSync() |
3661 | 3708 |
|
3709 |
self.feedback_fn("* preparing %s to accept the instance" % target_node) |
|
3710 |
result = self.rpc.call_accept_instance(target_node, |
|
3711 |
instance, |
|
3712 |
migration_info, |
|
3713 |
self.nodes_ip[target_node]) |
|
3714 |
|
|
3715 |
msg = result.RemoteFailMsg() |
|
3716 |
if msg: |
|
3717 |
logging.error("Instance pre-migration failed, trying to revert" |
|
3718 |
" disk status: %s", msg) |
|
3719 |
self._AbortMigration() |
|
3720 |
self._RevertDiskStatus() |
|
3721 |
raise errors.OpExecError("Could not pre-migrate instance %s: %s" % |
|
3722 |
(instance.name, msg)) |
|
3723 |
|
|
3662 | 3724 |
self.feedback_fn("* migrating instance to %s" % target_node) |
3663 | 3725 |
time.sleep(10) |
3664 | 3726 |
result = self.rpc.call_instance_migrate(source_node, instance, |
... | ... | |
3668 | 3730 |
if msg: |
3669 | 3731 |
logging.error("Instance migration failed, trying to revert" |
3670 | 3732 |
" disk status: %s", msg) |
3671 |
try: |
|
3672 |
self._EnsureSecondary(target_node) |
|
3673 |
self._GoStandalone() |
|
3674 |
self._GoReconnect(False) |
|
3675 |
self._WaitUntilSync() |
|
3676 |
except errors.OpExecError, err: |
|
3677 |
self.LogWarning("Migration failed and I can't reconnect the" |
|
3678 |
" drives: error '%s'\n" |
|
3679 |
"Please look and recover the instance status" % |
|
3680 |
str(err)) |
|
3681 |
|
|
3733 |
self._AbortMigration() |
|
3734 |
self._RevertDiskStatus() |
|
3682 | 3735 |
raise errors.OpExecError("Could not migrate instance %s: %s" % |
3683 | 3736 |
(instance.name, msg)) |
3684 | 3737 |
time.sleep(10) |
... | ... | |
3687 | 3740 |
# distribute new instance config to the other nodes |
3688 | 3741 |
self.cfg.Update(instance) |
3689 | 3742 |
|
3743 |
result = self.rpc.call_finalize_migration(target_node, |
|
3744 |
instance, |
|
3745 |
migration_info, |
|
3746 |
True) |
|
3747 |
msg = result.RemoteFailMsg() |
|
3748 |
if msg: |
|
3749 |
logging.error("Instance migration succeeded, but finalization failed:" |
|
3750 |
" %s" % msg) |
|
3751 |
raise errors.OpExecError("Could not finalize instance migration: %s" % |
|
3752 |
msg) |
|
3753 |
|
|
3690 | 3754 |
self._EnsureSecondary(source_node) |
3691 | 3755 |
self._WaitUntilSync() |
3692 | 3756 |
self._GoStandalone() |
b/lib/rpc.py | ||
---|---|---|
432 | 432 |
return self._SingleNodeCall(node, "instance_shutdown", |
433 | 433 |
[self._InstDict(instance)]) |
434 | 434 |
|
435 |
def call_migration_info(self, node, instance): |
|
436 |
"""Gather the information necessary to prepare an instance migration. |
|
437 |
|
|
438 |
This is a single-node call. |
|
439 |
|
|
440 |
@type node: string |
|
441 |
@param node: the node on which the instance is currently running |
|
442 |
@type instance: C{objects.Instance} |
|
443 |
@param instance: the instance definition |
|
444 |
|
|
445 |
""" |
|
446 |
return self._SingleNodeCall(node, "migration_info", |
|
447 |
[self._InstDict(instance)]) |
|
448 |
|
|
449 |
def call_accept_instance(self, node, instance, info, target): |
|
450 |
"""Prepare a node to accept an instance. |
|
451 |
|
|
452 |
This is a single-node call. |
|
453 |
|
|
454 |
@type node: string |
|
455 |
@param node: the target node for the migration |
|
456 |
@type instance: C{objects.Instance} |
|
457 |
@param instance: the instance definition |
|
458 |
@type info: opaque/hypervisor specific (string/data) |
|
459 |
@param info: result for the call_migration_info call |
|
460 |
@type target: string |
|
461 |
@param target: target hostname (usually ip address) (on the node itself) |
|
462 |
|
|
463 |
""" |
|
464 |
return self._SingleNodeCall(node, "accept_instance", |
|
465 |
[self._InstDict(instance), info, target]) |
|
466 |
|
|
467 |
def call_finalize_migration(self, node, instance, info, success): |
|
468 |
"""Finalize any target-node migration specific operation. |
|
469 |
|
|
470 |
This is called both in case of a successful migration and in case of error |
|
471 |
(in which case it should abort the migration). |
|
472 |
|
|
473 |
This is a single-node call. |
|
474 |
|
|
475 |
@type node: string |
|
476 |
@param node: the target node for the migration |
|
477 |
@type instance: C{objects.Instance} |
|
478 |
@param instance: the instance definition |
|
479 |
@type info: opaque/hypervisor specific (string/data) |
|
480 |
@param info: result for the call_migration_info call |
|
481 |
@type success: boolean |
|
482 |
@param success: whether the migration was a success or a failure |
|
483 |
|
|
484 |
""" |
|
485 |
return self._SingleNodeCall(node, "finalize_migration", |
|
486 |
[self._InstDict(instance), info, success]) |
|
487 |
|
|
435 | 488 |
def call_instance_migrate(self, node, instance, target, live): |
436 | 489 |
"""Migrate an instance. |
437 | 490 |
|
Also available in: Unified diff