From fe354ed183f87ca851e44c510dd49246cb282fa2 Mon Sep 17 00:00:00 2001 From: Sara-Touqan <55439434+Sara-Touqan@users.noreply.github.com> Date: Tue, 17 Dec 2019 08:20:32 +0200 Subject: [PATCH] Supporting general ntp configurations in Ansible (#63724) * Supporting general ntp configurations in Ansible * Modifying ntp module --- lib/ansible/modules/network/onyx/onyx_ntp.py | 243 ++++++++++++++++++ .../network/onyx/fixtures/onyx_ntp_show.cfg | 30 +++ .../modules/network/onyx/test_onyx_ntp.py | 76 ++++++ 3 files changed, 349 insertions(+) create mode 100644 lib/ansible/modules/network/onyx/onyx_ntp.py create mode 100644 test/units/modules/network/onyx/fixtures/onyx_ntp_show.cfg create mode 100644 test/units/modules/network/onyx/test_onyx_ntp.py diff --git a/lib/ansible/modules/network/onyx/onyx_ntp.py b/lib/ansible/modules/network/onyx/onyx_ntp.py new file mode 100644 index 00000000000..4db32c94078 --- /dev/null +++ b/lib/ansible/modules/network/onyx/onyx_ntp.py @@ -0,0 +1,243 @@ +#!/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_ntp +version_added: "2.10" +author: "Sara-Touqan (@sarato)" +short_description: Manage NTP general configurations and ntp keys configurations on Mellanox ONYX network devices +description: + - This module provides declarative management of NTP & NTP Keys + on Mellanox ONYX network devices. +options: + state: + description: + - State of the NTP configuration. + choices: ['enabled', 'disabled'] + type: str + authenticate_state: + description: + - State of the NTP authentication configuration. + choices: ['enabled', 'disabled'] + type: str + ntp_authentication_keys: + type: list + description: + - List of ntp authentication keys + suboptions: + auth_key_id: + description: + - Configures ntp key-id, range 1-65534 + required: true + type: int + auth_key_encrypt_type: + description: + - encryption type used to configure ntp authentication key. + required: true + choices: ['md5', 'sha1'] + type: str + auth_key_password: + description: + - password used for ntp authentication key. + required: true + type: str + auth_key_state: + description: + - Used to decide if you want to delete given ntp key or not + choices: ['present', 'absent'] + type: str + trusted_keys: + type: list + description: + - List of ntp trusted keys +""" + +EXAMPLES = """ +- name: configure NTP + onyx_ntp: + state: enabled + authenticate_state: enabled + ntp_authentication_keys: + - auth_key_id: 1 + auth_key_encrypt_type: md5 + auth_key_password: 12345 + auth_key_state: absent + trusted_keys: 1,2,3 +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always. + type: list + sample: + - ntp enable + - ntp disable + - ntp authenticate + - no ntp authenticate + - ntp authentication-key 1 md5 12345 + - no ntp authentication-key 1 + - ntp trusted-key 1,2,3 +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible.module_utils.network.onyx.onyx import BaseOnyxModule +from ansible.module_utils.network.onyx.onyx import show_cmd + + +class OnyxNTPModule(BaseOnyxModule): + + def init_module(self): + """ module initialization + """ + ntp_authentication_key_spec = dict(auth_key_id=dict(type='int', required=True), + auth_key_encrypt_type=dict(required=True, choices=['md5', 'sha1']), + auth_key_password=dict(required=True), + auth_key_state=dict(choices=['present', 'absent'])) + element_spec = dict( + state=dict(choices=['enabled', 'disabled']), + authenticate_state=dict(choices=['enabled', 'disabled']), + ntp_authentication_keys=dict(type='list', elements='dict', options=ntp_authentication_key_spec), + trusted_keys=dict(type='list', elements='int') + ) + argument_spec = dict() + argument_spec.update(element_spec) + self._module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True) + + def _validate_key_id(self): + keys_id_list = self._required_config.get("ntp_authentication_keys") + if keys_id_list: + for key_item in keys_id_list: + key_id = key_item.get("auth_key_id") + if (key_id < 1) or (key_id > 65534): + self._module.fail_json( + msg='Invalid Key value, value should be in the range 1-65534') + + def get_required_config(self): + module_params = self._module.params + self._required_config = dict(module_params) + self.validate_param_values(self._required_config) + self._validate_key_id() + + def _show_ntp_config(self): + show_cmds = [] + cmd = "show ntp" + show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)) + cmd = "show ntp keys" + show_cmds.append(show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False)) + return show_cmds + + def _set_ntp_keys_config(self, ntp_config): + if not ntp_config: + return + for req_ntp_auth_key in ntp_config: + ecryption_type = req_ntp_auth_key.get("Encryption Type") + self._current_config[req_ntp_auth_key.get("header")] = ecryption_type + + def _set_ntp_config(self, ntp_config): + ntp_config = ntp_config[0] + if not ntp_config: + return + self._current_config['state'] = ntp_config.get("NTP is administratively") + self._current_config['authenticate_state'] = ntp_config.get("NTP Authentication administratively") + + def load_current_config(self): + self._current_config = dict() + ntp_config = self._show_ntp_config() + if ntp_config: + if ntp_config[0]: + self._set_ntp_config(ntp_config[0]) + if ntp_config[1]: + self._set_ntp_keys_config(ntp_config[1]) + + def generate_commands(self): + current_state = self._current_config.get("state") + state = self._required_config.get("state") + if state is None: + state = current_state + if state is not None: + if current_state != state: + if state == 'enabled': + self._commands.append('ntp enable') + else: + self._commands.append('no ntp enable') + authenticate_state = self._required_config.get("authenticate_state") + if authenticate_state: + current_authenticate_state = self._current_config.get("authenticate_state") + if authenticate_state is not None: + if current_authenticate_state != authenticate_state: + if authenticate_state == 'enabled': + self._commands.append('ntp authenticate') + else: + self._commands.append('no ntp authenticate') + req_ntp_auth_keys = self._required_config.get('ntp_authentication_keys') + if req_ntp_auth_keys: + if req_ntp_auth_keys is not None: + for req_ntp_auth_key in req_ntp_auth_keys: + req_key_id = req_ntp_auth_key.get('auth_key_id') + req_key = 'NTP Key ' + str(req_key_id) + current_req_key = self._current_config.get(req_key) + auth_key_state = req_ntp_auth_key.get('auth_key_state') + req_encrypt_type = req_ntp_auth_key.get('auth_key_encrypt_type') + req_password = req_ntp_auth_key.get('auth_key_password') + if current_req_key: + if req_encrypt_type == current_req_key: + if auth_key_state: + if auth_key_state == 'absent': + self._commands.append('no ntp authentication-key {0}' .format(req_key_id)) + else: + continue + else: + if auth_key_state: + if auth_key_state == 'present': + self._commands.append('ntp authentication-key {0} {1} {2}' + .format(req_key_id, + req_encrypt_type, + req_password)) + else: + self._commands.append('ntp authentication-key {0} {1} {2}' + .format(req_key_id, + req_encrypt_type, + req_password)) + + else: + if auth_key_state: + if auth_key_state == 'present': + self._commands.append('ntp authentication-key {0} {1} {2}' + .format(req_key_id, + req_encrypt_type, + req_password)) + else: + self._commands.append('ntp authentication-key {0} {1} {2}' + .format(req_key_id, + req_encrypt_type, + req_password)) + + req_trusted_keys = self._required_config.get('trusted_keys') + if req_trusted_keys: + for key in req_trusted_keys: + self._commands.append('ntp trusted-key {0}' .format(key)) + + +def main(): + """ main entry point for module execution + """ + OnyxNTPModule.main() + + +if __name__ == '__main__': + main() diff --git a/test/units/modules/network/onyx/fixtures/onyx_ntp_show.cfg b/test/units/modules/network/onyx/fixtures/onyx_ntp_show.cfg new file mode 100644 index 00000000000..b0b4a8dbf5f --- /dev/null +++ b/test/units/modules/network/onyx/fixtures/onyx_ntp_show.cfg @@ -0,0 +1,30 @@ +[ +[ + { + "NTP is administratively": "enabled", + "NTP Authentication administratively": "disabled" + }, + { + "Lines": [ + "Clock is unsynchronized." + ] + }, + { + "Active servers and peers": [ + { + "Lines": [ + "No NTP associations present." + ] + } + ] + } +] , +[ + { + "header": "NTP Key 22", + "Encryption Type": "sha1", + "Trusted": "no" + } +] + +] \ No newline at end of file diff --git a/test/units/modules/network/onyx/test_onyx_ntp.py b/test/units/modules/network/onyx/test_onyx_ntp.py new file mode 100644 index 00000000000..aa4499c5b32 --- /dev/null +++ b/test/units/modules/network/onyx/test_onyx_ntp.py @@ -0,0 +1,76 @@ +# +# 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_ntp +from units.modules.utils import set_module_args +from .onyx_module import TestOnyxModule, load_fixture + + +class TestOnyxNTP(TestOnyxModule): + + module = onyx_ntp + enabled = False + + def setUp(self): + self.enabled = False + super(TestOnyxNTP, self).setUp() + self.mock_get_config = patch.object( + onyx_ntp.OnyxNTPModule, "_show_ntp_config") + 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(TestOnyxNTP, self).tearDown() + self.mock_get_config.stop() + self.mock_load_config.stop() + + def load_fixtures(self, commands=None, transport='cli'): + config_file = 'onyx_ntp_show.cfg' + data = load_fixture(config_file) + self.get_config.return_value = data + self.load_config.return_value = None + + def test_ntp_state_no_change(self): + set_module_args(dict(state='enabled')) + self.execute_module(changed=False) + + def test_ntp_state_with_change(self): + set_module_args(dict(state='disabled')) + commands = ['no ntp enable'] + self.execute_module(changed=True, commands=commands) + + def test_ntp_authenticate_state_no_change(self): + set_module_args(dict(authenticate_state='disabled')) + self.execute_module(changed=False) + + def test_ntp_authenticate_state_with_change(self): + set_module_args(dict(authenticate_state='enabled')) + commands = ['ntp authenticate'] + self.execute_module(changed=True, commands=commands) + + def test_ntp_authentication_key_no_change(self): + set_module_args(dict(ntp_authentication_keys=[dict(auth_key_id='22', + auth_key_encrypt_type='sha1', + auth_key_password='12345')])) + self.execute_module(changed=False) + + def test_ntp_authentication_key_with_change(self): + set_module_args(dict(ntp_authentication_keys=[dict(auth_key_id='22', + auth_key_encrypt_type='md5', + auth_key_password='12345')])) + commands = ['ntp authentication-key 22 md5 12345'] + self.execute_module(changed=True, commands=commands) + + def test_ntp_trusted_keys_with_change(self): + set_module_args(dict(trusted_keys='22')) + commands = ['ntp trusted-key 22'] + self.execute_module(changed=True, commands=commands)