profile.py 10.3 KB
Newer Older
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
1
# ---------------------------------------------------------------------
2
# Cisco.IOS profile
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
3
# ---------------------------------------------------------------------
4
# Copyright (C) 2007-2020 The NOC Project
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
5
6
# See LICENSE for details
# ---------------------------------------------------------------------
Dmitry Volodin's avatar
Dmitry Volodin committed
7

Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
8
# Python modules
9
import re
10

Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
11
# NOC modules
Dmitry Volodin's avatar
Dmitry Volodin committed
12
from noc.core.profile.base import BaseProfile
Dmitry Volodin's avatar
Dmitry Volodin committed
13

14

Dmitry Volodin's avatar
Dmitry Volodin committed
15
class Profile(BaseProfile):
Dmitry Volodin's avatar
Dmitry Volodin committed
16
    name = "Cisco.IOS"
17
    pattern_more = [
18
19
20
21
        (rb"^ --More-- ", b" "),
        (rb"(?:\?|interfaces)\s*\[confirm\]", b"\n"),
        (rb"^Destination filename \[\S+\]", b"\n"),
        (rb"^Proceed with reload\?\s*\[confirm\]", b"y\n"),
22
    ]
23
24
25
26
27
    pattern_unprivileged_prompt = rb"^\S+?>"
    pattern_syntax_error = (
        rb"% Invalid input detected at|% Ambiguous command:|% Incomplete command."
    )
    pattern_operation_error = rb"Command authorization failed."
Dmitry Volodin's avatar
Dmitry Volodin committed
28
    command_disable_pager = "terminal length 0"
29
    command_super = b"enable"
Dmitry Volodin's avatar
Dmitry Volodin committed
30
    command_enter_config = "configure terminal"
31
    command_leave_config = "end"
32
    command_exit = "exit"
Dmitry Volodin's avatar
Dmitry Volodin committed
33
    command_save_config = "copy running-config startup-config\n"
34
    pattern_prompt = rb"^(?P<hostname>[a-zA-Z0-9/.]\S{0,35})(?:[-_\d\w]+)?(?:\(config[^\)]*\))?#"
35
    can_strip_hostname_to = 20
Dmitry Volodin's avatar
Dmitry Volodin committed
36
    requires_netmask_conversion = True
Dmitry Volodin's avatar
Dmitry Volodin committed
37
    convert_mac = BaseProfile.convert_mac_to_cisco
Dmitry Volodin's avatar
Dmitry Volodin committed
38
    config_volatile = ["^ntp clock-period .*?^"]
39

Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
40
    rx_cable_if = re.compile(
41
42
        r"Cable\s*(?P<pr_if>\d+/\d+) U(pstream)?\s*(?P<sub_if>\d+)", re.IGNORECASE
    )
43
    config_tokenizer = "indent"
44
    config_tokenizer_settings = {"line_comment": "!"}
45
46
47
48
49
50
51
52
53
    config_normalizer = "CiscoIOSNormalizer"
    confdb_defaults = [
        ("hints", "interfaces", "defaults", "admin-status", True),
        ("hints", "protocols", "lldp", "status", False),
        ("hints", "protocols", "cdp", "status", True),
        ("hints", "protocols", "ntp", "mode", "server"),
        ("hints", "protocols", "ntp", "version", "3"),
        # ("hints", "protocols", "loop-detect", "status", False),
    ]
54
    rx_ver = re.compile(r"(\d+)\.(\d+)[\(.](\d+):?(\d+)?\w*[\).]\S*")
Andrey Vertiprahov's avatar
Andrey Vertiprahov committed
55

56
    matchers = {
57
        "is_platform_7200": {"platform": {"$regex": r"720[0146]|730[14]"}},
58
59
60
61
62
63
64
65
        "is_platform_7600": {"platform": {"$regex": r"76(0[3459](\-S)?|13)"}},
        "is_platform_me3x00x": {"platform": {"$regex": r"^ME\-3[68]00X"}},
        "is_isr_router": {"platform": {"$regex": r"^(19\d\d|29\d\d|39\d\d)$"}},
        "is_iosxe": {"platform": {"$regex": r"ASR100[0-6]"}},
        "is_cat6000": {"version": {"$regex": r"S[YXR]"}},
        "is_cat4000": {"platform": {"$regex": r"^WS-C4[059]\d\d"}},
        "is_small_cat": {"version": {"$regex": r"SE|EA|EZ|FX|EX|EY|E|WC"}},
        "is_5350": {"platform": {"$regex": r"^5350"}},
66
        "is_c2960": {"platform": {"$regex": r"C2960"}},
67
        "is_ubr": {"version": {"$regex": r"BC"}},
68
69
70
        "is_vlan_switch": {
            "platform": {
                "$regex": r"^([123][678]\d\d|7[235]\d\d|107\d\d|"
71
                r"C[23][69]00[a-z]?$|C8[7859]0|17\d\d|C18[01]X|19\d\d|2951|ASR\d+)"
72
73
74
75
            }
        },
    }

