File: test_helper.py

package info (click to toggle)
python-scrapli 2023.7.30-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 4,536 kB
  • sloc: python: 14,459; makefile: 72
file content (275 lines) | stat: -rw-r--r-- 8,095 bytes parent folder | download | duplicates (2)
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
import sys
from io import TextIOWrapper
from pathlib import Path
from shutil import get_terminal_size

import pkg_resources  # pylint: disable=C041
import pytest

from scrapli.exceptions import ScrapliValueError
from scrapli.helper import (
    _textfsm_get_template,
    format_user_warning,
    genie_parse,
    resolve_file,
    textfsm_parse,
    ttp_parse,
    user_warning,
)

IOS_ARP = """Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  172.31.254.1            -   0000.0c07.acfe  ARPA   Vlan254
Internet  172.31.254.2            -   c800.84b2.e9c2  ARPA   Vlan254
"""
IOS_ARP_NTC_TEMPLATE_URL = "https://raw.githubusercontent.com/networktocode/ntc-templates/master/ntc_templates/templates/cisco_ios_show_ip_arp.textfsm"


def test_textfsm_get_template():
    template = _textfsm_get_template("cisco_nxos", "show ip arp")
    template_dir = pkg_resources.resource_filename("ntc_templates", "templates")
    assert isinstance(template, TextIOWrapper)
    assert template.name == f"{template_dir}/cisco_nxos_show_ip_arp.textfsm"


def test_textfsm_get_template_invalid_template():
    template = _textfsm_get_template("cisco_nxos", "show racecar")
    assert not template


def test_textfsm_no_ntc_templates(monkeypatch):
    def _import_module(name, package):
        raise ModuleNotFoundError

    monkeypatch.setattr("importlib.import_module", _import_module)

    with pytest.warns(UserWarning) as exc:
        _textfsm_get_template("cisco_nxos", "show racecar")

    assert "Optional Extra Not Installed!" in str(exc.list[0].message)


@pytest.mark.parametrize(
    "test_data",
    [
        (
            False,
            ["Internet", "172.31.254.1", "-", "0000.0c07.acfe", "ARPA", "Vlan254"],
        ),
        (
            True,
            {
                "protocol": "Internet",
                "address": "172.31.254.1",
                "age": "-",
                "mac": "0000.0c07.acfe",
                "type": "ARPA",
                "interface": "Vlan254",
            },
        ),
    ],
    ids=["to_dict_false", "to_dict_true"],
)
def test_textfsm_parse(test_data):
    to_dict, expected_output = test_data
    template = _textfsm_get_template("cisco_ios", "show ip arp")
    result = textfsm_parse(template, IOS_ARP, to_dict=to_dict)
    assert isinstance(result, list)
    assert result[0] == expected_output


@pytest.mark.parametrize(
    "test_data",
    [
        (
            False,
            ["Internet", "172.31.254.1", "-", "0000.0c07.acfe", "ARPA", "Vlan254"],
        ),
        (
            True,
            {
                "protocol": "Internet",
                "address": "172.31.254.1",
                "age": "-",
                "mac": "0000.0c07.acfe",
                "type": "ARPA",
                "interface": "Vlan254",
            },
        ),
    ],
    ids=["to_dict_false", "to_dict_true"],
)
def test_textfsm_parse_string_path(test_data):
    to_dict, expected_output = test_data
    template = _textfsm_get_template("cisco_ios", "show ip arp")
    result = textfsm_parse(template.name, IOS_ARP, to_dict=to_dict)
    assert isinstance(result, list)
    assert result[0] == expected_output


@pytest.mark.parametrize(
    "test_data",
    [
        (
            False,
            ["Internet", "172.31.254.1", "-", "0000.0c07.acfe", "ARPA", "Vlan254"],
        ),
        (
            True,
            {
                "protocol": "Internet",
                "address": "172.31.254.1",
                "age": "-",
                "mac": "0000.0c07.acfe",
                "type": "ARPA",
                "interface": "Vlan254",
            },
        ),
    ],
    ids=["to_dict_false", "to_dict_true"],
)
def test_textfsm_parse_url_path(test_data):
    to_dict, expected_output = test_data
    template = IOS_ARP_NTC_TEMPLATE_URL
    result = textfsm_parse(template, IOS_ARP, to_dict=to_dict)
    assert isinstance(result, list)
    assert result[0] == expected_output


