diff --git a/changelogs/fragments/ios_service_fix.yml b/changelogs/fragments/ios_service_fix.yml new file mode 100644 index 000000000..3ec24d6a9 --- /dev/null +++ b/changelogs/fragments/ios_service_fix.yml @@ -0,0 +1,4 @@ +--- +bugfixes: + - ios_service - Fix timestamps attribute, to generate right configuration. + - ios_service - Add tcp_small_servers and udp_small_servers attributes, to generate configuration. diff --git a/docs/cisco.ios.ios_service_module.rst b/docs/cisco.ios.ios_service_module.rst index ed859129d..a2d5eef38 100644 --- a/docs/cisco.ios.ios_service_module.rst +++ b/docs/cisco.ios.ios_service_module.rst @@ -45,7 +45,7 @@ Parameters -
A dictionnary of service configuration
+
A dictionary of service configuration
@@ -658,7 +658,7 @@ Parameters -
TCP and UDP small servers are servers (daemons, in Unix parlance) that run in the router which are useful for diagnostics.
+
TCP small servers are servers (daemons, in Unix parlance) that run in the router which are useful for diagnostics.
@@ -690,14 +690,35 @@ Parameters max_servers
- string + integer
Set number of allowable TCP small servers
-
1 to 2147483647 or no-limit
+
1 to 2147483647
+ + + + + + +
+ no_limit + +
+ boolean +
+ + + + + +
No limit on number of allowable TCP small servers
@@ -921,7 +942,7 @@ Parameters -
TCP and UDP small servers are servers (daemons, in Unix parlance) that run in the router which are useful for diagnostics.
+
UDP small servers are servers (daemons, in Unix parlance) that run in the router which are useful for diagnostics.
@@ -953,14 +974,35 @@ Parameters max_servers
- string + integer
-
Set number of allowable TCP small servers
-
1 to 2147483647 or no-limit
+
Set number of allowable UDP small servers
+
1 to 2147483647
+ + + + + + +
+ no_limit + +
+ boolean +
+ + + + + +
No limit on number of allowable UDP small servers
@@ -1036,7 +1078,7 @@ Notes ----- .. note:: - - Tested against Cisco IOSXE Version 16.9 + - Tested against Cisco IOSXE Version 17.9.1a on CML. - This module works with connection ``network_cli``. See https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html @@ -1556,3 +1598,4 @@ Authors ~~~~~~~ - Ambroise Rosset (@earendilfr) +- Sagar Paul (@KB-perByte) diff --git a/plugins/module_utils/network/ios/argspec/service/service.py b/plugins/module_utils/network/ios/argspec/service/service.py index 238fc64a2..647eeb3d2 100644 --- a/plugins/module_utils/network/ios/argspec/service/service.py +++ b/plugins/module_utils/network/ios/argspec/service/service.py @@ -68,7 +68,8 @@ class ServiceArgs(object): # pylint: disable=R0903 "tcp_small_servers": { "options": { "enable": {"type": "bool"}, - "max_servers": {"type": "str"}, + "max_servers": {"type": "int"}, + "no_limit": {"type": "bool"}, }, "type": "dict", }, @@ -97,7 +98,8 @@ class ServiceArgs(object): # pylint: disable=R0903 "udp_small_servers": { "options": { "enable": {"type": "bool"}, - "max_servers": {"type": "str"}, + "max_servers": {"type": "int"}, + "no_limit": {"type": "bool"}, }, "type": "dict", }, diff --git a/plugins/module_utils/network/ios/config/service/service.py b/plugins/module_utils/network/ios/config/service/service.py index 6a4c8a1ff..603e1060b 100644 --- a/plugins/module_utils/network/ios/config/service/service.py +++ b/plugins/module_utils/network/ios/config/service/service.py @@ -75,7 +75,9 @@ def __init__(self, module): "slave_log", "tcp_keepalives_in", "tcp_keepalives_out", + "tcp_small_servers", "telnet_zeroidle", + "udp_small_servers", "unsupported_transceiver", ] @@ -138,9 +140,9 @@ def _compare_lists_attrs(self, want, have): for key, wanting in iteritems(i_want): haveing = i_have.pop(key, {}) if wanting != haveing: - self.addcmd(wanting, "timestamps") + self.addcmd(wanting, key + "_timestamps", False) for key, haveing in iteritems(i_have): - self.addcmd(haveing, "timestamps", negate=True) + self.addcmd(haveing, key + "_timestamps", negate=True) def _service_list_to_dict(self, data): """Convert all list of dicts to dicts of dicts""" diff --git a/plugins/module_utils/network/ios/facts/service/service.py b/plugins/module_utils/network/ios/facts/service/service.py index df2dcf993..6ada10748 100644 --- a/plugins/module_utils/network/ios/facts/service/service.py +++ b/plugins/module_utils/network/ios/facts/service/service.py @@ -33,7 +33,7 @@ def __init__(self, module, subspec="config", options="options"): self.argument_spec = ServiceArgs.argument_spec def get_service_data(self, connection): - return connection.get("show running-config all | section ^service ") + return connection.get("show running-config all | section ^service") def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for Service network resource @@ -59,7 +59,11 @@ def populate_facts(self, connection, ansible_facts, data=None): ansible_facts["ansible_network_resources"].pop("service", None) params = utils.remove_empties( - service_parser.validate_config(self.argument_spec, {"config": objs}, redact=True), + service_parser.validate_config( + self.argument_spec, + {"config": objs}, + redact=True, + ), ) facts["service"] = params.get("config", {}) diff --git a/plugins/module_utils/network/ios/rm_templates/service.py b/plugins/module_utils/network/ios/rm_templates/service.py index 992892db8..c81593226 100644 --- a/plugins/module_utils/network/ios/rm_templates/service.py +++ b/plugins/module_utils/network/ios/rm_templates/service.py @@ -22,6 +22,21 @@ ) +def handleTimestamp(config_data): + command = "service timestamps" + command += " " + config_data.get("msg") if config_data.get("msg") else "" + command += " " + config_data.get("timestamp") if config_data.get("timestamp") else "" + + if config_data.get("datetime_options"): + datetime_op = config_data.get("datetime_options") + command += " mesc" if datetime_op.get("msec") else "" + command += " localtime" if datetime_op.get("localtime") else "" + command += " show-timezone" if datetime_op.get("show_timezone") else "" + command += " year" if datetime_op.get("year") else "" + + return command + + class ServiceTemplate(NetworkTemplate): def __init__(self, lines=None, module=None): super(ServiceTemplate, self).__init__(lines=lines, tmplt=self, module=module) @@ -32,36 +47,36 @@ def __init__(self, lines=None, module=None): "name": "call_home", "getval": re.compile( r""" - ^service\s(?Pcall-home) + ^service\scall-home """, re.VERBOSE, ), "setval": "service call-home", "result": { - "call_home": "{{ not not call_home }}", + "call_home": True, }, }, { "name": "compress_config", "getval": re.compile( r""" - ^service\s(?Pcompress-config) + ^service\scompress-config """, re.VERBOSE, ), "setval": "service compress-config", "result": { - "compress_config": "{{ not not compress_config }}", + "compress_config": True, }, }, { "name": "config", "getval": re.compile( r""" - ^service\s(?Pconfig) + ^service\sconfig """, re.VERBOSE, ), "setval": "service config", "result": { - "config": "{{ not not config }}", + "config": True, }, }, { @@ -92,120 +107,120 @@ def __init__(self, lines=None, module=None): "name": "disable_ip_fast_frag", "getval": re.compile( r""" - ^service\s(?Pdisable-ip-fast-frag) + ^service\sdisable-ip-fast-frag """, re.VERBOSE, ), "setval": "service disable-ip-fast-frag", "result": { - "disable_ip_fast_frag": "{{ not not disable_ip_fast_frag }}", + "disable_ip_fast_frag": True, }, }, { "name": "exec_callback", "getval": re.compile( r""" - ^service\s(?Pexec-callback) + ^service\sexec-callback """, re.VERBOSE, ), "setval": "service exec-callback", "result": { - "exec_callback": "{{ not not exec_callback }}", + "exec_callback": True, }, }, { "name": "exec_wait", "getval": re.compile( r""" - ^service\s(?Pexec-wait) + ^service\sexec-wait """, re.VERBOSE, ), "setval": "service exec-wait", "return": { - "exec_wait": "{{ not not exec_wait }}", + "exec_wait": True, }, }, { "name": "hide_telnet_addresses", "getval": re.compile( r""" - ^service\s(?Phide-telnet-addresses) + ^service\shide-telnet-addresses """, re.VERBOSE, ), "setval": "service hide-telnet-addresses", "result": { - "hide_telnet_addresses": "{{ not not hide_telnet_addresses }}", + "hide_telnet_addresses": True, }, }, { "name": "internal", "getval": re.compile( r""" - ^service\s(?Pinternal) + ^service\sinternal """, re.VERBOSE, ), "setval": "service internal", "result": { - "internal": "{{ not not internal }}", + "internal": True, }, }, { "name": "linenumber", "getval": re.compile( r""" - ^service\s(?Plinenumber) + ^service\slinenumber """, re.VERBOSE, ), "setval": "service linenumber", "result": { - "linenumber": "{{ not not linenumber }}", + "linenumber": True, }, }, { "name": "log", "getval": re.compile( r""" - ^service\slog(\s(?Pbacktrace))? + ^service\slog\sbacktrace? """, re.VERBOSE, ), "setval": "service log backtrace", "result": { - "log": "{{ not not backtrace }}", + "log": True, }, }, { "name": "log_hidden", "getval": re.compile( r""" - ^service\s(?Plog-hidden) + ^service\slog-hidden """, re.VERBOSE, ), "setval": "service log-hidden", "result": { - "log_hidden": "{{ not not log_hidden }}", + "log_hidden": True, }, }, { "name": "nagle", "getval": re.compile( r""" - ^service\s(?Pnagle) + ^service\snagle """, re.VERBOSE, ), "setval": "service nagle", "result": { - "nagle": "{{ not not nagle }}", + "nagle": True, }, }, { "name": "old_slip_prompts", "getval": re.compile( r""" - ^service\s(?Pold-slip-prompts) + ^service\sold-slip-prompts """, re.VERBOSE, ), "setval": "service old-slip-prompts", "result": { - "old_slip_prompts": "{{ not not old_slip_prompts }}", + "old_slip_prompts": True, }, }, { @@ -260,12 +275,12 @@ def __init__(self, lines=None, module=None): "name": "password_encryption", "getval": re.compile( r""" - ^service\s(?Ppassword-encryption) + ^service\spassword-encryption """, re.VERBOSE, ), "setval": "service password-encryption", "result": { - "password_encryption": "{{ not not password_encryption }}", + "password_encryption": True, }, }, { @@ -309,48 +324,48 @@ def __init__(self, lines=None, module=None): "name": "pt_vty_logging", "getval": re.compile( r""" - ^service\s(?Ppt-vty-logging) + ^service\spt-vty-logging """, re.VERBOSE, ), "setval": "service pt-vty-logging", "result": { - "pt_vty_logging": "{{ not not pt_vty_logging }}", + "pt_vty_logging": True, }, }, { "name": "scripting", "getval": re.compile( r""" - ^service\s(?Pscripting) + ^service\sscripting """, re.VERBOSE, ), "setval": "service scripting", "result": { - "scripting": "{{ not not scripting }}", + "scripting": True, }, }, { "name": "sequence_numbers", "getval": re.compile( r""" - ^service\s(?Psequence-numbers) + ^service\ssequence-numbers """, re.VERBOSE, ), "setval": "service sequence-numbers", "result": { - "sequence_numbers": "{{ not not sequence_numbers }}", + "sequence_numbers": True, }, }, { "name": "slave_coredump", "getval": re.compile( r""" - ^service\s(?Pslave-coredump) + ^service\sslave-coredump """, re.VERBOSE, ), "setval": "service slave-coredump", "result": { - "slave_coredump": "{{ not not slave_coredump }}", + "slave_coredump": True, }, }, { @@ -389,6 +404,46 @@ def __init__(self, lines=None, module=None): "tcp_keepalives_out": True, }, }, + { + "name": "tcp_small_servers", + "getval": re.compile( + r""" + ^service\stcp-small-servers + (\s(?P\d+))? + (\s(?Pno-limit))? + """, re.VERBOSE, + ), + "setval": "service tcp-small-servers" + "{{ (' ' + tcp_small_servers.max_servers|string) if tcp_small_servers.max_servers is defined else '' }}" + "{{ (' no-limit') if tcp_small_servers.no_limit|d(False) else '' }}", + "result": { + "tcp_small_servers": { + "enable": True, + "max_servers": "{{ max_servers }}", + "no_limit": "{{ not not no_limit }}", + }, + }, + }, + { + "name": "udp_small_servers", + "getval": re.compile( + r""" + ^service\sudp-small-servers + (\s(?P\d+))? + (\s(?Pno-limit))? + """, re.VERBOSE, + ), + "setval": "{{ ('service udp-small-servers') if udp_small_servers.enable|d(False) else '' }}" + "{{ (' ' + udp_small_servers.max_servers|string) if udp_small_servers.max_servers is defined else '' }}" + "{{ (' no-limit') if udp_small_servers.no_limit|d(False) else '' }}", + "result": { + "udp_small_servers": { + "enable": True, + "max_servers": "{{ max_servers }}", + "no_limit": "{{ not not no_limit }}", + }, + }, + }, { "name": "telnet_zeroidle", "getval": re.compile( @@ -402,11 +457,39 @@ def __init__(self, lines=None, module=None): }, }, { - "name": "timestamps", + "name": "log_timestamps", + "getval": re.compile( + r""" + ^service\stimestamps\slog + (\s(?P\S+))? + (\s(?Pmsec))? + (\s(?Plocaltime))? + (\s(?Pshow-timezone))? + (\s(?Pyear))? + """, re.VERBOSE, + ), + "remval": "service timestamps log", + "setval": handleTimestamp, + "result": { + "timestamps": [ + { + "msg": "log", + "timestamp": "{{ timestamp if timestamp is defined else 'uptime' }}", + "datetime_options": { + "msec": "{{ True if msec else False}}", + "localtime": "{{ True if localtime else False }}", + "show_timezone": "{{ True if show_timezone else False }}", + "year": "{{ True if year else False }}", + }, + }, + ], + }, + }, + { + "name": "debug_timestamps", "getval": re.compile( r""" - ^service\stimestamps - (\s(?P\S+))? + ^service\stimestamps\sdebug (\s(?P\S+))? (\s(?Pmsec))? (\s(?Plocaltime))? @@ -414,23 +497,12 @@ def __init__(self, lines=None, module=None): (\s(?Pyear))? """, re.VERBOSE, ), - "remval": "service timestamps{{ (' ' + msg) if msg is defined else '' }}", - "setval": "service timestamps" - "{{ (' ' + msg) if msg is defined else '' }}" - "{% if msg is defined %}" - "{{ (' ' + timestamp) if timestamp is defined else '' }}" - "{% if timestamp == 'datetime' and datetime_options is defined %}" - "{{ ' msec' if datetime_options.msec else '' }}" - "{{ ' localtime' if datetime_options.localtime else '' }}" - "{{ ' show-timezone' if datetime_options.show_timezone else '' }}" - "{{ ' year' if datetime_options.year else '' }}" - "{% endif %}" - "{% endif %}" - "", + "remval": "service timestamps debug", + "setval": handleTimestamp, "result": { "timestamps": [ { - "msg": "{{ msg if msg is defined else 'debug' }}", + "msg": "debug", "timestamp": "{{ timestamp if timestamp is defined else 'uptime' }}", "datetime_options": { "msec": "{{ True if msec else False}}", diff --git a/plugins/modules/ios_service.py b/plugins/modules/ios_service.py index 46c456569..ebe0e7881 100644 --- a/plugins/modules/ios_service.py +++ b/plugins/modules/ios_service.py @@ -22,13 +22,14 @@ version_added: 4.6.0 author: - Ambroise Rosset (@earendilfr) + - Sagar Paul (@KB-perByte) notes: - - Tested against Cisco IOSXE Version 16.9 + - Tested against Cisco IOSXE Version 17.9.1a on CML. - This module works with connection C(network_cli). See U(https://docs.ansible.com/ansible/latest/network/user_guide/platform_ios.html) options: config: - description: A dictionnary of service configuration + description: A dictionary of service configuration suboptions: call_home: description: Cisco call-home service @@ -128,7 +129,7 @@ type: bool tcp_small_servers: description: - - TCP and UDP small servers are servers (daemons, in Unix parlance) that run in the + - TCP small servers are servers (daemons, in Unix parlance) that run in the router which are useful for diagnostics. suboptions: enable: @@ -137,8 +138,11 @@ max_servers: description: - Set number of allowable TCP small servers - - 1 to 2147483647 or no-limit - type: str + - 1 to 2147483647 + type: int + no_limit: + description: No limit on number of allowable TCP small servers + type: bool type: dict telnet_zeroidle: description: Set TCP window 0 when connection is idle @@ -181,7 +185,7 @@ type: list udp_small_servers: description: - - TCP and UDP small servers are servers (daemons, in Unix parlance) that run in the + - UDP small servers are servers (daemons, in Unix parlance) that run in the router which are useful for diagnostics. suboptions: enable: @@ -189,9 +193,12 @@ type: bool max_servers: description: - - Set number of allowable TCP small servers - - 1 to 2147483647 or no-limit - type: str + - Set number of allowable UDP small servers + - 1 to 2147483647 + type: int + no_limit: + description: No limit on number of allowable UDP small servers + type: bool type: dict unsupported_transceiver: description: enable support for third-party transceivers diff --git a/tests/unit/modules/network/ios/test_ios_service.py b/tests/unit/modules/network/ios/test_ios_service.py index fa92efcdc..69af87eda 100644 --- a/tests/unit/modules/network/ios/test_ios_service.py +++ b/tests/unit/modules/network/ios/test_ios_service.py @@ -128,14 +128,13 @@ def test_ios_service_merged(self): }, } merged = [ - "service timestamps debug uptime", - "service timestamps log datetime msec localtime show-timezone year", "service password-encryption", + "service timestamps debug uptime mesc", + "service timestamps log datetime mesc localtime show-timezone year", ] playbook["state"] = "merged" set_module_args(playbook) result = self.execute_module(changed=True) - self.assertEqual(sorted(result["commands"]), sorted(merged)) def test_ios_snm_server_deleted(self): @@ -213,18 +212,17 @@ def test_ios_service_overridden(self): overridden = [ "no service call-home", "no service config", - "no service pad", "service counters max age 5", + "no service pad", "service password-encryption", "service tcp-keepalives-in", "service tcp-keepalives-out", + "service timestamps log datetime mesc localtime show-timezone year", "service timestamps debug datetime", - "service timestamps log datetime msec localtime show-timezone year", ] playbook["state"] = "overridden" set_module_args(playbook) result = self.execute_module(changed=True) - self.assertEqual(sorted(result["commands"]), sorted(overridden)) def test_ios_service_replaced(self): @@ -258,6 +256,10 @@ def test_ios_service_replaced(self): { "msg": "debug", "timestamp": "datetime", + "datetime_options": { + "localtime": True, + "msec": True, + }, }, ], "tcp_keepalives_in": True, @@ -269,18 +271,17 @@ def test_ios_service_replaced(self): replaced = [ "no service call-home", "no service config", - "no service pad", "service counters max age 5", + "no service pad", "service password-encryption", "service tcp-keepalives-in", "service tcp-keepalives-out", - "service timestamps debug datetime", - "service timestamps log datetime msec localtime show-timezone year", + "service timestamps log datetime mesc localtime show-timezone year", + "service timestamps debug datetime mesc localtime", ] playbook["state"] = "replaced" set_module_args(playbook) result = self.execute_module(changed=True) - self.assertEqual(sorted(result["commands"]), sorted(replaced)) def test_ios_service_replaced_idempotent(self): @@ -368,8 +369,6 @@ def test_ios_service_replaced_idempotent_old(self): self.assertEqual(sorted(result["commands"]), sorted(replaced)) - #################### - def test_ios_service_parsed(self): set_module_args( dict( @@ -426,36 +425,36 @@ def test_ios_service_gathered(self): service timestamps log datetime msec localtime show-timezone year service timestamps debug uptime service call-home + service udp-small-servers + service tcp-small-servers no-limit """, ) set_module_args(dict(state="gathered")) gathered = { "timestamps": [ - { - "msg": "debug", - "timestamp": "uptime", - }, { "msg": "log", "timestamp": "datetime", "datetime_options": { - "localtime": True, "msec": True, + "localtime": True, "show_timezone": True, "year": True, }, }, + {"msg": "debug", "timestamp": "uptime"}, ], "call_home": True, - "dhcp": True, + "udp_small_servers": {"enable": True}, + "tcp_small_servers": {"enable": True, "no_limit": True}, "counters": 0, + "dhcp": True, "password_recovery": True, "prompt": True, "slave_log": True, } result = self.execute_module(changed=False) self.maxDiff = None - self.assertEqual(sorted(result["gathered"]), sorted(gathered)) def test_ios_service_rendered(self): @@ -472,6 +471,9 @@ def test_ios_service_rendered(self): { "msg": "debug", "timestamp": "uptime", + "datetime_options": { + "localtime": True, + }, }, { "msg": "log", @@ -497,10 +499,9 @@ def test_ios_service_rendered(self): "service slave-log", "service tcp-keepalives-in", "service tcp-keepalives-out", - "service timestamps debug uptime", - "service timestamps log datetime msec localtime show-timezone year", + "service timestamps debug uptime localtime", + "service timestamps log datetime mesc localtime show-timezone year", ] result = self.execute_module(changed=False) self.maxDiff = None - self.assertEqual(sorted(result["rendered"]), sorted(rendered))