1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00
community.general/plugins/modules/network/icx/icx_logging.py
Ansible Core Team aebc1b03fd Initial commit
2020-03-09 09:11:07 +00:00

581 lines
18 KiB
Python

#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: icx_logging
author: "Ruckus Wireless (@Commscope)"
short_description: Manage logging on Ruckus ICX 7000 series switches
description:
- This module provides declarative management of logging
on Ruckus ICX 7000 series switches.
notes:
- Tested against ICX 10.1.
- For information on using ICX platform, see L(the ICX OS Platform Options guide,../network/user_guide/platform_icx.html).
options:
dest:
description:
- Destination of the logs.
choices: ['on', 'host', 'console', 'buffered', 'persistence', 'rfc5424']
type: str
name:
description:
- ipv4 address/ipv6 address/name of syslog server.
type: str
udp_port:
description:
- UDP port of destination host(syslog server).
type: str
facility:
description:
- Specifies log facility to log messages from the device.
choices: ['auth','cron','daemon','kern','local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7', 'user',
'lpr','mail','news','syslog','sys9','sys10','sys11','sys12','sys13','sys14','user','uucp']
type: str
level:
description:
- Specifies the message level.
type: list
choices: ['alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational',
'notifications', 'warnings']
aggregate:
description:
- List of logging definitions.
type: list
suboptions:
dest:
description:
- Destination of the logs.
choices: ['on', 'host', 'console', 'buffered', 'persistence', 'rfc5424']
type: str
name:
description:
- ipv4 address/ipv6 address/name of syslog server.
type: str
udp_port:
description:
- UDP port of destination host(syslog server).
type: str
facility:
description:
- Specifies log facility to log messages from the device.
choices: ['auth','cron','daemon','kern','local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7', 'user',
'lpr','mail','news','syslog','sys9','sys10','sys11','sys12','sys13','sys14','user','uucp']
type: str
level:
description:
- Specifies the message level.
type: list
choices: ['alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational',
'notifications', 'warnings']
state:
description:
- State of the logging configuration.
choices: ['present', 'absent']
type: str
check_running_config:
description:
- Check running configuration. This can be set as environment variable.
Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter.
type: bool
state:
description:
- State of the logging configuration.
default: present
choices: ['present', 'absent']
type: str
check_running_config:
description:
- Check running configuration. This can be set as environment variable.
Module will use environment variable value(default:True), unless it is overridden, by specifying it as module parameter.
type: bool
default: yes
'''
EXAMPLES = """
- name: Configure host logging.
icx_logging:
dest: host
name: 172.16.0.1
udp_port: 5555
- name: Remove host logging configuration.
icx_logging:
dest: host
name: 172.16.0.1
udp_port: 5555
state: absent
- name: Disables the real-time display of syslog messages.
icx_logging:
dest: console
state: absent
- name: Enables local syslog logging.
icx_logging:
dest : on
state: present
- name: configure buffer level.
icx_logging:
dest: buffered
level: critical
- name: Configure logging using aggregate
icx_logging:
aggregate:
- { dest: buffered, level: ['notifications','errors'] }
- name: remove logging using aggregate
icx_logging:
aggregate:
- { dest: console }
- { dest: host, name: 172.16.0.1, udp_port: 5555 }
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- logging host 172.16.0.1
- logging console
"""
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule, env_fallback
from ansible.module_utils.connection import Connection, ConnectionError, exec_command
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import remove_default_spec, validate_ip_v6_address
from ansible_collections.community.general.plugins.module_utils.network.icx.icx import get_config, load_config
def search_obj_in_list(name, lst):
for o in lst:
if o['name'] == name:
return o
def diff_in_list(want, have):
adds = set()
removes = set()
for w in want:
if w['dest'] == 'buffered':
for h in have:
if h['dest'] == 'buffered':
adds = w['level'] - h['level']
removes = h['level'] - w['level']
return adds, removes
return adds, removes
def map_obj_to_commands(updates):
dest_group = ('host', 'console', 'persistence', 'enable')
commands = list()
want, have = updates
for w in want:
dest = w['dest']
name = w['name']
level = w['level']
state = w['state']
udp_port = w['udp_port']
facility = w['facility']
del w['state']
del w['facility']
facility_name = ''
facility_level = ''
if name is not None and validate_ip_v6_address(name):
name = 'ipv6 ' + name
if facility:
for item in have:
if item['dest'] == 'facility':
facility_name = item['dest']
facility_level = item['facility']
if state == 'absent':
if have == []:
if facility:
commands.append('no logging facility')
if dest == 'buffered':
for item in have:
if item['dest'] == 'buffered':
want_level = level
have_level = item['level']
for item in want_level:
commands.append('no logging buffered {0}'.format(item))
if dest == 'host':
if name and udp_port:
commands.append('no logging host {0} udp-port {1}'.format(name, udp_port))
elif name:
commands.append('no logging host {0}'.format(name))
else:
if dest == 'rfc5424':
commands.append('no logging enable {0}'.format(dest))
else:
if dest != 'buffered':
commands.append('no logging {0}'.format(dest))
if facility:
if facility_name == 'facility' and facility_level != 'user':
commands.append('no logging facility')
if dest == 'buffered':
for item in have:
if item['dest'] == 'buffered':
want_level = level
have_level = item['level']
for item in want_level:
if item in have_level:
commands.append('no logging buffered {0}'.format(item))
if w in have:
if dest == 'host':
if name and udp_port:
commands.append('no logging host {0} udp-port {1}'.format(name, udp_port))
elif name:
commands.append('no logging host {0}'.format(name))
else:
if dest == 'rfc5424':
commands.append('no logging enable {0}'.format(dest))
else:
if dest != 'buffered':
commands.append('no logging {0}'.format(dest))
if state == 'present':
if facility:
if facility != facility_level:
commands.append('logging facility {0}'.format(facility))
if w not in have:
if dest == 'host':
if name and udp_port:
commands.append('logging host {0} udp-port {1}'.format(name, udp_port))
elif name:
commands.append('logging host {0}'.format(name))
elif dest == 'buffered':
adds, removes = diff_in_list(want, have)
for item in adds:
commands.append('logging buffered {0}'.format(item))
for item in removes:
commands.append('no logging buffered {0}'.format(item))
elif dest == 'rfc5424':
commands.append('logging enable {0}'.format(dest))
else:
commands.append('logging {0}'.format(dest))
return commands
def parse_port(line, dest):
port = None
if dest == 'host':
match = re.search(r'logging host \S+\s+udp-port\s+(\d+)', line, re.M)
if match:
port = match.group(1)
else:
match_port = re.search(r'logging host ipv6 \S+\s+udp-port\s+(\d+)', line, re.M)
if match_port:
port = match_port.group(1)
return port
def parse_name(line, dest):
name = None
if dest == 'host':
match = re.search(r'logging host (\S+)', line, re.M)
if match:
if match.group(1) == 'ipv6':
ipv6_add = re.search(r'logging host ipv6 (\S+)', line, re.M)
name = ipv6_add.group(1)
else:
name = match.group(1)
return name
def parse_address(line, dest):
if dest == 'host':
match = re.search(r'^logging host ipv6 (\S+)', line.strip(), re.M)
if match:
return True
return False
def map_config_to_obj(module):
obj = []
facility = ''
addr6 = False
dest_group = ('host', 'console', 'buffered', 'persistence', 'enable')
dest_level = ('alerts', 'critical', 'debugging', 'emergencies', 'errors', 'informational', 'notifications', 'warnings')
buff_level = list()
if module.params['check_running_config'] is False:
return []
data = get_config(module, flags=['| include logging'])
facility_match = re.search(r'^logging facility (\S+)', data, re.M)
if facility_match:
facility = facility_match.group(1)
obj.append({
'dest': 'facility',
'facility': facility
})
else:
obj.append({
'dest': 'facility',
'facility': 'user'
})
for line in data.split('\n'):
match = re.search(r'^logging (\S+)', line.strip(), re.M)
if match:
if match.group(1) in dest_group:
dest = match.group(1)
if dest == 'host':
obj.append({
'dest': dest,
'name': parse_name(line.strip(), dest),
'udp_port': parse_port(line, dest),
'level': None,
'addr6': parse_address(line, dest)
})
elif dest == 'buffered':
obj.append({
'dest': dest,
'level': None,
'name': None,
'udp_port': None,
'addr6': False
})
else:
if dest == 'enable':
dest = 'rfc5424'
obj.append({
'dest': dest,
'level': None,
'name': None,
'udp_port': None,
'addr6': False
})
else:
ip_match = re.search(r'^no logging buffered (\S+)', line, re.M)
if ip_match:
dest = 'buffered'
buff_level.append(ip_match.group(1))
if 'no logging on' not in data:
obj.append({
'dest': 'on',
'level': None,
'name': None,
'udp_port': None,
'addr6': False
})
levels = set()
for items in dest_level:
if items not in buff_level:
levels.add(items)
obj.append({
'dest': 'buffered',
'level': levels,
'name': None,
'udp_port': None,
'addr6': False
})
return obj
def count_terms(check, param=None):
count = 0
for term in check:
if param[term] is not None:
count += 1
return count
def check_required_if(module, spec, param):
for sp in spec:
missing = []
max_missing_count = 0
is_one_of = False
if len(sp) == 4:
key, val, requirements, is_one_of = sp
else:
key, val, requirements = sp
if is_one_of:
max_missing_count = len(requirements)
term = 'any'
else:
term = 'all'
if key in param and param[key] == val:
for check in requirements:
count = count_terms((check,), param)
if count == 0:
missing.append(check)
if len(missing) and len(missing) >= max_missing_count:
msg = "%s is %s but %s of the following are missing: %s" % (key, val, term, ', '.join(missing))
module.fail_json(msg=msg)
def map_params_to_obj(module, required_if=None):
obj = []
addr6 = False
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
if item['name'] is not None and validate_ip_v6_address(item['name']):
addr6 = True
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
check_required_if(module, required_if, item)
item.update({'addr6': addr6})
d = item.copy()
d['level'] = set(d['level']) if d['level'] is not None else None
if d['dest'] != 'host':
d['name'] = None
d['udp_port'] = None
if d['dest'] != 'buffered':
d['level'] = None
del d['check_running_config']
obj.append(d)
else:
if module.params['name'] is not None and validate_ip_v6_address(module.params['name']):
addr6 = True
if module.params['dest'] != 'host':
module.params['name'] = None
module.params['udp_port'] = None
if module.params['dest'] != 'buffered':
module.params['level'] = None
obj.append({
'dest': module.params['dest'],
'name': module.params['name'],
'udp_port': module.params['udp_port'],
'level': set(module.params['level']) if module.params['level'] else None,
'facility': module.params['facility'],
'state': module.params['state'],
'addr6': addr6
})
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
dest=dict(
type='str',
choices=[
'on',
'host',
'console',
'buffered',
'persistence',
'rfc5424']),
name=dict(
type='str'),
udp_port=dict(),
level=dict(
type='list',
choices=[
'alerts',
'critical',
'debugging',
'emergencies',
'errors',
'informational',
'notifications',
'warnings']),
facility=dict(
type='str',
choices=[
'auth',
'cron',
'daemon',
'kern',
'local0',
'local1',
'local2',
'local3',
'local4',
'local5',
'local6',
'local7',
'user',
'lpr',
'mail',
'news',
'syslog',
'sys9',
'sys10',
'sys11',
'sys12',
'sys13',
'sys14',
'user',
'uucp']),
state=dict(
default='present',
choices=[
'present',
'absent']),
check_running_config=dict(default=True, type='bool', fallback=(env_fallback, ['ANSIBLE_CHECK_ICX_RUNNING_CONFIG'])))
aggregate_spec = deepcopy(element_spec)
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
)
argument_spec.update(element_spec)
required_if = [('dest', 'host', ['name']),
('dest', 'buffered', ['level'])]
module = AnsibleModule(argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True)
result = {'changed': False}
warnings = list()
exec_command(module, 'skip')
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module, required_if=required_if)
have = map_config_to_obj(module)
result['want'] = want
result['have'] = have
commands = map_obj_to_commands((want, have))
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()