From 9aa58dd4caaedf05a104374944782a7020fe941e Mon Sep 17 00:00:00 2001 From: QijunPan Date: Fri, 26 May 2017 00:28:06 +0800 Subject: [PATCH] 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 --- .../network/cloudengine/ce_info_center_log.py | 555 ++++++++++++++++++ 1 file changed, 555 insertions(+) create mode 100644 lib/ansible/modules/network/cloudengine/ce_info_center_log.py diff --git a/lib/ansible/modules/network/cloudengine/ce_info_center_log.py b/lib/ansible/modules/network/cloudengine/ce_info_center_log.py new file mode 100644 index 00000000000..3c2de25bd5a --- /dev/null +++ b/lib/ansible/modules/network/cloudengine/ce_info_center_log.py @@ -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 . +# + +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 = """ + + + + + + + + + + %s + %s + + + + + + + +""" + +CE_NC_GET_LOG_GLOBAL = """ + + + + + + + + + +""" + +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 "" 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 "" 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 = '' + if self.log_time_stamp: + if self.state == "present" and self.log_time_stamp.upper() != self.log_dict.get("logTimeStamp"): + xml_str += '%s' % 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 += 'DATE_SECOND' # 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 += '%s' % 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 += '%s' % 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 += '512' + self.updates_cmd.append("undo info-center logbuffer size") + + if xml_str == '': + return "" + else: + xml_str += '' + 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 = '' + 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 = '' + else: + xml_str = '' + cmd = 'undo info-center source %s channel %s log' % ( + self.module_name, self.channel_id) + + xml_str += '%s%s' % ( + 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 += '%s' % 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 += '%s' % 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 += '%s' % self.log_level + cmd += ' level %s' % self.log_level + elif self.state == "absent" and source and self.log_level == source.get("logEnLevel"): + xml_str += '%s' % CHANNEL_DEFAULT_LOG_LEVEL.get(self.channel_id) + cmd += ' level' + + if xml_str.endswith(""): + if self.log_enable == 'no_use' and not self.log_level and self.state == "absent": + xml_str += '' + self.updates_cmd.append(cmd) + return xml_str + else: + return '' + else: + xml_str += '' + 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 = """ + + + %s + + """ % 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()