def test_textfsm_parse_failed_to_parse():
    template = _textfsm_get_template("cisco_ios", "show ip arp")
    result = textfsm_parse(template, "not really arp data")
    assert result == []


@pytest.mark.skipif(
    sys.version_info.minor > 10, reason="genie not currently available for python 3.11"
)
def test_genie_parser():
    result = genie_parse("iosxe", "show ip arp", IOS_ARP)
    assert isinstance(result, dict)
    assert (
        result["interfaces"]["Vlan254"]["ipv4"]["neighbors"]["172.31.254.1"]["ip"] == "172.31.254.1"
    )


@pytest.mark.skipif(
    sys.version_info.minor > 10, reason="genie not currently available for python 3.11"
)
def test_genie_parse_failure():
    result = genie_parse("iosxe", "show ip arp", "not really arp data")
    assert result == []


def test_genie_no_genie_installed(monkeypatch):
    def _import_module(name, package):
        raise ModuleNotFoundError

    monkeypatch.setattr("importlib.import_module", _import_module)

    with pytest.warns(UserWarning) as exc:
        output = genie_parse("cisco_nxos", "show racecar", "something")

    assert "Optional Extra Not Installed!" in str(exc.list[0].message)
    assert output == []


def test_ttp_parse():
    # example data lifted straight out of ttp docs
    data_to_parse = """
    interface Loopback0
     description Router-id-loopback
     ip address 192.168.0.113/24
    !
    interface Vlan778
     description CPE_Acces_Vlan
     ip address 2002::fd37/124
     ip vrf CPE1
    !
    """

    ttp_template = """
    interface {{ interface }}
     ip address {{ ip }}/{{ mask }}
     description {{ description }}
     ip vrf {{ vrf }}
    """

    expected = [
        [
            {
                "ip": "192.168.0.113",
                "mask": "24",
                "description": "Router-id-loopback",
                "interface": "Loopback0",
            },
            {
                "vrf": "CPE1",
                "ip": "2002::fd37",
                "mask": "124",
                "description": "CPE_Acces_Vlan",
                "interface": "Vlan778",
            },
        ]
    ]
    result = ttp_parse(template=ttp_template, output=data_to_parse)
    assert result == expected


def test_ttp_parse_invalid_template():
    result = ttp_parse(template=None, output="blah")
    assert result == []


def test_ttp_parse_failed_to_parse():
    result = ttp_parse(template="mytemplateisneat", output="blah")
    assert result == [{}]


def test_ttp_no_ttp_installed(monkeypatch):
    def _import_module(name):
        raise ModuleNotFoundError

    monkeypatch.setattr("importlib.import_module", _import_module)

    with pytest.warns(UserWarning) as exc:
        output = ttp_parse("cisco_nxos", "show racecar")

    assert "Optional Extra Not Installed!" in str(exc.list[0].message)
    assert output == []


def test_resolve_file(fs_, real_ssh_config_file_path):
    # pyfakefs so this is not host dependent
    fs_.add_real_file(source_path=real_ssh_config_file_path, target_path="/some/neat/path/myfile")
    assert resolve_file(file="/some/neat/path/myfile") == "/some/neat/path/myfile"


def test_resolve_file_expanduser(fs_, real_ssh_config_file_path):
    # pyfakefs so this is not host dependent
    fs_.add_real_file(
        source_path=real_ssh_config_file_path, target_path=Path("~/myfile").expanduser()
    )
    assert resolve_file(file="~/myfile") == str(Path("~/myfile").expanduser())


def test_resolve_file_failure():
    with pytest.raises(ScrapliValueError) as exc:
        resolve_file(file=f"~/myneatfile")


def test_format_user_warning():
    warning_string = format_user_warning(title="blah", message="something")
    assert "* blah *" in warning_string
    assert "something" in warning_string


def test_format_user_warning_really_long_title():
    terminal_width = get_terminal_size().columns

    warning_string = format_user_warning(title=("blah" * 30), message="something")
    assert warning_string.lstrip().startswith("*" * terminal_width)


def test_user_warning():
    with pytest.warns(UserWarning):
        user_warning(title="blah", message="something")