icx: new module icx_logging (#60036)

* new module

* new terminal

* new terminal

* new cliconf

* cliconf

* icx cliconf

* icx_cliconf

* icx test units module

* icx units module

* icx banner unit test

* PR changes resolved

* changes resolved

* Changes Resolved

* check_running_config changes resolved

* added notes

* removed icx rst

* new changes

* deleted icx rst

* icx .rst

* modified platform_index.rst

* modified platform_index.rst

* changes resolved

* PR comments resolved

* Update platform_index.rst

PR comment resolved

* test

* Revert "test"

This reverts commit 99b72c6614.

* new module icx_logging

* new fixes

* new fix

* new fix

* new fix

* new changes

* notes updated
This commit is contained in:
sushma-alethea 2019-08-27 08:58:33 +05:30 committed by Ganesh Nalawade
parent b6e71c5ffb
commit b6290e1d15
3 changed files with 748 additions and 0 deletions

View file

@ -0,0 +1,582 @@
#!/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
version_added: "2.9"
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 overriden, 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 overriden, 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.module_utils.network.common.utils import remove_default_spec, validate_ip_v6_address
from ansible.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(
typr='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()

View file

@ -0,0 +1,17 @@
logging host 172.16.10.21
logging host 172.16.10.21 udp-port 2000
logging host 172.16.10.22
logging host 172.16.10.23 udp-port 2500
logging host 172.16.10.55 udp-port 2500
logging facility local1
logging host ipv6 2001:db8::1 udp-port 5500
logging buffered 200
no logging buffered critical
no logging buffered debugging
no logging buffered emergencies
no logging buffered errors
no logging buffered informational
no logging buffered notifications
logging enable rfc5424
logging console
logging persistence

View file

@ -0,0 +1,149 @@
# Copyright: (c) 2019, 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
import json
from units.compat.mock import patch
from ansible.modules.network.icx import icx_logging
from units.modules.utils import set_module_args
from .icx_module import TestICXModule, load_fixture
class TestICXLoggingModule(TestICXModule):
module = icx_logging
def setUp(self):
super(TestICXLoggingModule, self).setUp()
self.mock_get_config = patch('ansible.modules.network.icx.icx_logging.get_config')
self.get_config = self.mock_get_config.start()
self.mock_load_config = patch('ansible.modules.network.icx.icx_logging.load_config')
self.load_config = self.mock_load_config.start()
self.mock_exec_command = patch('ansible.modules.network.icx.icx_logging.exec_command')
self.exec_command = self.mock_exec_command.start()
self.set_running_config()
def tearDown(self):
super(TestICXLoggingModule, self).tearDown()
self.mock_get_config.stop()
self.mock_load_config.stop()
self.mock_exec_command.stop()
def load_fixtures(self, commands=None):
compares = None
def load_file(*args, **kwargs):
module = args
for arg in args:
if arg.params['check_running_config'] is True:
return load_fixture('icx_logging_config.cfg').strip()
else:
return ''
self.get_config.side_effect = load_file
self.load_config.return_value = None
def test_icx_logging_set_host(self):
set_module_args(dict(dest='host', name='172.16.10.15'))
if not self.ENV_ICX_USE_DIFF:
commands = ['logging host 172.16.10.15']
self.execute_module(changed=True, commands=commands)
else:
commands = ['logging host 172.16.10.15']
self.execute_module(changed=True, commands=commands)
def test_icx_logging_set_ipv6_host(self):
set_module_args(dict(dest='host', name='2001:db8::1'))
if not self.ENV_ICX_USE_DIFF:
commands = ['logging host 2001:db8::1']
else:
commands = ['logging host 2001:db8::1']
def test_icx_logging_set_host_udp_port(self):
set_module_args(dict(dest='host', name='172.16.10.15', udp_port=2500))
if not self.ENV_ICX_USE_DIFF:
commands = ['logging host 172.16.10.15 udp-port 2500']
self.execute_module(changed=True, commands=commands)
else:
commands = ['logging host 172.16.10.15 udp-port 2500']
self.execute_module(changed=True, commands=commands)
def test_icx_logging_remove_console(self):
set_module_args(dict(dest='console', state='absent'))
if not self.ENV_ICX_USE_DIFF:
commands = ['no logging console']
self.execute_module(changed=True, commands=commands)
else:
commands = ['no logging console']
self.execute_module(changed=True, commands=commands)
def test_icx_logging_remove_on(self):
set_module_args(dict(dest='on', state='absent'))
if not self.ENV_ICX_USE_DIFF:
commands = ['no logging on']
self.exec_command(changed=True, commands=commands)
else:
commands = ['no logging on']
self.exec_command(changed=True, commands=commands)
def test_icx_logging_set_aggregate(self):
aggregate = [
dict(dest='host', name='172.16.10.16', udp_port=2500, facility='local0'),
dict(dest='host', name='2001:db8::1', udp_port=5000)
]
set_module_args(dict(aggregate=aggregate, state='present'))
if not self.ENV_ICX_USE_DIFF:
result = self.execute_module(changed=True)
expected_commands = [
'logging facility local0',
'logging host 172.16.10.16 udp-port 2500',
'logging host ipv6 2001:db8::1 udp-port 5000'
]
self.assertEqual(result['commands'], expected_commands)
else:
result = self.execute_module(changed=True)
expected_commands = [
'logging facility local0',
'logging host 172.16.10.16 udp-port 2500',
'logging host ipv6 2001:db8::1 udp-port 5000'
]
self.assertEqual(result['commands'], expected_commands)
def test_icx_logging_set_aggregate_remove(self):
aggregate = [
dict(dest='host', name='172.16.10.55', udp_port=2500, facility='local0'),
dict(dest='host', name='2001:db8::1', udp_port=5500)
]
set_module_args(dict(aggregate=aggregate, state='absent'))
if not self.ENV_ICX_USE_DIFF:
result = self.execute_module(changed=True)
expected_commands = [
'no logging facility',
'no logging host 172.16.10.55 udp-port 2500',
'no logging host ipv6 2001:db8::1 udp-port 5500'
]
self.assertEqual(result['commands'], expected_commands)
else:
result = self.execute_module(changed=True)
expected_commands = [
'no logging facility',
'no logging host 172.16.10.55 udp-port 2500',
'no logging host ipv6 2001:db8::1 udp-port 5500'
]
self.assertEqual(result['commands'], expected_commands)
def test_icx_logging_compare(self):
set_module_args(dict(dest='host', name='172.16.10.21', check_running_config=True))
if self.get_running_config(compare=True):
if not self.ENV_ICX_USE_DIFF:
self.execute_module(changed=False)
else:
self.execute_module(changed=False)