From 59cd693218a31b9f8817d8874baf505fa2975cce Mon Sep 17 00:00:00 2001 From: Nilashish Chakraborty Date: Thu, 9 Aug 2018 18:36:54 +0530 Subject: [PATCH] Add source interface and use-vrf features (#43418) --- .../modules/network/nxos/nxos_logging.py | 135 +++++++++++++++--- .../nxos_logging/tests/common/basic.yaml | 28 +++- 2 files changed, 143 insertions(+), 20 deletions(-) diff --git a/lib/ansible/modules/network/nxos/nxos_logging.py b/lib/ansible/modules/network/nxos/nxos_logging.py index de85817b83..66ef5704b6 100644 --- a/lib/ansible/modules/network/nxos/nxos_logging.py +++ b/lib/ansible/modules/network/nxos/nxos_logging.py @@ -41,6 +41,18 @@ options: description: - Hostname or IP Address for remote logging (when dest is 'server'). version_added: '2.7' + use_vrf: + description: + - VRF to be used while configuring remote logging (when dest is 'server'). + version_added: '2.7' + interface_type: + description: + - Type of interface to be used when configuring Source-Interface for logging (e.g., 'Ethernet', 'mgmt'). + version_added: '2.7' + interface: + description: + - Interface number to be used when configuring Source-Interface for logging (e.g., '1/1', '1/3', '0'). + version_added: '2.7' name: description: - If value of C(dest) is I(logfile) it indicates file-name. @@ -91,6 +103,19 @@ EXAMPLES = """ facility: daemon facility_level: 0 state: absent +- name: Configure Remote Logging + nxos_logging: + dest: server + remote_server: test-syslogserver.com + facility: auth + facility_level: 1 + use_vrf: management + state: present +- name: Configure Source Interface for Logging + nxos_logging: + interface_type: mgmt + interface: 0 + state: present - name: Configure logging using aggregate nxos_logging: @@ -144,6 +169,9 @@ def map_obj_to_commands(updates): if w['dest'] == 'server': commands.append('no logging server {}'.format(w['remote_server'])) + if w['interface_type']: + commands.append('no logging source-interface') + if state == 'present' and w not in have: if w['facility'] is None: if w['dest']: @@ -154,26 +182,44 @@ def map_obj_to_commands(updates): commands.append('logging logfile {} {}'.format(w['name'], w['dest_level'])) elif w['dest'] == 'server': - if w['dest_level']: - commands.append('logging server {0} {1}'.format( - w['remote_server'], w['dest_level'])) + if w['facility_level']: + if w['use_vrf']: + commands.append('logging server {0} {1} use-vrf {2}'.format( + w['remote_server'], w['facility_level'], w['use_vrf'])) + else: + commands.append('logging server {0} {1}'.format( + w['remote_server'], w['facility_level'])) + else: - commands.append('logging server {0}'.format(w['remote_server'])) - else: - pass + if w['use_vrf']: + commands.append('logging server {0} use-vrf {1}'.format( + w['remote_server'], w['use_vrf'])) + else: + commands.append('logging server {0}'.format(w['remote_server'])) if w['facility']: if w['dest'] == 'server': - if w['dest_level']: - commands.append('logging server {0} {1} facility {2}'.format( - w['remote_server'], w['dest_level'], w['facility'])) + if w['facility_level']: + if w['use_vrf']: + commands.append('logging server {0} {1} facility {2} use-vrf {3}'.format( + w['remote_server'], w['facility_level'], w['facility'], w['use_vrf'])) + else: + commands.append('logging server {0} {1} facility {2}'.format( + w['remote_server'], w['facility_level'], w['facility'])) else: - commands.append('logging server {0} facility {1}'.format(w['remote_server'], - w['facility'])) + if w['use_vrf']: + commands.append('logging server {0} facility {1} use-vrf {2}'.format( + w['remote_server'], w['facility'], w['use_vrf'])) + else: + commands.append('logging server {0} facility {1}'.format(w['remote_server'], + w['facility'])) else: commands.append('logging level {} {}'.format(w['facility'], w['facility_level'])) + if w['interface_type']: + commands.append('logging source-interface {0} {1}'.format(w['interface_type'], + w['interface'])) return commands @@ -214,7 +260,7 @@ def parse_dest_level(line, dest, name): pass return level - if dest is not None: + if dest and dest != 'server': if dest == 'logfile': match = re.search(r'logging logfile {} (\S+)'.format(name), line, re.M) if match: @@ -232,10 +278,15 @@ def parse_dest_level(line, dest, name): return dest_level -def parse_facility_level(line, facility): +def parse_facility_level(line, facility, dest): facility_level = None - if facility is not None: + if dest == 'server': + match = re.search(r'logging server (?:\S+) (\d+)', line, re.M) + if match: + facility_level = match.group(1) + + elif facility is not None: match = re.search(r'logging level {} (\S+)'.format(facility), line, re.M) if match: facility_level = match.group(1) @@ -253,6 +304,37 @@ def parse_facility(line): return facility +def parse_use_vrf(line, dest): + use_vrf = None + + if dest and dest == 'server': + match = re.search(r'logging server (?:\S+) (?:\d+) use-vrf (\S+)', line, re.M) + if match: + use_vrf = match.group(1) + + return use_vrf + + +def parse_interface_type(line): + interface_type = None + + match = re.search(r'logging source-interface (\D+)', line, re.M) + if match: + interface_type = match.group(1) + + return interface_type + + +def parse_interface(line): + interface = None + + match = re.search(r'logging source-interface (?:\D+)(\d*([/]?\d+))', line, re.M) + if match: + interface = match.group(1) + + return interface + + def map_config_to_obj(module): obj = [] @@ -280,10 +362,13 @@ def map_config_to_obj(module): obj.append({'dest': dest, 'remote_server': parse_remote_server(line, dest), + 'use_vrf': parse_use_vrf(line, dest), 'name': parse_name(line, dest), 'facility': facility, 'dest_level': parse_dest_level(line, dest, parse_name(line, dest)), - 'facility_level': parse_facility_level(line, facility)}) + 'facility_level': parse_facility_level(line, facility, dest), + 'interface_type': parse_interface_type(line), + 'interface': parse_interface(line)}) cmd = [{'command': 'show logging | section enabled | section console', 'output': 'text'}, {'command': 'show logging | section enabled | section monitor', 'output': 'text'}] @@ -306,7 +391,10 @@ def map_config_to_obj(module): 'name': None, 'facility': None, 'dest_level': dest_level, - 'facility_level': None}) + 'facility_level': None, + 'use_vrf': None, + 'interface_type': None, + 'interface': None}) return obj @@ -317,10 +405,13 @@ def map_params_to_obj(module): if 'aggregate' in module.params and module.params['aggregate']: args = {'dest': '', 'remote_server': '', + 'use_vrf': '', 'name': '', 'facility': '', 'dest_level': '', - 'facility_level': ''} + 'facility_level': '', + 'interface_type': '', + 'interface': ''} for c in module.params['aggregate']: d = c.copy() @@ -335,6 +426,9 @@ def map_params_to_obj(module): if d['facility_level'] is not None: d['facility_level'] = str(d['facility_level']) + if d['interface_type']: + d['interface'] = str(d['interface']) + if 'state' not in d: d['state'] = module.params['state'] @@ -353,10 +447,13 @@ def map_params_to_obj(module): obj.append({ 'dest': module.params['dest'], 'remote_server': module.params['remote_server'], + 'use_vrf': module.params['use_vrf'], 'name': module.params['name'], 'facility': module.params['facility'], 'dest_level': dest_level, 'facility_level': facility_level, + 'interface_type': module.params['interface_type'], + 'interface': module.params['interface'], 'state': module.params['state'] }) return obj @@ -370,8 +467,11 @@ def main(): name=dict(), facility=dict(), remote_server=dict(), + use_vrf=dict(), dest_level=dict(type='int', aliases=['level']), facility_level=dict(type='int'), + interface_type=dict(), + interface=dict(), state=dict(default='present', choices=['present', 'absent']), aggregate=dict(type='list') ) @@ -383,6 +483,7 @@ def main(): module = AnsibleModule(argument_spec=argument_spec, required_if=required_if, + required_together=[['interface_type', 'interface']], supports_check_mode=True) warnings = list() diff --git a/test/integration/targets/nxos_logging/tests/common/basic.yaml b/test/integration/targets/nxos_logging/tests/common/basic.yaml index 28f79af184..3fb0d26923 100644 --- a/test/integration/targets/nxos_logging/tests/common/basic.yaml +++ b/test/integration/targets/nxos_logging/tests/common/basic.yaml @@ -175,7 +175,8 @@ dest: server remote_server: test-syslogserver.com facility: auth - dest_level: 1 + facility_level: 1 + use_vrf: management state: present provider: "{{ connection }}" register: result @@ -183,7 +184,7 @@ - assert: that: - 'result.changed == true' - - '"logging server test-syslogserver.com 1 facility auth" in result.commands' + - '"logging server test-syslogserver.com 1 facility auth use-vrf management" in result.commands' - name: Configure Remote Logging (idempotent) nxos_logging: *rlog @@ -191,6 +192,25 @@ - assert: *false +- name: Configure Source Interface for Logging + nxos_logging: &srcint + interface_type: mgmt + interface: 0 + register: result + +- assert: + that: + - 'result.changed == true' + - '"logging source-interface mgmt 0" in result.commands' + +- name: Configure Source Interface for Logging (idempotent) + nxos_logging: *srcint + register: result + +- assert: + that: + - 'result.changed == false' + - name: remove logging as collection tearDown nxos_logging: &agg aggregate: @@ -199,7 +219,8 @@ - { dest: monitor, dest_level: 5 } - { dest: logfile, dest_level: 1, name: test } - { facility: daemon, facility_level: 4 } - - { dest: server, remote_server: test-syslogserver.com, facility: auth, dest_level: 1 } + - { dest: server, remote_server: test-syslogserver.com, facility: auth, facility_level: 1, use_vrf: management } + - { interface_type: mgmt, interface: 0 } provider: "{{ connection }}" state: absent register: result @@ -212,6 +233,7 @@ - '"no logging monitor" in result.commands' - '"no logging module" in result.commands' - '"no logging server test-syslogserver.com" in result.commands' + - '"no logging source-interface" in result.commands' - name: remove aggregate logging (idempotent) nxos_logging: *agg