From 0a78e14f879536e719e445c82a3a0559122dd156 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sat, 17 Mar 2018 16:28:31 +0100 Subject: [PATCH] lldpd: add an option to keep some specified ports A user can specify a pattern of ports to not delete even when they are removed from the system. If a port is removed from the system and match the pattern, it will be kept in memory. --- NEWS | 3 ++ src/client/conf-system.c | 46 ++++++++++++++++++++++++++++++- src/client/display.c | 2 ++ src/client/lldpcli.8.in | 25 +++++++++++++++++ src/daemon/client.c | 7 +++++ src/daemon/lldpd.c | 33 +++++++++++++++++----- src/lib/atoms/config.c | 8 ++++++ src/lib/lldpctl.h | 1 + src/lldpd-structs.c | 1 + src/lldpd-structs.h | 2 ++ tests/integration/test_lldpcli.py | 34 +++++++++++++++++++++++ 11 files changed, 154 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 2c3760d8..e2fd0b8d 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,9 @@ lldpd (1.0.0) chassisid". + Port description can be overriden directly with "configure lldp portdescription". + + Command "configure system interface permanent" enables one to + specify a pattern for interfaces to be kept in memory even when + they are removed from the system. * Fix: + Ensure chassis-related changes are propagated immediately. + Ensure management address change is correctly detected. diff --git a/src/client/conf-system.c b/src/client/conf-system.c index 9958b1a5..f2ad664b 100644 --- a/src/client/conf-system.c +++ b/src/client/conf-system.c @@ -50,6 +50,34 @@ cmd_iface_pattern(struct lldpctl_conn_t *conn, struct writer *w, return 1; } +static int +cmd_perm_iface_pattern(struct lldpctl_conn_t *conn, struct writer *w, + struct cmd_env *env, void *arg) +{ + log_debug("lldpctl", "set permanent iface pattern"); + + lldpctl_atom_t *config = lldpctl_get_configuration(conn); + if (config == NULL) { + log_warnx("lldpctl", "unable to get configuration from lldpd. %s", + lldpctl_last_strerror(conn)); + return 0; + } + + const char *value = cmdenv_get(env, "iface-pattern"); + if (lldpctl_atom_set_str(config, + lldpctl_k_config_perm_iface_pattern, + value) == NULL) { + log_warnx("lldpctl", "unable to set permanent iface pattern. %s", + lldpctl_last_strerror(conn)); + lldpctl_atom_dec_ref(config); + return 0; + } + log_info("lldpctl", "permanent iface pattern set to new value %s", + value?value:"(none)"); + lldpctl_atom_dec_ref(config); + return 1; +} + static int cmd_iface_promisc(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env, void *arg) @@ -453,9 +481,25 @@ register_commands_configure_system(struct cmd_node *configure, commands_new(unconfigure_interface, "pattern", "Delete any interface pattern", NULL, NULL, NULL), - NEWLINE, "Delete any interface pattern", + NEWLINE, "Clear interface pattern", NULL, cmd_iface_pattern, NULL); + commands_new( + commands_new( + commands_new(configure_interface, + "permanent", "Set permanent interface pattern", + NULL, NULL, NULL), + NULL, "Permanent interface pattern (comma-separated list of wildcards)", + NULL, cmd_store_env_value, "iface-pattern"), + NEWLINE, "Set permanent interface pattern", + NULL, cmd_perm_iface_pattern, NULL); + commands_new( + commands_new(unconfigure_interface, + "permanent", "Clear permanent interface pattern", + NULL, NULL, NULL), + NEWLINE, "Delete any interface pattern", + NULL, cmd_perm_iface_pattern, NULL); + commands_new( commands_new(configure_interface, "description", "Update interface descriptions with neighbor name", diff --git a/src/client/display.c b/src/client/display.c index cd95931d..f452fc52 100644 --- a/src/client/display.c +++ b/src/client/display.c @@ -893,6 +893,8 @@ display_configuration(lldpctl_conn_t *conn, struct writer *w) N(lldpctl_atom_get_str(configuration, lldpctl_k_config_mgmt_pattern))); tag_datatag(w, "iface-pattern", "Interface pattern", N(lldpctl_atom_get_str(configuration, lldpctl_k_config_iface_pattern))); + tag_datatag(w, "perm-iface-pattern", "Permanent interface pattern", + N(lldpctl_atom_get_str(configuration, lldpctl_k_config_perm_iface_pattern))); tag_datatag(w, "cid-pattern", "Interface pattern for chassis ID", N(lldpctl_atom_get_str(configuration, lldpctl_k_config_cid_pattern))); tag_datatag(w, "cid-string", "Override chassis ID with", diff --git a/src/client/lldpcli.8.in b/src/client/lldpcli.8.in index ef583f89..ff2a8828 100644 --- a/src/client/lldpcli.8.in +++ b/src/client/lldpcli.8.in @@ -307,6 +307,31 @@ Remove any previously configured interface pattern and use all physical interfaces. This option undoes the previous one. .Ed +.Cd configure +.Cd system interface permanent Ar pattern +.Bd -ragged -offset XXXXXX +Specify interfaces whose configuration is permanently kept by +.Nm lldpd . +By default, +.Nm lldpd +disregard any data about interfaces when they are removed from the +system (statistics, custom configuration). This option allows one to +specify a pattern similar to the interface pattern. If an interface +disappear but matches the pattern, its data is kept in memory and +reused if the interface reappear at some point. For example, on Linux, +one could use the pattern +.Em eth*,eno*,enp* , +which should match fixed interfaces on most systems. +.Ed + +.Cd unconfigure +.Cd system interface permanent +.Bd -ragged -offset XXXXXX +Remove any previously configured permanent interface pattern. Any +interface removed from the system will be forgotten. This option +undoes the previous one. +.Ed + .Cd configure .Cd system interface description .Bd -ragged -offset XXXXXX diff --git a/src/daemon/client.c b/src/daemon/client.c index e5547b0f..e2ca4ff2 100644 --- a/src/daemon/client.c +++ b/src/daemon/client.c @@ -130,6 +130,13 @@ client_handle_set_configuration(struct lldpd *cfg, enum hmsg_type *type, cfg->g_config.c_iface_pattern = xstrdup(config->c_iface_pattern); levent_update_now(cfg); } + if (CHANGED_STR(c_perm_ifaces)) { + log_debug("rpc", "change permanent interface pattern to %s", + config->c_perm_ifaces?config->c_perm_ifaces:"(NULL)"); + free(cfg->g_config.c_perm_ifaces); + cfg->g_config.c_perm_ifaces = xstrdup(config->c_perm_ifaces); + levent_update_now(cfg); + } if (CHANGED_STR(c_mgmt_pattern)) { log_debug("rpc", "change management pattern to %s", config->c_mgmt_pattern?config->c_mgmt_pattern:"(NULL)"); diff --git a/src/daemon/lldpd.c b/src/daemon/lldpd.c index 7b5c1267..1914569c 100644 --- a/src/daemon/lldpd.c +++ b/src/daemon/lldpd.c @@ -145,9 +145,14 @@ lldpd_get_hardware(struct lldpd *cfg, char *name, int index) { struct lldpd_hardware *hardware; TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { - if ((strcmp(hardware->h_ifname, name) == 0) && - (hardware->h_ifindex == index)) - break; + if (strcmp(hardware->h_ifname, name) == 0) { + if (hardware->h_flags == 0) { + hardware->h_ifindex = index; + break; + } + if (hardware->h_ifindex == index) + break; + } } return hardware; } @@ -434,10 +439,24 @@ lldpd_cleanup(struct lldpd *cfg) hardware = hardware_next) { hardware_next = TAILQ_NEXT(hardware, h_entries); if (!hardware->h_flags) { - TRACE(LLDPD_INTERFACES_DELETE(hardware->h_ifname)); - TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries); - lldpd_remote_cleanup(hardware, notify_clients_deletion, 1); - lldpd_hardware_cleanup(cfg, hardware); + int m = cfg->g_config.c_perm_ifaces? + pattern_match(hardware->h_ifname, cfg->g_config.c_perm_ifaces, 0): + 0; + switch (m) { + case 0: + log_debug("localchassis", "delete non-permanent interface %s", + hardware->h_ifname); + TRACE(LLDPD_INTERFACES_DELETE(hardware->h_ifname)); + TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries); + lldpd_remote_cleanup(hardware, notify_clients_deletion, 1); + lldpd_hardware_cleanup(cfg, hardware); + break; + case 1: + case 2: + log_debug("localchassis", "do not delete %s, permanent", + hardware->h_ifname); + break; + } } else { lldpd_remote_cleanup(hardware, notify_clients_deletion, !(hardware->h_flags & IFF_RUNNING)); diff --git a/src/lib/atoms/config.c b/src/lib/atoms/config.c index 02836088..6971b400 100644 --- a/src/lib/atoms/config.c +++ b/src/lib/atoms/config.c @@ -89,6 +89,8 @@ _lldpctl_atom_get_str_config(lldpctl_atom_t *atom, lldpctl_key_t key) res = c->config->c_mgmt_pattern; break; case lldpctl_k_config_iface_pattern: res = c->config->c_iface_pattern; break; + case lldpctl_k_config_perm_iface_pattern: + res = c->config->c_perm_ifaces; break; case lldpctl_k_config_cid_pattern: res = c->config->c_cid_pattern; break; case lldpctl_k_config_cid_string: @@ -146,6 +148,12 @@ _lldpctl_atom_set_str_config(lldpctl_atom_t *atom, lldpctl_key_t key, int rc; switch (key) { + case lldpctl_k_config_perm_iface_pattern: + if (!__lldpctl_atom_set_str_config(c, + &config.c_perm_ifaces, &c->config->c_perm_ifaces, + value)) + return NULL; + break; case lldpctl_k_config_iface_pattern: if (!__lldpctl_atom_set_str_config(c, &config.c_iface_pattern, &c->config->c_iface_pattern, diff --git a/src/lib/lldpctl.h b/src/lib/lldpctl.h index 97ba2979..bb996d9e 100644 --- a/src/lib/lldpctl.h +++ b/src/lib/lldpctl.h @@ -666,6 +666,7 @@ typedef enum { lldpctl_k_config_chassis_cap_advertise, /**< `(I,WO)` Enable or disable chassis capabilities advertisement */ lldpctl_k_config_chassis_mgmt_advertise, /**< `(I,WO)` Enable or disable management addresses advertisement */ lldpctl_k_config_cid_string, /**< `(S,WON)` User defined string for the chassis ID */ + lldpctl_k_config_perm_iface_pattern, /**< `(S,WON)` Pattern of permanent interfaces */ lldpctl_k_interface_name = 1000, /**< `(S)` The interface name. */ diff --git a/src/lldpd-structs.c b/src/lldpd-structs.c index 24b13527..26e65427 100644 --- a/src/lldpd-structs.c +++ b/src/lldpd-structs.c @@ -235,6 +235,7 @@ lldpd_config_cleanup(struct lldpd_config *config) free(config->c_cid_pattern); free(config->c_cid_string); free(config->c_iface_pattern); + free(config->c_perm_ifaces); free(config->c_hostname); free(config->c_platform); free(config->c_description); diff --git a/src/lldpd-structs.h b/src/lldpd-structs.h index b44a43f3..6cea04ff 100644 --- a/src/lldpd-structs.h +++ b/src/lldpd-structs.h @@ -380,6 +380,7 @@ struct lldpd_config { char *c_cid_pattern; /* Pattern to match interfaces to use for chassis ID */ char *c_cid_string; /* User defined string for chassis ID */ char *c_iface_pattern; /* Pattern to match interfaces to use */ + char *c_perm_ifaces; /* Pattern to match interfaces to keep */ char *c_platform; /* Override platform description (for CDP) */ char *c_description; /* Override chassis description */ @@ -407,6 +408,7 @@ MARSHAL_STR(lldpd_config, c_mgmt_pattern) MARSHAL_STR(lldpd_config, c_cid_pattern) MARSHAL_STR(lldpd_config, c_cid_string) MARSHAL_STR(lldpd_config, c_iface_pattern) +MARSHAL_STR(lldpd_config, c_perm_ifaces) MARSHAL_STR(lldpd_config, c_hostname) MARSHAL_STR(lldpd_config, c_platform) MARSHAL_STR(lldpd_config, c_description) diff --git a/tests/integration/test_lldpcli.py b/tests/integration/test_lldpcli.py index add740b6..ba1f09a2 100644 --- a/tests/integration/test_lldpcli.py +++ b/tests/integration/test_lldpcli.py @@ -430,6 +430,40 @@ def test_port_forget_configuration(lldpd, lldpcli, assert 'lldp.eth3.port.power.device-type' not in out +@pytest.mark.skipif('Dot3' not in pytest.config.lldpd.features, + reason="Dot3 not supported") +def test_port_keep_configuration_of_permanent_ports(lldpd, lldpcli, + namespaces, links): + with namespaces(1): + links.dummy('eth3') + links.dummy('noteth3') + lldpd() + result = lldpcli(*("configure system interface permanent e*").split()) + assert result.returncode == 0 + result = lldpcli(*("configure dot3 power " + "pse supported enabled paircontrol powerpairs " + "spare class class-3").split()) + assert result.returncode == 0 + time.sleep(3) + links.remove('eth3') + links.remove('noteth3') + time.sleep(4) + # eth3 configuration is kept because it matches the permanent + # port pattern. + out = lldpcli("-f", "keyvalue", "show", "interfaces", "details") + assert out['lldp.eth3.port.power.device-type'] == 'PSE' + assert 'lldp.noteth3.port.power.device-type' not in out + + links.dummy('eth3') + links.dummy('noteth3') + time.sleep(4) + # eth3 configuration is unchanged + out = lldpcli("-f", "keyvalue", "show", "interfaces", "details") + assert out['lldp.eth3.port.power.device-type'] == 'PSE' + # noteth3 inherited from default + assert out['lldp.noteth3.port.power.device-type'] == 'PSE' + + def test_watch(lldpd1, lldpd, lldpcli, namespaces, links): with namespaces(2): lldpd() -- 2.39.5