Andrey Vertiprahov's avatar
Andrey Vertiprahov committed
76
    def cmp_version(self, x, y):
77
        """12(25)SEC2, 12.4(19b)"""
78
79
80
        a = [int(z) for z in self.rx_ver.findall(x)[0]]
        b = [int(z) for z in self.rx_ver.findall(y)[0]]
        return (a > b) - (a < b)
81

82
    def convert_interface_name(self, interface):
83
        interface = str(interface)
84
85
        if " efp_id " in interface:
            l, r = interface.split(" efp_id ", 1)
86
            return "%s.SI.%d" % (self.convert_interface_name_cisco(l.strip()), int(r.strip()))
87
88
        if "+Efp" in interface:
            l, r = interface.split("+Efp", 1)
89
            return "%s.SI.%d" % (self.convert_interface_name_cisco(l.strip()), int(r.strip()))
90
91
        if " point-to-point" in interface:
            interface = interface.replace(" point-to-point", "")
92
93
        if ".ServiceInstance." in interface:
            interface = interface.replace(".ServiceInstance.", ".SI.")
94
95
        if ".SI." in interface:
            l, r = interface.split(".SI.", 1)
96
            return "%s.SI.%d" % (self.convert_interface_name_cisco(l.strip()), int(r.strip()))
97
        if isinstance(interface, str):
Aleksey Shirokih's avatar
Aleksey Shirokih committed
98
99
100
            il = interface.lower()
        else:
            il = interface.name.lower()
101
102
        if il.startswith("nde_"):
            return "NDE_" + interface[4:]
103
        if il.startswith("dot11radio"):
104
            return "Dot11Radio" + interface[10:]
105
106
        if il.startswith("bdi"):
            return "BDI" + interface[3:]
107
        if il.startswith("bvi"):
Dmitry Volodin's avatar
Dmitry Volodin committed
108
            return "BVI" + interface[3:]
109
        if il.startswith("e1"):
110
            return "E1 %s" % interface[2:].strip()
111
        if il.startswith("t1"):
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
112
            return "T1 %s" % interface[2:].strip()
113
        if il.startswith("fxo null"):
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
114
            return "FXO %s" % interface[8:].strip()
115
        if il.startswith("fxs"):
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
116
            return "FXS %s" % interface[3:].strip()
117
118
119
120
121
122
        if il.startswith("foreign exchange station"):
            return "FXS %s" % interface[24:].strip()
        if il.startswith("receive and transmit"):
            return "E&M %s" % interface[21:].strip()
        if il.startswith("cas channel"):
            return "ds0-group %s" % interface[11:].strip()
123
124
125
126
        if il.startswith("voice encapsulation (pots)"):
            return "Dial-peer voice %s" % interface[33:].strip()
        if il.startswith("voice over ip peer"):
            return "Dial-peer voip %s" % interface[20:].strip()
127
        if il.startswith("efxs"):
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
128
            return "EFXS %s" % interface[4:].strip()
129
        if il.startswith("cpp"):
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
130
            return "CPP"
131
        if il.startswith("srp"):
132
            return "SRP %s" % interface[3:].strip()
133
134
135
136
        if il.startswith("twe"):
            if il.startswith("twe "):
                return il.capitalize()
            return "Twe %s" % interface[14:].strip()
137
138
139
        if il.startswith("cable"):
            match = self.rx_cable_if.search(interface)
            if match:
140
                return "Ca %s/%s" % (match.group("pr_if"), match.group("sub_if"))
141
142
143
        # StackSub, Stack
        if il.startswith("stack"):
            return il
144
145
        if il.endswith(" type tunnel"):
            il = il[:-12]
146
147
        if il.startswith("virtual-template"):
            return "Vi %s" % il[16:].strip()
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
148
149
        if il.startswith("service-engine"):
            return "Service-Engine %s" % il[14:].strip()
150
        # Serial0/1/0:15-Signaling -> Serial0/1/0:15
151
152
        # GigabitEthernet0/1.100-802.1Q vLAN subif -> GigabitEthernet0/1.100
        if (il.startswith("se") or il.endswith("vlan subif")) and "-" in interface:
153
            interface = interface.split("-")[0]
154
155
        # Control Plane Interface
        # @todo: Does it relates to CPP?
156
        if il == "control plane interface":
157
            return "Control Plane Interface"
158
159
160
        # Fake name. Used only with FM
        if il == "all":
            return "all"
161
        return self.convert_interface_name_cisco(interface)
Dmitry Volodin's avatar
Dmitry Volodin committed
162

