diff --git a/changelogs/fragments/add_virtual_interface.yaml b/changelogs/fragments/add_virtual_interface.yaml new file mode 100644 index 000000000..d5710b460 --- /dev/null +++ b/changelogs/fragments/add_virtual_interface.yaml @@ -0,0 +1,3 @@ +--- +bugfixes: + - Add 'virtual' key to denote the existence of virtual address on an interface.(https://github.com/ansible-collections/arista.eos/pull/170). diff --git a/plugins/module_utils/network/eos/argspec/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/eos/argspec/l3_interfaces/l3_interfaces.py index ae8738fa5..fd3865fe2 100644 --- a/plugins/module_utils/network/eos/argspec/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/eos/argspec/l3_interfaces/l3_interfaces.py @@ -47,6 +47,7 @@ def __init__(self, **kwargs): "options": { "address": {"type": "str"}, "secondary": {"type": "bool"}, + "virtual": {"type": "bool"}, }, "type": "list", }, diff --git a/plugins/module_utils/network/eos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/eos/config/l3_interfaces/l3_interfaces.py index 453332eaa..ad4ce835e 100644 --- a/plugins/module_utils/network/eos/config/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/eos/config/l3_interfaces/l3_interfaces.py @@ -172,7 +172,6 @@ def _state_replaced(want, have): if intf_commands: commands.append("interface {0}".format(interface_name)) commands.extend(intf_commands) - return commands @staticmethod @@ -191,9 +190,9 @@ def _state_overridden(want, have): desired = dict() if desired.get("ipv4"): for ipv4 in desired["ipv4"]: - if ipv4["secondary"] is None: - del ipv4["secondary"] - + for k in ["secondary", "virtual"]: + if ipv4[k] is None: + del ipv4[k] intf_commands = set_interface(desired, extant) intf_commands.extend(clear_interface(desired, extant)) @@ -253,7 +252,6 @@ def _state_deleted(want, have): def set_interface(want, have): commands = [] - want_ipv4 = set( tuple(sorted(address.items())) for address in want.get("ipv4") or [] ) @@ -262,14 +260,17 @@ def set_interface(want, have): ) for address in want_ipv4 - have_ipv4: address = dict(address) - if "secondary" in address and not address["secondary"]: - del address["secondary"] - if tuple(address.items()) in have_ipv4: - continue + for param in ["secondary", "virtual"]: + if param in address and not address[param]: + del address[param] + if tuple(sorted(address.items())) in have_ipv4: + continue address_cmd = "ip address {0}".format(address["address"]) if address.get("secondary"): address_cmd += " secondary" + if address.get("virtual"): + address_cmd = "ip address virtual {0}".format(address["address"]) commands.append(address_cmd) want_ipv6 = set( @@ -297,14 +298,20 @@ def clear_interface(want, have): else: for address in have_ipv4 - want_ipv4: address = dict(address) - if "secondary" not in address: - address["secondary"] = False - if tuple(address.items()) in want_ipv4: - continue + for param in ["secondary", "virtual"]: + if param not in address: + address[param] = None + if tuple(sorted(address.items())) in want_ipv4: + continue if address.get("secondary"): - address_cmd = " {0} secondary".format(address["address"]) - commands.append(address_cmd) + commands.append( + "no ip address {0} secondary".format(address["address"]) + ) + if address.get("virtual"): + commands.append( + "no ip address virtual {0}".format(address["address"]) + ) if "secondary" not in address: # Removing non-secondary removes all other interfaces diff --git a/plugins/module_utils/network/eos/facts/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/eos/facts/l3_interfaces/l3_interfaces.py index afce14455..15181fe53 100644 --- a/plugins/module_utils/network/eos/facts/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/eos/facts/l3_interfaces/l3_interfaces.py @@ -102,7 +102,10 @@ def render_config(self, spec, conf): config["ipv4"] = [] for match in matches: address, dummy, remainder = match.partition(" ") - ipv4 = {"address": address} + if address == "virtual": + ipv4 = {"virtual": True, "address": remainder} + else: + ipv4 = {"address": address} if remainder == "secondary": ipv4["secondary"] = True config["ipv4"].append(ipv4) diff --git a/plugins/modules/eos_l3_interfaces.py b/plugins/modules/eos_l3_interfaces.py index 38ab683b6..a0064da5f 100644 --- a/plugins/modules/eos_l3_interfaces.py +++ b/plugins/modules/eos_l3_interfaces.py @@ -70,6 +70,10 @@ description: - Whether or not this address is a secondary address. type: bool + virtual: + description: + - Whether or not this address is a virtual address. + type: bool ipv6: description: - List of IPv6 addresses to be set for the Layer 3 interface mentioned in diff --git a/tests/unit/modules/network/eos/fixtures/eos_l3_interfaces_config.cfg b/tests/unit/modules/network/eos/fixtures/eos_l3_interfaces_config.cfg index a818fa98f..5666612fe 100644 --- a/tests/unit/modules/network/eos/fixtures/eos_l3_interfaces_config.cfg +++ b/tests/unit/modules/network/eos/fixtures/eos_l3_interfaces_config.cfg @@ -1,5 +1,6 @@ interface Ethernet1 ip address 192.0.2.12/24 + ip address 192.87.33.4/24 secondary ! interface Ethernet2 ipv6 address 2001:db8::1/64 @@ -7,3 +8,6 @@ interface Ethernet2 interface Management1 ip address dhcp ipv6 address auto-config +! +interface Vlan100 + ip address virtual 192.13.45.12/24 diff --git a/tests/unit/modules/network/eos/test_eos_l3_interfaces.py b/tests/unit/modules/network/eos/test_eos_l3_interfaces.py index ea6b1dcaa..ddfa9c282 100644 --- a/tests/unit/modules/network/eos/test_eos_l3_interfaces.py +++ b/tests/unit/modules/network/eos/test_eos_l3_interfaces.py @@ -82,6 +82,10 @@ def test_eos_l3_interfaces_merged(self): name="Ethernet2", ipv4=[dict(address="203.0.113.27/24")], ), + dict( + name="Vlan200", + ipv4=[dict(address="198.151.10.14/24", virtual=True)], + ), ], state="merged", ) @@ -91,6 +95,8 @@ def test_eos_l3_interfaces_merged(self): "ip address 198.51.100.14/24", "interface Ethernet2", "ip address 203.0.113.27/24", + "interface Vlan200", + "ip address virtual 198.151.10.14/24", ] self.execute_module(changed=True, commands=commands) @@ -99,11 +105,19 @@ def test_eos_l3_interfaces_merged_idempotent(self): dict( config=[ dict( - name="Ethernet1", ipv4=[dict(address="192.0.2.12/24")] + name="Ethernet1", + ipv4=[ + dict(address="192.0.2.12/24"), + dict(address="192.87.33.4/24", secondary=True), + ], ), dict( name="Ethernet2", ipv6=[dict(address="2001:db8::1/64")] ), + dict( + name="Vlan100", + ipv4=[dict(address="192.13.45.12/24", virtual=True)], + ), ], state="merged", ) @@ -133,6 +147,8 @@ def test_eos_l3_interfaces_overridden(self): "ip address 203.0.113.27/24", "interface Ethernet1", "no ip address", + "interface Vlan100", + "no ip address", ] self.execute_module(changed=True, commands=commands) @@ -144,13 +160,21 @@ def test_eos_l3_interfaces_overridden_idempotent(self): name="Ethernet2", ipv6=[dict(address="2001:db8::1/64")] ), dict( - name="Ethernet1", ipv4=[dict(address="192.0.2.12/24")] + name="Ethernet1", + ipv4=[ + dict(address="192.0.2.12/24"), + dict(address="192.87.33.4/24", secondary=True), + ], ), dict( name="Management1", ipv4=[dict(address="dhcp")], ipv6=[dict(address="auto-config")], ), + dict( + name="Vlan100", + ipv4=[dict(address="192.13.45.12/24", virtual=True)], + ), ], state="overridden", ) @@ -184,13 +208,21 @@ def test_eos_l3_interfaces_replaced_idempotent(self): name="Ethernet2", ipv6=[dict(address="2001:db8::1/64")] ), dict( - name="Ethernet1", ipv4=[dict(address="192.0.2.12/24")] + name="Ethernet1", + ipv4=[ + dict(address="192.0.2.12/24"), + dict(address="192.87.33.4/24", secondary=True), + ], ), dict( name="Management1", ipv4=[dict(address="dhcp")], ipv6=[dict(address="auto-config")], ), + dict( + name="Vlan100", + ipv4=[dict(address="192.13.45.12/24", virtual=True)], + ), ], state="replaced", ) @@ -258,12 +290,22 @@ def test_eos_l3_interfaces_gathered(self): set_module_args(dict(state="gathered")) result = self.execute_module(changed=False) gather_list = [ - {"name": "Ethernet1", "ipv4": [{"address": "192.0.2.12/24"}]}, + { + "name": "Ethernet1", + "ipv4": [ + {"address": "192.0.2.12/24"}, + {"address": "192.87.33.4/24", "secondary": True}, + ], + }, {"name": "Ethernet2", "ipv6": [{"address": "2001:db8::1/64"}]}, { "name": "Management1", "ipv4": [{"address": "dhcp"}], "ipv6": [{"address": "auto-config"}], }, + { + "ipv4": [{"address": "192.13.45.12/24", "virtual": True}], + "name": "Vlan100", + }, ] self.assertEqual(gather_list, result["gathered"])