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:
parent
a54e77193b
commit
33338decf7
3 changed files with 400 additions and 0 deletions
252
lib/ansible/modules/network/onyx/onyx_syslog_files.py
Normal file
252
lib/ansible/modules/network/onyx/onyx_syslog_files.py
Normal 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()
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
113
test/units/modules/network/onyx/test_onyx_syslog_files.py
Normal file
113
test/units/modules/network/onyx/test_onyx_syslog_files.py
Normal 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)
|
Loading…
Reference in a new issue