Dmitry Volodin's avatar
Dmitry Volodin committed
163
164
165
166
167
168
169
170
171
172
173
174
175
    def generate_prefix_list(self, name, pl):
        """
        Generate prefix list _name_. pl is a list of (prefix, min_len, max_len)
        """
        me = "ip prefix-list %s permit %%s" % name
        mne = "ip prefix-list %s permit %%s le %%d" % name
        r = ["no ip prefix-list %s" % name]
        for prefix, min_len, max_len in pl:
            if min_len == max_len:
                r += [me % prefix]
            else:
                r += [mne % (prefix, max_len)]
        return "\n".join(r)
Dmitry Volodin's avatar
Dmitry Volodin committed
176

177
    def setup_session(self, script):
Dmitry Volodin's avatar
Dmitry Volodin committed
178
179
180
181
182
        """
        Perform session initialization
        Process specific path parameters:
        cluster:id - switch to cluster member
        """
Dmitry Volodin's avatar
Dmitry Volodin committed
183
184
185
186
        path = script.credentials.get("path")
        if path:
            cluster_member = None
            # Parse path parameters
187
            for p in script.credentials.get("path", "").split("/"):
Dmitry Volodin's avatar
Dmitry Volodin committed
188
189
190
191
                if p.startswith("cluster:"):
                    cluster_member = p[8:].strip()
            # Switch to cluster member, if necessary
            if cluster_member:
192
                script.logger.debug("Switching to cluster member '%s'" % cluster_member)
Dmitry Volodin's avatar
Dmitry Volodin committed
193
                script.cli("rc %s" % cluster_member)
Dmitry Volodin's avatar
Dmitry Volodin committed
194

195
196
197
198
199
200
201
202
203
204
205
206
207
    INTERFACE_TYPES = {
        "As": "physical",  # Async
        "AT": "physical",  # ATM
        "At": "physical",  # ATM
        "Br": "physical",  # ISDN Basic Rate Interface
        "BD": "physical",  # Bridge Domain Interface
        "BV": "aggregated",  # BVI
        "Bu": "aggregated",  # Bundle
        "C": "physical",  # @todo: fix
        "Ca": "physical",  # Cable
        "CD": "physical",  # CDMA Ix
        "Ce": "physical",  # Cellular
        "Em": "physical",  # Embedded Service Engine
208
        "E1": "other",  # E1
209
210
211
        "Et": "physical",  # Ethernet
        "Fa": "physical",  # FastEthernet
        "Fd": "physical",  # Fddi
212
        "Fi": "physical",  # FiveGigabitEthernet
213
        "Fo": "physical",  # FortyGigabitEthernet
214
215
216
217
218
219
220
221
222
        "Gi": "physical",  # GigabitEthernet
        "Gm": "physical",  # GMPLS
        "Gr": "physical",  # Group-Async
        "Lo": "loopback",  # Loopback
        "In": "physical",  # Integrated-service-engine
        "M": "management",  # @todo: fix
        "MF": "aggregated",  # Multilink Frame Relay
        "Mf": "aggregated",  # Multilink Frame Relay
        "Mu": "aggregated",  # Multilink-group interface
223
        "ND": "other",  # Netflow Data Exporter
224
225
226
227
228
229
230
        "PO": "physical",  # Packet OC-3 Port Adapter
        "Po": "aggregated",  # Port-channel/Portgroup
        "R": "aggregated",  # @todo: fix
        "SR": "physical",  # Spatial Reuse Protocol
        "Sr": "physical",  # Spatial Reuse Protocol
        "Se": "physical",  # Serial
        "Sp": "physical",  # Special-Services-Engine
231
        "St": "physiocal",  # StackSub-St, StackPort1
232
233
234
        "Te": "physical",  # TenGigabitEthernet
        "To": "physical",  # TokenRing
        "Tu": "tunnel",  # Tunnel
235
        "Tw": "physical",  # TwoGigabitEthernet or TwentyFiveGigE
Dmitry Lukhtionov's avatar
Dmitry Lukhtionov committed
236
        "Vi": "template",  # Virtual-Template
237
238
239
        "VL": "SVI",  # VLAN, found on C3500XL
        "Vl": "SVI",  # Vlan
        "Vo": "physical",  # Voice
240
        "XT": "SVI",  # Extended Tag ATM
241
242
243
244
245
246
    }

    @classmethod
    def get_interface_type(cls, name):
        return cls.INTERFACE_TYPES.get(name[:2])

247

Dmitry Volodin's avatar
Dmitry Volodin committed
248
def uBR(v):
Dmitry Volodin's avatar
Dmitry Volodin committed
249
250
251
    """
    uBR series selector
    """
Dmitry Volodin's avatar
Dmitry Volodin committed
252
    return "BC" in v["version"]
253
254
255
256
257
258
259
260
261
262
263


def MESeries(v):
    """
    MExxxx series selector
    :param v:
    :type v: dict
    :return:
    :rtype: bool
    """
    return v["platform"].startswith("ME")