From e74e8b8e7553ca2e0d3b4ffd472ff7c5cbb22e6c Mon Sep 17 00:00:00 2001 From: tstoner <33665760+tstoner@users.noreply.github.com> Date: Tue, 25 Sep 2018 01:32:09 -0400 Subject: [PATCH] Additional feature enhancements to nxos_logging (#45844) * Various changes to nxos_logging. Plus added purge capibility. * Made a few new nxapi_logging test cases conditional based on version and/or platform. * Addressed PR comments and ansibot shippable. Fixed up nxos_logging documentation format. * Addressed ansibot shippable issues with whitespaces and documentation. * Resolved ansibot codestyle trailing whitespace --- lib/ansible/module_utils/network/nxos/nxos.py | 40 +++ .../modules/network/nxos/nxos_logging.py | 331 ++++++++++++++++-- lib/ansible/plugins/cliconf/nxos.py | 12 + lib/ansible/plugins/terminal/nxos.py | 2 +- .../nxos_logging/tests/common/basic.yaml | 305 +++++++++++++++- .../nxos_logging/tests/common/purge.yaml | 109 ++++++ 6 files changed, 758 insertions(+), 41 deletions(-) create mode 100644 test/integration/targets/nxos_logging/tests/common/purge.yaml diff --git a/lib/ansible/module_utils/network/nxos/nxos.py b/lib/ansible/module_utils/network/nxos/nxos.py index d20211982d..4caa34fce0 100644 --- a/lib/ansible/module_utils/network/nxos/nxos.py +++ b/lib/ansible/module_utils/network/nxos/nxos.py @@ -224,6 +224,24 @@ class Cli: self._module._capabilities = json.loads(capabilities) return self._module._capabilities + def read_module_context(self, module_key): + connection = self._get_connection() + try: + module_context = connection.read_module_context(module_key) + except ConnectionError as exc: + self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) + + return module_context + + def save_module_context(self, module_key, module_context): + connection = self._get_connection() + try: + connection.save_module_context(module_key, module_context) + except ConnectionError as exc: + self._module.fail_json(msg=to_text(exc, errors='surrogate_then_replace')) + + return None + class Nxapi: @@ -238,6 +256,7 @@ class Nxapi: self._module = module self._nxapi_auth = None self._device_configs = {} + self._module_context = {} self._module.params['url_username'] = self._module.params['username'] self._module.params['url_password'] = self._module.params['password'] @@ -464,6 +483,17 @@ class Nxapi: result['network_api'] = 'nxapi' return result + def read_module_context(self, module_key): + if self._module_context.get(module_key): + return self._module_context[module_key] + + return None + + def save_module_context(self, module_key, module_context): + self._module_context[module_key] = module_context + + return None + def is_json(cmd): return str(cmd).endswith('| json') @@ -589,3 +619,13 @@ def get_interface_type(interface): return 'nve' else: return 'unknown' + + +def read_module_context(module): + conn = get_connection(module) + return conn.read_module_context(module._name) + + +def save_module_context(module, module_context): + conn = get_connection(module) + return conn.save_module_context(module._name, module_context) diff --git a/lib/ansible/modules/network/nxos/nxos_logging.py b/lib/ansible/modules/network/nxos/nxos_logging.py index c458b7245e..0ac3d812e1 100644 --- a/lib/ansible/modules/network/nxos/nxos_logging.py +++ b/lib/ansible/modules/network/nxos/nxos_logging.py @@ -69,6 +69,38 @@ options: - State of the logging configuration. default: present choices: ['present', 'absent'] + event: + description: + - Link/trunk enable/default interface configuration logging + choices: ['link-enable', 'link-default', 'trunk-enable', 'trunk-default'] + version_added: '2.8' + message: + description: + - Add interface description to interface syslogs. + Does not work with version 6.0 images using nxapi as a transport. + choices: ['add-interface-description'] + version_added: '2.8' + file_size: + description: + - Set logfile size + version_added: '2.8' + facility_link_status: + description: + - Set logging facility ethpm link status. + Not idempotent with version 6.0 images. + choices: ['link-down-notif', 'link-down-error', 'link-up-notif', 'link-up-error'] + version_added: '2.8' + timestamp: + description: + - Set logging timestamp format + choices: ['microseconds', 'milliseconds', 'seconds'] + version_added: '2.8' + purge: + description: + - Remove any switch logging configuration that does not match what has been configured + type: bool + default: no + version_added: '2.8' extends_documentation_fragment: nxos """ @@ -89,6 +121,12 @@ EXAMPLES = """ name: testfile dest_level: 3 state: present +- name: Configure logging logfile with size + nxos_logging: + dest: logfile + name: testfile + dest_level: 3 + file_size: 16384 - name: configure facility level logging nxos_logging: facility: daemon @@ -111,7 +149,26 @@ EXAMPLES = """ nxos_logging: interface: mgmt0 state: present - +- name: Purge nxos_logging configuration not managed by this playbook + nxos_logging: + purge: true +- name: Configure logging timestamp + nxos_logging: + timestamp: milliseconds + state: present +- name: Configure logging facility ethpm link status + nxos_logging: + facility: ethpm + facility_link_status: link-up-notif + state: present +- name: Configure logging message ethernet description + nxos_logging: + message: add-interface-description + state: present +- name: Configure logging event link enable + nxos_logging: + event: link-enable + state: present - name: Configure logging using aggregate nxos_logging: aggregate: @@ -133,11 +190,33 @@ commands: """ import re +import copy -from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands +from ansible.module_utils.network.nxos.nxos import get_config, load_config, run_commands, save_module_context, read_module_context from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args, normalize_interface from ansible.module_utils.basic import AnsibleModule +STATIC_CLI = {'link-enable': 'logging event link-status enable', + 'link-default': 'logging event link-status default', + 'trunk-enable': 'logging event trunk-status enable', + 'trunk-default': 'logging event trunk-status default', + 'microseconds': 'logging timestamp microseconds', + 'milliseconds': 'logging timestamp milliseconds', + 'seconds': 'logging timestamp seconds', + 'link-up-error': 'link-up error', + 'link-up-notif': 'link-up notif', + 'link-down-error': 'link-down error', + 'link-down-notif': 'link-down notif', + 'add-interface-description': 'logging message interface type ethernet description'} + +DEFAULT_LOGGING_LEVEL = {0: [], + 1: [], + 2: [], + 3: ['adjmgr', 'arp', 'icmpv6', 'l2rib', 'netstack'], + 4: [], + 5: [], + 6: [], + 7: []} DEST_GROUP = ['console', 'logfile', 'module', 'monitor', 'server'] @@ -152,8 +231,11 @@ def map_obj_to_commands(updates): if state == 'absent' and w in have: if w['facility'] is not None: - if not w['dest']: - commands.append('no logging level {}'.format(w['facility'])) + if not w['dest'] and not w['facility_link_status'] and w['facility'] not in DEFAULT_LOGGING_LEVEL[int(w['facility_level'])]: + commands.append('no logging level {} {}'.format(w['facility'], w['facility_level'])) + + if w['facility_link_status'] and w['facility'] in ('ethpm'): + commands.append('no logging level {} {}'.format(w['facility'], STATIC_CLI[w['facility_link_status']])) if w['name'] is not None: commands.append('no logging logfile') @@ -167,6 +249,15 @@ def map_obj_to_commands(updates): if w['interface']: commands.append('no logging source-interface') + if w['event'] and w['event'] in STATIC_CLI: + commands.append('no ' + STATIC_CLI[w['event']]) + + if w['message'] and w['message'] in STATIC_CLI: + commands.append('no ' + STATIC_CLI[w['message']]) + + if w['timestamp'] and w['timestamp'] in STATIC_CLI: + commands.append('no ' + STATIC_CLI[w['timestamp']]) + if state == 'present' and w not in have: if w['facility'] is None: if w['dest']: @@ -174,7 +265,12 @@ def map_obj_to_commands(updates): commands.append('logging {} {}'.format(w['dest'], w['dest_level'])) elif w['dest'] == 'logfile': - commands.append('logging logfile {} {}'.format(w['name'], w['dest_level'])) + if w['file_size']: + commands.append('logging logfile {} {} size {}'.format( + w['name'], w['dest_level'], w['file_size'])) + else: + commands.append('logging logfile {} {}'.format( + w['name'], w['dest_level'])) elif w['dest'] == 'server': if w['facility_level']: @@ -209,12 +305,25 @@ def map_obj_to_commands(updates): 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['facility_link_status']: + commands.append('logging level {} {}'.format( + w['facility'], STATIC_CLI[w['facility_link_status']])) + else: + commands.append('logging level {} {}'.format(w['facility'], + w['facility_level'])) if w['interface']: commands.append('logging source-interface {0} {1}'.format(*split_interface(w['interface']))) + if w['event'] and w['event'] in STATIC_CLI: + commands.append(STATIC_CLI[w['event']]) + + if w['message'] and w['message'] in STATIC_CLI: + commands.append(STATIC_CLI[w['message']]) + + if w['timestamp'] and w['timestamp'] in STATIC_CLI: + commands.append(STATIC_CLI[w['timestamp']]) + return commands @@ -224,6 +333,75 @@ def split_interface(interface): return match.group(1), match.group(2) +def parse_facility_link_status(line, facility, status): + facility_link_status = None + + if facility is not None: + match = re.search(r'logging level {} {} (\S+)'.format(facility, status), line, re.M) + if match: + facility_link_status = status + "-" + match.group(1) + + return facility_link_status + + +def parse_event_status(line, event): + status = None + + match = re.search(r'logging event {} (\S+)'.format(event + '-status'), line, re.M) + if match: + state = match.group(1) + if state: + status = state + + return status + + +def parse_event(line): + event = None + + match = re.search(r'logging event (\S+)', line, re.M) + if match: + state = match.group(1) + if state == 'link-status': + event = 'link' + elif state == 'trunk-status': + event = 'trunk' + + return event + + +def parse_message(line): + message = None + + match = re.search(r'logging message interface type ethernet description', line, re.M) + if match: + message = 'add-interface-description' + + return message + + +def parse_file_size(line, name, level): + file_size = None + + match = re.search(r'logging logfile {} {} size (\S+)'.format(name, level), line, re.M) + if match: + file_size = match.group(1) + if file_size == '8192': + file_size = None + + return file_size + + +def parse_timestamp(line): + timestamp = None + + match = re.search(r'logging timestamp (\S+)', line, re.M) + if match: + timestamp = match.group(1) + + return timestamp + + def parse_name(line, dest): name = None @@ -329,36 +507,76 @@ def parse_interface(line): def map_config_to_obj(module): obj = [] - data = get_config(module, flags=['| section logging']) + data = get_config(module, flags=[' all | section logging']) for line in data.split('\n'): - match = re.search(r'logging (\S+)', line, re.M) + if re.search(r'no (\S+)', line, re.M): + state = 'absent' + else: + state = 'present' + + match = re.search(r'logging (\S+)', line, re.M) + if state == 'present' and match: + event_status = None + name = None + dest_level = None + dest = None + facility = None + remote_server = None + facility_link_status = None + file_size = None + facility_level = None - if match: if match.group(1) in DEST_GROUP: dest = match.group(1) - facility = None + + name = parse_name(line, dest) + remote_server = parse_remote_server(line, dest) + dest_level = parse_dest_level(line, dest, name) if dest == 'server': facility = parse_facility(line) + facility_level = parse_facility_level(line, facility, dest) + + if dest == 'logfile': + file_size = parse_file_size(line, name, dest_level) + elif match.group(1) == 'level': match_facility = re.search(r'logging level (\S+)', line, re.M) facility = match_facility.group(1) - dest = None + + level = parse_facility_level(line, facility, dest) + if level.isdigit(): + facility_level = level + else: + facility_link_status = parse_facility_link_status(line, facility, level) + + elif match.group(1) == 'event' and state == 'present': + event = parse_event(line) + if event: + status = parse_event_status(line, event) + if status: + event_status = event + '-' + status + else: + continue else: - dest = None - facility = None + pass obj.append({'dest': dest, - 'remote_server': parse_remote_server(line, dest), + 'remote_server': remote_server, 'use_vrf': parse_use_vrf(line, dest), - 'name': parse_name(line, dest), + 'name': name, 'facility': facility, - 'dest_level': parse_dest_level(line, dest, parse_name(line, dest)), - 'facility_level': parse_facility_level(line, facility, dest), - 'interface': parse_interface(line)}) + 'dest_level': dest_level, + 'facility_level': facility_level, + 'interface': parse_interface(line), + 'facility_link_status': facility_link_status, + 'event': event_status, + 'file_size': file_size, + 'message': parse_message(line), + 'timestamp': parse_timestamp(line)}) cmd = [{'command': 'show logging | section enabled | section console', 'output': 'text'}, {'command': 'show logging | section enabled | section monitor', 'output': 'text'}] @@ -383,7 +601,12 @@ def map_config_to_obj(module): 'dest_level': dest_level, 'facility_level': None, 'use_vrf': None, - 'interface': None}) + 'interface': None, + 'facility_link_status': None, + 'event': None, + 'file_size': None, + 'message': None, + 'timestamp': None}) return obj @@ -399,7 +622,12 @@ def map_params_to_obj(module): 'facility': '', 'dest_level': '', 'facility_level': '', - 'interface': ''} + 'interface': '', + 'facility_link_status': None, + 'event': None, + 'file_size': None, + 'message': None, + 'timestamp': None} for c in module.params['aggregate']: d = c.copy() @@ -420,11 +648,15 @@ def map_params_to_obj(module): if 'state' not in d: d['state'] = module.params['state'] + if d['file_size']: + d['file_size'] = str(d['file_size']) + obj.append(d) else: dest_level = None facility_level = None + file_size = None if module.params['dest_level'] is not None: dest_level = str(module.params['dest_level']) @@ -432,6 +664,9 @@ def map_params_to_obj(module): if module.params['facility_level'] is not None: facility_level = str(module.params['facility_level']) + if module.params['file_size'] is not None: + file_size = str(module.params['file_size']) + obj.append({ 'dest': module.params['dest'], 'remote_server': module.params['remote_server'], @@ -441,11 +676,44 @@ def map_params_to_obj(module): 'dest_level': dest_level, 'facility_level': facility_level, 'interface': normalize_interface(module.params['interface']), - 'state': module.params['state'] + 'state': module.params['state'], + 'facility_link_status': module.params['facility_link_status'], + 'event': module.params['event'], + 'message': module.params['message'], + 'file_size': file_size, + 'timestamp': module.params['timestamp'] }) return obj +def merge_wants(wants, want): + if not wants: + wants = list() + + for w in want: + w = copy.copy(w) + state = w['state'] + del w['state'] + + if state == 'absent': + if w in wants: + wants.remove(w) + elif w not in wants: + wants.append(w) + + return wants + + +def absent(h): + h['state'] = 'absent' + return h + + +def outliers(haves, wants): + wants = list(wants) + return [absent(h) for h in haves if not (h in wants or wants.append(h))] + + def main(): """ main entry point for module execution """ @@ -458,8 +726,14 @@ def main(): dest_level=dict(type='int', aliases=['level']), facility_level=dict(type='int'), interface=dict(), + facility_link_status=dict(choices=['link-down-notif', 'link-down-error', 'link-up-notif', 'link-up-error']), + event=dict(choices=['link-enable', 'link-default', 'trunk-enable', 'trunk-default']), + message=dict(choices=['add-interface-description']), + file_size=dict(type='int'), + timestamp=dict(choices=['microseconds', 'milliseconds', 'seconds']), state=dict(default='present', choices=['present', 'absent']), - aggregate=dict(type='list') + aggregate=dict(type='list'), + purge=dict(default=False, type='bool') ) argument_spec.update(nxos_argument_spec) @@ -479,6 +753,7 @@ def main(): result['warnings'] = warnings want = map_params_to_obj(module) + merged_wants = merge_wants(read_module_context(module), want) have = map_config_to_obj(module) commands = map_obj_to_commands((want, have)) @@ -489,6 +764,16 @@ def main(): load_config(module, commands) result['changed'] = True + save_module_context(module, merged_wants) + + if module.params.get('purge'): + pcommands = map_obj_to_commands((outliers(have, merged_wants), have)) + if pcommands: + if not module.check_mode: + load_config(module, pcommands) + result['changed'] = True + result['commands'] += pcommands + module.exit_json(**result) diff --git a/lib/ansible/plugins/cliconf/nxos.py b/lib/ansible/plugins/cliconf/nxos.py index e7678e438d..9bb17cec9f 100644 --- a/lib/ansible/plugins/cliconf/nxos.py +++ b/lib/ansible/plugins/cliconf/nxos.py @@ -36,8 +36,20 @@ from ansible.plugins.connection.httpapi import Connection as HttpApi class Cliconf(CliconfBase): def __init__(self, *args, **kwargs): + self._module_context = {} super(Cliconf, self).__init__(*args, **kwargs) + def read_module_context(self, module_key): + if self._module_context.get(module_key): + return self._module_context[module_key] + + return None + + def save_module_context(self, module_key, module_context): + self._module_context[module_key] = module_context + + return None + def send_command(self, command, **kwargs): """Executes a cli command and returns the results This method will execute the CLI command on the connection and return diff --git a/lib/ansible/plugins/terminal/nxos.py b/lib/ansible/plugins/terminal/nxos.py index d6d183c44e..b31927b4cd 100644 --- a/lib/ansible/plugins/terminal/nxos.py +++ b/lib/ansible/plugins/terminal/nxos.py @@ -48,7 +48,7 @@ class TerminalModule(TerminalBase): re.compile(br"unknown command"), re.compile(br"user not present"), re.compile(br"invalid (.+?)at '\^' marker", re.I), - re.compile(br"baud rate of console should be (\d*) to increase severity level", re.I), + re.compile(br"[B|b]aud rate of console should be.* (\d*) to increase [a-z]* level", re.I), ] def on_become(self, passwd=None): diff --git a/test/integration/targets/nxos_logging/tests/common/basic.yaml b/test/integration/targets/nxos_logging/tests/common/basic.yaml index 1e60b4e0ea..4c0640fddd 100644 --- a/test/integration/targets/nxos_logging/tests/common/basic.yaml +++ b/test/integration/targets/nxos_logging/tests/common/basic.yaml @@ -78,25 +78,29 @@ - 'result.changed == true' - '"logging console 3" in result.commands' -- name: Logfile logging with level - nxos_logging: &llog - dest: logfile - name: test - dest_level: 1 - provider: "{{ connection }}" - state: present - register: result +- block: + - name: Logfile logging with level + nxos_logging: &llog + dest: logfile + name: test + dest_level: 1 + provider: "{{ connection }}" + state: present + register: result -- assert: - that: - - 'result.changed == true' - - '"logging logfile test 1" in result.commands' + - assert: + that: + - 'result.changed == true' + - '"logging logfile test 1" in result.commands' + -- name: Logfile logging with level (idempotent) - nxos_logging: *llog - register: result + - name: Logfile logging with level (idempotent) + nxos_logging: *llog + register: result -- assert: *false + - assert: *false + + when: platform is not search('N5K|N7K') and imagetag is not search("A8") - name: Configure module with level nxos_logging: &molog @@ -228,11 +232,22 @@ that: - 'result.changed == true' - '"no logging logfile" in result.commands' - - '"no logging level daemon" in result.commands' + - '"no logging level daemon 4" in result.commands' - '"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' + when: platform is not search('N5K|N7K') and imagetag is not search("A8") + +- assert: + that: + - 'result.changed == true' + - '"no logging level daemon 4" in result.commands' + - '"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' + when: platform is search('N5K|N7K') or imagetag is search("A8") - name: remove aggregate logging (idempotent) nxos_logging: *agg @@ -240,4 +255,260 @@ - assert: *false +- block: + - name: Configure Logging message + nxos_logging: &logm + message: add-interface-description + state: present + provider: "{{ connection }}" + register: result + + - assert: &true + that: + - 'result.changed == true' + + - name: Configure Logging message (idempotent) + nxos_logging: *logm + register: result + + - assert: *false + + - name: Remove Logging message + nxos_logging: + message: add-interface-description + state: absent + provider: "{{ connection }}" + register: result + + - assert: *true + when: platform is not search('N5K') and imagetag is not search("A8") + + +- name: Logfile logging with level and size + nxos_logging: &llogs + dest: logfile + name: test + dest_level: 1 + file_size: 16384 + provider: "{{ connection }}" + state: present + register: result + +- assert: + that: + - 'result.changed == true' + - '"logging logfile test 1 size 16384" in result.commands' + +- name: Logfile logging with level and size (idempotent) + nxos_logging: *llogs + register: result + +- assert: *false + +- name: Remove logfile logging with level and size + nxos_logging: + dest: logfile + name: test + dest_level: 1 + file_size: 16384 + provider: "{{ connection }}" + state: absent + register: result + +- assert: *true + +- name: Set up logging event link enable + nxos_logging: &logle + event: link-enable + register: result + +- assert: + that: + - 'result.changed == true' + - '"logging event link-status enable" in result.commands' + +- name: Set up logging event link enable again (idempotent) + nxos_logging: *logle + register: result + +- assert: *false + +- name: Remove logging event link enable + nxos_logging: &rlogle + event: link-enable + state: absent + register: result + +- assert: *true + +- name: Remove logging event link enable again (idempotent) + nxos_logging: *rlogle + register: result + +- assert: *false + +- name: Set up logging event link default + nxos_logging: &logld + event: link-default + register: result + +- assert: + that: + - 'result.changed == true' + - '"logging event link-status default" in result.commands' + +- name: Set up logging event link default again (idempotent) + nxos_logging: *logld + register: result + +- assert: *false + +- name: Remove logging event link default + nxos_logging: &rlogld + event: link-default + state: absent + register: result + +- assert: *true + +- name: Remove logging event link default again (idempotent) + nxos_logging: *rlogld + register: result + +- assert: *false + +- name: Set up logging event trunk enable + nxos_logging: &logte + event: trunk-enable + register: result + +- assert: + that: + - 'result.changed == true' + - '"logging event trunk-status enable" in result.commands' + +- name: Set up logging event trunk enable again (idempotent) + nxos_logging: *logte + register: result + +- assert: *false + +- name: Remove logging event trunk enable + nxos_logging: &rlogte + event: trunk-enable + state: absent + register: result + +- assert: *true + +- name: Remove logging event trunk enable again (idempotent) + nxos_logging: *rlogte + register: result + +- assert: *false +- name: Set up logging event trunk default + nxos_logging: &logtd + event: trunk-default + register: result + +- assert: + that: + - 'result.changed == true' + - '"logging event trunk-status default" in result.commands' + +- name: Set up logging event trunk default again (idempotent) + nxos_logging: *logtd + register: result + +- assert: *false + +- name: Remove logging event trunk default + nxos_logging: &rlogtd + event: trunk-default + state: absent + register: result + +- assert: *true + +- name: Remove logging event trunk default again (idempotent) + nxos_logging: *rlogtd + register: result + +- assert: *false + +- name: Set up Logging Timestamp + nxos_logging: <ms + timestamp: microseconds + provider: "{{ connection }}" + state: present + register: result + +- assert: *true + +- name: Set up Logging Timestamp (idempotent) + nxos_logging: *ltms + register: result + +- assert: *false + +- name: Remove Logging Timestamp + nxos_logging: + timestamp: microseconds + state: absent + register: result + +- assert: *true + +- name: Set up Facility ethpm Link UP Error + + nxos_logging: &felue + facility: ethpm + facility_link_status: link-up-error + provider: "{{ connection }}" + state: present + register: result + +- assert: *true + +- name: Set up Facility ethpm Link UP Error (idempotent) + nxos_logging: *felue + register: result + +- assert: *false + +- name: Remove Facility ethpm Link UP Error + nxos_logging: + facility: ethpm + facility_link_status: link-up-error + state: absent + register: result + +- assert: *true + +- name: Set up Facility ethpm Link DOWN Error + + nxos_logging: &felde + facility: ethpm + facility_link_status: link-down-error + provider: "{{ connection }}" + state: present + register: result + +- assert: *true + +- name: Set up Facility ethpm Link DOWN Error (idempotent) + nxos_logging: *felde + register: result + +- assert: *false + +- name: Remove Facility ethpm Link DOWN Error + nxos_logging: + facility: ethpm + facility_link_status: link-down-error + state: absent + register: result + +- assert: *true + - debug: msg="END connection={{ ansible_connection }} nxos_logging basic test" diff --git a/test/integration/targets/nxos_logging/tests/common/purge.yaml b/test/integration/targets/nxos_logging/tests/common/purge.yaml new file mode 100644 index 0000000000..bae8cecd2d --- /dev/null +++ b/test/integration/targets/nxos_logging/tests/common/purge.yaml @@ -0,0 +1,109 @@ +--- +- debug: msg="START connection={{ ansible_connection }} nxos_logging purge test" + +- block: + + - name: Set up console logging + nxos_logging: &clog + dest: console + dest_level: 0 + provider: "{{ connection }}" + state: present + register: result + + - assert: + that: + - 'result.changed == true' + - '"logging console 0" in result.commands' + + - name: Set up Logging Timestamp + nxos_logging: <ms + timestamp: microseconds + provider: "{{ connection }}" + state: present + register: result + + - assert: + that: + - 'result.changed == true' + - '"logging timestamp microseconds" in result.commands' + + - name: Configure monitor with level + nxos_logging: &mlog + dest: monitor + dest_level: 3 + provider: "{{ connection }}" + register: result + + - assert: + that: + - 'result.changed == true' + - '"logging monitor 3" in result.commands' + + - name: Configure facility with level + nxos_logging: &flog + facility: daemon + facility_level: 4 + provider: "{{ connection }}" + register: result + + - assert: + that: + - 'result.changed == true' + - '"logging level daemon 4" in result.commands' + + - name: Configure logging level virtual-service 7 using nxos_config + nxos_config: + lines: logging level virtual-service 7 + provider: "{{ connection }}" + register: result + + - assert: + that: + - "result.changed == true" + + - name: Purge the outliers + nxos_logging: + purge: yes + provider: "{{ connection }}" + register: result + + - assert: + that: + - 'result.changed == true' + - '"no logging level virtual-service 7" in result.commands' + + - block: + - name: Purge the outliers (idempotent) + nxos_logging: + purge: yes + provider: "{{ connection }}" + register: result + + - assert: + that: + - 'result.changed == false' + when: imagetag is not search("A8") + + - name: remove logging as collection tearDown + nxos_logging: &agg + aggregate: + - { dest: console, dest_level: 0 } + - { dest: monitor, dest_level: 3 } + - { timestamp: microseconds } + - { facility: daemon, facility_level: 4 } + provider: "{{ connection }}" + state: absent + register: result + + - assert: + that: + - 'result.changed == true' + - '"no logging console" in result.commands' + - '"no logging timestamp microseconds" in result.commands' + - '"no logging level daemon 4" in result.commands' + - '"no logging monitor" in result.commands' + + when: ansible_connection != "local" + +- debug: msg="END connection={{ ansible_connection }} nxos_logging purge test"