#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright (c) 2017 F5 Networks Inc. # 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 = r''' module: bigip_remote_syslog short_description: Manipulate remote syslog settings on a BIG-IP description: - Manipulate remote syslog settings on a BIG-IP. version_added: 2.5 options: remote_host: description: - Specifies the IP address, or hostname, for the remote system to which the system sends log messages. required: True remote_port: description: - Specifies the port that the system uses to send messages to the remote logging server. When creating a remote syslog, if this parameter is not specified, the default value C(514) is used. local_ip: description: - Specifies the local IP address of the system that is logging. To provide no local IP, specify the value C(none). When creating a remote syslog, if this parameter is not specified, the default value C(none) is used. notes: - Requires the f5-sdk Python package on the host. This is as easy as pip install f5-sdk. - Requires the netaddr Python package on the host. This is as easy as pip install netaddr. extends_documentation_fragment: f5 requirements: - f5-sdk >= 2.2.0 - netaddr author: - Tim Rupp (@caphrim007) ''' EXAMPLES = r''' - name: Add a remote syslog server to log to bigip_remote_syslog: remote_host: 10.10.10.10 password: secret server: lb.mydomain.com user: admin validate_certs: no delegate_to: localhost - name: Add a remote syslog server on a non-standard port to log to bigip_remote_syslog: remote_host: 10.10.10.10 remote_port: 1234 password: secret server: lb.mydomain.com user: admin validate_certs: no delegate_to: localhost ''' RETURN = r''' remote_port: description: New remote port of the remote syslog server. returned: changed type: int sample: 514 local_ip: description: The new local IP of the remote syslog server returned: changed type: string sample: 10.10.10.10 ''' import re try: import netaddr HAS_NETADDR = True except ImportError: HAS_NETADDR = False from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.f5_utils import AnsibleF5Parameters from ansible.module_utils.f5_utils import F5ModuleError from ansible.module_utils.six import iteritems from collections import defaultdict try: from ansible.module_utils.f5_utils import HAS_F5SDK from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError except ImportError: HAS_F5SDK = False class Parameters(AnsibleF5Parameters): updatables = [ 'remote_port', 'local_ip', 'remoteServers' ] returnables = [ 'remote_port', 'local_ip' ] api_attributes = [ 'remoteServers' ] def __init__(self, params=None): self._values = defaultdict(lambda: None) self._values['__warnings'] = [] if params: self.update(params=params) def update(self, params=None): if params: for k, v in iteritems(params): if self.api_map is not None and k in self.api_map: map_key = self.api_map[k] else: map_key = k # Handle weird API parameters like `dns.proxy.__iter__` by # using a map provided by the module developer class_attr = getattr(type(self), map_key, None) if isinstance(class_attr, property): # There is a mapped value for the api_map key if class_attr.fset is None: # If the mapped value does not have # an associated setter self._values[map_key] = v else: # The mapped value has a setter setattr(self, map_key, v) else: # If the mapped value is not a @property self._values[map_key] = v def to_return(self): result = {} for returnable in self.returnables: result[returnable] = getattr(self, returnable) result = self._filter_params(result) return result def api_params(self): result = {} for api_attribute in self.api_attributes: if self.api_map is not None and api_attribute in self.api_map: result[api_attribute] = getattr(self, self.api_map[api_attribute]) else: result[api_attribute] = getattr(self, api_attribute) result = self._filter_params(result) return result @property def remote_host(self): try: # Check for valid IPv4 or IPv6 entries netaddr.IPAddress(self._values['remote_host']) return self._values['remote_host'] except netaddr.core.AddrFormatError: # else fallback to checking reasonably well formatted hostnames if self.is_valid_hostname(self._values['remote_host']): return str(self._values['remote_host']) raise F5ModuleError( "The provided 'remote_host' is not a valid IP or hostname" ) def is_valid_hostname(self, host): """Reasonable attempt at validating a hostname Compiled from various paragraphs outlined here https://tools.ietf.org/html/rfc3696#section-2 https://tools.ietf.org/html/rfc1123 Notably, * Host software MUST handle host names of up to 63 characters and SHOULD handle host names of up to 255 characters. * The "LDH rule", after the characters that it permits. (letters, digits, hyphen) * If the hyphen is used, it is not permitted to appear at either the beginning or end of a label :param host: :return: """ if len(host) > 255: return False host = host.rstrip(".") allowed = re.compile(r'(?!-)[A-Z0-9-]{1,63}(?