diff options
Diffstat (limited to 'src')
| -rwxr-xr-x | src/op_mode/show_neigh.py | 162 | 
1 files changed, 84 insertions, 78 deletions
| diff --git a/src/op_mode/show_neigh.py b/src/op_mode/show_neigh.py index 94e745493..4d96ee869 100755 --- a/src/op_mode/show_neigh.py +++ b/src/op_mode/show_neigh.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2022 VyOS maintainers and contributors  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License version 2 or later as @@ -14,83 +14,89 @@  # You should have received a copy of the GNU General Public License  # along with this program.  If not, see <http://www.gnu.org/licenses/>. -#ip -j -f inet neigh list | jq -#[ -  #{ -    #"dst": "192.168.101.8", -    #"dev": "enp0s25", -    #"lladdr": "78:d2:94:72:77:7e", -    #"state": [ -      #"STALE" -    #] -  #}, -  #{ -    #"dst": "192.168.101.185", -    #"dev": "enp0s25", -    #"lladdr": "34:46:ec:76:f8:9b", -    #"state": [ -      #"STALE" -    #] -  #}, -  #{ -    #"dst": "192.168.101.225", -    #"dev": "enp0s25", -    #"lladdr": "c2:cb:fa:bf:a0:35", -    #"state": [ -      #"STALE" -    #] -  #}, -  #{ -    #"dst": "192.168.101.1", -    #"dev": "enp0s25", -    #"lladdr": "00:98:2b:f8:3f:11", -    #"state": [ -      #"REACHABLE" -    #] -  #}, -  #{ -    #"dst": "192.168.101.181", -    #"dev": "enp0s25", -    #"lladdr": "d8:9b:3b:d5:88:22", -    #"state": [ -      #"STALE" -    #] -  #} -#] +# Sample output of `ip --json neigh list`: +# +# [ +#   { +#     "dst": "192.168.1.1", +#     "dev": "eth0",                 # Missing if `dev ...` option is used +#     "lladdr": "00:aa:bb:cc:dd:ee", # May be missing for failed entries +#     "state": [ +#       "REACHABLE" +#     ] +#  }, +# ]  import sys -import argparse -import json -from vyos.util import cmd - -def main(): -    #parese args -    parser = argparse.ArgumentParser() -    parser.add_argument('--family', help='Protocol family', required=True) -    args = parser.parse_args() -     -    neigh_raw_json = cmd(f'ip -j -f {args.family} neigh list') -    neigh_raw_json = neigh_raw_json.lower() -    neigh_json = json.loads(neigh_raw_json) -     -    format_neigh = '%-50s %-10s %-20s %s' -    print(format_neigh % ("IP Address", "Device", "State", "LLADDR")) -    print(format_neigh % ("----------", "------", "-----", "------")) -     -    if neigh_json is not None: -        for neigh_item in neigh_json: -            dev = neigh_item['dev'] -            dst = neigh_item['dst'] -            lladdr = neigh_item['lladdr'] if 'lladdr' in neigh_item else '' -            state = neigh_item['state'] -             -            i = 0 -            for state_item in  state: -                if i == 0: -                    print(format_neigh % (dst, dev, state_item, lladdr)) -                else: -                    print(format_neigh % ('', '', state_item, '')) -                i+=1 -             + + +def get_raw_data(family, device=None, state=None): +    from json import loads +    from vyos.util import cmd + +    if device: +        device = "dev {0}".format(device) +    else: +        device = "" + +    if state: +        state = "nud {0}".format(state) +    else: +        state = "" + +    neigh_cmd = "ip --family {0} --json neighbor list {1} {2}".format(family, device, state) + +    data = loads(cmd(neigh_cmd)) + +    return data + +def get_formatted_output(family, device=None, state=None): +    from tabulate import tabulate + +    def entry_to_list(e, intf=None): +        dst = e["dst"] + +        # State is always a list in the iproute2 output +        state = ", ".join(e["state"]) + +        # Link layer address is absent from e.g. FAILED entries +        if "lladdr" in e: +            lladdr = e["lladdr"] +        else: +            lladdr = None + +        # Device field is absent from outputs of `ip neigh list dev ...` +        if "dev" in e: +            dev = e["dev"] +        elif device: +            dev = device +        else: +            raise ValueError("interface is not defined") + +        return [dst, dev, lladdr, state] + +    neighs = get_raw_data(family, device=device, state=state) +    neighs = map(entry_to_list, neighs) + +    headers = ["Address", "Interface", "Link layer address",  "State"] +    return tabulate(neighs, headers) +  if __name__ == '__main__': -    main() +    from argparse import ArgumentParser + +    parser = ArgumentParser() +    parser.add_argument("-f", "--family", type=str, default="inet", help="Address family") +    parser.add_argument("-i", "--interface", type=str, help="Network interface") +    parser.add_argument("-s", "--state", type=str, help="Neighbor table entry state") + +    args = parser.parse_args() + +    if args.state: +        if args.state not in ["reachable", "failed", "stale", "permanent"]: +            raise ValueError("Incorrect state {0}! Must be one of: reachable, stale, failed, permanent".format(args.state)) + +    try: +        print(get_formatted_output(args.family, device=args.interface, state=args.state)) +    except ValueError as e: +        print(e) +        sys.exit(1) | 
