Skip to content

Commit

Permalink
- Fixe the integrations test to be correct
Browse files Browse the repository at this point in the history
- Fixe the config module to:
 - doesn't modify lines not indicated with the deleted and replaced
   profiles
 - take correctly in case the case where we have aux line present on
   device
  • Loading branch information
earendilfr committed Dec 22, 2023
1 parent 8c75435 commit 9cebfa1
Show file tree
Hide file tree
Showing 16 changed files with 172 additions and 192 deletions.
6 changes: 3 additions & 3 deletions plugins/module_utils/network/ios/argspec/line/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from __future__ import absolute_import, division, print_function


__metaclass__ = type

#############################################
Expand All @@ -29,8 +30,7 @@


class LineArgs(object): # pylint: disable=R0903
"""The arg spec for the ios_line module
"""
"""The arg spec for the ios_line module"""

argument_spec = {
"config": {
Expand Down Expand Up @@ -209,7 +209,7 @@ class LineArgs(object): # pylint: disable=R0903
},
},
},
}
},
},
},
"running_config": {"type": "str"},
Expand Down
59 changes: 39 additions & 20 deletions plugins/module_utils/network/ios/config/line/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,28 +109,28 @@ def generate_commands(self):
want, have and desired state.
"""
wantd = deepcopy(self.want)
wantd["lines"] = self._convert_list_to_dict(data=wantd.get("lines", []))
wantd["lines"] = self._list_to_dict(data=wantd.get("lines", []))
haved = deepcopy(self.have)
haved["lines"] = self._convert_list_to_dict(data=haved.get("lines", []))
haved["lines"] = self._list_to_dict(data=haved.get("lines", []))

# if state is merged, merge want onto have and then compare
if self.state == "merged":
wantd = dict_merge(haved, wantd)

# if state is deleted, empty out wantd and set haved to wantd
if self.state == "deleted":
haved = {k: v for k, v in haved.items() if k in wantd or not wantd}
wantd = {"lines": {"con 0": {"name": "con 0"}, "vty 0 4": {"name": "vty 0 4"}}}
if self.state == "deleted" and wantd["lines"] != {}:
haved["lines"] = {k: v for k, v in haved["lines"].items() if k in wantd["lines"]}

# remove superfluous config
if self.state in ["overridden", "deleted"]:
for k, have in haved["lines"].items():
if k not in wantd["lines"]:
self._compare(want={}, have=have)
if k == "con 0" or k == "vty 0 4" or k.startswith("aux"):
self._compare(want={"name": k}, have=have)
else:
self._compare(want={}, have=have)
elif self.state in ["replaced"]:
only_have_line_k = [k for k in haved["lines"].keys() if k not in wantd["lines"].keys()]
for k in only_have_line_k:
haved["lines"].pop(k)
haved["lines"] = {k: v for k, v in haved["lines"].items() if k in wantd["lines"]}

for k, want in wantd["lines"].items():
self._compare(want=want, have=haved["lines"].pop(k, {}))
Expand Down Expand Up @@ -171,10 +171,20 @@ def _compare(self, want, have):
"motd": True,
"privilege": 1,
}
config_default_transport = {
"transport": {
"input": {
"name": "input",
"ssh": True,
},
},
}
begin = len(self.commands)
if want != {}:
want = dict_merge(config_default, want)
have = dict_merge(config_default, have)
if "vty 0" in want["name"] or want["name"].startswith("aux"):
want = dict_merge(config_default_transport, want)
self._compare_lists(want=want, have=have)
if len(self.commands) != begin:
self.commands.insert(begin, self._tmplt.render(want or have, "line", False))
Expand Down Expand Up @@ -205,13 +215,11 @@ def _compare_lists(self, want, have):
for p_key, p_h_entry in l1_h_entry.items():
self.addcmd(data={p_key: p_h_entry}, tmplt=self.parsers[l1], negate=True)
elif l1_key == "commands":
l1_w_entry = self._convert_list_to_dict(data=l1_w_entry, key="level")
l1_h_entry = self._convert_list_to_dict(
data=l1_have.pop(l1_key, []),
key="level",
)
l1_h_entry = l1_have.pop(l1_key, {})
for c_key, c_w_entry in l1_w_entry.items():
c_h_entry = l1_h_entry.pop(c_key, {"level": c_w_entry["level"], "command": "default"})
c_h_entry = l1_h_entry.pop(
c_key, {"level": c_w_entry["level"], "command": "default"}
)
self.compare(
parsers=self.parsers[l1],
want={l1_key: c_w_entry},
Expand Down Expand Up @@ -239,7 +247,6 @@ def _compare_lists(self, want, have):
negate=True,
)
elif l1_key == "commands":
l1_h_entry = self._convert_list_to_dict(data=l1_h_entry, key="level")
for c_key, c_h_entry in l1_h_entry.items():
self.compare(
parsers=self.parsers[l1],
Expand All @@ -258,9 +265,7 @@ def _compare_lists(self, want, have):
if key == "name":
continue
if key == "transport":
h_entry = have.pop(key, [])
w_entry = self._convert_list_to_dict(data=w_entry)
h_entry = self._convert_list_to_dict(data=h_entry)
h_entry = have.get(key, {})
for t_key, t_w_entry in w_entry.items():
t_h_entry = h_entry.pop(t_key, {})
self.compare(
Expand All @@ -275,7 +280,6 @@ def _compare_lists(self, want, have):
if key == "name":
continue
if key == "transport":
h_entry = self._convert_list_to_dict(data=h_entry)
for t_key, t_h_entry in h_entry.items():
self.compare(
parsers=self.parsers[key],
Expand All @@ -287,3 +291,18 @@ def _compare_lists(self, want, have):

def _convert_list_to_dict(self, data, key="name"):
return {_k.get(key, ""): _k for _k in data} if data else {}

def _list_to_dict(self, data):
l_result = self._convert_list_to_dict(data=data)

for _name, _line in l_result.items():
for k, v in _line.items():
if k == "transport":
l_result[_name][k] = self._convert_list_to_dict(data=v)
elif k in ["accounting", "authorization", "exec"]:
for _sk, _sv in v.items():
if _sk == "commands":
l_result[_name][k][_sk] = self._convert_list_to_dict(
data=_sv, key="level"
)
return l_result
36 changes: 18 additions & 18 deletions plugins/modules/ios_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from __future__ import absolute_import, division, print_function


__metaclass__ = type

DOCUMENTATION = """
Expand Down Expand Up @@ -55,7 +56,7 @@
type: dict
suboptions:
arap:
description:
description:
- For Appletalk Remote Access Protocol
- Use an accounting list with this name
- I(default) is the default name
Expand All @@ -68,7 +69,7 @@
elements: dict
suboptions:
level:
description:
description:
- Enable level
type: int
command:
Expand Down Expand Up @@ -116,7 +117,7 @@
elements: dict
suboptions:
level:
description:
description:
- Enable level
type: int
command:
Expand Down Expand Up @@ -147,7 +148,7 @@
description: Set the soft escape character for this line
type: bool
value:
description:
description:
- Escape character configured
- I(BREAK) - Cause escape on BREAK
- I(DEFAULT) - Use default escape character
Expand Down Expand Up @@ -202,11 +203,11 @@
type: str
choices: [ '0', '1', '2', '3', '4', '5', '6', '7', 'all']
limit:
description:
description:
- Messages queue size (Value between C(<0-2147483647>))
type: int
login:
description:
description:
- Enable password checking for authentication
- Use an authentication list with this name
- I(default) is the default name
Expand Down Expand Up @@ -256,10 +257,8 @@
value:
description: The actual hashed password to be configured
type: str
no_log: true
no_log: false
privilege:
description:
description:
- Change privilege level for line
- The I(privilege) valu should be between C(0) and C(15)
type: int
Expand All @@ -275,15 +274,15 @@
description: Set maximum number of sessions (Between C(<0-4294967295>))
type: int
timeout:
description:
description:
- Set interval for closing connection when there is no input traffic
- (Between C(<0-35791>))
type: int
speed:
description: Set the transmit and receive speeds (Between C(<0-4294967295>))
type: int
stopbits:
description:
description:
- Set async line stop bits
- I(1) - One stop bit
- I(1.5) - One and one-half stop bits
Expand All @@ -296,12 +295,12 @@
elements: dict
suboptions:
all:
description:
description:
- All protocols are allowed
- Not used if I(name) is configured at I(preferred)
type: bool
name:
description:
description:
- Type of transport to configure
- I(input) - Configure incomming connections
- I(output) - Configure outgoing connections
Expand Down Expand Up @@ -344,16 +343,18 @@
platform specific CLI commands which will be returned in the I(rendered) key
within the result. For state I(rendered) active connection to remote host is
not required.
- The state I(overridden) modify/add the lines defined, deleted all other lines.
- The state I(replaced) will only override the configuration part of the defined lines.
- The state I(gathered) will fetch the running configuration from device and transform
it into structured data in the format as per the resource module argspec and
the value is returned in the I(gathered) key within the result.
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into JSON format as per the resource module parameters and the
value is returned in the I(parsed) key within the result. The value of C(running_config)
option should be the same format as the output of command I(show running-config
| sec ^line) executed on device. For state I(parsed) active connection to
option should be the same format as the output of command I(show running-config
| sec ^line) executed on device. For state I(parsed) active connection to
remote host is not required.
- The state I(deleted), deletes only the specified lines, or all if not specified.
type: str
choices:
- merged
Expand Down Expand Up @@ -833,12 +834,11 @@
"""

from ansible.module_utils.basic import AnsibleModule

from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.line.line import (
LineArgs,
)
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.line.line import (
Line,
)
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.config.line.line import Line


def main():
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/targets/ios_line/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
- name: Main task for acls
- name: Main task for lines
ansible.builtin.include_tasks: cli.yaml
tags:
- network_cli
18 changes: 3 additions & 15 deletions tests/integration/targets/ios_line/tests/cli/_populate_config.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
---
- name: Populate configuration
vars:
lines: |
line con 0
session-timeout 15
login authentication limited
escape-character 3
stopbits 1
line aux 0
stopbits 1
line vty 0 4
session-timeout 15
exec-timeout 60 0
password 7 02050D480809
login authentication remote
logging synchronous
transport input ssh telnet
lines: "line con 0\n session-timeout 15\n login authentication limited\n escape-character 3\n stopbits 1\n
line aux 0\n stopbits 1\n
line vty 0 4\n session-timeout 15\n exec-timeout 60 0\n password 7 02050D480809\n login authentication remote\n logging synchronous\n transport input ssh telnet"
ansible.netcommon.cli_config:
config: "{{ lines }}"
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
---
- name: Populate configuration
vars:
lines: |
line con 0
session-timeout 15
login authentication limited
escape-character 3
stopbits 1
line aux 0
stopbits 1
line aux 0
no exec
transport input ssh
stopbits 1
line vty 0 4
session-timeout 15
exec-timeout 60 0
password 7 02050D480809
login authentication remote
logging synchronous
transport input ssh telnet
lines:

Check failure on line 4 in tests/integration/targets/ios_line/tests/cli/_populate_config_replaced.yaml

View workflow job for this annotation

GitHub Actions / Ansible Lint

yaml[trailing-spaces]

Trailing spaces
"line con 0\n session-timeout 15\n login authentication limited\n escape-character 3\n stopbits 1\n
line aux 0\n stopbits 1\n no exec\n transport input ssh\n stopbits 1\n
line vty 0 4\n session-timeout 15\n exec-timeout 60 0\n password 7 02050D480809\n login authentication remote\n logging synchronous\n transport input ssh telnet"
ansible.netcommon.cli_config:
config: "{{ lines }}"
8 changes: 4 additions & 4 deletions tests/integration/targets/ios_line/tests/cli/deleted.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

- ansible.builtin.assert:
that:
- result.commands|length == 4
- result.commands|length == 5
- result.changed == true
- result.commands|symmetric_difference(deleted.commands) == []

Expand All @@ -41,11 +41,11 @@
config:
lines:
- name: "vty 0 4"
state: deleted
state: deleted

- ansible.builtin.assert:
that:
- result.commands|length == 4
- result.commands|length == 7
- result.changed == true
- result.commands|symmetric_difference(deleted_line.commands) == []

Expand All @@ -69,7 +69,7 @@

- ansible.builtin.assert:
that:
- result.commands|length == 15
- result.commands|length == 12
- result.changed == true
- result.commands|symmetric_difference(deleted_all.commands) == []

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
register: result
ignore_errors: true
cisco.ios.ios_line:
config:
running_config:
state: parsed

- ansible.builtin.assert:
that:
- result.msg == 'value of config parameter must not be empty for state parsed'
- result.msg == 'value of running_config parameter must not be empty for state parsed'
Loading

0 comments on commit 9cebfa1

Please sign in to comment.