├── .gitignore ├── LICENSE ├── README.md ├── galaxy.yml ├── meta └── runtime.yml ├── plugins ├── README.md ├── cliconf │ ├── __init__.py │ └── apcos.py ├── module_utils │ └── network │ │ └── apcos │ │ ├── __init__.py │ │ └── apcos.py ├── modules │ ├── __init__.py │ ├── apcos_command.py │ ├── apcos_dns.py │ ├── apcos_ftp.py │ ├── apcos_ntp.py │ ├── apcos_radius.py │ ├── apcos_smtp.py │ ├── apcos_snmp.py │ ├── apcos_snmpv3.py │ ├── apcos_system.py │ ├── apcos_web.py │ └── network │ │ └── apcos │ │ ├── __init__.py │ │ ├── apcos_command.py │ │ ├── apcos_dns.py │ │ ├── apcos_ftp.py │ │ ├── apcos_ntp.py │ │ ├── apcos_radius.py │ │ ├── apcos_smtp.py │ │ ├── apcos_snmp.py │ │ ├── apcos_snmpv3.py │ │ ├── apcos_system.py │ │ └── apcos_web.py └── terminal │ ├── __init__.py │ └── apcos.py └── tests ├── requirements.yml └── unit └── plugins └── modules └── network └── apcos ├── __init__.py ├── apcos_module.py ├── fixtures ├── apcos_config_dns.cfg ├── apcos_config_ftp.cfg ├── apcos_config_ntp.cfg ├── apcos_config_radius.cfg ├── apcos_config_smtp.cfg ├── apcos_config_snmp.cfg ├── apcos_config_snmpv3.cfg ├── apcos_config_system.cfg └── apcos_config_web.cfg ├── test_apcos_dns.py ├── test_apcos_ftp.py ├── test_apcos_ntp.py ├── test_apcos_radius.py ├── test_apcos_smtp.py ├── test_apcos_snmp.py ├── test_apcos_snmpv3.py ├── test_apcos_system.py └── test_apcos_web.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | conf.py 3 | *.pyc 4 | __pycache__ 5 | .vscode 6 | *.code-workspace 7 | .venv 8 | *.tar.gz 9 | tests/output 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 North Carolina State University 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Collection - haught.apcos 2 | 3 | This collection allows for the network configuration of APC UPS v3 NMCs running v1.4.2.1 or greater. 4 | 5 | All APC NMCv2 and NMCv3 with version less than v1.4.2.1 are not supported due to a weird command line echoing buffering that makes Ansible fail. APC was kind enough to fix this bug for us in the v1.4.2.1 release for the v3 cards. 6 | 7 | ## Current Modules 8 | 9 | [haught.apcos.apcos_command](plugins/modules/network/apcos/apcos_command.py) - A module to run CLI commands against APC NMCs. 10 | 11 | [haught.apcos.apcos_dns](plugins/modules/network/apcos/apcos_dns.py) - A module to configure DNS on APC NMCs. 12 | 13 | [haught.apcos.apcos_ftp](plugins/modules/network/apcos/apcos_ftp.py) - A module to configure ftp option on APC NMCs. 14 | 15 | [haught.apcos.apcos_ntp](plugins/modules/network/apcos/apcos_ntp.py) - A module to configure NTP on APC NMCs. 16 | 17 | [haught.apcos.apcos_radius](plugins/modules/network/apcos/apcos_radius.py) - A module to configure RADIUS on APC NMCs. 18 | 19 | [haught.apcos.apcos_smtp](plugins/modules/network/apcos/apcos_smtp.py) - A module to configure SMTP option on APC NMCs. 20 | 21 | [haught.apcos.apcos_snmp](plugins/modules/network/apcos/apcos_snmp.py) - A module to configure SNMP v2c on APC NMCs. 22 | 23 | [haught.apcos.apcos_snmpv3](plugins/modules/network/apcos/apcos_snmpv3.py) - A module to configure SNMP v3 on APC NMCs. 24 | 25 | [haught.apcos.apcos_system](plugins/modules/network/apcos/apcos_system.py) - A module to configure system option on APC NMCs. 26 | 27 | [haught.apcos.apcos_web](plugins/modules/network/apcos/apcos_web.py) - A module to configure web option on APC NMCs. 28 | 29 | # Usage 30 | 31 | To install from galaxy use the ansible-galaxy command: 32 | ```bash 33 | ansible-galaxy collection install haught.apcos 34 | ``` 35 | 36 | In order to use these modules you will need to specifiy their OS in your inventory: 37 | ```ini 38 | ansible_network_os=haught.apcos.apcos 39 | ``` 40 | These modules also use ansible.netcommon.network_cli, so your playbook or inventory will need to specify the connection: 41 | ```ini 42 | ansible_connection=ansible.netcommon.network_cli 43 | ``` 44 | These can also be added to a playbook vars without the *ansible_*. 45 | 46 | # Developing 47 | 48 | Create the directory hierarchy *ansible_collections/haught/apcos* and clone the repo directly into *apcos* 49 | 50 | Symlink the *apcos* directory to your default ansible collection directory with the haught subdirectory. For example: 51 | ```bash 52 | mkdir ~/.ansible/collections/ansible_collections/haught 53 | ln -s ~/devel/ansible_collections/haught/apcos ~/.ansible/collections/ansible_collections/haught/ 54 | ``` 55 | 56 | 57 | Sanity tests of all modules: 58 | ```bash 59 | ansible-test sanity --docker --python 3.6 60 | ``` 61 | 62 | Unit tests of all modules: 63 | ```bash 64 | ansible-test units --docker --python 3.6 65 | ``` 66 | 67 | (You can add a specific module name to the end of the command to the test just that module) -------------------------------------------------------------------------------- /galaxy.yml: -------------------------------------------------------------------------------- 1 | ### REQUIRED 2 | # The namespace of the collection. This can be a company/brand/organization or product namespace under which all 3 | # content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with 4 | # underscores or numbers and cannot contain consecutive underscores 5 | namespace: haught 6 | 7 | # The name of the collection. Has the same character restrictions as 'namespace' 8 | name: apcos 9 | 10 | # The version of the collection. Must be compatible with semantic versioning 11 | version: 1.2.2 12 | 13 | # The path to the Markdown (.md) readme file. This path is relative to the root of the collection 14 | readme: README.md 15 | 16 | # A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) 17 | # @nicks:irc/im.site#channel' 18 | authors: 19 | - Matt Haught (@haught) 20 | 21 | 22 | ### OPTIONAL but strongly recommended 23 | # A short summary description of the collection 24 | description: A collection to configure APC OS UPSs 25 | 26 | # Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only 27 | # accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file' 28 | # license: 29 | # - GPL-2.0-or-later 30 | 31 | # The path to the license file for the collection. This path is relative to the root of the collection. This key is 32 | # mutually exclusive with 'license' 33 | license_file: 'LICENSE' 34 | 35 | # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character 36 | # requirements as 'namespace' and 'name' 37 | tags: ['network', 'apc', 'ups'] 38 | 39 | # Collections that this collection requires to be installed for it to be usable. The key of the dict is the 40 | # collection label 'namespace.name'. The value is a version range 41 | # L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version 42 | # range specifiers can be set and are separated by ',' 43 | dependencies: 44 | ansible.netcommon: '>=1.0.0' 45 | community.network: '>=1.0.0' 46 | 47 | # The URL of the originating SCM repository 48 | repository: https://github.com/haught/ansible_apcos 49 | 50 | # The URL to any online docs 51 | documentation: https://github.com/haught/ansible_apcos 52 | 53 | # The URL to the homepage of the collection/project 54 | homepage: https://github.com/haught/ansible_apcos 55 | 56 | # The URL to the collection issue tracker 57 | issues: https://github.com/haught/ansible_apcos 58 | 59 | # A list of file glob-like patterns used to filter any files or directories that should not be included in the build 60 | # artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This 61 | # uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', 62 | # and '.git' are always filtered 63 | build_ignore: ['.venv', '.vscode', '*.code-workspace', '*.tar.gz', 'tests/output/*'] 64 | -------------------------------------------------------------------------------- /meta/runtime.yml: -------------------------------------------------------------------------------- 1 | --- 2 | requires_ansible: '>=2.10.0' -------------------------------------------------------------------------------- /plugins/README.md: -------------------------------------------------------------------------------- 1 | # Collections Plugins Directory 2 | 3 | This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that 4 | is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that 5 | would contain module utils and modules respectively. 6 | 7 | Here is an example directory of the majority of plugins currently supported by Ansible: 8 | 9 | ``` 10 | └── plugins 11 | ├── action 12 | ├── become 13 | ├── cache 14 | ├── callback 15 | ├── cliconf 16 | ├── connection 17 | ├── filter 18 | ├── httpapi 19 | ├── inventory 20 | ├── lookup 21 | ├── module_utils 22 | ├── modules 23 | ├── netconf 24 | ├── shell 25 | ├── strategy 26 | ├── terminal 27 | ├── test 28 | └── vars 29 | ``` 30 | 31 | A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.11/plugins/plugins.html). 32 | -------------------------------------------------------------------------------- /plugins/cliconf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haught/ansible_apcos/92e6e1156e2867d546bc6f6a67ae763d39b99e8f/plugins/cliconf/__init__.py -------------------------------------------------------------------------------- /plugins/cliconf/apcos.py: -------------------------------------------------------------------------------- 1 | # 2 | # (c) 2017 Red Hat Inc. 3 | # 4 | # This file is part of Ansible 5 | # 6 | # Ansible is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Ansible is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Ansible. If not, see . 18 | # 19 | from __future__ import (absolute_import, division, print_function) 20 | __metaclass__ = type 21 | 22 | DOCUMENTATION = ''' 23 | --- 24 | author: Matt Haught (@haught) 25 | name: apcos 26 | short_description: Use apcos cliconf to run command on APC OS devices 27 | description: 28 | - This apcos plugin provides low level abstraction apis for 29 | sending and receiving CLI commands from APC OS devices. 30 | ''' 31 | 32 | import re 33 | import json 34 | 35 | from ansible.module_utils._text import to_text 36 | from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list 37 | from ansible.plugins.cliconf import CliconfBase 38 | 39 | 40 | class Cliconf(CliconfBase): 41 | 42 | def get_device_info(self): 43 | device_info = {} 44 | 45 | device_info['network_os'] = 'apcos' 46 | reply = self.get('about') 47 | data = to_text(reply, errors='surrogate_or_strict').strip() 48 | 49 | match = re.search(r'Hardware Revision:\s+(\S+)', data) 50 | if match: 51 | device_info['network_os_version'] = match.group(1) 52 | 53 | match = re.search(r'^Model Number:\s+(\S+)', data, re.M) 54 | if match: 55 | device_info['network_os_model'] = match.group(1) 56 | 57 | reply = self.get('dns') 58 | data = to_text(reply, errors='surrogate_or_strict').strip() 59 | 60 | match = re.search(r'^Host Name:\s+(\S+)', data, re.M) 61 | if match: 62 | device_info['network_os_hostname'] = match.group(1) 63 | 64 | return device_info 65 | 66 | def get_config(self, source='date', flags=None): 67 | if source not in ('boot', 'cipher', 'console', 'date', 'dns', 'eapol', 68 | 'email', 'firewall', 'ftp', 'ntp', 'portspeed', 'prompt', 69 | 'radius', 'session', 'smtp', 'snmp', 'snmptrap', 'snmpv3', 70 | 'system', 'tcpip', 'tcpip6', 'user', 'userdflt', 'web'): 71 | raise ValueError("fetching configuration from %s is not supported" % source) 72 | cmd = source 73 | 74 | flags = [] if flags is None else flags 75 | cmd += ' '.join(flags) 76 | cmd = cmd.strip() 77 | 78 | return self.send_command(cmd) 79 | 80 | def edit_config(self, command): 81 | for cmd in to_list(command): 82 | if isinstance(cmd, dict): 83 | command = cmd['command'] 84 | prompt = cmd['prompt'] 85 | answer = cmd['answer'] 86 | newline = cmd.get('newline', True) 87 | else: 88 | command = cmd 89 | prompt = None 90 | answer = None 91 | newline = True 92 | self.send_command(command=command, prompt=prompt, answer=answer, sendonly=False, newline=newline) 93 | 94 | def get(self, command, prompt=None, answer=None, sendonly=False, newline=True, check_all=False): 95 | return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all) 96 | 97 | def get_capabilities(self): 98 | result = super(Cliconf, self).get_capabilities() 99 | return json.dumps(result) 100 | -------------------------------------------------------------------------------- /plugins/module_utils/network/apcos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haught/ansible_apcos/92e6e1156e2867d546bc6f6a67ae763d39b99e8f/plugins/module_utils/network/apcos/__init__.py -------------------------------------------------------------------------------- /plugins/module_utils/network/apcos/apcos.py: -------------------------------------------------------------------------------- 1 | # This code is part of Ansible, but is an independent component. 2 | # This particular file snippet, and this file snippet only, is BSD licensed. 3 | # Modules you write using this snippet, which is embedded dynamically by Ansible 4 | # still belong to the author of the module, and may assign their own license 5 | # to the complete work. 6 | # 7 | # (c) 2016 Red Hat Inc. 8 | # 9 | # Redistribution and use in source and binary forms, with or without modification, 10 | # are permitted provided that the following conditions are met: 11 | # 12 | # * Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # * Redistributions in binary form must reproduce the above copyright notice, 15 | # this list of conditions and the following disclaimer in the documentation 16 | # and/or other materials provided with the distribution. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 26 | # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | # 28 | 29 | from __future__ import absolute_import, division, print_function 30 | __metaclass__ = type 31 | 32 | import json 33 | import re 34 | from ansible.module_utils._text import to_text 35 | from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list 36 | from ansible.module_utils.connection import Connection 37 | 38 | 39 | def get_connection(module): 40 | """Get switch connection 41 | 42 | Creates reusable SSH connection to the switch described in a given module. 43 | 44 | Args: 45 | module: A valid AnsibleModule instance. 46 | 47 | Returns: 48 | An instance of `ansible.module_utils.connection.Connection` with a 49 | connection to the switch described in the provided module. 50 | 51 | Raises: 52 | AnsibleConnectionFailure: An error occurred connecting to the device 53 | """ 54 | if hasattr(module, 'apcos_connection'): 55 | return module.apcos_connection 56 | 57 | capabilities = get_capabilities(module) 58 | network_api = capabilities.get('network_api') 59 | if network_api == 'cliconf': 60 | module.apcos_connection = Connection(module._socket_path) 61 | else: 62 | module.fail_json(msg='Invalid connection type %s' % network_api) 63 | 64 | return module.apcos_connection 65 | 66 | 67 | def get_capabilities(module): 68 | """Get switch capabilities 69 | 70 | Collects and returns a python object with the switch capabilities. 71 | 72 | Args: 73 | module: A valid AnsibleModule instance. 74 | 75 | Returns: 76 | A dictionary containing the switch capabilities. 77 | """ 78 | if hasattr(module, 'apcos_capabilities'): 79 | return module.apcos_capabilities 80 | 81 | capabilities = Connection(module._socket_path).get_capabilities() 82 | module.apcos_capabilities = json.loads(capabilities) 83 | return module.apcos_capabilities 84 | 85 | 86 | def run_commands(module, commands): 87 | """Run command list against connection. 88 | 89 | Get new or previously used connection and send commands to it one at a time, 90 | collecting response. 91 | 92 | Args: 93 | module: A valid AnsibleModule instance. 94 | commands: Iterable of command strings. 95 | 96 | Returns: 97 | A list of output strings. 98 | """ 99 | responses = list() 100 | connection = get_connection(module) 101 | 102 | for cmd in to_list(commands): 103 | command = cmd['command'] 104 | prompt = cmd['prompt'] 105 | answer = cmd['answer'] 106 | 107 | out = connection.get(command, prompt, answer) 108 | 109 | try: 110 | out = to_text(out, errors='surrogate_or_strict') 111 | except UnicodeError: 112 | module.fail_json(msg=u'Failed to decode output from %s: %s' % (cmd, to_text(out))) 113 | 114 | responses.append(out) 115 | 116 | return responses 117 | 118 | 119 | def get_config(module, source="date"): 120 | """Get switch configuration 121 | 122 | Gets the described device's current configuration. If a configuration has 123 | already been retrieved it will return the previously obtained configuration. 124 | 125 | Args: 126 | module: A valid AnsibleModule instance. 127 | 128 | Returns: 129 | A string containing the configuration. 130 | """ 131 | if not hasattr(module, 'device_configs'): 132 | module.device_configs = {} 133 | elif module.device_configs != {}: 134 | return module.device_configs 135 | 136 | connection = get_connection(module) 137 | out = connection.get_config(source=source) 138 | cfg = to_text(out, errors='surrogate_then_replace').strip() 139 | module.device_configs = cfg 140 | return cfg 141 | 142 | 143 | def load_config(module, commands): 144 | """Apply a list of commands to a device. 145 | 146 | Given a list of commands apply them to the device to modify the 147 | configuration in bulk. 148 | 149 | Args: 150 | module: A valid AnsibleModule instance. 151 | commands: Iterable of command strings. 152 | 153 | Returns: 154 | None 155 | """ 156 | connection = get_connection(module) 157 | connection.edit_config(commands) 158 | 159 | 160 | def parse_config(config): 161 | parsed = {} 162 | for line in config.split('\n'): 163 | line_parts = re.match(r'^(.+):\s+(.+)$', line) 164 | if hasattr(line_parts, 'group'): 165 | key = line_parts.group(1).replace(" ", "").lower() 166 | value = line_parts.group(2) if re.search(r'\S', line_parts.group(2)) else "" 167 | parsed[key] = value 168 | return parsed 169 | 170 | 171 | def parse_config_section(config, section, index=None, indexName="Index"): 172 | found_section = False 173 | found_index = None 174 | section_values = [] 175 | for line in config.split('\n'): 176 | if found_section is True: 177 | if re.match(r'^\S', line): 178 | break 179 | if re.match(r'^\s+(.+)', line): 180 | section_values.append(line) 181 | if line == section: 182 | found_section = True 183 | subsection = {} 184 | for line in section_values: 185 | if index is not None: 186 | index_search = re.match(r'\s+?' + indexName + r':\s+(.+)', line) 187 | if hasattr(index_search, 'group'): 188 | found_index = int(index_search.group(1)) 189 | subsection[found_index] = [] 190 | if found_index is not None: 191 | subsection[found_index].append(line) 192 | if index is not None: 193 | for key in subsection: 194 | subsection[key] = parse_config("\n".join(subsection[key])) 195 | if index in subsection.keys(): 196 | return subsection[index] 197 | return parse_config("\n".join(section_values)) 198 | -------------------------------------------------------------------------------- /plugins/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haught/ansible_apcos/92e6e1156e2867d546bc6f6a67ae763d39b99e8f/plugins/modules/__init__.py -------------------------------------------------------------------------------- /plugins/modules/apcos_command.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_command.py -------------------------------------------------------------------------------- /plugins/modules/apcos_dns.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_dns.py -------------------------------------------------------------------------------- /plugins/modules/apcos_ftp.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_ftp.py -------------------------------------------------------------------------------- /plugins/modules/apcos_ntp.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_ntp.py -------------------------------------------------------------------------------- /plugins/modules/apcos_radius.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_radius.py -------------------------------------------------------------------------------- /plugins/modules/apcos_smtp.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_smtp.py -------------------------------------------------------------------------------- /plugins/modules/apcos_snmp.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_snmp.py -------------------------------------------------------------------------------- /plugins/modules/apcos_snmpv3.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_snmpv3.py -------------------------------------------------------------------------------- /plugins/modules/apcos_system.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_system.py -------------------------------------------------------------------------------- /plugins/modules/apcos_web.py: -------------------------------------------------------------------------------- 1 | network/apcos/apcos_web.py -------------------------------------------------------------------------------- /plugins/modules/network/apcos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haught/ansible_apcos/92e6e1156e2867d546bc6f6a67ae763d39b99e8f/plugins/modules/network/apcos/__init__.py -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_command.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | DOCUMENTATION = ''' 8 | --- 9 | module: apcos_command 10 | author: "Matt Haught (@haught)" 11 | short_description: Run commands on remote devices running APC OS 12 | description: 13 | - Sends arbitrary commands to an APC UPS NMC and returns the results 14 | read from the device. This module includes an 15 | argument that will cause the module to wait for a specific condition 16 | before returning or timing out if the condition is not met. 17 | notes: 18 | - Tested APC NMC v3 (AP9641) running APC OS v1.4.2.1 19 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 20 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 21 | stalls output and will not work with ansible 22 | options: 23 | commands: 24 | description: 25 | - List of commands to send to the remote APC device over the 26 | configured provider. The resulting output from the command 27 | is returned. If the I(wait_for) argument is provided, the 28 | module is not returned until the condition is satisfied or 29 | the number of retries has expired. 30 | - If a command sent to the device requires answering a prompt, 31 | checkall and newline if multiple prompts, it is possible to pass 32 | a dict containing I(command), I(answer), I(prompt), I(check_all) 33 | and I(newline).Common answers are 'y' or "\\r" (carriage return, 34 | must be double quotes). See examples. 35 | required: true 36 | type: list 37 | elements: raw 38 | wait_for: 39 | description: 40 | - List of conditions to evaluate against the output of the 41 | command. The task will wait for each condition to be true 42 | before moving forward. If the conditional is not true 43 | within the configured number of retries, the task fails. 44 | See examples. 45 | type: list 46 | elements: str 47 | match: 48 | description: 49 | - The I(match) argument is used in conjunction with the 50 | I(wait_for) argument to specify the match policy. Valid 51 | values are C(all) or C(any). If the value is set to C(all) 52 | then all conditionals in the wait_for must be satisfied. If 53 | the value is set to C(any) then only one of the values must be 54 | satisfied. 55 | default: all 56 | choices: ['any', 'all'] 57 | type: str 58 | retries: 59 | description: 60 | - Specifies the number of retries a command should by tried 61 | before it is considered failed. The command is run on the 62 | target device every retry and evaluated against the 63 | I(wait_for) conditions. 64 | default: 10 65 | type: int 66 | interval: 67 | description: 68 | - Configures the interval in seconds to wait between retries 69 | of the command. If the command does not pass the specified 70 | conditions, the interval indicates how long to wait before 71 | trying the command again. 72 | default: 1 73 | type: int 74 | ''' 75 | 76 | EXAMPLES = """ 77 | tasks: 78 | - name: Run system on remote devices 79 | haught.apcos.apcos_command: 80 | commands: system 81 | 82 | - name: Run multiple commands on remote nodes 83 | haught.apcos.apcos_command: 84 | commands: 85 | - system 86 | - dns 87 | 88 | - name: Run multiple commands and evaluate the output 89 | haught.apcos.apcos_command: 90 | commands: 91 | - system 92 | - dns 93 | wait_for: 94 | - result[0] contains UPS01 95 | - result[1] contains example.net 96 | 97 | - name: Run command that requires answering a prompt 98 | haught.apcos.apcos_command: 99 | commands: 100 | - command: "reboot" 101 | prompt: "Enter 'YES' to continue or ENTER to cancel:" 102 | answer: "YES" 103 | """ 104 | 105 | RETURN = """ 106 | stdout: 107 | description: The set of responses from the commands 108 | returned: always apart from low level errors (such as action plugin) 109 | type: list 110 | sample: ['...', '...'] 111 | stdout_lines: 112 | description: The value of stdout split into a list 113 | returned: always apart from low level errors (such as action plugin) 114 | type: list 115 | sample: [['...', '...'], ['...'], ['...']] 116 | failed_conditions: 117 | description: The list of conditionals that have failed 118 | returned: failed 119 | type: list 120 | sample: ['...', '...'] 121 | """ 122 | import re 123 | import time 124 | 125 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import run_commands 126 | from ansible.module_utils.basic import AnsibleModule 127 | from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ComplexList 128 | from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional 129 | from ansible.module_utils.six import string_types 130 | 131 | 132 | __metaclass__ = type 133 | 134 | 135 | def to_lines(stdout): 136 | for item in stdout: 137 | if isinstance(item, string_types): 138 | item = str(item).replace('\t', ' ').split('\n') 139 | yield item 140 | 141 | 142 | def parse_commands(module, warnings): 143 | command = ComplexList(dict( 144 | command=dict(key=True), 145 | prompt=dict(), 146 | answer=dict() 147 | ), module) 148 | commands = command(module.params['commands']) 149 | for item in list(commands): 150 | if module.check_mode: 151 | disallowed_check_commands = ['bye', 'exit', 'quit', 'delete', 'format', 'clrrst', 152 | 'reboot', 'logzip', 'upsfwupdate', 'resetToDef', 'ledblink'] 153 | if (re.match(r'\S+\s\S+', item['command']) is not None) or (item['command'] in disallowed_check_commands): 154 | warnings.append( 155 | 'only show commands are supported when using check mode, not ' 156 | 'executing `%s`' % item['command'] 157 | ) 158 | commands.remove(item) 159 | return commands 160 | 161 | 162 | def main(): 163 | """main entry point for module execution 164 | """ 165 | argument_spec = dict( 166 | commands=dict(type='list', elements='raw', required=True), 167 | 168 | wait_for=dict(type='list', elements='str'), 169 | match=dict(default='all', choices=['all', 'any']), 170 | 171 | retries=dict(default=10, type='int'), 172 | interval=dict(default=1, type='int') 173 | ) 174 | 175 | module = AnsibleModule( 176 | argument_spec=argument_spec, 177 | supports_check_mode=True 178 | ) 179 | 180 | result = {'changed': False} 181 | 182 | warnings = list() 183 | commands = parse_commands(module, warnings) 184 | result['warnings'] = warnings 185 | 186 | wait_for = module.params['wait_for'] or list() 187 | conditionals = [Conditional(c) for c in wait_for] 188 | 189 | retries = module.params['retries'] 190 | interval = module.params['interval'] 191 | match = module.params['match'] 192 | 193 | while retries > 0: 194 | responses = run_commands(module, commands) 195 | 196 | for item in list(conditionals): 197 | if item(responses): 198 | if match == 'any': 199 | conditionals = list() 200 | break 201 | conditionals.remove(item) 202 | 203 | if not conditionals: 204 | break 205 | 206 | time.sleep(interval) 207 | retries -= 1 208 | 209 | if conditionals: 210 | failed_conditions = [item.raw for item in conditionals] 211 | msg = 'One or more conditional statements have not been satisfied' 212 | module.fail_json(msg=msg, failed_conditions=failed_conditions) 213 | 214 | result.update({ 215 | 'changed': False, 216 | 'stdout': responses, 217 | 'stdout_lines': list(to_lines(responses)) 218 | }) 219 | 220 | module.exit_json(**result) 221 | 222 | 223 | if __name__ == '__main__': 224 | main() 225 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_dns.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_dns 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage dns configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC UPS dns 17 | configuration on APC OS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v1.4.2.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | primaryserver: 25 | description: 26 | - Set the primary DNS server. 27 | type: str 28 | secondaryserver: 29 | description: 30 | - Set the secondary DNS server. 31 | type: str 32 | hostname: 33 | description: 34 | - Set the host name 35 | type: str 36 | domainname: 37 | description: 38 | - Set the domain name 39 | type: str 40 | domainnameipv6: 41 | description: 42 | - Set the domain name IPv6. 43 | type: str 44 | systemnamesync: 45 | description: 46 | - Synchronizes the system name and the hostname. 47 | type: bool 48 | overridemanual: 49 | description: 50 | - Override the manual DNS. 51 | type: bool 52 | ''' 53 | 54 | EXAMPLES = """ 55 | - name: Set dns name 56 | haught.apcos.apcos_dns: 57 | primarydns: "1.1.1.1" 58 | 59 | - name: Set two dns settings 60 | haught.apcos.apcos_dns: 61 | primarydns: "1.1.1.1" 62 | secondarydns: "4.4.4.4" 63 | """ 64 | 65 | RETURN = """ 66 | commands: 67 | description: The list of configuration mode commands to send to the device 68 | returned: always 69 | type: list 70 | sample: 71 | - dns -n ups001 72 | """ 73 | 74 | from ansible.module_utils.basic import AnsibleModule 75 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 76 | load_config, 77 | get_config, 78 | parse_config, 79 | ) 80 | 81 | SOURCE = "dns" 82 | 83 | 84 | def build_commands(module): 85 | commands = [] 86 | config = parse_config(get_config(module, source=SOURCE)) 87 | if module.params['primaryserver']: 88 | if config['primarydnsserver'] != module.params['primaryserver']: 89 | commands.append(SOURCE + ' -p ' + module.params['primaryserver']) 90 | if module.params['secondaryserver']: 91 | if config['secondarydnsserver'] != module.params['secondaryserver']: 92 | commands.append(SOURCE + ' -s ' + module.params['secondaryserver']) 93 | if module.params['domainname']: 94 | if config['domainname'] != module.params['domainname']: 95 | commands.append(SOURCE + ' -d ' + module.params['domainname']) 96 | if module.params['domainnameipv6']: 97 | if config['domainnameipv6'] != module.params['domainnameipv6']: 98 | commands.append(SOURCE + ' -n ' + module.params['domainnameipv6']) 99 | if module.params['hostname']: 100 | if config['hostname'] != module.params['hostname']: 101 | commands.append(SOURCE + ' -h ' + module.params['hostname']) 102 | if module.params['systemnamesync'] is not None: 103 | if config['systemnamesync'].lower() == "disabled" and module.params['systemnamesync'] is True: 104 | commands.append(SOURCE + ' -y enable') 105 | elif config['systemnamesync'].lower() == "enabled" and module.params['systemnamesync'] is False: 106 | commands.append(SOURCE + ' -y disable') 107 | if module.params['overridemanual'] is not None: 108 | if config['overridemanualdnssettings'].lower() == "disabled" and module.params['overridemanual'] is True: 109 | commands.append(SOURCE + ' -OM enable') 110 | elif config['overridemanualdnssettings'].lower() == "enabled" and module.params['overridemanual'] is False: 111 | commands.append(SOURCE + ' -OM disable') 112 | return commands 113 | 114 | 115 | def main(): 116 | """ main entry point for module execution 117 | """ 118 | argument_spec = dict( 119 | primaryserver=dict(type='str'), 120 | secondaryserver=dict(type='str'), 121 | domainname=dict(type='str'), 122 | domainnameipv6=dict(type='str'), 123 | hostname=dict(type='str'), 124 | systemnamesync=dict(type='bool'), 125 | overridemanual=dict(type='bool') 126 | ) 127 | 128 | module = AnsibleModule( 129 | argument_spec=argument_spec, 130 | supports_check_mode=True 131 | ) 132 | 133 | warnings = list() 134 | 135 | result = {'changed': False} 136 | 137 | if warnings: 138 | result['warnings'] = warnings 139 | 140 | commands = build_commands(module) 141 | 142 | result['commands'] = commands 143 | 144 | if commands: 145 | if not module.check_mode: 146 | load_config(module, commands) 147 | 148 | result['changed'] = True 149 | 150 | module.exit_json(**result) 151 | 152 | 153 | if __name__ == '__main__': 154 | main() 155 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_ftp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_ftp 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage FTP configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC FTP 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v2.2.1.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | enable: 25 | description: 26 | - FTP enable. 27 | type: bool 28 | port: 29 | description: 30 | - Port FTP uses. 31 | type: int 32 | ''' 33 | 34 | EXAMPLES = """ 35 | - name: Enable FTP 36 | haught.apcos.apcos_ftp: 37 | enable: true 38 | """ 39 | 40 | RETURN = """ 41 | commands: 42 | description: The list of configuration mode commands to send to the device 43 | returned: always 44 | type: list 45 | sample: 46 | - ftp -S enable 47 | """ 48 | 49 | from ansible.module_utils.basic import AnsibleModule 50 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 51 | load_config, 52 | get_config, 53 | parse_config, 54 | ) 55 | 56 | SOURCE = "ftp" 57 | 58 | 59 | def build_commands(module): 60 | commands = [] 61 | config = parse_config(get_config(module, source=SOURCE)) 62 | if module.params['enable'] is not None: 63 | if config['service'].lower() == "disabled" and module.params['enable'] is True: 64 | commands.append(SOURCE + ' -S enable') 65 | elif config['service'].lower() == "enabled" and module.params['enable'] is False: 66 | commands.append(SOURCE + ' -S disable') 67 | if module.params['port'] is not None: 68 | if config['ftpport'] != str(module.params['port']): 69 | commands.append(SOURCE + ' -p ' + str(module.params['port'])) 70 | return commands 71 | 72 | 73 | def main(): 74 | """ main entry point for module execution 75 | """ 76 | argument_spec = dict( 77 | enable=dict(type='bool'), 78 | port=dict(type='int') 79 | ) 80 | 81 | module = AnsibleModule( 82 | argument_spec=argument_spec, 83 | supports_check_mode=True 84 | ) 85 | 86 | warnings = list() 87 | 88 | result = {'changed': False} 89 | 90 | if warnings: 91 | result['warnings'] = warnings 92 | 93 | commands = [] 94 | commands = build_commands(module) 95 | 96 | result['commands'] = commands 97 | 98 | if commands: 99 | if not module.check_mode: 100 | load_config(module, commands) 101 | 102 | result['changed'] = True 103 | 104 | module.exit_json(**result) 105 | 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_ntp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_ntp 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage ntp configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC ntp 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v1.4.2.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansibles 23 | options: 24 | enable: 25 | description: 26 | - Enable ntp on device. 27 | type: bool 28 | primaryserver: 29 | description: 30 | - Primary ntp server ip. 31 | type: str 32 | secondaryserver: 33 | description: 34 | - Secondary ntp server ip. 35 | type: str 36 | overridemanual: 37 | description: 38 | - Override the manual time settings. 39 | type: bool 40 | ''' 41 | 42 | EXAMPLES = """ 43 | - name: Set ntp name 44 | haught.apcos.apcos_ntp: 45 | primaryip: "10.1.1.1" 46 | 47 | - name: Set two ntp settings 48 | haught.apcos.apcos_ntp: 49 | enable: True 50 | primaryip: "10.1.1.1" 51 | secondaryip: "10.4.4.4" 52 | """ 53 | 54 | RETURN = """ 55 | commands: 56 | description: The list of configuration mode commands to send to the device 57 | returned: always 58 | type: list 59 | sample: 60 | - ntp -a ntplocal 61 | """ 62 | 63 | from ansible.module_utils.basic import AnsibleModule 64 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 65 | load_config, 66 | get_config, 67 | parse_config, 68 | ) 69 | 70 | SOURCE = "ntp" 71 | 72 | 73 | def build_commands(module): 74 | commands = [] 75 | commands = [] 76 | config = parse_config(get_config(module, source=SOURCE)) 77 | if module.params['enable'] is not None: 78 | if config['ntpstatus'].lower() == "disabled" and module.params['enable'] is True: 79 | commands.append(SOURCE + ' -e enable') 80 | elif config['ntpstatus'].lower() == "enabled" and module.params['enable'] is False: 81 | commands.append(SOURCE + ' -e disable') 82 | if module.params['primaryserver']: 83 | if config['primaryntpserver'] != module.params['primaryserver']: 84 | commands.append(SOURCE + ' -p ' + module.params['primaryserver']) 85 | if module.params['secondaryserver']: 86 | if config['secondaryntpserver'] != module.params['secondaryserver']: 87 | commands.append(SOURCE + ' -s ' + module.params['secondaryserver']) 88 | if module.params['overridemanual'] is not None: 89 | if config['overridemanualntpsettings'].lower() == "disabled" and module.params['overridemanual'] is True: 90 | commands.append(SOURCE + ' -OM enable') 91 | elif config['overridemanualntpsettings'].lower() == "enabled" and module.params['overridemanual'] is False: 92 | commands.append(SOURCE + ' -OM disable') 93 | return commands 94 | 95 | 96 | def main(): 97 | """ main entry point for module execution 98 | """ 99 | argument_spec = dict( 100 | enable=dict(type='bool'), 101 | primaryserver=dict(type='str'), 102 | secondaryserver=dict(type='str'), 103 | overridemanual=dict(type='bool') 104 | ) 105 | 106 | module = AnsibleModule( 107 | argument_spec=argument_spec, 108 | supports_check_mode=True 109 | ) 110 | 111 | warnings = list() 112 | 113 | result = {'changed': False} 114 | 115 | if warnings: 116 | result['warnings'] = warnings 117 | 118 | commands = build_commands(module) 119 | result['commands'] = commands 120 | 121 | if commands: 122 | if not module.check_mode: 123 | load_config(module, commands) 124 | result['changed'] = True 125 | module.exit_json(**result) 126 | 127 | 128 | if __name__ == '__main__': 129 | main() 130 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_radius.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_radius 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage radius configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC radius 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v1.4.2.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | access: 25 | description: 26 | - Authentication type of local, radiuslocal, and radius. A value of "local" disables radius, 27 | while "radiuslocal" tries radius first and then falls back to local, and "radius" only 28 | authenticates to radius. 29 | type: str 30 | choices: ['local', 'radiuslocal', 'radius'] 31 | primaryserver: 32 | description: 33 | - Primary radius server ip. 34 | type: str 35 | primaryport: 36 | description: 37 | - Primary radius server port. 38 | type: int 39 | primarysecret: 40 | description: 41 | - Primary radius authentication shared secret. 42 | type: str 43 | primarytimeout: 44 | description: 45 | - Primary radius authentication timeout. 46 | type: int 47 | secondaryserver: 48 | description: 49 | - Secondary radius server ip. 50 | type: str 51 | secondaryport: 52 | description: 53 | - Secondary radius server port. 54 | type: int 55 | secondarysecret: 56 | description: 57 | - Secondary radius authentication shared secret. 58 | type: str 59 | secondarytimeout: 60 | description: 61 | - Secondary radius authentication timeout. 62 | type: int 63 | forcepwchange: 64 | description: 65 | - Force a password change 66 | type: bool 67 | default: False 68 | ''' 69 | 70 | EXAMPLES = """ 71 | - name: Set radius name 72 | haught.apcos.apcos_radius: 73 | primaryip: "10.1.1.1" 74 | 75 | - name: Set two radius settings 76 | haught.apcos.apcos_radius: 77 | primaryip: "10.1.1.1" 78 | secondaryip: "10.4.4.4" 79 | """ 80 | 81 | RETURN = """ 82 | commands: 83 | description: The list of configuration mode commands to send to the device 84 | returned: always 85 | type: list 86 | sample: 87 | - radius -a radiuslocal 88 | """ 89 | 90 | from ansible.module_utils.basic import AnsibleModule 91 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 92 | load_config, 93 | get_config, 94 | parse_config, 95 | ) 96 | 97 | SOURCE = "radius" 98 | 99 | 100 | def build_commands(module): 101 | commands = [] 102 | config = parse_config(get_config(module, source=SOURCE)) 103 | if module.params['access']: 104 | if module.params['access'] == 'local': 105 | if config['access'] != 'Local Only': 106 | commands.append(SOURCE + ' -a ' + module.params['access']) 107 | elif module.params['access'] == 'radiuslocal': 108 | if config['access'] != 'RADIUS, then Local': 109 | commands.append(SOURCE + ' -a ' + module.params['access']) 110 | elif module.params['access'] == 'radius': 111 | if config['access'] != 'RADIUS Only': 112 | commands.append(SOURCE + ' -a ' + module.params['access']) 113 | if module.params['primaryserver'] or module.params['forcepwchange'] is True: 114 | if module.params['primaryserver']: 115 | if config['primaryserver'] != module.params['primaryserver']: 116 | commands.append(SOURCE + ' -p1 ' + module.params['primaryserver']) 117 | if config['primaryserver'] != module.params['primaryserver'] or module.params['forcepwchange'] is True: 118 | if module.params['primarysecret']: 119 | if config['primaryserversecret'] != module.params['primarysecret']: 120 | commands.append(SOURCE + ' -s1 ' + module.params['primarysecret']) 121 | if module.params['primaryport']: 122 | if config['primaryserverport'] != str(module.params['primaryport']): 123 | commands.append(SOURCE + ' -o1 ' + str(module.params['primaryport'])) 124 | if module.params['primarytimeout']: 125 | if config['primaryservertimeout'] != str(module.params['primarytimeout']): 126 | commands.append(SOURCE + ' -t1 ' + str(module.params['primarytimeout'])) 127 | if module.params['secondaryserver'] or module.params['forcepwchange'] is True: 128 | if module.params['secondaryserver']: 129 | if config['secondaryserver'] != module.params['secondaryserver']: 130 | commands.append(SOURCE + ' -p2 ' + module.params['secondaryserver']) 131 | if config['secondaryserver'] != module.params['secondaryserver'] or module.params['forcepwchange'] is True: 132 | if module.params['secondarysecret']: 133 | if config['secondaryserversecret'] != module.params['secondarysecret']: 134 | commands.append(SOURCE + ' -s2 ' + module.params['secondarysecret']) 135 | if module.params['secondaryport']: 136 | if config['secondaryserverport'] != str(module.params['secondaryport']): 137 | commands.append(SOURCE + ' -o2 ' + str(module.params['secondaryport'])) 138 | if module.params['secondarytimeout']: 139 | if config['secondaryservertimeout'] != str(module.params['secondarytimeout']): 140 | commands.append(SOURCE + ' -t2 ' + str(module.params['secondarytimeout'])) 141 | return commands 142 | 143 | 144 | def main(): 145 | """ main entry point for module execution 146 | """ 147 | argument_spec = dict( 148 | access=dict(type='str', choices=['local', 'radiuslocal', 'radius']), 149 | primaryserver=dict(type='str'), 150 | primaryport=dict(type='int'), 151 | primarysecret=dict(type='str', no_log=True), 152 | primarytimeout=dict(type='int'), 153 | secondaryserver=dict(type='str'), 154 | secondaryport=dict(type='int'), 155 | secondarysecret=dict(type='str', no_log=True), 156 | secondarytimeout=dict(type='int'), 157 | forcepwchange=dict(type='bool', default=False) 158 | ) 159 | 160 | module = AnsibleModule( 161 | argument_spec=argument_spec, 162 | supports_check_mode=True 163 | ) 164 | 165 | warnings = list() 166 | 167 | result = {'changed': False} 168 | 169 | if warnings: 170 | result['warnings'] = warnings 171 | 172 | commands = [] 173 | commands = build_commands(module) 174 | 175 | result['commands'] = commands 176 | 177 | if commands: 178 | if not module.check_mode: 179 | load_config(module, commands) 180 | 181 | result['changed'] = True 182 | 183 | module.exit_json(**result) 184 | 185 | 186 | if __name__ == '__main__': 187 | main() 188 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_smtp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_smtp 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage SMTP configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC SMTP 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v2.2.1.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | from_address: 25 | description: 26 | - From address. 27 | type: str 28 | server: 29 | description: 30 | - SMTP server. 31 | type: str 32 | port: 33 | description: 34 | - Port SMTP uses. 35 | type: int 36 | auth: 37 | description: 38 | - SMTP authentication enabled. 39 | type: bool 40 | user: 41 | description: 42 | - Username for auth. 43 | type: str 44 | password: 45 | description: 46 | - Password for auth. 47 | type: str 48 | encryption: 49 | description: 50 | - Encryption option for connection. 51 | type: str 52 | choices: ['none', 'ifavail', 'always', 'implicit'] 53 | require_certificate: 54 | description: 55 | - Require certificate for connection. 56 | type: bool 57 | certificate: 58 | description: 59 | - Certificate file name. 60 | type: str 61 | forcepwchange: 62 | description: 63 | - Force a password change 64 | type: bool 65 | default: False 66 | ''' 67 | 68 | EXAMPLES = """ 69 | - name: Set SMTP server 70 | haught.apcos.apcos_smtp: 71 | server: smtp.example.com 72 | """ 73 | 74 | RETURN = """ 75 | commands: 76 | description: The list of configuration mode commands to send to the device 77 | returned: always 78 | type: list 79 | sample: 80 | - smtp -a enable 81 | """ 82 | 83 | from ansible.module_utils.basic import AnsibleModule 84 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 85 | load_config, 86 | get_config, 87 | parse_config, 88 | ) 89 | 90 | SOURCE = "smtp" 91 | 92 | 93 | def build_commands(module): 94 | commands = [] 95 | config = parse_config(get_config(module, source=SOURCE)) 96 | if module.params['from_address'] is not None: 97 | if config['from'] != module.params['from_address']: 98 | commands.append(SOURCE + ' -f ' + module.params['from_address']) 99 | if module.params['server'] is not None: 100 | if config['server'] != module.params['server']: 101 | commands.append(SOURCE + ' -s ' + module.params['server']) 102 | if module.params['port'] is not None: 103 | if config['port'] != str(module.params['port']): 104 | commands.append(SOURCE + ' -p ' + str(module.params['port'])) 105 | if module.params['auth'] is not None: 106 | if config['auth'].lower() == "disabled" and module.params['auth'] is True: 107 | commands.append(SOURCE + ' -a enable') 108 | elif config['auth'].lower() == "enabled" and module.params['auth'] is False: 109 | commands.append(SOURCE + ' -a disable') 110 | if module.params['user'] is not None: 111 | if config['user'] != module.params['user']: 112 | commands.append(SOURCE + ' -u ' + module.params['user']) 113 | if module.params['password'] is not None: 114 | if config['password'] == '' or module.params['forcepwchange'] is True: 115 | commands.append(SOURCE + ' -w ' + module.params['password']) 116 | if module.params['encryption'] is not None: 117 | if config['encryption'] != module.params['encryption']: 118 | commands.append(SOURCE + ' -e ' + module.params['encryption']) 119 | if module.params['require_certificate'] is not None: 120 | if config['req.cert'].lower() == "disabled" and module.params['require_certificate'] is True: 121 | commands.append(SOURCE + ' -c enable') 122 | elif config['req.cert'].lower() == "enabled" and module.params['require_certificate'] is False: 123 | commands.append(SOURCE + ' -c disable') 124 | if module.params['certificate'] is not None: 125 | if config['certfile'] != module.params['certificate']: 126 | commands.append(SOURCE + ' -i ' + module.params['certificate']) 127 | return commands 128 | 129 | 130 | def main(): 131 | """ main entry point for module execution 132 | """ 133 | argument_spec = dict( 134 | from_address=dict(type='str'), 135 | server=dict(type='str'), 136 | port=dict(type='int'), 137 | auth=dict(type='bool'), 138 | user=dict(type='str'), 139 | password=dict(type='str', no_log=True), 140 | encryption=dict(type='str', choices=['none', 'ifavail', 'always', 'implicit']), 141 | require_certificate=dict(type='bool'), 142 | certificate=dict(type='str'), 143 | forcepwchange=dict(type='bool', default=False) 144 | ) 145 | 146 | module = AnsibleModule( 147 | argument_spec=argument_spec, 148 | supports_check_mode=True 149 | ) 150 | 151 | warnings = list() 152 | 153 | result = {'changed': False} 154 | 155 | if warnings: 156 | result['warnings'] = warnings 157 | 158 | commands = [] 159 | commands = build_commands(module) 160 | 161 | result['commands'] = commands 162 | 163 | if commands: 164 | if not module.check_mode: 165 | load_config(module, commands) 166 | 167 | result['changed'] = True 168 | 169 | module.exit_json(**result) 170 | 171 | 172 | if __name__ == '__main__': 173 | main() 174 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_snmp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_snmp 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage snmp configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC snmp 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v1.4.2.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | enable: 25 | description: 26 | - Global SNMPv1 enable. 27 | type: bool 28 | index: 29 | description: 30 | - Index of SNMPv1 user. 31 | type: int 32 | choices: [1, 2, 3, 4] 33 | community: 34 | description: 35 | - SNMPv1 community name. 36 | type: str 37 | accesstype: 38 | description: 39 | - SNMP access enable for index. 40 | type: str 41 | choices: ['disabled', 'read', 'write', 'writeplus'] 42 | accessaddress: 43 | description: 44 | - SNMPv1 NMS IP/CIDR address for index. 45 | type: str 46 | ''' 47 | 48 | EXAMPLES = """ 49 | - name: Set snmp name 50 | haught.apcos.apcos_snmp: 51 | index: 1 52 | community: "public" 53 | accesstype: "read" 54 | """ 55 | 56 | RETURN = """ 57 | commands: 58 | description: The list of configuration mode commands to send to the device 59 | returned: always 60 | type: list 61 | sample: 62 | - snmp -c1 public 63 | """ 64 | 65 | from ansible.module_utils.basic import AnsibleModule 66 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 67 | load_config, 68 | get_config, 69 | parse_config, 70 | parse_config_section, 71 | ) 72 | 73 | SOURCE = "snmp" 74 | 75 | 76 | def build_commands(module): 77 | commands = [] 78 | config = {} 79 | config['config'] = parse_config(config=get_config(module, source=SOURCE)) 80 | config['access'] = parse_config_section( 81 | config=get_config(module, source=SOURCE), 82 | section='Access Control Summary:', 83 | index=module.params['index'], 84 | indexName='Access Control #') 85 | if module.params['enable'] is not None: 86 | if config['config']['snmpv1'].lower() == "disabled" and module.params['enable'] is True: 87 | commands.append(SOURCE + ' -S enable') 88 | elif config['config']['snmpv1'].lower() == "enabled" and module.params['enable'] is False: 89 | commands.append(SOURCE + ' -S disable') 90 | if module.params['community'] and module.params['index']: 91 | if config['access']['community'] != module.params['community']: 92 | commands.append(SOURCE + ' -c' + str(module.params['index']) + ' ' + module.params['community']) 93 | if module.params['accesstype'] and module.params['index']: 94 | if config['access']['accesstype'] != module.params['accesstype']: 95 | commands.append(SOURCE + ' -a' + str(module.params['index']) + ' ' + module.params['accesstype']) 96 | if module.params['accessaddress'] and module.params['index']: 97 | if config['access']['address'] != module.params['accessaddress']: 98 | commands.append(SOURCE + ' -n' + str(module.params['index']) + ' ' + module.params['accessaddress']) 99 | return commands 100 | 101 | 102 | def main(): 103 | """ main entry point for module execution 104 | """ 105 | argument_spec = dict( 106 | enable=dict(type='bool'), 107 | index=dict(type='int', choices=[1, 2, 3, 4]), 108 | community=dict(type='str', no_log=True), 109 | accesstype=dict(type='str', choices=['disabled', 'read', 'write', 'writeplus']), 110 | accessaddress=dict(type='str') 111 | ) 112 | 113 | module = AnsibleModule( 114 | argument_spec=argument_spec, 115 | required_by={ 116 | 'community': 'index', 117 | 'accesstype': 'index', 118 | 'accessaddress': 'index', 119 | }, 120 | supports_check_mode=True 121 | ) 122 | 123 | warnings = list() 124 | 125 | result = {'changed': False} 126 | 127 | if warnings: 128 | result['warnings'] = warnings 129 | 130 | commands = [] 131 | commands = build_commands(module) 132 | 133 | result['commands'] = commands 134 | 135 | if commands: 136 | if not module.check_mode: 137 | load_config(module, commands) 138 | 139 | result['changed'] = True 140 | 141 | module.exit_json(**result) 142 | 143 | 144 | if __name__ == '__main__': 145 | main() 146 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_snmpv3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_snmpv3 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage snmpv3 configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC snmpv3 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v1.4.2.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | enable: 25 | description: 26 | - Global SNMPv3 enable. 27 | type: bool 28 | index: 29 | description: 30 | - Index of SNMPv3 user. 31 | type: int 32 | choices: [1, 2, 3, 4] 33 | username: 34 | description: 35 | - SNMPv3 user name for index. 36 | type: str 37 | authprotocol: 38 | description: 39 | - SNMPv3 authentication protocol for index. 40 | type: str 41 | choices: ['SHA', 'MD5', 'NONE'] 42 | authphrase: 43 | description: 44 | - SNMPv3 authentication phrase for index. 45 | type: str 46 | privprotocol: 47 | description: 48 | - SNMPv3 privacy protocol for index. 49 | type: str 50 | choices: ['AES', 'DES', 'NONE'] 51 | privphrase: 52 | description: 53 | - SNMPv3 privacy phrase for index. 54 | type: str 55 | access: 56 | description: 57 | - SNMPv3 access enable for index. 58 | type: bool 59 | accessusername: 60 | description: 61 | - SNMPv3 access user name for index. 62 | type: str 63 | accessaddress: 64 | description: 65 | - SNMPv3 NMS IP/CIDR address for index. 66 | type: str 67 | forcepwchange: 68 | description: 69 | - Force a auth/priv phrase change 70 | type: bool 71 | default: False 72 | ''' 73 | 74 | EXAMPLES = """ 75 | - name: Set snmpv3 name 76 | haught.apcos.apcos_snmpv3: 77 | primarysnmpv3: "1.1.1.1" 78 | 79 | - name: Set two snmpv3 settings 80 | haught.apcos.apcos_snmpv3: 81 | primarysnmpv3: "1.1.1.1" 82 | secondarysnmpv3: "4.4.4.4" 83 | """ 84 | 85 | RETURN = """ 86 | commands: 87 | description: The list of configuration mode commands to send to the device 88 | returned: always 89 | type: list 90 | sample: 91 | - snmpv3 -n ups001 92 | """ 93 | 94 | from ansible.module_utils.basic import AnsibleModule 95 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 96 | load_config, 97 | get_config, 98 | parse_config_section, 99 | ) 100 | 101 | SOURCE = "snmpv3" 102 | 103 | 104 | def build_commands(module): 105 | commands = [] 106 | config = {} 107 | config['config'] = parse_config_section(get_config(module, source=SOURCE), 'SNMPv3 Configuration') 108 | config['user'] = parse_config_section(get_config(module, source=SOURCE), 'SNMPv3 User Profiles', module.params['index']) 109 | config['access'] = parse_config_section(get_config(module, source=SOURCE), 'SNMPv3 Access Control', module.params['index']) 110 | if module.params['enable'] is not None: 111 | if config['config']['snmpv3'].lower() == "disabled" and module.params['enable'] is True: 112 | commands.append(SOURCE + ' -S enable') 113 | elif config['config']['snmpv3'].lower() == "enabled" and module.params['enable'] is False: 114 | commands.append(SOURCE + ' -S disable') 115 | if module.params['authprotocol'] and module.params['index']: 116 | if config['user']['authentication'] != module.params['authprotocol']: 117 | commands.append(SOURCE + ' -ap' + str(module.params['index']) + ' ' + module.params['authprotocol']) 118 | if module.params['privprotocol'] and module.params['index']: 119 | if config['user']['encryption'] != module.params['privprotocol']: 120 | commands.append(SOURCE + ' -pp' + str(module.params['index']) + ' ' + module.params['privprotocol']) 121 | if (module.params['username'] or module.params['forcepwchange'] is True) and module.params['index']: 122 | if module.params['username'] and config['user']['username'] != module.params['username']: 123 | commands.append(SOURCE + ' -u' + str(module.params['index']) + ' ' + module.params['username']) 124 | # set password if username changes or set to force 125 | if (module.params['username'] and config['user']['username'] != module.params['username']) or module.params['forcepwchange'] is True: 126 | if module.params['authphrase'] and module.params['index']: 127 | commands.append(SOURCE + ' -a' + str(module.params['index']) + ' ' + module.params['authphrase']) 128 | if module.params['privphrase'] and module.params['index']: 129 | commands.append(SOURCE + ' -c' + str(module.params['index']) + ' ' + module.params['privphrase']) 130 | if module.params['accessusername'] and module.params['index']: 131 | if config['access']['username'] != module.params['accessusername']: 132 | commands.append(SOURCE + ' -au' + str(module.params['index']) + ' ' + module.params['accessusername']) 133 | if module.params['access'] is not None and module.params['index']: 134 | if config['access']['access'].lower() == "disabled" and module.params['access'] is True: 135 | commands.append(SOURCE + ' -ac' + str(module.params['index']) + ' enable') 136 | elif config['access']['access'].lower() == "enabled" and module.params['access'] is False: 137 | commands.append(SOURCE + ' -ac' + str(module.params['index']) + ' disable') 138 | if module.params['accessaddress'] and module.params['index']: 139 | if config['access']['nmsip/hostname'] != module.params['accessaddress']: 140 | commands.append(SOURCE + ' -n' + str(module.params['index']) + ' ' + module.params['accessaddress']) 141 | return commands 142 | 143 | 144 | def main(): 145 | """ main entry point for module execution 146 | """ 147 | argument_spec = dict( 148 | enable=dict(type='bool'), 149 | index=dict(type='int', choices=[1, 2, 3, 4]), 150 | username=dict(type='str'), 151 | authphrase=dict(type='str', no_log=True), 152 | authprotocol=dict(type='str', choices=['SHA', 'MD5', 'NONE']), 153 | privphrase=dict(type='str', no_log=True), 154 | privprotocol=dict(type='str', choices=['AES', 'DES', 'NONE']), 155 | access=dict(type='bool'), 156 | accessusername=dict(type='str'), 157 | accessaddress=dict(type='str'), 158 | forcepwchange=dict(type='bool', default=False) 159 | ) 160 | 161 | module = AnsibleModule( 162 | argument_spec=argument_spec, 163 | required_by={ 164 | 'username': 'index', 165 | 'authphrase': 'index', 166 | 'authprotocol': 'index', 167 | 'privphrase': 'index', 168 | 'privprotocol': 'index', 169 | 'access': 'index', 170 | 'accessusername': 'index', 171 | 'accessaddress': 'index' 172 | }, 173 | supports_check_mode=True 174 | ) 175 | 176 | warnings = list() 177 | 178 | result = {'changed': False} 179 | 180 | if warnings: 181 | result['warnings'] = warnings 182 | 183 | commands = [] 184 | commands = build_commands(module) 185 | 186 | result['commands'] = commands 187 | 188 | if commands: 189 | if not module.check_mode: 190 | load_config(module, commands) 191 | 192 | result['changed'] = True 193 | 194 | module.exit_json(**result) 195 | 196 | 197 | if __name__ == '__main__': 198 | main() 199 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_system.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_system 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage system configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC OS system 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v1.4.2.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | name: 25 | description: 26 | - System system name of device. 27 | type: str 28 | contact: 29 | description: 30 | - Contact name for device. 31 | type: str 32 | location: 33 | description: 34 | - Location of device. 35 | type: str 36 | motd: 37 | description: 38 | - Show a custom message on the logon page of the web UI or the CLI. 39 | type: str 40 | hostnamesync: 41 | description: 42 | - Synchronize the system and the hostname. 43 | type: bool 44 | default: False 45 | ''' 46 | 47 | EXAMPLES = """ 48 | - name: Set system name 49 | haught.apcos.apcos_system: 50 | name: "device01" 51 | 52 | - name: Set two system settings 53 | haught.apcos.apcos_system: 54 | name: "device01" 55 | location: "Bldg-101" 56 | """ 57 | 58 | RETURN = """ 59 | commands: 60 | description: The list of configuration mode commands to send to the device 61 | returned: always 62 | type: list 63 | sample: 64 | - system -l Bldg 101 65 | """ 66 | 67 | from ansible.module_utils.basic import AnsibleModule 68 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 69 | load_config, 70 | get_config, 71 | parse_config, 72 | ) 73 | 74 | SOURCE = "system" 75 | 76 | 77 | def build_commands(module): 78 | commands = [] 79 | config = parse_config(get_config(module, source=SOURCE)) 80 | if module.params['name']: 81 | if config['name'] != module.params['name']: 82 | commands.append(SOURCE + ' -n ' + module.params['name']) 83 | if module.params['contact']: 84 | if config['contact'] != module.params['contact']: 85 | commands.append(SOURCE + ' -c ' + module.params['contact']) 86 | if module.params['location']: 87 | if config['location'] != module.params['location']: 88 | commands.append(SOURCE + ' -l ' + module.params['location']) 89 | if module.params['motd']: 90 | if config['message'] != module.params['motd']: 91 | commands.append(SOURCE + ' -m ' + module.params['motd']) 92 | if module.params['hostnamesync'] is not None: 93 | if config['hostnamesync'].lower() == "disabled" and module.params['hostnamesync'] is True: 94 | commands.append(SOURCE + ' -s enable') 95 | elif config['hostnamesync'].lower() == "enabled" and module.params['hostnamesync'] is False: 96 | commands.append(SOURCE + ' -s disable') 97 | return commands 98 | 99 | 100 | def main(): 101 | """ main entry point for module execution 102 | """ 103 | argument_spec = dict( 104 | name=dict(type='str'), 105 | contact=dict(type='str'), 106 | location=dict(type='str'), 107 | motd=dict(type='str'), 108 | hostnamesync=dict(type='bool', default=False) 109 | ) 110 | 111 | module = AnsibleModule( 112 | argument_spec=argument_spec, 113 | supports_check_mode=True 114 | ) 115 | 116 | warnings = list() 117 | 118 | result = {'changed': False} 119 | 120 | if warnings: 121 | result['warnings'] = warnings 122 | 123 | commands = build_commands(module) 124 | 125 | result['commands'] = commands 126 | 127 | if commands: 128 | if not module.check_mode: 129 | load_config(module, commands) 130 | 131 | result['changed'] = True 132 | 133 | module.exit_json(**result) 134 | 135 | 136 | if __name__ == '__main__': 137 | main() 138 | -------------------------------------------------------------------------------- /plugins/modules/network/apcos/apcos_web.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright: Ansible Team 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | 6 | from __future__ import (absolute_import, division, print_function) 7 | __metaclass__ = type 8 | 9 | 10 | DOCUMENTATION = ''' 11 | --- 12 | module: apcos_web 13 | author: "Matt Haught (@haught)" 14 | short_description: Manage web configuration on APC OS devices. 15 | description: 16 | - This module provides declarative management of APC web 17 | configuration on APC UPS NMC systems. 18 | notes: 19 | - Tested APC NMC v3 (AP9641) running APC OS v2.2.1.1 20 | - APC NMC v2 cards running AOS <= v6.8.2 and APC 21 | NMC v3 cards running AOS < v1.4.2.1 have a bug that 22 | stalls output and will not work with ansible 23 | options: 24 | enablehttp: 25 | description: 26 | - http enable. 27 | type: bool 28 | enablehttps: 29 | description: 30 | - https enable. 31 | type: bool 32 | httpport: 33 | description: 34 | - Port http uses. 35 | type: int 36 | httpsport: 37 | description: 38 | - Port https uses. 39 | type: int 40 | httpsproto: 41 | description: 42 | - Minimum https protocol 43 | type: str 44 | choices: ['TLS1.1', 'TLS1.2'] 45 | limitedstatus: 46 | description: 47 | - Limited status page enabled 48 | type: bool 49 | limitedstatusdefault: 50 | description: 51 | - Limited status page enabled as default 52 | type: bool 53 | tls12ciphersuite: 54 | description: 55 | - TLS1.2 Cipher Suite Filter 56 | type: int 57 | choices: [0, 1, 2, 3, 4] 58 | 59 | ''' 60 | 61 | EXAMPLES = """ 62 | - name: Enable HTTPS 63 | haught.apcos.apcos_web: 64 | httpsenable: true 65 | """ 66 | 67 | RETURN = """ 68 | commands: 69 | description: The list of configuration mode commands to send to the device 70 | returned: always 71 | type: list 72 | sample: 73 | - web -s enable 74 | """ 75 | 76 | from ansible.module_utils.basic import AnsibleModule 77 | from ansible_collections.haught.apcos.plugins.module_utils.network.apcos.apcos import ( 78 | load_config, 79 | get_config, 80 | parse_config, 81 | ) 82 | 83 | SOURCE = "web" 84 | 85 | 86 | def build_commands(module): 87 | commands = [] 88 | config = parse_config(get_config(module, source=SOURCE)) 89 | if module.params['enablehttp'] is not None: 90 | if config['http'].lower() == "disabled" and module.params['enablehttp'] is True: 91 | commands.append(SOURCE + ' -h enable') 92 | elif config['http'].lower() == "enabled" and module.params['enablehttp'] is False: 93 | commands.append(SOURCE + ' -h disable') 94 | if module.params['enablehttps'] is not None: 95 | if config['https'].lower() == "disabled" and module.params['enablehttps'] is True: 96 | commands.append(SOURCE + ' -s enable') 97 | elif config['https'].lower() == "enabled" and module.params['enablehttps'] is False: 98 | commands.append(SOURCE + ' -s disable') 99 | if module.params['httpport'] is not None: 100 | if config['httpport'] != str(module.params['httpport']): 101 | commands.append(SOURCE + ' -ph ' + str(module.params['httpport'])) 102 | if module.params['httpsport'] is not None: 103 | if config['httpsport'] != str(module.params['httpsport']): 104 | commands.append(SOURCE + ' -ps ' + str(module.params['httpsport'])) 105 | if module.params['httpsproto'] is not None: 106 | if config['minimumprotocol'] != module.params['httpsproto']: 107 | commands.append(SOURCE + ' -mp ' + module.params['httpsproto']) 108 | if module.params['limitedstatus'] is not None: 109 | if config['limitedstatusaccess'].lower() == "disabled" and module.params['limitedstatus'] is True: 110 | commands.append(SOURCE + ' -lsp enable') 111 | elif config['limitedstatusaccess'].lower() == "enabled" and module.params['limitedstatus'] is False: 112 | commands.append(SOURCE + ' -lsp disable') 113 | if module.params['limitedstatusdefault'] is not None: 114 | if config['lim.statuspageused'].lower() == "disabled" and module.params['limitedstatusdefault'] is True: 115 | commands.append(SOURCE + ' -lsd enable') 116 | elif config['lim.statuspageused'].lower() == "enabled" and module.params['limitedstatusdefault'] is False: 117 | commands.append(SOURCE + ' -lsd disable') 118 | if module.params['tls12ciphersuite'] is not None: 119 | if config['tls1.2ciphersuitefilter'] != str(module.params['tls12ciphersuite']): 120 | commands.append(SOURCE + ' -cs ' + str(module.params['tls12ciphersuite'])) 121 | return commands 122 | 123 | 124 | def main(): 125 | """ main entry point for module execution 126 | """ 127 | argument_spec = dict( 128 | enablehttp=dict(type='bool'), 129 | enablehttps=dict(type='bool'), 130 | httpport=dict(type='int'), 131 | httpsport=dict(type='int'), 132 | httpsproto=dict(type='str', choices=['TLS1.1', 'TLS1.2']), 133 | limitedstatus=dict(type='bool'), 134 | limitedstatusdefault=dict(type='bool'), 135 | tls12ciphersuite=dict(type='int', choices=[0, 1, 2, 3, 4]) 136 | ) 137 | 138 | module = AnsibleModule( 139 | argument_spec=argument_spec, 140 | supports_check_mode=True 141 | ) 142 | 143 | warnings = list() 144 | 145 | result = {'changed': False} 146 | 147 | if warnings: 148 | result['warnings'] = warnings 149 | 150 | commands = [] 151 | commands = build_commands(module) 152 | 153 | result['commands'] = commands 154 | 155 | if commands: 156 | if not module.check_mode: 157 | load_config(module, commands) 158 | 159 | result['changed'] = True 160 | 161 | module.exit_json(**result) 162 | 163 | 164 | if __name__ == '__main__': 165 | main() 166 | -------------------------------------------------------------------------------- /plugins/terminal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haught/ansible_apcos/92e6e1156e2867d546bc6f6a67ae763d39b99e8f/plugins/terminal/__init__.py -------------------------------------------------------------------------------- /plugins/terminal/apcos.py: -------------------------------------------------------------------------------- 1 | # 2 | # (c) 2018 Red Hat Inc. 3 | # 4 | # This file is part of Ansible 5 | # 6 | # Ansible is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Ansible is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Ansible. If not, see . 18 | # 19 | from __future__ import (absolute_import, division, print_function) 20 | __metaclass__ = type 21 | 22 | import re 23 | from ansible.plugins.terminal import TerminalBase 24 | 25 | 26 | class TerminalModule(TerminalBase): 27 | 28 | terminal_stdout_re = [ 29 | re.compile(br"apc>$"), 30 | ] 31 | 32 | terminal_stderr_re = [ 33 | re.compile(br"E10[0-7]"), 34 | ] 35 | 36 | def on_open_shell(self): 37 | return 38 | -------------------------------------------------------------------------------- /tests/requirements.yml: -------------------------------------------------------------------------------- 1 | integration_tests_dependencies: 2 | - ansible.netcommon 3 | unit_tests_dependencies: 4 | - ansible.netcommon 5 | - community.network -------------------------------------------------------------------------------- /tests/unit/plugins/modules/network/apcos/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haught/ansible_apcos/92e6e1156e2867d546bc6f6a67ae763d39b99e8f/tests/unit/plugins/modules/network/apcos/__init__.py -------------------------------------------------------------------------------- /tests/unit/plugins/modules/network/apcos/apcos_module.py: -------------------------------------------------------------------------------- 1 | # (c) 2018 Red Hat Inc. 2 | # 3 | # This file is part of Ansible 4 | # 5 | # Ansible is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Ansible is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with Ansible. If not, see . 17 | 18 | from __future__ import (absolute_import, division, print_function) 19 | __metaclass__ = type 20 | 21 | import os 22 | import json 23 | 24 | from ansible_collections.community.network.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase 25 | 26 | 27 | fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') 28 | fixture_data = {} 29 | 30 | 31 | def load_fixture(name): 32 | path = os.path.join(fixture_path, name) 33 | 34 | if path in fixture_data: 35 | return fixture_data[path] 36 | 37 | with open(path) as f: 38 | data = f.read() 39 | 40 | try: 41 | data = json.loads(data) 42 | except Exception: 43 | pass 44 | 45 | fixture_data[path] = data 46 | return data 47 | 48 | 49 | class TestApcosModule(ModuleTestCase): 50 | 51 | def execute_module(self, failed=False, changed=False, commands=None, sort=True, defaults=False): 52 | self.load_fixtures(commands) 53 | 54 | if failed: 55 | result = self.failed() 56 | self.assertTrue(result['failed'], result) 57 | else: 58 | result = self.changed(changed) 59 | self.assertEqual(result['changed'], changed, result) 60 | 61 | if commands is not None: 62 | if sort: 63 | self.assertEqual(sorted(commands), sorted(result['commands']), result['commands']) 64 | else: 65 | self.assertEqual(commands, result['commands'], result['commands']) 66 | 67 | return result 68 | 69 | def failed(self): 70 | with self.assertRaises(AnsibleFailJson) as exc: 71 | self.module.main() 72 | 73 | result = exc.exception.args[0] 74 | self.assertTrue(result['failed'], result) 75 | return result 76 | 77 | def changed(self, changed=False): 78 | with self.assertRaises(AnsibleExitJson) as exc: 79 | self.module.main() 80 | 81 | result = exc.exception.args[0] 82 | self.assertEqual(result['changed'], changed, result) 83 | return result 84 | 85 | def load_fixtures(self, commands=None): 86 | pass 87 | -------------------------------------------------------------------------------- /tests/unit/plugins/modules/network/apcos/fixtures/apcos_config_dns.cfg: -------------------------------------------------------------------------------- 1 | E000: Success 2 | Active Primary DNS Server: 1.1.1.1 3 | Active Secondary DNS Server: 8.8.4.4 4 | 5 | Override Manual DNS Settings: enabled 6 | Primary DNS Server: 1.1.1.1 7 | Secondary DNS Server: 8.8.4.4 8 | Domain Name: example.net 9 | Domain Name IPv6: example.net 10 | System Name Sync: Disabled 11 | Host Name: apctest2-1 -------------------------------------------------------------------------------- /tests/unit/plugins/modules/network/apcos/fixtures/apcos_config_ftp.cfg: -------------------------------------------------------------------------------- 1 | E000: Success 2 | Service: disabled 3 | Ftp Port: 21 -------------------------------------------------------------------------------- /tests/unit/plugins/modules/network/apcos/fixtures/apcos_config_ntp.cfg: -------------------------------------------------------------------------------- 1 | E000: Success 2 | NTP status: Enabled 3 | 4 | Active Primary NTP Server: 10.10.10.10 5 | Active Secondary NTP Server: 10.22.10.10 6 | 7 | Override Manual NTP Settings: enabled 8 | Primary NTP Server: 10.10.10.10 9 | Secondary NTP Server: 10.22.10.10 -------------------------------------------------------------------------------- /tests/unit/plugins/modules/network/apcos/fixtures/apcos_config_radius.cfg: -------------------------------------------------------------------------------- 1 | E000: Success 2 | Access: Local Only 3 | Primary Server: 10.11.11.11 4 | Primary Server Port: 1812 5 | Primary Server Secret: