Contributing lib/ansible/modules/network/cloudengine/ce_info_center_log.py module to manage HUAWEI data center CloudEngine (#22048)

* add ce_info_center_log

add ce_info_center_log

* fix review issues
This commit is contained in:
QijunPan 2017-05-26 00:28:06 +08:00 committed by John R Barker
parent 2e126edb91
commit 9aa58dd4ca

View file

@ -0,0 +1,555 @@
#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'status': ['preview'],
'supported_by': 'community',
'metadata_version': '1.0'}
DOCUMENTATION = """
---
module: ce_info_center_log
version_added: "2.4"
short_description: Manages information center log configuration on HUAWEI CloudEngine switches.
description:
- Setting the Timestamp Format of Logs.
Configuring the Device to Output Logs to the Log Buffer.
author: QijunPan (@CloudEngine-Ansible)
options:
log_time_stamp:
description:
- Sets the timestamp format of logs.
required: false
default: null
choices: ['date_boot', 'date_second', 'date_tenthsecond', 'date_millisecond',
'shortdate_second', 'shortdate_tenthsecond', 'shortdate_millisecond',
'formatdate_second', 'formatdate_tenthsecond', 'formatdate_millisecond']
log_buff_enable:
description:
- Enables the Switch to send logs to the log buffer.
required: false
default: no_use
choices: ['no_use','true', 'false']
log_buff_size:
description:
- Specifies the maximum number of logs in the log buffer.
The value is an integer that ranges from 0 to 10240. If logbuffer-size is 0, logs are not displayed.
required: false
default: null
module_name:
description:
- Specifies the name of a module.
The value is a module name in registration logs.
required: false
default: null
channel_id:
description:
- Specifies a channel ID.
The value is an integer ranging from 0 to 9.
required: false
default: null
log_enable:
description:
- Indicates whether log filtering is enabled.
required: false
default: no_use
choices: ['no_use','true', 'false']
log_level:
description:
- Specifies a log severity.
required: false
default: null
choices: ['emergencies', 'alert', 'critical', 'error',
'warning', 'notification', 'informational', 'debugging']
state:
description:
- Determines whether the config should be present or not
on the device.
required: false
default: present
choices: ['present', 'absent']
"""
EXAMPLES = '''
- name: CloudEngine info center log test
hosts: cloudengine
connection: local
gather_facts: no
vars:
cli:
host: "{{ inventory_hostname }}"
port: "{{ ansible_ssh_port }}"
username: "{{ username }}"
password: "{{ password }}"
transport: cli
tasks:
- name: "Setting the timestamp format of logs"
ce_info_center_log:
log_time_stamp: date_tenthsecond
provider: "{{ cli }}"
- name: "Enabled to output information to the log buffer"
ce_info_center_log:
log_buff_enable: true
provider: "{{ cli }}"
- name: "Set the maximum number of logs in the log buffer"
ce_info_center_log:
log_buff_size: 100
provider: "{{ cli }}"
- name: "Set a rule for outputting logs to a channel"
ce_info_center_log:
module_name: aaa
channel_id: 1
log_enable: true
log_level: critical
provider: "{{ cli }}"
'''
RETURN = '''
proposed:
description: k/v pairs of parameters passed into module
returned: verbose mode
type: dict
sample: {"log_time_stamp": "date_tenthsecond", "state": "present"}
existing:
description: k/v pairs of existing configuration
returned: verbose mode
type: dict
sample: {"log_time_stamp": "date_second"}
end_state:
description: k/v pairs of configuration after module execution
returned: verbose mode
type: dict
sample: {"log_time_stamp": "date_tenthsecond"}
updates:
description: commands sent to the device
returned: always
type: list
sample: ["info-center timestamp log date precision-time tenth-second"]
changed:
description: check to see if a change was made on the device
returned: always
type: boolean
sample: true
'''
from xml.etree import ElementTree
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ce import get_nc_config, set_nc_config, ce_argument_spec
CE_NC_GET_LOG = """
<filter type="subtree">
<syslog xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
<globalParam>
<bufferSize></bufferSize>
<logTimeStamp></logTimeStamp>
<icLogBuffEn></icLogBuffEn>
</globalParam>
<icSources>
<icSource>
<moduleName>%s</moduleName>
<icChannelId>%s</icChannelId>
<icChannelName></icChannelName>
<logEnFlg></logEnFlg>
<logEnLevel></logEnLevel>
</icSource>
</icSources>
</syslog>
</filter>
"""
CE_NC_GET_LOG_GLOBAL = """
<filter type="subtree">
<syslog xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
<globalParam>
<bufferSize></bufferSize>
<logTimeStamp></logTimeStamp>
<icLogBuffEn></icLogBuffEn>
</globalParam>
</syslog>
</filter>
"""
TIME_STAMP_DICT = {"date_boot": "boot",
"date_second": "date precision-time second",
"date_tenthsecond": "date precision-time tenth-second",
"date_millisecond": "date precision-time millisecond",
"shortdate_second": "short-date precision-time second",
"shortdate_tenthsecond": "short-date precision-time tenth-second",
"shortdate_millisecond": "short-date precision-time millisecond",
"formatdate_second": "format-date precision-time second",
"formatdate_tenthsecond": "format-date precision-time tenth-second",
"formatdate_millisecond": "format-date precision-time millisecond"}
CHANNEL_DEFAULT_LOG_STATE = {"0": "true",
"1": "true",
"2": "true",
"3": "false",
"4": "true",
"5": "false",
"6": "true",
"7": "true",
"8": "true",
"9": "true"}
CHANNEL_DEFAULT_LOG_LEVEL = {"0": "warning",
"1": "warning",
"2": "informational",
"3": "informational",
"4": "warning",
"5": "debugging",
"6": "debugging",
"7": "warning",
"8": "debugging",
"9": "debugging"}
class InfoCenterLog(object):
"""
Manages information center log configuration
"""
def __init__(self, argument_spec):
self.spec = argument_spec
self.module = None
self.init_module()
# module input info
self.log_time_stamp = self.module.params['log_time_stamp']
self.log_buff_enable = self.module.params['log_buff_enable']
self.log_buff_size = self.module.params['log_buff_size']
self.module_name = self.module.params['module_name']
self.channel_id = self.module.params['channel_id']
self.log_enable = self.module.params['log_enable']
self.log_level = self.module.params['log_level']
self.state = self.module.params['state']
# state
self.log_dict = dict()
self.changed = False
self.updates_cmd = list()
self.commands = list()
self.results = dict()
self.proposed = dict()
self.existing = dict()
self.end_state = dict()
def init_module(self):
"""init module"""
self.module = AnsibleModule(argument_spec=self.spec, supports_check_mode=True)
def check_response(self, xml_str, xml_name):
"""Check if response message is already succeed"""
if "<ok/>" not in xml_str:
self.module.fail_json(msg='Error: %s failed.' % xml_name)
def get_log_dict(self):
""" log config dict"""
log_dict = dict()
if self.module_name:
if self.module_name.lower() == "default":
conf_str = CE_NC_GET_LOG % (self.module_name.lower(), self.channel_id)
else:
conf_str = CE_NC_GET_LOG % (self.module_name.upper(), self.channel_id)
else:
conf_str = CE_NC_GET_LOG_GLOBAL
xml_str = get_nc_config(self.module, conf_str)
if "<data/>" in xml_str:
return log_dict
xml_str = xml_str.replace('\r', '').replace('\n', '').\
replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
root = ElementTree.fromstring(xml_str)
# get global param info
glb = root.find("data/syslog/globalParam")
if glb:
for attr in glb:
if attr.tag in ["bufferSize", "logTimeStamp", "icLogBuffEn"]:
log_dict[attr.tag] = attr.text
# get info-center source info
log_dict["source"] = dict()
src = root.find("data/syslog/icSources/icSource")
if src:
for attr in src:
if attr.tag in ["moduleName", "icChannelId", "icChannelName", "logEnFlg", "logEnLevel"]:
log_dict["source"][attr.tag] = attr.text
return log_dict
def config_log_global(self):
"""config log global param"""
xml_str = '<globalParam operation="merge">'
if self.log_time_stamp:
if self.state == "present" and self.log_time_stamp.upper() != self.log_dict.get("logTimeStamp"):
xml_str += '<logTimeStamp>%s</logTimeStamp>' % self.log_time_stamp.upper()
self.updates_cmd.append(
"info-center timestamp log %s" % TIME_STAMP_DICT.get(self.log_time_stamp))
elif self.state == "absent" and self.log_time_stamp.upper() == self.log_dict.get("logTimeStamp"):
xml_str += '<logTimeStamp>DATE_SECOND</logTimeStamp>' # set default
self.updates_cmd.append("undo info-center timestamp log")
else:
pass
if self.log_buff_enable != 'no_use':
if self.log_dict.get("icLogBuffEn") != self.log_buff_enable:
xml_str += '<icLogBuffEn>%s</icLogBuffEn>' % self.log_buff_enable
if self.log_buff_enable == "true":
self.updates_cmd.append("info-center logbuffer")
else:
self.updates_cmd.append("undo info-center logbuffer")
if self.log_buff_size:
if self.state == "present" and self.log_dict.get("bufferSize") != self.log_buff_size:
xml_str += '<bufferSize>%s</bufferSize>' % self.log_buff_size
self.updates_cmd.append(
"info-center logbuffer size %s" % self.log_buff_size)
elif self.state == "absent" and self.log_dict.get("bufferSize") == self.log_buff_size:
xml_str += '<bufferSize>512</bufferSize>'
self.updates_cmd.append("undo info-center logbuffer size")
if xml_str == '<globalParam operation="merge">':
return ""
else:
xml_str += '</globalParam>'
return xml_str
def config_log_soruce(self):
"""config info-center sources"""
xml_str = ''
if not self.module_name or not self.channel_id:
return xml_str
source = self.log_dict["source"]
if self.state == "present":
xml_str = '<icSources><icSource operation="merge">'
cmd = 'info-center source %s channel %s log' % (
self.module_name, self.channel_id)
else:
if not source or self.module_name != source.get("moduleName").lower() or \
self.channel_id != source.get("icChannelId"):
return ''
if self.log_enable == 'no_use' and not self.log_level:
xml_str = '<icSources><icSource operation="delete">'
else:
xml_str = '<icSources><icSource operation="merge">'
cmd = 'undo info-center source %s channel %s log' % (
self.module_name, self.channel_id)
xml_str += '<moduleName>%s</moduleName><icChannelId>%s</icChannelId>' % (
self.module_name, self.channel_id)
# log_enable
if self.log_enable != 'no_use':
if self.state == "present" and (not source or self.log_enable != source.get("logEnFlg")):
xml_str += '<logEnFlg>%s</logEnFlg>' % self.log_enable
if self.log_enable == "true":
cmd += ' state on'
else:
cmd += ' state off'
elif self.state == "absent" and source and self.log_level == source.get("logEnLevel"):
xml_str += '<logEnFlg>%s</logEnFlg>' % CHANNEL_DEFAULT_LOG_STATE.get(self.channel_id)
cmd += ' state'
# log_level
if self.log_level:
if self.state == "present" and (not source or self.log_level != source.get("logEnLevel")):
xml_str += '<logEnLevel>%s</logEnLevel>' % self.log_level
cmd += ' level %s' % self.log_level
elif self.state == "absent" and source and self.log_level == source.get("logEnLevel"):
xml_str += '<logEnLevel>%s</logEnLevel>' % CHANNEL_DEFAULT_LOG_LEVEL.get(self.channel_id)
cmd += ' level'
if xml_str.endswith("</icChannelId>"):
if self.log_enable == 'no_use' and not self.log_level and self.state == "absent":
xml_str += '</icSource></icSources>'
self.updates_cmd.append(cmd)
return xml_str
else:
return ''
else:
xml_str += '</icSource></icSources>'
self.updates_cmd.append(cmd)
return xml_str
def netconf_load_config(self, xml_str):
"""load log config by netconf"""
if not xml_str:
return
xml_cfg = """
<config>
<syslog xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
%s
</syslog>
</config>""" % xml_str
recv_xml = set_nc_config(self.module, xml_cfg)
self.check_response(recv_xml, "SET_LOG")
self.changed = True
def check_params(self):
"""Check all input params"""
# check log_buff_size ranges from 0 to 10240
if self.log_buff_size:
if not self.log_buff_size.isdigit():
self.module.fail_json(
msg="Error: log_buff_size is not digit.")
if int(self.log_buff_size) < 0 or int(self.log_buff_size) > 10240:
self.module.fail_json(
msg="Error: log_buff_size is not ranges from 0 to 10240.")
# check channel_id ranging from 0 to 9
if self.channel_id:
if not self.channel_id.isdigit():
self.module.fail_json(msg="Error: channel_id is not digit.")
if int(self.channel_id) < 0 or int(self.channel_id) > 9:
self.module.fail_json(
msg="Error: channel_id is not ranges from 0 to 9.")
# module_name and channel_id must be set at the same time
if bool(self.module_name) != bool(self.channel_id):
self.module.fail_json(
msg="Error: module_name and channel_id must be set at the same time.")
def get_proposed(self):
"""get proposed info"""
if self.log_time_stamp:
self.proposed["log_time_stamp"] = self.log_time_stamp
if self.log_buff_enable != 'no_use':
self.proposed["log_buff_enable"] = self.log_buff_enable
if self.log_buff_size:
self.proposed["log_buff_size"] = self.log_buff_size
if self.module_name:
self.proposed["module_name"] = self.module_name
if self.channel_id:
self.proposed["channel_id"] = self.channel_id
if self.log_enable != 'no_use':
self.proposed["log_enable"] = self.log_enable
if self.log_level:
self.proposed["log_level"] = self.log_level
self.proposed["state"] = self.state
def get_existing(self):
"""get existing info"""
if not self.log_dict:
return
if self.log_time_stamp:
self.existing["log_time_stamp"] = self.log_dict.get("logTimeStamp").lower()
if self.log_buff_enable != 'no_use':
self.existing["log_buff_enable"] = self.log_dict.get("icLogBuffEn")
if self.log_buff_size:
self.existing["log_buff_size"] = self.log_dict.get("bufferSize")
if self.module_name:
self.existing["source"] = self.log_dict.get("source")
def get_end_state(self):
"""get end state info"""
log_dict = self.get_log_dict()
if not log_dict:
return
if self.log_time_stamp:
self.end_state["log_time_stamp"] = log_dict.get("logTimeStamp").lower()
if self.log_buff_enable != 'no_use':
self.end_state["log_buff_enable"] = log_dict.get("icLogBuffEn")
if self.log_buff_size:
self.end_state["log_buff_size"] = log_dict.get("bufferSize")
if self.module_name:
self.end_state["source"] = log_dict.get("source")
def work(self):
"""worker"""
self.check_params()
self.log_dict = self.get_log_dict()
self.get_existing()
self.get_proposed()
# deal present or absent
xml_str = ''
if self.log_time_stamp or self.log_buff_enable != 'no_use' or self.log_buff_size:
xml_str += self.config_log_global()
if self.module_name:
xml_str += self.config_log_soruce()
if xml_str:
self.netconf_load_config(xml_str)
self.changed = True
self.get_end_state()
self.results['changed'] = self.changed
self.results['proposed'] = self.proposed
self.results['existing'] = self.existing
self.results['end_state'] = self.end_state
if self.changed:
self.results['updates'] = self.updates_cmd
else:
self.results['updates'] = list()
self.module.exit_json(**self.results)
def main():
"""Module main"""
argument_spec = dict(
log_time_stamp=dict(required=False, type='str',
choices=['date_boot', 'date_second', 'date_tenthsecond', 'date_millisecond',
'shortdate_second', 'shortdate_tenthsecond', 'shortdate_millisecond',
'formatdate_second', 'formatdate_tenthsecond', 'formatdate_millisecond']),
log_buff_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']),
log_buff_size=dict(required=False, type='str'),
module_name=dict(required=False, type='str'),
channel_id=dict(required=False, type='str'),
log_enable=dict(type='str', default='no_use', choices=['no_use', 'true', 'false']),
log_level=dict(required=False, type='str',
choices=['emergencies', 'alert', 'critical', 'error',
'warning', 'notification', 'informational', 'debugging']),
state=dict(required=False, default='present',
choices=['present', 'absent'])
)
argument_spec.update(ce_argument_spec)
module = InfoCenterLog(argument_spec)
module.work()
if __name__ == '__main__':
main()