Eelco Chaudron <echau...@redhat.com> writes:

> On 20 Aug 2025, at 5:04, Changliang Wu wrote:
>
>> Hi, Aaron and Eelco
>>
>> I'm preparing the next version patch.
>> Before submitting it, I'd like to discuss and confirm the final output 
>> format.
>>
>> I prefer to keep the JSON format unchanged and modify the CLI format
>> according to Eeclo's comments in v5.
>
> Thanks for following through with this series. Your plan looks good to me.

+1

> Cheers,
>
> Eelco
>
>
>> On Thu, Jul 17, 2025 at 10:18 AM Changliang Wu <changliang...@smartx.com> 
>> wrote:
>>>
>>> Hi, Aaron
>>>
>>> On Wed, Jul 16, 2025 at 9:56 PM Aaron Conole <acon...@redhat.com> wrote:
>>>>
>>>> Hi Changliang,
>>>>
>>>> Changliang Wu <changliang...@smartx.com> writes:
>>>>
>>>>> New appctl 'lldp/neighbor' displays lldp neighbor information.
>>>>> Support json output with --format json --pretty
>>>>>
>>>>> Signed-off-by: Changliang Wu <changliang...@smartx.com>
>>>>> ---
>>>>
>>>> Thanks for continuing with this.  It's getting closer.  There are some
>>>> comments below.  I think Eelco had thoughts on the output of the
>>>> commands, so please wait for his reply before submitting the v10.
>>>>
>>>>>  NEWS                       |   1 +
>>>>>  lib/ovs-lldp.c             | 362 +++++++++++++++++++++++++++++++++++++
>>>>>  vswitchd/ovs-vswitchd.8.in |   4 +
>>>>>  3 files changed, 367 insertions(+)
>>>>>
>>>>> diff --git a/NEWS b/NEWS
>>>>> index c7bb53f2d..931fe5c71 100644
>>>>> --- a/NEWS
>>>>> +++ b/NEWS
>>>>> @@ -20,6 +20,7 @@ Post-v3.5.0
>>>>>         core file size.
>>>>>     - ovs-appctl:
>>>>>       * Added JSON output support to the 'ovs/route/show' command.
>>>>> +     * Added 'lldp/neighbor' command that displays lldp neighbor 
>>>>> infomation.
>>>>
>>>> Just an FYI, there was a mid-air collision on the NEWS file, but it
>>>> isn't difficult to resolve.
>>> Yes, fix in next version, also a typo here.
>>>>
>>>>>     - SSL/TLS:
>>>>>       * Support for deprecated TLSv1 and TLSv1.1 protocols on OpenFlow and
>>>>>         database connections is now removed.
>>>>> diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
>>>>> index 152777248..2f3f6046a 100644
>>>>> --- a/lib/ovs-lldp.c
>>>>> +++ b/lib/ovs-lldp.c
>>>>> @@ -36,7 +36,10 @@
>>>>>  #include <stdlib.h>
>>>>>  #include "openvswitch/dynamic-string.h"
>>>>>  #include "flow.h"
>>>>> +#include "openvswitch/json.h"
>>>>>  #include "openvswitch/list.h"
>>>>> +#include "lldp/lldp-const.h"
>>>>> +#include "lldp/lldp-tlv.h"
>>>>>  #include "lldp/lldpd.h"
>>>>>  #include "lldp/lldpd-structs.h"
>>>>>  #include "netdev.h"
>>>>> @@ -310,6 +313,321 @@ aa_print_isid_status(struct ds *ds, struct lldp 
>>>>> *lldp) OVS_REQUIRES(mutex)
>>>>>      }
>>>>>  }
>>>>>
>>>>> +static void
>>>>> +lldp_print_neighbor(struct ds *ds, struct lldp *lldp) OVS_REQUIRES(mutex)
>>>>> +{
>>>>> +    const char *none_str = "<None>";
>>>>> +    struct lldpd_hardware *hw;
>>>>> +    struct lldpd_port *port;
>>>>> +
>>>>> +    if (!lldp->lldpd) {
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    bool is_first_interface = true;
>>>>> +    LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) {
>>>>> +        if (!hw->h_rports.next) {
>>>>> +            continue;
>>>>> +        }
>>>>> +        LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
>>>>> +            struct ds chassis_id = DS_EMPTY_INITIALIZER;
>>>>> +            if (!port->p_chassis) {
>>>>> +                continue;
>>>>> +            }
>>>>> +            if (is_first_interface) {
>>>>> +                is_first_interface = false;
>>>>> +            } else {
>>>>> +                ds_put_format(ds, "\n");
>>>>> +            }
>>>>
>>>> A nit - but please prefer putting the common case first:
>>>>
>>>>   if (!is_first_interface) {
>>>>       ds_put_format(ds, "\n")
>>>>   }
>>>>   is_first_interface = false;
>>> Yes, that's more clear.
>>>>
>>>>> +            ds_put_format(ds, "Interface: %s\n", lldp->name);
>>>>> +            ds_put_format(ds, "  Chassis:\n");
>>>>> +
>>>>> +            if (port->p_chassis->c_id_len > 0) {
>>>>
>>>> This can be:
>>>>
>>>>    if (port->p_chassis->c_id_len) {
>>>>
>>>> It applies where all the len > 0 checks are present.
>>> Ok, I'll check all the other places.
>>>>
>>>>> +                ds_put_hex_with_delimiter(&chassis_id, 
>>>>> port->p_chassis->c_id,
>>>>> +                                          port->p_chassis->c_id_len, 
>>>>> ":");
>>>>> +            }
>>>>> +            ds_put_format(ds, "    %-20s%s\n", "Chassis ID:",
>>>>> +                          chassis_id.length ? ds_cstr_ro(&chassis_id)
>>>>> +                                            : none_str);
>>>>> +            ds_put_format(ds, "    %-20s%s\n", "SysName:",
>>>>> +                          strlen(port->p_chassis->c_name)
>>>>
>>>> I was preparing to apply this and just manually fix up the little nits,
>>>> but noticed that we only conditionally set port->p_chassis->c_name; so
>>>> these strlen() checks need to be prefaced with a check that c_name is
>>>> set.
>>>>
>>>>> +                              ? port->p_chassis->c_name
>>>>> +                              : none_str);
>>>>> +            ds_put_format(ds, "    %-20s%s\n", "SysDescr:",
>>>>> +                          strlen(port->p_chassis->c_descr)
>>>>
>>>> Likely here (see the below paragraph).
>>>>
>>>>> +                              ? port->p_chassis->c_descr
>>>>> +                              : none_str);
>>>>> +            ds_destroy(&chassis_id);
>>>>> +
>>>>> +            struct lldpd_mgmt *mgmt;
>>>>> +            LIST_FOR_EACH (mgmt, m_entries, &port->p_chassis->c_mgmt) {
>>>>> +                struct in6_addr ip;
>>>>> +                switch (mgmt->m_family) {
>>>>> +                case LLDPD_AF_IPV4:
>>>>> +                    in6_addr_set_mapped_ipv4(&ip, 
>>>>> mgmt->m_addr.inet.s_addr);
>>>>> +                    break;
>>>>> +
>>>>> +                case LLDPD_AF_IPV6:
>>>>> +                    ip = mgmt->m_addr.inet6;
>>>>> +                    break;
>>>>> +
>>>>> +                default:
>>>>> +                    continue;
>>>>> +                }
>>>>> +                ds_put_format(ds, "    %-20s", "MgmtIP:");
>>>>> +                ipv6_format_mapped(&ip, ds);
>>>>> +                ds_put_format(ds, "\n");
>>>>> +                ds_put_format(ds, "    %-20s%d\n",
>>>>> +                              "MgmtIface:", mgmt->m_iface);
>>>>> +            }
>>>>> +
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_BRIDGE) {
>>>>> +                ds_put_format(ds, "    %-20sBridge, %s\n","Capability:",
>>>>> +                              port->p_chassis->c_cap_enabled & 
>>>>> LLDP_CAP_BRIDGE
>>>>> +                                  ? "on"
>>>>> +                                  : "off");
>>>>> +            }
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_ROUTER) {
>>>>> +                ds_put_format(ds, "    %-20sRouter, %s\n","Capability:",
>>>>> +                              port->p_chassis->c_cap_enabled & 
>>>>> LLDP_CAP_ROUTER
>>>>> +                                  ? "on"
>>>>> +                                  : "off");
>>>>> +            }
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_WLAN) {
>>>>> +                ds_put_format(ds, "    %-20sWlan, %s\n","Capability:",
>>>>> +                              port->p_chassis->c_cap_enabled & 
>>>>> LLDP_CAP_WLAN
>>>>> +                                  ? "on"
>>>>> +                                  : "off");
>>>>> +            }
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_STATION) {
>>>>> +                ds_put_format(ds, "    %-20sStation, %s\n","Capability:",
>>>>> +                              port->p_chassis->c_cap_enabled & 
>>>>> LLDP_CAP_STATION
>>>>> +                                  ? "on"
>>>>> +                                  : "off");
>>>>> +            }
>>>>> +
>>>>> +            ds_put_format(ds, "  Port:\n");
>>>>> +
>>>>> +            if (port->p_id_subtype == LLDP_PORTID_SUBTYPE_LLADDR) {
>>>>> +                struct ds lladdr = DS_EMPTY_INITIALIZER;
>>>>> +                if (port->p_id_len > 0) {
>>>>> +                    ds_put_hex_with_delimiter(&lladdr, (uint8_t *) 
>>>>> port->p_id,
>>>>> +                                              port->p_id_len, ":");
>>>>> +                }
>>>>> +                ds_put_format(ds, "    %-20s%s\n", "PortID:",
>>>>> +                              lladdr.length ? ds_cstr_ro(&lladdr) : 
>>>>> none_str);
>>>>> +                ds_destroy(&lladdr);
>>>>> +            } else {
>>>>> +                ds_put_format(ds, "    %-20s%.*s\n", "PortID:",
>>>>> +                              (int) (port->p_id ? port->p_id_len
>>>>> +                                                : strlen(none_str)),
>>>>> +                              port->p_id ?: none_str);
>>>>> +            }
>>>>> +            ds_put_format(ds, "    %-20s%s\n", "PortDescr:",
>>>>> +                          strlen(port->p_descr) ? port->p_descr : 
>>>>> none_str);
>>>>
>>>> Likewise p_descr is conditionally initialized.
>>>>
>>>>> +            ds_put_format(ds, "    %-20s%d\n", "TTL:", 
>>>>> port->p_chassis->c_ttl);
>>>>> +        }
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +lldp_print_neighbor_json(struct json *interface_json, struct lldp *lldp)
>>>>> +    OVS_REQUIRES(mutex)
>>>>> +{
>>>>> +    const char *none_str = "<None>";
>>>>> +    struct lldpd_hardware *hw;
>>>>> +    struct lldpd_port *port;
>>>>> +
>>>>> +    if (!lldp->lldpd) {
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    struct json *interface_item_warp_json_last = NULL;
>>>>> +    struct json *interface_array_json = NULL;
>>>>> +    bool has_multi_interfaces = false;
>>>>> +
>>>>> +    LIST_FOR_EACH (hw, h_entries, &lldp->lldpd->g_hardware) {
>>>>> +        if (!hw->h_rports.next) {
>>>>> +            continue;
>>>>> +        }
>>>>> +        LIST_FOR_EACH (port, p_entries, &hw->h_rports) {
>>>>> +            struct ds chassis_id = DS_EMPTY_INITIALIZER;
>>>>> +            if (!port->p_chassis) {
>>>>> +                continue;
>>>>> +            }
>>>>> +
>>>>> +            struct json *interface_item_warp_json = json_object_create();
>>>>> +            struct json *interface_item_json = json_object_create();
>>>>> +            struct json *chassis_json = json_object_create();
>>>>> +            struct json *chassis_sys_json = json_object_create();
>>>>> +            struct json *chassis_id_json = json_object_create();
>>>>> +            struct json *chassis_mgmt_ip_json = 
>>>>> json_array_create_empty();
>>>>> +            struct json *chassis_mgmt_iface_json = 
>>>>> json_array_create_empty();
>>>>> +            struct json *chassis_capability_json = 
>>>>> json_array_create_empty();
>>>>> +            struct json *chassis_capability_item_json;
>>>>> +            struct json *port_json = json_object_create();
>>>>> +            struct json *port_id_json = json_object_create();
>>>>> +
>>>>> +            if (port->p_chassis->c_id_len > 0) {
>>>>> +                ds_put_hex_with_delimiter(&chassis_id, 
>>>>> port->p_chassis->c_id,
>>>>> +                                          port->p_chassis->c_id_len, 
>>>>> ":");
>>>>> +            }
>>>>> +
>>>>> +            json_object_put(chassis_id_json, "type",
>>>>> +                            json_string_create("mac"));
>>>>> +            json_object_put(chassis_id_json, "value",
>>>>> +                            json_string_create(chassis_id.length
>>>>> +                                                   ? 
>>>>> ds_cstr_ro(&chassis_id)
>>>>> +                                                   : none_str));
>>>>> +            json_object_put(chassis_sys_json, "id", chassis_id_json);
>>>>> +            json_object_put(chassis_json,
>>>>> +                            strlen(port->p_chassis->c_name)
>>>>> +                                ? port->p_chassis->c_name
>>>>> +                                : none_str,
>>>>> +                            chassis_sys_json);
>>>>
>>>> Although it looks like the chassis c_name and c_descr are getting
>>>> initialized, in the places it gets used there are checks that it is not
>>>> null before actually using these variables.  So I'm inclined to consider
>>>> that we should check here as well.
>>>>
>>>>> +            json_object_put(chassis_sys_json, "descr",
>>>>> +                            
>>>>> json_string_create(strlen(port->p_chassis->c_descr)
>>>>> +                                                   ? 
>>>>> port->p_chassis->c_descr
>>>>> +                                                   : none_str));
>>>>> +            ds_destroy(&chassis_id);
>>>>> +
>>>>> +            struct lldpd_mgmt *mgmt;
>>>>> +            LIST_FOR_EACH (mgmt, m_entries, &port->p_chassis->c_mgmt) {
>>>>> +                char addr_str[INET6_ADDRSTRLEN];
>>>>> +                struct in6_addr ip;
>>>>> +                switch (mgmt->m_family) {
>>>>> +                case LLDPD_AF_IPV4:
>>>>> +                    in6_addr_set_mapped_ipv4(&ip, 
>>>>> mgmt->m_addr.inet.s_addr);
>>>>> +                    break;
>>>>> +
>>>>> +                case LLDPD_AF_IPV6:
>>>>> +                    ip = mgmt->m_addr.inet6;
>>>>> +                    break;
>>>>> +
>>>>> +                default:
>>>>> +                    continue;
>>>>> +                }
>>>>> +
>>>>> +                ipv6_string_mapped(addr_str, &ip);
>>>>> +                json_array_add(chassis_mgmt_ip_json,
>>>>> +                               json_string_create(addr_str));
>>>>> +                json_array_add(chassis_mgmt_iface_json,
>>>>> +                               json_integer_create(mgmt->m_iface));
>>>>> +            }
>>>>> +            json_object_put(chassis_sys_json, "mgmt-ip", 
>>>>> chassis_mgmt_ip_json);
>>>>> +            json_object_put(chassis_sys_json, "mgmt-iface",
>>>>> +                            chassis_mgmt_iface_json);
>>>>> +
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_BRIDGE) {
>>>>> +                chassis_capability_item_json = json_object_create();
>>>>> +                json_object_put(chassis_capability_item_json, "type",
>>>>> +                                json_string_create("Bridge"));
>>>>> +                json_object_put(
>>>>> +                    chassis_capability_item_json, "enabled",
>>>>> +                    json_boolean_create(port->p_chassis->c_cap_enabled &
>>>>> +                                        LLDP_CAP_BRIDGE));
>>>>> +                json_array_add(chassis_capability_json,
>>>>> +                               chassis_capability_item_json);
>>>>> +            }
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_ROUTER) {
>>>>> +                chassis_capability_item_json = json_object_create();
>>>>> +                json_object_put(chassis_capability_item_json, "type",
>>>>> +                                json_string_create("Router"));
>>>>> +                json_object_put(
>>>>> +                    chassis_capability_item_json, "enabled",
>>>>> +                    json_boolean_create(port->p_chassis->c_cap_enabled &
>>>>> +                                        LLDP_CAP_ROUTER));
>>>>> +                json_array_add(chassis_capability_json,
>>>>> +                               chassis_capability_item_json);
>>>>> +            }
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_WLAN) {
>>>>> +                chassis_capability_item_json = json_object_create();
>>>>> +                json_object_put(chassis_capability_item_json, "type",
>>>>> +                                json_string_create("Wlan"));
>>>>> +                json_object_put(
>>>>> +                    chassis_capability_item_json, "enabled",
>>>>> +                    json_boolean_create(port->p_chassis->c_cap_enabled &
>>>>> +                                        LLDP_CAP_WLAN));
>>>>> +                json_array_add(chassis_capability_json,
>>>>> +                               chassis_capability_item_json);
>>>>> +            }
>>>>> +            if (port->p_chassis->c_cap_available & LLDP_CAP_STATION) {
>>>>> +                chassis_capability_item_json = json_object_create();
>>>>> +                json_object_put(chassis_capability_item_json, "type",
>>>>> +                                json_string_create("Station"));
>>>>> +                json_object_put(
>>>>> +                    chassis_capability_item_json, "enabled",
>>>>> +                    json_boolean_create(port->p_chassis->c_cap_enabled &
>>>>> +                                        LLDP_CAP_STATION));
>>>>> +                json_array_add(chassis_capability_json,
>>>>> +                               chassis_capability_item_json);
>>>>> +            }
>>>>> +            json_object_put(chassis_sys_json, "capability",
>>>>> +                            chassis_capability_json);
>>>>> +
>>>>> +            if (port->p_id_subtype == LLDP_PORTID_SUBTYPE_LLADDR) {
>>>>> +                struct ds lladdr = DS_EMPTY_INITIALIZER;
>>>>> +                if (port->p_id_len > 0) {
>>>>> +                    ds_put_hex_with_delimiter(&lladdr, port->p_id,
>>>>> +                                              port->p_id_len, ":");
>>>>> +                }
>>>>> +                json_object_put(port_id_json, "type",
>>>>> +                                json_string_create("mac"));
>>>>> +                json_object_put(port_id_json, "value",
>>>>> +                                json_string_create(lladdr.length
>>>>> +                                                       ? 
>>>>> ds_cstr_ro(&lladdr)
>>>>> +                                                       : none_str));
>>>>> +                ds_destroy(&lladdr);
>>>>> +            } else {
>>>>> +                json_object_put(port_id_json, "type",
>>>>> +                                json_string_create("ifname"));
>>>>> +                if (port->p_id) {
>>>>> +                    struct ds port_ds = DS_EMPTY_INITIALIZER;
>>>>> +                    ds_put_buffer(&port_ds, port->p_id, port->p_id_len);
>>>>> +                    json_object_put(port_id_json, "value",
>>>>> +                                    
>>>>> json_string_create(ds_cstr_ro(&port_ds)));
>>>>> +                    ds_destroy(&port_ds);
>>>>> +                } else {
>>>>> +                    json_object_put(port_id_json, "value",
>>>>> +                                    json_string_create(none_str));
>>>>> +                }
>>>>> +            }
>>>>> +            json_object_put(port_json, "id", port_id_json);
>>>>> +
>>>>> +            json_object_put(port_json, "desc",
>>>>> +                            json_string_create(strlen(port->p_descr)
>>>>> +                                                   ? port->p_descr
>>>>> +                                                   : none_str));
>>>>
>>>> Likewise, p_descr is conditionally initialized.
>>>>
>>>> Given the number of places we need to do these checks, an improvement
>>>> I would suggest is to introduce a new function (unless I'm missing one
>>>> already present) in util.h:
>>>>
>>>>   static inline bool nullable_string_not_empty(const char *s)
>>>>   {
>>>>       return s && *s != '\0';
>>>>   }
>>>>
>>>> That could also be used in lldp/lldp.c for:
>>>>
>>>>   lib/lldp/lldp.c:194:    if (chassis->c_name && *chassis->c_name != '\0') 
>>>> {
>>>>   lib/lldp/lldp.c:201:    if (chassis->c_descr && *chassis->c_descr != 
>>>> '\0') {
>>>>   lib/lldp/lldp.c:234:    if (port->p_descr && *port->p_descr != '\0') {
>>>>
>>>> as (ex):
>>>>
>>>>   if (nullable_string_not_empty(chassis->c_name))
>>>>   if (nullable_string_not_empty(chassis->c_descr))
>>>>   if (nullable_string_not_empty(port->p_descr))
>>>>
>>>> If you decide to introduce the function instead of open-coding the check
>>>> (as done in other parts of lldp code), then please introduce it as the
>>>> first patch in the series just adding the nullable_string_not_empty, and
>>>> modifying the lib/lldp/lldp.c lines above.  Then use in this patch (and
>>>> following patches in the series where needed).
>>> Ok, I will implement it in the next version.
>>>>
>>>>> +            json_object_put(port_json, "ttl",
>>>>> +                            json_integer_create(port->p_chassis->c_ttl));
>>>>> +
>>>>> +            json_object_put(interface_item_json, "chassis", 
>>>>> chassis_json);
>>>>> +            json_object_put(interface_item_json, "port", port_json);
>>>>> +
>>>>> +            json_object_put(interface_item_warp_json, lldp->name,
>>>>> +                            interface_item_json);
>>>>> +
>>>>> +            if (has_multi_interfaces) {
>>>>> +                if (!interface_array_json) {
>>>>> +                    interface_array_json = json_array_create_empty();
>>>>> +                    json_array_add(interface_array_json,
>>>>> +                                   interface_item_warp_json_last);
>>>>> +                }
>>>>> +                json_array_add(interface_array_json, 
>>>>> interface_item_warp_json);
>>>>> +            } else {
>>>>> +                has_multi_interfaces = true;
>>>>> +                interface_item_warp_json_last = interface_item_warp_json;
>>>>> +            }
>>>>> +        }
>>>>> +    }
>>>>> +    if (interface_array_json || interface_item_warp_json_last) {
>>>>> +        json_object_put(interface_json, "interface",
>>>>> +                        interface_array_json == NULL
>>>>> +                            ? interface_item_warp_json_last
>>>>> +                            : interface_array_json);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>>  static void
>>>>>  aa_unixctl_status(struct unixctl_conn *conn, int argc OVS_UNUSED,
>>>>>                    const char *argv[] OVS_UNUSED, void *aux OVS_UNUSED)
>>>>> @@ -368,6 +686,48 @@ aa_unixctl_statistics(struct unixctl_conn *conn, int 
>>>>> argc OVS_UNUSED,
>>>>>      unixctl_command_reply(conn, ds_cstr(&ds));
>>>>>  }
>>>>>
>>>>> +static void
>>>>> +lldp_unixctl_show_neighbor(struct unixctl_conn *conn, int argc,
>>>>> +                           const char *argv[], void *aux OVS_UNUSED)
>>>>> +    OVS_EXCLUDED(mutex)
>>>>> +{
>>>>> +    struct lldp *lldp;
>>>>> +
>>>>> +    if (unixctl_command_get_output_format(conn) == 
>>>>> UNIXCTL_OUTPUT_FMT_JSON) {
>>>>> +        struct json *lldp_json = json_object_create();
>>>>> +        struct json *interface_json = json_object_create();
>>>>> +
>>>>> +        ovs_mutex_lock(&mutex);
>>>>> +        HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
>>>>> +            if (argc > 1 && strcmp(argv[1], lldp->name)) {
>>>>> +                continue;
>>>>> +            }
>>>>> +            lldp_print_neighbor_json(interface_json, lldp);
>>>>> +        }
>>>>> +        ovs_mutex_unlock(&mutex);
>>>>> +
>>>>> +        json_object_put(lldp_json, "lldp", interface_json);
>>>>> +
>>>>> +        unixctl_command_reply_json(conn, lldp_json);
>>>>> +    } else {
>>>>> +        struct ds ds = DS_EMPTY_INITIALIZER;
>>>>> +
>>>>> +        ds_put_format(&ds, "LLDP neighbor:\n");
>>>>> +
>>>>> +        ovs_mutex_lock(&mutex);
>>>>> +        HMAP_FOR_EACH (lldp, hmap_node, all_lldps) {
>>>>> +            if (argc > 1 && strcmp(argv[1], lldp->name)) {
>>>>> +                continue;
>>>>> +            }
>>>>> +            lldp_print_neighbor(&ds, lldp);
>>>>> +        }
>>>>> +        ovs_mutex_unlock(&mutex);
>>>>> +
>>>>> +        unixctl_command_reply(conn, ds_cstr(&ds));
>>>>> +        ds_destroy(&ds);
>>>>> +    }
>>>>> +}
>>>>> +
>>>>>  /* An Auto Attach mapping was configured.  Populate the corresponding
>>>>>   * structures in the LLDP hardware.
>>>>>   */
>>>>> @@ -635,6 +995,8 @@ lldp_init(void)
>>>>>                               aa_unixctl_show_isid, NULL);
>>>>>      unixctl_command_register("autoattach/statistics", "[bridge]", 0, 1,
>>>>>                               aa_unixctl_statistics, NULL);
>>>>> +    unixctl_command_register("lldp/neighbor", "[interface]", 0, 1,
>>>>> +                             lldp_unixctl_show_neighbor, NULL);
>>>>>  }
>>>>>
>>>>>  /* Returns true if 'lldp' should process packets from 'flow'.  Sets
>>>>> diff --git a/vswitchd/ovs-vswitchd.8.in b/vswitchd/ovs-vswitchd.8.in
>>>>> index 98e58951d..51b0a46b5 100644
>>>>> --- a/vswitchd/ovs-vswitchd.8.in
>>>>> +++ b/vswitchd/ovs-vswitchd.8.in
>>>>> @@ -149,6 +149,10 @@ enabled.
>>>>>  Force the fault status of the CFM module on \fIinterface\fR (or all
>>>>>  interfaces if none is given) to be \fIstatus\fR.  \fIstatus\fR can be
>>>>>  "true", "false", or "normal" which reverts to the standard behavior.
>>>>> +.IP "\fBlldp/neighbor\fR [\fIinterface\fR]"
>>>>> +Displays detailed information about LLDP neighbors on \fIinterface\fR.
>>>>> +If \fIinterface\fR is not specified, then it displays detailed 
>>>>> information
>>>>> +about all interfaces with LLDP enabled.
>>>>>  .IP "\fBstp/tcn\fR [\fIbridge\fR]"
>>>>>  Forces a topology change event on \fIbridge\fR if it's running STP.  This
>>>>>  may cause it to send Topology Change Notifications to its peers and flush
>>>>

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to