Feature/onyx syslog files (#62746)

* support syslog files

* nested doc fix

* required togother

* fix docs type dict

* add file-debug feature same as files logging

* remove duplicated validation and change dict key access method

* add format of upload url and change the way of getting data from dict
This commit is contained in:
anas-shami 2019-12-17 07:54:33 +02:00 committed by Ganesh Nalawade
parent a54e77193b
commit 33338decf7
3 changed files with 400 additions and 0 deletions

View file

@ -0,0 +1,252 @@
#!/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: onyx_syslog_files
version_added: "2.10"
author: "Anas Shami (@anass)"
short_description: Configure file management syslog module
description:
- This module provides declarative management of syslog
on Mellanox ONYX network devices.
notes:
options:
debug:
description:
- Configure settings for debug log files
type: bool
default: False
delete_group:
description:
- Delete certain log files
choices: ['current', 'oldest']
type: str
rotation:
description:
- rotation related attributes
type: dict
suboptions:
frequency:
description:
- Rotate log files on a fixed time-based schedule
choices: ['daily', 'weekly', 'monthly']
type: str
force:
description:
- force an immediate rotation of log files
type: bool
max_num:
description:
- Sepcify max_num of old log files to keep
type: int
size:
description:
- Rotate files when they pass max size
type: float
size_pct:
description:
- Rotatoe files when they pass percent of HD
type: float
upload_url:
description:
- upload local log files to remote host (ftp, scp, sftp, tftp) with format protocol://username[:password]@server/path
type: str
upload_file:
description:
- Upload compressed log file (current or filename)
type: str
"""
EXAMPLES = """
- name: syslog delete old files
- onyx_syslog_files:
delete_group: oldest
- name: syslog upload file
- onyx_syslog_files:
upload_url: scp://username:password@hostnamepath/filename
upload_file: current
- name: syslog rotation force, frequency and max number
- onyx_syslog_files:
rotation:
force: true
max_num: 30
frequency: daily
size: 128
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device.
returned: always
type: list
sample:
- logging files delete current
- logging files rotate criteria
- logging files upload current url
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.onyx.onyx import show_cmd
from ansible.module_utils.network.onyx.onyx import BaseOnyxModule
class OnyxSyslogFilesModule(BaseOnyxModule):
MAX_FILES = 999999
URL_REGEX = re.compile(
r'^(ftp|scp|ftps):\/\/[a-z0-9\.]*:(.*)@(.*):([a-zA-Z\/\/])*$')
FREQUANCIES = ['daily', 'weekly', 'monthly']
ROTATION_KEYS = ['frequency', 'max_num', 'size', 'size_pct', 'force']
ROTATION_CMDS = {'size': 'logging {0} rotation criteria size {1}',
'frequency': 'logging {0} rotation criteria frequency {1}',
'max_num': 'logging {0} rotation max-num {1}',
'size_pct': 'logging {0} rotation criteria size-pct {1}',
'force': 'logging {0} rotation force'}
def init_module(self):
"""" Ansible module initialization
"""
rotation_spec = dict(frequency=dict(choices=self.FREQUANCIES),
max_num=dict(type="int"),
force=dict(type="bool"),
size=dict(type="float"),
size_pct=dict(type="float"))
element_spec = dict(delete_group=dict(choices=['oldest', 'current']),
rotation=dict(type="dict", options=rotation_spec),
upload_file=dict(type="str"),
upload_url=dict(type="str"),
debug=dict(type="bool", default=False))
argument_spec = dict()
argument_spec.update(element_spec)
self._module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_together=[['upload_file', 'upload_url']])
def validate_rotation(self, rotation):
size_pct = rotation.get('size_pct', None)
max_num = rotation.get('max_num', None)
if size_pct is not None and (float(size_pct) < 0 or float(size_pct) > 100):
self._module.fail_json(
msg='logging size_pct must be in range 0-100')
elif max_num is not None and (int(max_num) < 0 or int(max_num) > self.MAX_FILES):
self._module.fail_json(
msg='logging max_num must be positive number less than {0}'.format(self.MAX_FILES))
def validate_upload_url(self, upload_url):
check = self.URL_REGEX.match(upload_url)
if upload_url and not check:
self._module.fail_json(
msg='Invalid url, make sure that you use "[ftp, scp, tftp, sftp]://username:password@hostname:/location" format')
def show_logging(self):
show_logging = show_cmd(self._module, "show logging", json_fmt=True, fail_on_error=False)
running_config = show_cmd(self._module, "show running-config | include .*logging.*debug-files.*", json_fmt=True, fail_on_error=False)
if len(show_logging) > 0:
show_logging[0]['debug'] = running_config['Lines'] if 'Lines' in running_config else []
else:
show_logging = [{
'debug': running_config['Lines'] if 'Lines' in running_config else []
}]
return show_logging
def load_current_config(self):
self._current_config = dict()
current_config = self.show_logging()[0]
freq = current_config.get('Log rotation frequency') # daily (Once per day at midnight)
size = current_config.get('Log rotation size threshold') # 19.07 megabytes or 10.000% of partition (987.84 megabytes)
max_num = current_config.get('Number of archived log files to keep')
if freq is not None:
freq_str = freq.split()[0]
self._current_config['frequency'] = freq_str
if size is not None:
size_arr = size.split(' ')
if '%' in size:
size_pct_value = size_arr[0].replace('%', '')
self._current_config['size_pct'] = float(size_pct_value)
size_value = re.sub(r'(\(|\)|megabytes)', '', size_arr[-2]).strip()
self._current_config['size'] = float(size_value)
else:
size_value = size_arr[0]
self._current_config['size'] = float(size_value)
if max_num is not None:
self._current_config['max_num'] = int(max_num)
'''debug params'''
for line in current_config['debug']:
if 'size' in line:
self._current_config['debug_size'] = float(line.split(' ')[-1])
elif 'frequency' in line:
self._current_config['debug_frequency'] = line.split(' ')[-1]
elif 'size-pct' in line:
self._current_config['debug_size_pct'] = float(line.split(' ')[-1])
elif 'max-num' in line:
self._current_config['debug_max_num'] = int(line.split(' ')[-1])
def get_required_config(self):
self._required_config = dict()
required_config = dict()
module_params = self._module.params
delete_group = module_params.get('delete_group')
upload_file = module_params.get('upload_file')
rotation = module_params.get('rotation')
if delete_group:
required_config['delete_group'] = delete_group
if upload_file:
required_config.update({'upload_file': upload_file,
'upload_url': module_params.get('upload_url')})
if rotation:
required_config['rotation'] = rotation
required_config['debug'] = module_params['debug']
self.validate_param_values(required_config)
self._required_config = required_config
def generate_commands(self):
required_config = self._required_config
current_config = self._current_config
logging_files_type = 'debug-files' if required_config['debug'] else 'files'
debug_prefix = 'debug_' if required_config['debug'] else ''
rotation = required_config.get('rotation')
if rotation:
for key in rotation:
if rotation.get(key) and current_config.get(debug_prefix + key) != rotation.get(key):
cmd = self.ROTATION_CMDS[key].format(logging_files_type, rotation[key]) if key != 'force' else\
self.ROTATION_CMDS[key].format(logging_files_type)
self._commands.append(cmd)
delete_group = required_config.get('delete_group')
if delete_group:
self._commands.append('logging {0} delete {1}'.format(logging_files_type,
delete_group))
upload_file = required_config.get('upload_file')
if upload_file:
self._commands.append('logging {0} upload {1} {2}'.format(logging_files_type,
upload_file, required_config.get('upload_url')))
def main():
""" main entry point for module execution
"""
OnyxSyslogFilesModule.main()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,35 @@
[
{
"Log format": "standard",
"Log rotation size threshold": "10.000% of partition (986.46 megabytes)",
"Allow receiving of messages from remote hosts": "yes",
"Override for class debug-module": "notice",
"Local logging level": "info",
"Number of archived log files to keep": "10",
"Default remote logging level": "alert",
"Subsecond timestamp field": "disabled",
"Log rotation frequency": "weekly",
"debug": [
"logging debug-files rotation criteria frequency daily",
"logging debug-files rotation criteria size 20",
"logging debug-files rotation max-num 20"
]
},
{
"Levels at which messages are logged": [
{
"CLI commands": "notice",
"Audit messages": "notice"
}
]
},
{
"Remote syslog servers": [
{
"Lines": [
"No remote syslog servers configured"
]
}
]
}
]

