mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
libxl: Remus - xl remus command
xl remus acts as a frontend to enable remus for a given domain. * At the moment, only memory checkpointing and blackhole replication is supported. Support for disk checkpointing and network buffering will be added in future. * Replication is done over ssh connection currently (like live migration with xl). Future versions will have an option to use simple tcp socket based replication channel (for both Remus & live migration). Signed-off-by: Shriram Rajagopalan <rshriram@cs.ubc.ca> Acked-by: Ian Campbell <ian.campbell@citrix.com> Committed-by: Ian Campbell <ian.campbell@citrix.com>
This commit is contained in:
1 parent
d473b016fb
commit
4df8bbb936
4 files changed
+189
-4
No files matched your search
@@ -389,6 +389,41 @@ Send <config> instead of config file from creation.
|
||||
|
||||
=back
|
||||
|
||||
=item B<remus> [I<OPTIONS>] I<domain-id> I<host>
|
||||
|
||||
Enable Remus HA for domain. By default B<xl> relies on ssh as a transport
|
||||
mechanism between the two hosts.
|
||||
|
||||
B<OPTIONS>
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<-i> I<MS>
|
||||
|
||||
Checkpoint domain memory every MS milliseconds (default 200ms).
|
||||
|
||||
=item B<-b>
|
||||
|
||||
Do not checkpoint the disk. Replicate memory checkpoints to /dev/null
|
||||
(blackhole). Network output buffering remains enabled (unless --no-net is
|
||||
supplied). Generally useful for debugging.
|
||||
|
||||
=item B<-u>
|
||||
|
||||
Disable memory checkpoint compression.
|
||||
|
||||
=item B<-s> I<sshcommand>
|
||||
|
||||
Use <sshcommand> instead of ssh. String will be passed to sh.
|
||||
If empty, run <host> instead of ssh <host> xl migrate-receive -r [-e].
|
||||
|
||||
=item B<-e>
|
||||
|
||||
On the new host, do not wait in the background (on <host>) for the death
|
||||
of the domain. See the corresponding option of the I<create> subcommand.
|
||||
|
||||
=back
|
||||
|
||||
=item B<pause> I<domain-id>
|
||||
|
||||
Pause a domain. When in a paused state the domain will still consume
|
||||
|
||||
@@ -97,6 +97,7 @@ int main_cpupoolnumasplit(int argc, char **argv);
|
||||
int main_getenforce(int argc, char **argv);
|
||||
int main_setenforce(int argc, char **argv);
|
||||
int main_loadpolicy(int argc, char **argv);
|
||||
int main_remus(int argc, char **argv);
|
||||
|
||||
void help(const char *command);
|
||||
|
||||
|
||||
+139
-4
@@ -3117,7 +3117,7 @@ static void core_dump_domain(const char *domain_spec, const char *filename)
|
||||
}
|
||||
|
||||
static void migrate_receive(int debug, int daemonize, int monitor,
|
||||
int send_fd, int recv_fd)
|
||||
int send_fd, int recv_fd, int remus)
|
||||
{
|
||||
int rc, rc2;
|
||||
char rc_buf;
|
||||
@@ -3151,6 +3151,41 @@ static void migrate_receive(int debug, int daemonize, int monitor,
|
||||
exit(-rc);
|
||||
}
|
||||
|
||||
if (remus) {
|
||||
/* If we are here, it means that the sender (primary) has crashed.
|
||||
* TODO: Split-Brain Check.
|
||||
*/
|
||||
fprintf(stderr, "migration target: Remus Failover for domain %u\n",
|
||||
domid);
|
||||
|
||||
/*
|
||||
* If domain renaming fails, lets just continue (as we need the domain
|
||||
* to be up & dom names may not matter much, as long as its reachable
|
||||
* over network).
|
||||
*
|
||||
* If domain unpausing fails, destroy domain ? Or is it better to have
|
||||
* a consistent copy of the domain (memory, cpu state, disk)
|
||||
* on atleast one physical host ? Right now, lets just leave the domain
|
||||
* as is and let the Administrator decide (or troubleshoot).
|
||||
*/
|
||||
if (migration_domname) {
|
||||
rc = libxl_domain_rename(ctx, domid, migration_domname,
|
||||
common_domname);
|
||||
if (rc)
|
||||
fprintf(stderr, "migration target (Remus): "
|
||||
"Failed to rename domain from %s to %s:%d\n",
|
||||
migration_domname, common_domname, rc);
|
||||
}
|
||||
|
||||
rc = libxl_domain_unpause(ctx, domid);
|
||||
if (rc)
|
||||
fprintf(stderr, "migration target (Remus): "
|
||||
"Failed to unpause domain %s (id: %u):%d\n",
|
||||
common_domname, domid, rc);
|
||||
|
||||
exit(rc ? -ERROR_FAIL: 0);
|
||||
}
|
||||
|
||||
fprintf(stderr, "migration target: Transfer complete,"
|
||||
" requesting permission to start domain.\n");
|
||||
|
||||
@@ -3299,10 +3334,10 @@ int main_restore(int argc, char **argv)
|
||||
|
||||
int main_migrate_receive(int argc, char **argv)
|
||||
{
|
||||
int debug = 0, daemonize = 1, monitor = 1;
|
||||
int debug = 0, daemonize = 1, monitor = 1, remus = 0;
|
||||
int opt;
|
||||
|
||||
while ((opt = def_getopt(argc, argv, "Fed", "migrate-receive", 0)) != -1) {
|
||||
while ((opt = def_getopt(argc, argv, "Fedr", "migrate-receive", 0)) != -1) {
|
||||
switch (opt) {
|
||||
case 0: case 2:
|
||||
return opt;
|
||||
@@ -3316,6 +3351,9 @@ int main_migrate_receive(int argc, char **argv)
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'r':
|
||||
remus = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3324,7 +3362,8 @@ int main_migrate_receive(int argc, char **argv)
|
||||
return 2;
|
||||
}
|
||||
migrate_receive(debug, daemonize, monitor,
|
||||
STDOUT_FILENO, STDIN_FILENO);
|
||||
STDOUT_FILENO, STDIN_FILENO,
|
||||
remus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -6496,6 +6535,102 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main_remus(int argc, char **argv)
|
||||
{
|
||||
int opt, rc, daemonize = 1;
|
||||
const char *ssh_command = "ssh";
|
||||
char *host = NULL, *rune = NULL, *domain = NULL;
|
||||
libxl_domain_remus_info r_info;
|
||||
int send_fd = -1, recv_fd = -1;
|
||||
pid_t child = -1;
|
||||
uint8_t *config_data;
|
||||
int config_len;
|
||||
|
||||
memset(&r_info, 0, sizeof(libxl_domain_remus_info));
|
||||
/* Defaults */
|
||||
r_info.interval = 200;
|
||||
r_info.blackhole = 0;
|
||||
r_info.compression = 1;
|
||||
|
||||
while ((opt = def_getopt(argc, argv, "bui:s:e", "remus", 2)) != -1) {
|
||||
switch (opt) {
|
||||
case 0: case 2:
|
||||
return opt;
|
||||
|
||||
case 'i':
|
||||
r_info.interval = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
r_info.blackhole = 1;
|
||||
break;
|
||||
case 'u':
|
||||
r_info.compression = 0;
|
||||
break;
|
||||
case 's':
|
||||
ssh_command = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
daemonize = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
domain = argv[optind];
|
||||
host = argv[optind + 1];
|
||||
|
||||
if (r_info.blackhole) {
|
||||
find_domain(domain);
|
||||
send_fd = open("/dev/null", O_RDWR, 0644);
|
||||
if (send_fd < 0) {
|
||||
perror("failed to open /dev/null");
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!ssh_command[0]) {
|
||||
rune = host;
|
||||
} else {
|
||||
if (asprintf(&rune, "exec %s %s xl migrate-receive -r %s",
|
||||
ssh_command, host,
|
||||
daemonize ? "" : " -e") < 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
save_domain_core_begin(domain, NULL, &config_data, &config_len);
|
||||
|
||||
if (!config_len) {
|
||||
fprintf(stderr, "No config file stored for running domain and "
|
||||
"none supplied - cannot start remus.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
child = create_migration_child(rune, &send_fd, &recv_fd);
|
||||
|
||||
migrate_do_preamble(send_fd, recv_fd, child, config_data, config_len,
|
||||
rune);
|
||||
}
|
||||
|
||||
/* Point of no return */
|
||||
rc = libxl_domain_remus_start(ctx, &r_info, domid, send_fd, recv_fd);
|
||||
|
||||
/* If we are here, it means backup has failed/domain suspend failed.
|
||||
* Try to resume the domain and exit gracefully.
|
||||
* TODO: Split-Brain check.
|
||||
*/
|
||||
fprintf(stderr, "remus sender: libxl_domain_suspend failed"
|
||||
" (rc=%d)\n", rc);
|
||||
|
||||
if (rc == ERROR_GUEST_TIMEDOUT)
|
||||
fprintf(stderr, "Failed to suspend domain at primary.\n");
|
||||
else {
|
||||
fprintf(stderr, "Remus: Backup failed? resuming domain at primary.\n");
|
||||
libxl_domain_resume(ctx, domid, 1);
|
||||
}
|
||||
|
||||
close(send_fd);
|
||||
return -ERROR_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
|
||||
@@ -446,6 +446,20 @@ struct cmd_spec cmd_table[] = {
|
||||
"Loads a new policy int the Flask Xen security module",
|
||||
"<policy file>",
|
||||
},
|
||||
{ "remus",
|
||||
&main_remus, 0, 1,
|
||||
"Enable Remus HA for domain",
|
||||
"[options] <Domain> [<host>]",
|
||||
"-i MS Checkpoint domain memory every MS milliseconds (def. 200ms).\n"
|
||||
"-b Replicate memory checkpoints to /dev/null (blackhole)\n"
|
||||
"-u Disable memory checkpoint compression.\n"
|
||||
"-s <sshcommand> Use <sshcommand> instead of ssh. String will be passed\n"
|
||||
" to sh. If empty, run <host> instead of \n"
|
||||
" ssh <host> xl migrate-receive -r [-e]\n"
|
||||
"-e Do not wait in the background (on <host>) for the death\n"
|
||||
" of the domain."
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
int cmdtable_len = sizeof(cmd_table)/sizeof(struct cmd_spec);
|
||||
|
||||