diff --git a/lib/ansible/modules/network/cloudengine/ce_ntp_auth.py b/lib/ansible/modules/network/cloudengine/ce_ntp_auth.py new file mode 100644 index 0000000000..70c61c90cb --- /dev/null +++ b/lib/ansible/modules/network/cloudengine/ce_ntp_auth.py @@ -0,0 +1,589 @@ +#!/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_ntp_auth +version_added: "2.4" +short_description: Manages NTP authentication configuration on HUAWEI CloudEngine switches. +description: + - Manages NTP authentication configuration on HUAWEI CloudEngine switches. +author: + - Zhijin Zhou (@CloudEngine-Ansible) +notes: + - If C(state=absent), the module will attempt to remove the given key configuration. + If a matching key configuration isn't found on the device, the module will fail. + - If C(state=absent) and C(authentication=on), authentication will be turned on. + - If C(state=absent) and C(authentication=off), authentication will be turned off. +options: + key_id: + description: + - Authentication key identifier (numeric). + required: true + auth_pwd: + description: + - Plain text with length of 1 to 255, encrypted text with length of 20 to 392. + required: false + default: null + auth_mode: + description: + - Specify authentication algorithm. + required: false + default: null + choices: ['hmac-sha256', 'md5'] + auth_type: + description: + - Whether the given password is in cleartext or + has been encrypted. If in cleartext, the device + will encrypt it before storing it. + required: false + default: encrypt + choices: ['text', 'encrypt'] + trusted_key: + description: + - Whether the given key is required to be supplied by a time source + for the device to synchronize to the time source. + required: false + default: 'disable' + choices: ['enable', 'disable'] + authentication: + description: + - Configure ntp authentication enable or unconfigure ntp authentication enable. + required: false + default: null + choices: ['enable', 'disable'] + state: + description: + - Manage the state of the resource. + required: false + default: present + choices: ['present','absent'] +''' + +EXAMPLES = ''' +- name: NTP AUTH 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: "Configure ntp authentication key-id" + ce_ntp_auth: + key_id: 32 + auth_mode: md5 + auth_pwd: 11111111111111111111111 + provider: "{{ cli }}" + + - name: "Configure ntp authentication key-id and trusted authentication keyid" + ce_ntp_auth: + key_id: 32 + auth_mode: md5 + auth_pwd: 11111111111111111111111 + trusted_key: enable + provider: "{{ cli }}" + + - name: "Configure ntp authentication key-id and authentication enable" + ce_ntp_auth: + key_id: 32 + auth_mode: md5 + auth_pwd: 11111111111111111111111 + authentication: enable + provider: "{{ cli }}" + + - name: "Unconfigure ntp authentication key-id and trusted authentication keyid" + ce_ntp_auth: + key_id: 32 + state: absent + provider: "{{ cli }}" + + - name: "Unconfigure ntp authentication key-id and authentication enable" + ce_ntp_auth: + key_id: 32 + authentication: enable + state: absent + provider: "{{ cli }}" +''' + +RETURN = ''' +proposed: + description: k/v pairs of parameters passed into module + returned: always + type: dict + sample: { + "auth_type": "text", + "authentication": "enable", + "key_id": "32", + "auth_pwd": "1111", + "auth_mode": "md5", + "trusted_key": "enable", + "state": "present" + } +existing: + description: k/v pairs of existing ntp authentication + returned: always + type: dict + sample: { + "authentication": "off", + "authentication-keyid": [ + { + "auth_mode": "md5", + "key_id": "1", + "trusted_key": "disable" + } + ] + } +end_state: + description: k/v pairs of ntp authentication after module execution + returned: always + type: dict + sample: { + "authentication": "off", + "authentication-keyid": [ + { + "auth_mode": "md5", + "key_id": "1", + "trusted_key": "disable" + }, + { + "auth_mode": "md5", + "key_id": "32", + "trusted_key": "enable" + } + ] + } +state: + description: state as sent in from the playbook + returned: always + type: string + sample: "present" +updates: + description: command sent to the device + returned: always + type: list + sample: [ + "ntp authentication-key 32 md5 1111", + "ntp trusted-key 32", + "ntp authentication enable" + ] +changed: + description: check to see if a change was made on the device + returned: always + type: boolean + sample: true +''' + +import re +import copy +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.ce import ce_argument_spec, load_config, get_nc_config, set_nc_config + +CE_NC_GET_NTP_AUTH_CONFIG = """ + + + + + %s + + + + + + + +""" + +CE_NC_GET_ALL_NTP_AUTH_CONFIG = """ + + + + + + + + + + + + +""" + +CE_NC_GET_NTP_AUTH_ENABLE = """ + + + + + + + +""" + +CE_NC_MERGE_NTP_AUTH_CONFIG = """ + + + + + %s + %s + %s + %s + + + + +""" + +CE_NC_MERGE_NTP_AUTH_ENABLE = """ + + + + %s + + + +""" + +CE_NC_DELETE_NTP_AUTH_CONFIG = """ + + + + + %s + + + + +""" + + +class NtpAuth(object): + """Manage ntp authentication""" + + def __init__(self, argument_spec): + self.spec = argument_spec + self.module = None + self.init_module() + + # ntp_auth configration info + self.key_id = self.module.params['key_id'] + self.password = self.module.params['auth_pwd'] or None + self.auth_mode = self.module.params['auth_mode'] or None + self.auth_type = self.module.params['auth_type'] + self.trusted_key = self.module.params['trusted_key'] + self.authentication = self.module.params['authentication'] or None + self.state = self.module.params['state'] + self.check_params() + + self.ntp_auth_conf = dict() + self.key_id_exist = False + self.cur_trusted_key = 'disable' + + # state + self.changed = False + self.updates_cmd = list() + self.results = dict() + self.proposed = dict() + self.existing = list() + self.end_state = list() + + self.get_ntp_auth_exist_config() + + def check_params(self): + """Check all input params""" + + if not self.key_id.isdigit(): + self.module.fail_json( + msg='Error: key_id is not digit.') + + if (long(self.key_id) < 1) or (long(self.key_id) > 4294967295): + self.module.fail_json( + msg='Error: The length of key_id is between 1 and 4294967295.') + + if self.state == "present": + if (self.auth_type == 'encrypt') and\ + ((len(self.password) < 20) or (len(self.password) > 392)): + self.module.fail_json( + msg='Error: The length of encrypted password is between 20 and 392.') + elif (self.auth_type == 'text') and\ + ((len(self.password) < 1) or (len(self.password) > 255)): + self.module.fail_json( + msg='Error: The length of text password is between 1 and 255.') + + def init_module(self): + """Init module object""" + + required_if = [("state", "present", ("password", "auth_mode"))] + self.module = AnsibleModule( + argument_spec=self.spec, + required_if=required_if, + 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_ntp_auth_enable(self): + """Get ntp authentication enable state""" + + xml_str = CE_NC_GET_NTP_AUTH_ENABLE + con_obj = get_nc_config(self.module, xml_str) + if "" in con_obj: + return + + # get ntp authentication enable + auth_en = re.findall( + r'.*(.*).*', con_obj) + if auth_en: + if auth_en[0] == 'true': + self.ntp_auth_conf['authentication'] = 'enable' + else: + self.ntp_auth_conf['authentication'] = 'disable' + + def get_ntp_all_auth_keyid(self): + """Get all authentication keyid info""" + + ntp_auth_conf = list() + + xml_str = CE_NC_GET_ALL_NTP_AUTH_CONFIG + con_obj = get_nc_config(self.module, xml_str) + if "" in con_obj: + self.ntp_auth_conf["authentication-keyid"] = "None" + return ntp_auth_conf + + # get ntp authentication config + ntp_auth = re.findall( + r'.*(.*).*\s*(.*).*\s*' + r'(.*).*\s*(.*).*', con_obj) + + for ntp_auth_num in ntp_auth: + if ntp_auth_num[0] == self.key_id: + self.key_id_exist = True + if ntp_auth_num[3] == 'true': + self.cur_trusted_key = 'enable' + else: + self.cur_trusted_key = 'disable' + + if ntp_auth_num[3] == 'true': + trusted_key = 'enable' + else: + trusted_key = 'disable' + ntp_auth_conf.append(dict(key_id=ntp_auth_num[0], + auth_mode=ntp_auth_num[1].lower(), + trusted_key=trusted_key)) + self.ntp_auth_conf["authentication-keyid"] = ntp_auth_conf + + return ntp_auth_conf + + def get_ntp_auth_exist_config(self): + """Get ntp authentication existed configure""" + + self.get_ntp_auth_enable() + self.get_ntp_all_auth_keyid() + + def config_ntp_auth_keyid(self): + """Config ntp authentication keyid""" + + if self.trusted_key == 'enable': + trusted_key = 'true' + else: + trusted_key = 'false' + xml_str = CE_NC_MERGE_NTP_AUTH_CONFIG % ( + self.key_id, self.auth_mode.upper(), self.password, trusted_key) + ret_xml = set_nc_config(self.module, xml_str) + self.check_response(ret_xml, "NTP_AUTH_KEYID_CONFIG") + + def config_ntp_auth_enable(self): + """Config ntp authentication enable""" + + if self.ntp_auth_conf['authentication'] != self.authentication: + if self.authentication == 'enable': + state = 'true' + else: + state = 'false' + xml_str = CE_NC_MERGE_NTP_AUTH_ENABLE % state + ret_xml = set_nc_config(self.module, xml_str) + self.check_response(ret_xml, "NTP_AUTH_ENABLE") + + def undo_config_ntp_auth_keyid(self): + """Undo ntp authentication key-id""" + + xml_str = CE_NC_DELETE_NTP_AUTH_CONFIG % self.key_id + ret_xml = set_nc_config(self.module, xml_str) + self.check_response(ret_xml, "UNDO_NTP_AUTH_KEYID_CONFIG") + + def cli_load_config(self, commands): + """Load config by cli""" + + if not self.module.check_mode: + load_config(self.module, commands) + + def config_ntp_auth_keyid_by_cli(self): + """Config ntp authentication keyid bye the way of CLI""" + + commands = list() + config_cli = "ntp authentication-keyid %s authentication-mode %s %s" % ( + self.key_id, self.auth_mode, self.password) + commands.append(config_cli) + self.cli_load_config(commands) + + def config_ntp_auth(self): + """Config ntp authentication""" + + if self.state == "present": + if self.auth_type == 'encrypt': + self.config_ntp_auth_keyid() + else: + self.config_ntp_auth_keyid_by_cli() + else: + if not self.key_id_exist: + self.module.fail_json( + msg='Error: The Authentication-keyid does not exist.') + self.undo_config_ntp_auth_keyid() + + if self.authentication: + self.config_ntp_auth_enable() + + self.changed = True + + def get_existing(self): + """Get existing info""" + + self.existing = copy.deepcopy(self.ntp_auth_conf) + + def get_proposed(self): + """Get proposed result""" + + auth_type = self.auth_type + trusted_key = self.trusted_key + if self.state == 'absent': + auth_type = None + trusted_key = None + self.proposed = dict(key_id=self.key_id, auth_pwd=self.password, + auth_mode=self.auth_mode, auth_type=auth_type, + trusted_key=trusted_key, authentication=self.authentication, + state=self.state) + + def get_update_cmd(self): + """Get updated commands""" + + cli_str = "" + if self.state == "present": + cli_str = "ntp authentication-keyid %s authentication-mode %s " % ( + self.key_id, self.auth_mode) + if self.auth_type == 'encrypt': + cli_str = "%s cipher %s" % (cli_str, self.password) + else: + cli_str = "%s %s" % (cli_str, self.password) + else: + cli_str = "undo ntp authentication-keyid %s" % self.key_id + + self.updates_cmd.append(cli_str) + + if self.authentication: + cli_str = "" + + if self.ntp_auth_conf['authentication'] != self.authentication: + if self.authentication == 'enable': + cli_str = "ntp authentication enable" + else: + cli_str = "undo ntp authentication enable" + + if cli_str != "": + self.updates_cmd.append(cli_str) + + cli_str = "" + if self.state == "present": + if self.trusted_key != self.cur_trusted_key: + if self.trusted_key == 'enable': + cli_str = "ntp trusted authentication-keyid %s" % self.key_id + else: + cli_str = "undo ntp trusted authentication-keyid %s" % self.key_id + else: + cli_str = "undo ntp trusted authentication-keyid %s" % self.key_id + + if cli_str != "": + self.updates_cmd.append(cli_str) + + def get_end_state(self): + """Get end state info""" + + self.ntp_auth_conf = dict() + self.get_ntp_auth_exist_config() + self.end_state = copy.deepcopy(self.ntp_auth_conf) + + def show_result(self): + """Show result""" + + 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 work(self): + """Excute task""" + + self.get_existing() + self.get_proposed() + self.get_update_cmd() + + self.config_ntp_auth() + + self.get_end_state() + self.show_result() + + +def main(): + """Main function entry""" + + argument_spec = dict( + key_id=dict(required=True, type='str'), + auth_pwd=dict(type='str', no_log=True), + auth_mode=dict(choices=['md5', 'hmac-sha256'], type='str'), + auth_type=dict(choices=['text', 'encrypt'], default='encrypt'), + trusted_key=dict(choices=['enable', 'disable'], default='disable'), + authentication=dict(choices=['enable', 'disable']), + state=dict(choices=['absent', 'present'], default='present'), + ) + argument_spec.update(ce_argument_spec) + ntp_auth_obj = NtpAuth(argument_spec) + ntp_auth_obj.work() + + +if __name__ == '__main__': + main()