View file

@ -0,0 +1,113 @@
#
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from units.compat.mock import patch
from ansible.modules.network.onyx import onyx_syslog_files
from units.modules.utils import set_module_args
from .onyx_module import TestOnyxModule, load_fixture
class TestOnyxSyslogFilesModule(TestOnyxModule):
module = onyx_syslog_files
def setUp(self):
self.enabled = False
super(TestOnyxSyslogFilesModule, self).setUp()
self.mock_get_config = patch.object(
onyx_syslog_files.OnyxSyslogFilesModule, "show_logging")
self.get_config = self.mock_get_config.start()
self.mock_load_config = patch(
'ansible.module_utils.network.onyx.onyx.load_config')
self.load_config = self.mock_load_config.start()
def tearDown(self):
super(TestOnyxSyslogFilesModule, self).tearDown()
self.mock_get_config.stop()
self.mock_load_config.stop()
def load_fixtures(self, commands=None, transport='cli'):
config_file = 'onyx_logging_show.cfg'
self.get_config.return_value = load_fixture(config_file)
self.load_config.return_value = None
def test_syslog_files_force_rotate(self):
set_module_args(dict(rotation=dict(force=True)))
commands = ["logging files rotation force"]
self.execute_module(changed=True, commands=commands)
def test_syslog_files_max_num(self):
set_module_args(dict(rotation=dict(max_num=30)))
commands = ["logging files rotation max-num 30"]
self.execute_module(changed=True, commands=commands)
def test_syslog_files_freq(self):
set_module_args(dict(rotation=dict(frequency="daily")))
commands = ["logging files rotation criteria frequency daily"]
self.execute_module(changed=True, commands=commands)
def test_syslog_files_size(self):
set_module_args(dict(rotation=dict(size=10.5)))
commands = ["logging files rotation criteria size 10.5"]
self.execute_module(changed=True, commands=commands)
def test_syslog_files_delete(self):
set_module_args(dict(delete_group="oldest"))
commands = ["logging files delete oldest"]
self.execute_module(changed=True, commands=commands)
def test_syslog_debug_files_force_rotate(self):
set_module_args(dict(rotation=dict(force=True), debug=True))
commands = ["logging debug-files rotation force"]
self.execute_module(changed=True, commands=commands)
def test_syslog_debug_files_max_num(self):
set_module_args(dict(rotation=dict(max_num=30), debug=True))
commands = ["logging debug-files rotation max-num 30"]
self.execute_module(changed=True, commands=commands)
def test_syslog_debug_files_freq(self):
set_module_args(dict(rotation=dict(frequency="weekly"), debug=True))
commands = ["logging debug-files rotation criteria frequency weekly"]
self.execute_module(changed=True, commands=commands)
def test_syslog_debug_files_size(self):
set_module_args(dict(rotation=dict(size=10.5), debug=True))
commands = ["logging debug-files rotation criteria size 10.5"]
self.execute_module(changed=True, commands=commands)
def test_syslog_debug_files_delete(self):
set_module_args(dict(delete_group="oldest", debug=True))
commands = ["logging debug-files delete oldest"]
self.execute_module(changed=True, commands=commands)
''' nochange '''
def test_syslog_files_max_num_no_change(self):
set_module_args(dict(rotation=dict(max_num=10)))
self.execute_module(changed=False)
def test_syslog_files_freq_no_change(self):
set_module_args(dict(rotation=dict(frequency="weekly")))
self.execute_module(changed=False)
def test_syslog_files_size_no_change(self):
set_module_args(dict(rotation=dict(size_pct=10)))
self.execute_module(changed=False)
def test_syslog_debug_files_max_num_no_change(self):
set_module_args(dict(rotation=dict(max_num=20), debug=True))
self.execute_module(changed=False)
def test_syslog_debug_files_freq_no_change(self):
set_module_args(dict(rotation=dict(frequency="daily"), debug=True))
self.execute_module(changed=False)
def test_syslog_debug_files_size_no_change(self):
set_module_args(dict(rotation=dict(size=20), debug=True))
self.execute_module(changed=False)