]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
lldpd: add an option to keep some specified ports feature/keep-some-ports 274/head
authorVincent Bernat <vincent@bernat.im>
Sat, 17 Mar 2018 15:28:31 +0000 (16:28 +0100)
committerVincent Bernat <vincent@bernat.im>
Sat, 17 Mar 2018 16:04:47 +0000 (17:04 +0100)
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
src/client/conf-system.c
src/client/display.c
src/client/lldpcli.8.in
src/daemon/client.c
src/daemon/lldpd.c
src/lib/atoms/config.c
src/lib/lldpctl.h
src/lldpd-structs.c
src/lldpd-structs.h
tests/integration/test_lldpcli.py

diff --git a/NEWS b/NEWS
index 2c3760d8aedba1b5278ec2c549a685c2853d7486..e2fd0b8d3740486ba158d9d4f0bac843baf4a4bc 100644 (file)
--- 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.
index 9958b1a5f175bb3fd3b8b24859e441730c1a421f..f2ad664b2617912491ca14fc4410346f4e300f64 100644 (file)
@@ -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",
index cd95931d3f0481cbd3efe9f64e267d9b53fda80c..f452fc5239585f5ff2a57d67eb7487b4c64d277b 100644 (file)
@@ -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",
index ef583f89606f896519f92dc423feb58ed1270485..ff2a8828db3b9a36bc09c2f522d19370a3506164 100644 (file)
@@ -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
index e5547b0f5b787cb411cca130c76ab7d14c599b07..e2ca4ff29586ee2c5b0fe536a7f3397333093758 100644 (file)
@@ -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)");
index 7b5c1267de3e279b7cb3e19461a0422e9275a8d7..1914569cb6104bc7cc8dc132a4dc18adae6e286b 100644 (file)
@@ -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));
index 02836088e3b802093a454e064dcf0fdc36e38d05..6971b40040c824a0e88e6e9bfccd49bcaccc9ae8 100644 (file)
@@ -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,
index 97ba2979c1225919ad0435e9052a719dd08e08f9..bb996d9efb164991636de190ca2bbe5f03b3c694 100644 (file)
@@ -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. */
 
index 24b13527d35d5a92ecfd772b3058c74b5b7ed3ce..26e654272c71fb857b45b070468ab7047a118a46 100644 (file)
@@ -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);
index b44a43f34035389bc25e33ea7e50d7d1fb5c9bb3..6cea04ff961787b59b6b13d48a630eb683d9eb70 100644 (file)
@@ -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)
index add740b6112c1830ac3e4ad400d46c6e7e42f67a..ba1f09a230831188e8edafd095505d15d1f7fc34 100644 (file)
@@ -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()