1# This code is part of Ansible, but is an independent component.
2# This particular file snippet, and this file snippet only, is BSD licensed.
3# Modules you write using this snippet, which is embedded dynamically by Ansible
4# still belong to the author of the module, and may assign their own license
5# to the complete work.
6#
7# (c) 2016 Red Hat Inc.
8#
9# Redistribution and use in source and binary forms, with or without modification,
10# are permitted provided that the following conditions are met:
11#
12#    * Redistributions of source code must retain the above copyright
13#      notice, this list of conditions and the following disclaimer.
14#    * Redistributions in binary form must reproduce the above copyright notice,
15#      this list of conditions and the following disclaimer in the documentation
16#      and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27#
28import json
29
30from ansible.module_utils._text import to_text
31from ansible.module_utils.basic import env_fallback
32from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
33    to_list,
34)
35from ansible.module_utils.connection import Connection, ConnectionError
36
37_DEVICE_CONFIGS = {}
38
39ios_provider_spec = {
40    "host": dict(),
41    "port": dict(type="int"),
42    "username": dict(fallback=(env_fallback, ["ANSIBLE_NET_USERNAME"])),
43    "password": dict(
44        fallback=(env_fallback, ["ANSIBLE_NET_PASSWORD"]), no_log=True
45    ),
46    "ssh_keyfile": dict(
47        fallback=(env_fallback, ["ANSIBLE_NET_SSH_KEYFILE"]), type="path"
48    ),
49    "authorize": dict(
50        fallback=(env_fallback, ["ANSIBLE_NET_AUTHORIZE"]), type="bool"
51    ),
52    "auth_pass": dict(
53        fallback=(env_fallback, ["ANSIBLE_NET_AUTH_PASS"]), no_log=True
54    ),
55    "timeout": dict(type="int"),
56}
57ios_argument_spec = {
58    "provider": dict(
59        type="dict", options=ios_provider_spec, removed_in_version=2.14
60    )
61}
62
63
64def get_provider_argspec():
65    return ios_provider_spec
66
67
68def get_connection(module):
69    if hasattr(module, "_ios_connection"):
70        return module._ios_connection
71
72    capabilities = get_capabilities(module)
73    network_api = capabilities.get("network_api")
74    if network_api == "cliconf":
75        module._ios_connection = Connection(module._socket_path)
76    else:
77        module.fail_json(msg="Invalid connection type %s" % network_api)
78
79    return module._ios_connection
80
81
82def get_capabilities(module):
83    if hasattr(module, "_ios_capabilities"):
84        return module._ios_capabilities
85    try:
86        capabilities = Connection(module._socket_path).get_capabilities()
87    except ConnectionError as exc:
88        module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
89    module._ios_capabilities = json.loads(capabilities)
90    return module._ios_capabilities
91
92
93def get_defaults_flag(module):
94    connection = get_connection(module)
95    try:
96        out = connection.get_defaults_flag()
97    except ConnectionError as exc:
98        module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
99    return to_text(out, errors="surrogate_then_replace").strip()
100
101
102def get_config(module, flags=None):
103    flags = to_list(flags)
104
105    section_filter = False
106    if flags and "section" in flags[-1]:
107        section_filter = True
108
109    flag_str = " ".join(flags)
110
111    try:
112        return _DEVICE_CONFIGS[flag_str]
113    except KeyError:
114        connection = get_connection(module)
115        try:
116            out = connection.get_config(flags=flags)
117        except ConnectionError as exc:
118            if section_filter:
119                # Some ios devices don't understand `| section foo`
120                out = get_config(module, flags=flags[:-1])
121            else:
122                module.fail_json(
123                    msg=to_text(exc, errors="surrogate_then_replace")
124                )
125        cfg = to_text(out, errors="surrogate_then_replace").strip()
126        _DEVICE_CONFIGS[flag_str] = cfg
127        return cfg
128
129
130def run_commands(module, commands, check_rc=True):
131    connection = get_connection(module)
132    try:
133        return connection.run_commands(commands=commands, check_rc=check_rc)
134    except ConnectionError as exc:
135        module.fail_json(msg=to_text(exc))
136
137
138def load_config(module, commands):
139    connection = get_connection(module)
140
141    try:
142        resp = connection.edit_config(commands)
143        return resp.get("response")
144    except ConnectionError as exc:
145        module.fail_json(msg=to_text(exc))
146
147
148def normalize_interface(name):
149    """Return the normalized interface name
150    """
151    if not name:
152        return
153
154    def _get_number(name):
155        digits = ""
156        for char in name:
157            if char.isdigit() or char in "/.":
158                digits += char
159        return digits
160
161    if name.lower().startswith("gi"):
162        if_type = "GigabitEthernet"
163    elif name.lower().startswith("te"):
164        if_type = "TenGigabitEthernet"
165    elif name.lower().startswith("fa"):
166        if_type = "FastEthernet"
167    elif name.lower().startswith("fo"):
168        if_type = "FortyGigabitEthernet"
169    elif name.lower().startswith("et"):
170        if_type = "Ethernet"
171    elif name.lower().startswith("vl"):
172        if_type = "Vlan"
173    elif name.lower().startswith("lo"):
174        if_type = "loopback"
175    elif name.lower().startswith("po"):
176        if_type = "port-channel"
177    elif name.lower().startswith("nv"):
178        if_type = "nve"
179    elif name.lower().startswith("twe"):
180        if_type = "TwentyFiveGigE"
181    elif name.lower().startswith("hu"):
182        if_type = "HundredGigE"
183    else:
184        if_type = None
185
186    number_list = name.split(" ")
187    if len(number_list) == 2:
188        if_number = number_list[-1].strip()
189    else:
190        if_number = _get_number(name)
191
192    if if_type:
193        proper_interface = if_type + if_number
194    else:
195        proper_interface = name
196
197    return proper_interface
198