[teamd] Add Warm-reboot startup and shutdown mode for teamd (#2173)

* Add Warm-reboot startup and shutdown mode for teamd

* Address comments, fix some bugs

* Use tab instead of 8 spaces
This commit is contained in:
pavel-shirshov 2018-11-05 16:37:57 -08:00 committed by GitHub
parent 4b21ff167f
commit 4157f1d230
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 346 additions and 0 deletions

View File

@ -0,0 +1,345 @@
diff --git a/teamd/teamd.c b/teamd/teamd.c
index c987333..53aec1d 100644
--- a/teamd/teamd.c
+++ b/teamd/teamd.c
@@ -116,7 +116,9 @@ static void print_help(const struct teamd_context *ctx) {
" -D --dbus-enable Enable D-Bus interface\n"
" -Z --zmq-enable=ADDRESS Enable ZeroMQ interface\n"
" -U --usock-enable Enable UNIX domain socket interface\n"
- " -u --usock-disable Disable UNIX domain socket interface\n",
+ " -u --usock-disable Disable UNIX domain socket interface\n"
+ " -w --warm-start Warm-start startup mode\n"
+ " -L --lacp-directory Directory for saving lacp pdu dumps\n",
ctx->argv0);
printf("Available runners: ");
for (i = 0; i < TEAMD_RUNNER_LIST_SIZE; i++) {
@@ -129,7 +131,7 @@ static void print_help(const struct teamd_context *ctx) {
static int parse_command_line(struct teamd_context *ctx,
int argc, char *argv[]) {
- int opt;
+ int opt, err;
static const struct option long_options[] = {
{ "help", no_argument, NULL, 'h' },
{ "daemonize", no_argument, NULL, 'd' },
@@ -149,10 +151,12 @@ static int parse_command_line(struct teamd_context *ctx,
{ "zmq-enable", required_argument, NULL, 'Z' },
{ "usock-enable", no_argument, NULL, 'U' },
{ "usock-disable", no_argument, NULL, 'u' },
+ { "warm-start", no_argument, NULL, 'w' },
+ { "lacp-directory", required_argument, NULL, 'L' },
{ NULL, 0, NULL, 0 }
};
- while ((opt = getopt_long(argc, argv, "hdkevf:c:p:groNt:nDZ:Uu",
+ while ((opt = getopt_long(argc, argv, "hdkevf:c:p:groNt:nDZ:UuwL:",
long_options, NULL)) >= 0) {
switch(opt) {
@@ -230,6 +234,17 @@ static int parse_command_line(struct teamd_context *ctx,
case 'u':
ctx->usock.enabled = false;
break;
+ case 'w':
+ ctx->warm_start = true;
+ break;
+ case 'L':
+ ctx->lacp_directory = strdup(optarg);
+ if (access(ctx->lacp_directory, R_OK | W_OK | X_OK) != 0) {
+ fprintf(stderr, "Can't write to the lacp directory '%s': %s\n", ctx->lacp_directory, strerror(errno));
+ free(ctx->lacp_directory);
+ ctx->lacp_directory = NULL;
+ }
+ break;
default:
return -1;
}
@@ -384,8 +399,14 @@ static int teamd_run_loop_run(struct teamd_context *ctx)
if (err != -1) {
switch(ctrl_byte) {
case 'q':
+ case 'w':
if (quit_in_progress)
return -EBUSY;
+ if (ctrl_byte == 'w') {
+ ctx->keep_ports = true;
+ ctx->no_quit_destroy = true;
+ teamd_ports_flush_data(ctx);
+ }
teamd_refresh_ports(ctx);
err = teamd_flush_ports(ctx);
if (err)
@@ -428,6 +449,12 @@ void teamd_run_loop_quit(struct teamd_context *ctx, int err)
teamd_run_loop_sent_ctrl_byte(ctx, 'q');
}
+static void teamd_run_loop_quit_w_boot(struct teamd_context *ctx, int err)
+{
+ ctx->run_loop.err = err;
+ teamd_run_loop_sent_ctrl_byte(ctx, 'w');
+}
+
void teamd_run_loop_restart(struct teamd_context *ctx)
{
teamd_run_loop_sent_ctrl_byte(ctx, 'r');
@@ -694,6 +721,10 @@ static int callback_daemon_signal(struct teamd_context *ctx, int events,
teamd_log_warn("Got SIGINT, SIGQUIT or SIGTERM.");
teamd_run_loop_quit(ctx, 0);
break;
+ case SIGUSR1:
+ teamd_log_warn("Got SIGUSR1.");
+ teamd_run_loop_quit_w_boot(ctx, 0);
+ break;
}
return 0;
}
@@ -1507,7 +1538,7 @@ static int teamd_start(struct teamd_context *ctx, enum teamd_exit_code *p_ret)
return -errno;
}
- if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, 0) < 0) {
+ if (daemon_signal_init(SIGINT, SIGTERM, SIGQUIT, SIGHUP, SIGUSR1, 0) < 0) {
teamd_log_err("Could not register signal handlers.");
daemon_retval_send(errno);
err = -errno;
diff --git a/teamd/teamd.h b/teamd/teamd.h
index ef0fb1c..b1b6dfe 100644
--- a/teamd/teamd.h
+++ b/teamd/teamd.h
@@ -125,6 +125,9 @@ struct teamd_context {
char * hwaddr;
uint32_t hwaddr_len;
bool hwaddr_explicit;
+ bool warm_start;
+ bool keep_ports;
+ char * lacp_directory;
struct {
struct list_item callback_list;
int ctrl_pipe_r;
@@ -191,12 +194,15 @@ struct teamd_event_watch_ops {
struct teamd_port *tdport, void *priv);
void (*refresh)(struct teamd_context *ctx,
struct teamd_port *tdport, void *priv);
+ void (*port_flush_data)(struct teamd_context *ctx,
+ struct teamd_port *tdport, void *priv);
int (*option_changed)(struct teamd_context *ctx,
struct team_option *option, void *priv);
char *option_changed_match_name;
};
void teamd_refresh_ports(struct teamd_context *ctx);
+void teamd_ports_flush_data(struct teamd_context *ctx);
int teamd_event_port_added(struct teamd_context *ctx,
struct teamd_port *tdport);
void teamd_event_port_removed(struct teamd_context *ctx,
diff --git a/teamd/teamd_events.c b/teamd/teamd_events.c
index 5c2ef56..50e5a08 100644
--- a/teamd/teamd_events.c
+++ b/teamd/teamd_events.c
@@ -47,6 +47,19 @@ void teamd_refresh_ports(struct teamd_context *ctx)
}
}
+void teamd_ports_flush_data(struct teamd_context *ctx)
+{
+ struct teamd_port *tdport;
+ struct event_watch_item *watch;
+
+ teamd_for_each_tdport(tdport, ctx) {
+ list_for_each_node_entry(watch, &ctx->event_watch_list, list) {
+ if (!watch->ops->port_flush_data) continue;
+ watch->ops->port_flush_data(ctx, tdport, watch->priv);
+ }
+ }
+}
+
int teamd_event_port_added(struct teamd_context *ctx,
struct teamd_port *tdport)
{
diff --git a/teamd/teamd_runner_lacp.c b/teamd/teamd_runner_lacp.c
index 81324de..1d908db 100644
--- a/teamd/teamd_runner_lacp.c
+++ b/teamd/teamd_runner_lacp.c
@@ -174,6 +174,8 @@ struct lacp_port {
struct lacp_port *agg_lead; /* leading port of aggregator.
* NULL in case this port is not selected */
enum lacp_port_state state;
+ bool lacpdu_saved;
+ struct lacpdu last_pdu;
struct {
uint32_t speed;
uint8_t duplex;
@@ -1084,26 +1086,23 @@ static int lacpdu_send(struct lacp_port *lacp_port)
return err;
}
-static int lacpdu_recv(struct lacp_port *lacp_port)
+static int lacpdu_process(struct lacp_port *lacp_port, struct lacpdu* lacpdu)
{
- struct lacpdu lacpdu;
- struct sockaddr_ll ll_from;
int err;
- err = teamd_recvfrom(lacp_port->sock, &lacpdu, sizeof(lacpdu), 0,
- (struct sockaddr *) &ll_from, sizeof(ll_from));
- if (err <= 0)
- return err;
-
- if (!lacpdu_check(&lacpdu)) {
+ if (!lacpdu_check(lacpdu)) {
teamd_log_warn("malformed LACP PDU came.");
return 0;
}
+ /* save received lacp pdu frame */
+ (void)memcpy(&lacp_port->last_pdu, lacpdu, sizeof(struct lacpdu));
+ lacp_port->lacpdu_saved = true;
+
/* Check if we have correct info about the other side */
- if (memcmp(&lacpdu.actor, &lacp_port->partner,
+ if (memcmp(&lacpdu->actor, &lacp_port->partner,
sizeof(struct lacpdu_info))) {
- lacp_port->partner = lacpdu.actor;
+ lacp_port->partner = lacpdu->actor;
err = lacp_port_partner_update(lacp_port);
if (err)
return err;
@@ -1118,7 +1117,7 @@ static int lacpdu_recv(struct lacp_port *lacp_port)
/* Check if the other side has correct info about us */
if (!lacp_port->periodic_on &&
- memcmp(&lacpdu.partner, &lacp_port->actor,
+ memcmp(&lacpdu->partner, &lacp_port->actor,
sizeof(struct lacpdu_info))) {
err = lacpdu_send(lacp_port);
if (err)
@@ -1133,6 +1132,53 @@ static int lacpdu_recv(struct lacp_port *lacp_port)
return 0;
}
+static int lacpdu_recv(struct lacp_port *lacp_port)
+{
+ struct lacpdu lacpdu;
+ struct sockaddr_ll ll_from;
+ int err;
+
+ err = teamd_recvfrom(lacp_port->sock, &lacpdu, sizeof(lacpdu), 0,
+ (struct sockaddr *) &ll_from, sizeof(ll_from));
+ if (err <= 0)
+ return err;
+
+ return lacpdu_process(lacp_port, &lacpdu);
+}
+
+static int lacpdu_read(struct lacp_port *lacp_port)
+{
+ FILE* fp;
+ char filename[PATH_MAX];
+ struct lacpdu lacpdu;
+ int err, nitems;
+
+ strcpy(filename, lacp_port->ctx->lacp_directory);
+ if (filename[strlen(filename) - 1] != '/')
+ strcat(filename, "/"); /* Add trailing slash if we don't have one in the filename */
+ strcat(filename, lacp_port->tdport->ifname);
+ fp = fopen(filename, "r");
+ if (!fp) {
+ teamd_log_err("Can't open lacp-saved dump from file %s: %s", filename, strerror(errno));
+ return errno;
+ }
+
+ nitems = fread(&lacpdu, sizeof(struct lacpdu), 1, fp);
+ (void)fclose(fp);
+
+ err = unlink(filename);
+ if(err < 0) {
+ teamd_log_err("Can't remove file %s: %s", filename, strerror(errno));
+ }
+
+ if (nitems != 1) {
+ teamd_log_err("Can't read lacp-saved dump from file %s: %s", filename, strerror(errno));
+ return err;
+ }
+
+ return lacpdu_process(lacp_port, &lacpdu);
+}
+
static int lacp_callback_timeout(struct teamd_context *ctx, int events,
void *priv)
{
@@ -1299,6 +1345,13 @@ static int lacp_port_added(struct teamd_context *ctx,
lacp_port_actor_init(lacp_port);
lacp_port_link_update(lacp_port);
+ /* Read data from file and process it */
+ if (ctx->warm_start && ctx->lacp_directory) {
+ (void)lacpdu_read(lacp_port);
+ ctx->warm_start = false;
+ /* Once started, keep running in normal mode */
+ }
+
teamd_loop_callback_enable(ctx, LACP_SOCKET_CB_NAME, lacp_port);
return 0;
@@ -1321,7 +1374,11 @@ static void lacp_port_removed(struct teamd_context *ctx,
{
struct lacp_port *lacp_port = priv;
- lacp_port_set_state(lacp_port, PORT_STATE_DISABLED);
+ if (!lacp_port->ctx->keep_ports) {
+ /* Don't transition into DISABLED state,
+ which sends EXPIRED LACP PDU update */
+ lacp_port_set_state(lacp_port, PORT_STATE_DISABLED);
+ }
teamd_loop_callback_del(ctx, LACP_TIMEOUT_CB_NAME, lacp_port);
teamd_loop_callback_del(ctx, LACP_PERIODIC_CB_NAME, lacp_port);
teamd_loop_callback_del(ctx, LACP_SOCKET_CB_NAME, lacp_port);
@@ -1413,6 +1470,31 @@ static void lacp_event_watch_refresh(struct teamd_context *ctx, struct teamd_por
(void) lacpdu_send(lacp_port);
}
+static void lacp_event_watch_port_flush_data(struct teamd_context *ctx, struct teamd_port *tdport, void *priv)
+{
+ struct lacp *lacp = priv;
+
+ struct lacp_port *lacp_port = lacp_port_get(lacp, tdport);
+ if(lacp_port->lacpdu_saved && lacp_port->ctx->lacp_directory) {
+ char filename[PATH_MAX];
+ strcpy(filename, lacp_port->ctx->lacp_directory);
+ if (filename[strlen(filename) - 1] != '/')
+ strcat(filename, "/"); /* Add trailing slash if we don't have one in the filename */
+ strcat(filename, lacp_port->tdport->ifname);
+ FILE *fp = fopen(filename, "wb");
+ if (fp != NULL) {
+ (void)fwrite(&lacp_port->last_pdu, sizeof(struct lacpdu), 1, fp);
+ (void)fclose(fp);
+ } else {
+ teamd_log_err("Can't open file %s for writing %s", filename, strerror(errno));
+ }
+ } else {
+ teamd_log_err("Can't dump received lacp pdu for port %s. "
+ "Either it wasn't received, or directory to save wasn't configured",
+ lacp_port->tdport->ifname);
+ }
+}
+
static const struct teamd_event_watch_ops lacp_event_watch_ops = {
.hwaddr_changed = lacp_event_watch_hwaddr_changed,
.port_added = lacp_event_watch_port_added,
@@ -1420,6 +1502,7 @@ static const struct teamd_event_watch_ops lacp_event_watch_ops = {
.port_changed = lacp_event_watch_port_changed,
.admin_state_changed = lacp_event_watch_admin_state_changed,
.refresh = lacp_event_watch_refresh,
+ .port_flush_data = lacp_event_watch_port_flush_data,
};
static int lacp_carrier_init(struct teamd_context *ctx, struct lacp *lacp)
@@ -1946,7 +2029,7 @@ static void lacp_fini(struct teamd_context *ctx, void *priv)
teamd_state_val_unregister(ctx, &lacp_state_vg, lacp);
teamd_balancer_fini(lacp->tb);
teamd_event_watch_unregister(ctx, &lacp_event_watch_ops, lacp);
- lacp_carrier_fini(ctx, lacp);
+ if (!ctx->keep_ports) lacp_carrier_fini(ctx, lacp);
}
const struct teamd_runner teamd_runner_lacp = {

View File

@ -19,6 +19,7 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
git apply ../0002-libteam-Temporarily-remove-redundant-debug-mes.patch
git apply ../0003-teamd-lacp-runner-will-send-lacp-update-right-after-.patch
git apply ../0004-libteam-Add-lacp-fallback-support-for-single-member-.patch
git apply ../0005-libteam-Add-warm_reboot-mode.patch
popd
# Obtain debian packaging