[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:
parent
4b21ff167f
commit
4157f1d230
345
src/libteam/0005-libteam-Add-warm_reboot-mode.patch
Normal file
345
src/libteam/0005-libteam-Add-warm_reboot-mode.patch
Normal 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 = {
|
@ -19,6 +19,7 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
|
|||||||
git apply ../0002-libteam-Temporarily-remove-redundant-debug-mes.patch
|
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 ../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 ../0004-libteam-Add-lacp-fallback-support-for-single-member-.patch
|
||||||
|
git apply ../0005-libteam-Add-warm_reboot-mode.patch
|
||||||
popd
|
popd
|
||||||
|
|
||||||
# Obtain debian packaging
|
# Obtain debian packaging
|
||||||
|
Loading…
Reference in New Issue
Block a user