summaryrefslogtreecommitdiff
path: root/src/op_mode/bgp.py
blob: af9ea788b921a678209f37c9b37e6f499babe95b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python3
#
# Copyright (C) 2023 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
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Purpose:
#    Displays BGP neighbors and tables information.

import re
import sys
import typing

from jinja2 import Template

import vyos.opmode

frr_command_template = Template("""
show bgp

{## VRF and family modifiers that may precede any options ##}

{% if vrf %}
    vrf {{vrf}}
{% endif %}

{% if family == "inet" %}
    ipv4
{% elif family == "inet6" %}
    ipv6
{% elif family == "l2vpn" %}
    l2vpn evpn
{% endif %}

{% if family_modifier == "unicast" %}
    unicast
{% elif family_modifier == "multicast" %}
    multicast
{% elif family_modifier == "flowspec" %}
    flowspec
{% elif family_modifier == "vpn" %}
    vpn
{% endif %}

{## Mutually exclusive query parameters ##}

{# Network prefix #}
{% if prefix %}
    {{prefix}}

    {% if longer_prefixes %}
      longer-prefixes
    {% elif best_path %}
      bestpath
    {% endif %}
{% endif %}

{# Regex #}
{% if regex %}
    regex {{regex}}
{% endif %}

{## Raw modifier ##}

{% if raw %}
    json
{% endif %}
""")

ArgFamily = typing.Literal['inet', 'inet6', 'l2vpn']
ArgFamilyModifier = typing.Literal['unicast', 'labeled_unicast', 'multicast', 'vpn', 'flowspec']

def show_summary(raw: bool):
    from vyos.util import cmd

    if raw:
        from json import loads

        output = cmd(f"vtysh -c 'show bgp summary json'").strip()

        # FRR 8.5 correctly returns an empty object when BGP is not running,
        # we don't need to do anything special here
        return loads(output)
    else:
        output = cmd(f"vtysh -c 'show bgp summary'")
        return output

def show_neighbors(raw: bool):
    from vyos.util import cmd
    from vyos.utils.dict import dict_to_list

    if raw:
        from json import loads

        output = cmd(f"vtysh -c 'show bgp neighbors json'").strip()
        d = loads(output)
        return dict_to_list(d, save_key_to="neighbor")
    else:
        output = cmd(f"vtysh -c 'show bgp neighbors'")
        return output

def show(raw: bool,
         family: ArgFamily,
         family_modifier: ArgFamilyModifier,
         prefix: typing.Optional[str],
         longer_prefixes: typing.Optional[bool],
         best_path: typing.Optional[bool],
         regex: typing.Optional[str],
         vrf: typing.Optional[str]):
    from vyos.utils.dict import dict_to_list

    if (longer_prefixes or best_path) and (prefix is None):
        raise ValueError("longer_prefixes and best_path can only be used when prefix is given")
    elif (family == "l2vpn") and (family_modifier is not None):
        raise ValueError("l2vpn family does not accept any modifiers")
    else:
        kwargs = dict(locals())

        frr_command = frr_command_template.render(kwargs)
        frr_command = re.sub(r'\s+', ' ', frr_command)

        from vyos.util import cmd
        output = cmd(f"vtysh -c '{frr_command}'")

        if raw:
            from json import loads
            d = loads(output)
            if not ("routes" in d):
                raise vyos.opmode.InternalError("FRR returned a BGP table with no routes field")
            d = d["routes"]
            routes = dict_to_list(d, save_key_to="route_key")
            return routes
        else:
            return output

if __name__ == '__main__':
    try:
        res = vyos.opmode.run(sys.modules[__name__])
        if res:
            print(res)
    except (ValueError, vyos.opmode.Error) as e:
        print(e)
        sys.exit(1)