#!/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': 'certified'} DOCUMENTATION = r''' --- module: bigip_log_destination short_description: Manages log destinations on a BIG-IP. description: - Manages log destinations on a BIG-IP. version_added: 2.6 options: name: description: - Specifies the name of the log destination. required: True type: description: - Specifies the type of log destination. - Once created, this parameter cannot be changed. choices: - remote-high-speed-log - remote-syslog - arcsight - splunk - management-port - ipfix required: True description: description: - The description of the log destination. pool_settings: description: - This parameter is only available when C(type) is C(remote-high-speed-log). - Deprecated. Use the equivalent top-level parameters instead. suboptions: pool: description: - Specifies the existing pool of remote high-speed log servers where logs will be sent. - When creating a new destination (and C(type) is C(remote-high-speed-log)), this parameter is required. protocol: description: - Specifies the protocol for the system to use to send logs to the pool of remote high-speed log servers, where the logs are stored. - When creating a new log destination (and C(type) is C(remote-high-speed-log)), if this parameter is not specified, the default is C(tcp). choices: - tcp - udp distribution: description: - Specifies the distribution method used by the Remote High Speed Log destination to send messages to pool members. - When C(adaptive), connections to pool members will be added as required to provide enough logging bandwidth. This can have the undesirable effect of logs accumulating on only one pool member when it provides sufficient logging bandwidth on its own. - When C(balanced), sends each successive log to a new pool member, balancing the logs among them according to the pool's load balancing method. - When C(replicated), replicates each log to all pool members, for redundancy. - When creating a new log destination (and C(type) is C(remote-high-speed-log)), if this parameter is not specified, the default is C(adaptive). choices: - adaptive - balanced - replicated syslog_settings: description: - This parameter is only available when C(type) is C(remote-syslog). - Deprecated. Use the equivalent top-level parameters instead. suboptions: syslog_format: description: - Specifies the method to use to format the logs associated with the remote Syslog log destination. - When creating a new log destination (and C(type) is C(remote-syslog)), if this parameter is not specified, the default is C(bsd-syslog). - The C(syslog) and C(rfc5424) choices are two ways of saying the same thing. - The C(bsd-syslog) and C(rfc3164) choices are two ways of saying the same thing. choices: - bsd-syslog - syslog - legacy-bigip - rfc5424 - rfc3164 forward_to: description: - Specifies the management port log destination, which will be used to forward the logs to a single log server, or a remote high-speed log destination, which will be used to forward the logs to a pool of remote log servers. - When creating a new log destination (and C(type) is C(remote-syslog)), this parameter is required. syslog_format: description: - Specifies the method to use to format the logs associated with the remote Syslog log destination. - When creating a new log destination (and C(type) is C(remote-syslog)), if this parameter is not specified, the default is C(bsd-syslog). - The C(syslog) and C(rfc5424) choices are two ways of saying the same thing. - The C(bsd-syslog) and C(rfc3164) choices are two ways of saying the same thing. choices: - bsd-syslog - syslog - legacy-bigip - rfc5424 - rfc3164 version_added: 2.8 forward_to: description: - When C(type) is C(remote-syslog), specifies the management port log destination, which will be used to forward the logs to a single log server, or a remote high-speed log destination, which will be used to forward the logs to a pool of remote log servers. - When C(type) is C(splunk) or C(arcsight), specifies the log destination to which logs are forwarded. This log destination may be a management port destination, a remote high-speed log destination, or a remote Syslog destination which is configured to send logs to an ArcSight or Splunk server. - When creating a new log destination and C(type) is C(remote-syslog), C(splunk), or C(arcsight), this parameter is required. version_added: 2.8 pool: description: - When C(type) is C(remote-high-speed-log), specifies the existing pool of remote high-speed log servers where logs will be sent. - When C(type) is C(ipfix), specifies the existing LTM pool of remote IPFIX collectors. Any BIG-IP application that uses this log destination sends its IP-traffic logs to this pool of collectors. - When creating a new destination and C(type) is C(remote-high-speed-log) or C(ipfix), this parameter is required. version_added: 2.8 protocol: description: - When C(type) is C(remote-high-speed-log), specifies the protocol for the system to use to send logs to the pool of remote high-speed log servers, where the logs are stored. - When C(type) is C(ipfix), can be IPFIX or Netflow v9, depending on the type of collectors you have in the pool that you specify. - When C(type) is C(management-port), specifies the protocol used to send messages to the specified location. - When C(type) is C(management-port), only C(tcp) and C(udp) are valid values. choices: - tcp - udp - ipfix - netflow-9 version_added: 2.8 distribution: description: - Specifies the distribution method used by the Remote High Speed Log destination to send messages to pool members. - When C(adaptive), connections to pool members will be added as required to provide enough logging bandwidth. This can have the undesirable effect of logs accumulating on only one pool member when it provides sufficient logging bandwidth on its own. - When C(balanced), sends each successive log to a new pool member, balancing the logs among them according to the pool's load balancing method. - When C(replicated), replicates each log to all pool members, for redundancy. - When creating a new log destination and C(type) is C(remote-high-speed-log), if this parameter is not specified, the default is C(adaptive). choices: - adaptive - balanced - replicated version_added: 2.8 address: description: - Specifies the IP address that will receive messages from the specified local Log Destination. - This parameter is only available when C(type) is C(management-port). - When creating a new log destination and C(type) is C(management-port), this parameter is required. version_added: 2.8 port: description: - Specifies the port of the IP address that will receive messages from the specified local Log Destination. - This parameter is only available when C(type) is C(management-port). - When creating a new log destination and C(type) is C(management-port), this parameter is required. version_added: 2.8 transport_profile: description: - Is a transport profile based on either TCP or UDP. - This profile defines the TCP or UDP options used to send IP-traffic logs to the pool of collectors. - This parameter is only available when C(type) is C(ipfix). version_added: 2.8 server_ssl_profile: description: - If the C(transport_profile) is a TCP profile, you can use this field to choose a Secure Socket Layer (SSL) profile for sending logs to the IPFIX collectors. - An SSL server profile defines how to communicate securely over SSL or Transport Layer Security (TLS). - This parameter is only available when C(type) is C(ipfix). version_added: 2.8 template_retransmit_interval: description: - Enter the time (in seconds) between each transmission of IPFIX templates to the pool of IPFIX collectors. - The logging destination periodically retransmits all of its IPFIX templates at the interval you set in this field. These retransmissions are helpful for UDP, a lossy transport mechanism. - This parameter is only available when C(type) is C(ipfix). version_added: 2.8 template_delete_delay: description: - Enter the time (in seconds) that the BIG-IP device should pause between deleting an obsolete IPFIX template and reusing its template ID. - This feature is useful for systems where you use iRules to create customized IPFIX templates. version_added: 2.8 partition: description: - Device partition to manage resources on. default: Common state: description: - When C(present), ensures that the resource exists. - When C(absent), ensures the resource is removed. default: present choices: - present - absent extends_documentation_fragment: f5 author: - Tim Rupp (@caphrim007) ''' EXAMPLES = r''' - name: Create a high-speed logging destination bigip_log_destination: name: foo type: remote-high-speed-log pool: my-ltm-pool provider: password: secret server: lb.mydomain.com user: admin delegate_to: localhost - name: Create a remote-syslog logging destination bigip_log_destination: name: foo type: remote-syslog syslog_format: rfc5424 forward_to: my-destination provider: password: secret server: lb.mydomain.com user: admin delegate_to: localhost ''' RETURN = r''' forward_to: description: The new Forward To value. returned: changed type: string sample: /Common/dest1 pool: description: The new Pool value. returned: changed type: string sample: /Common/pool1 distribution: description: The new Distribution Method value. returned: changed type: string sample: balanced protocol: description: The new Protocol value. returned: changed type: string sample: tcp syslog_format: description: The new Syslog format value. returned: changed type: string sample: syslog address: description: The new Address value. returned: changed type: string sample: 1.2.3.2 port: description: The new Port value. returned: changed type: int sample: 2020 template_delete_delay: description: The new Template Delete Delay value. returned: changed type: int sample: 20 template_retransmit_interval: description: The new Template Retransmit Interval value. returned: changed type: int sample: 200 transport_profile: description: The new Transport Profile value. returned: changed type: string sample: /Common/tcp server_ssl_profile: description: The new Server SSL Profile value. returned: changed type: string sample: /Common/serverssl ''' from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback try: from library.module_utils.network.f5.bigip import F5RestClient from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import AnsibleF5Parameters from library.module_utils.network.f5.common import cleanup_tokens from library.module_utils.network.f5.common import fq_name from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import transform_name from library.module_utils.network.f5.common import exit_json from library.module_utils.network.f5.common import fail_json from library.module_utils.network.f5.compare import cmp_str_with_none except ImportError: from ansible.module_utils.network.f5.bigip import F5RestClient from ansible.module_utils.network.f5.common import F5ModuleError from ansible.module_utils.network.f5.common import AnsibleF5Parameters from ansible.module_utils.network.f5.common import cleanup_tokens from ansible.module_utils.network.f5.common import fq_name from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import transform_name from ansible.module_utils.network.f5.common import exit_json from ansible.module_utils.network.f5.common import fail_json from ansible.module_utils.network.f5.compare import cmp_str_with_none class V1Parameters(AnsibleF5Parameters): """Base Parameters for remote-syslog """ api_map = { 'remoteHighSpeedLog': 'forward_to', 'format': 'syslog_format' } api_attributes = [ 'remoteHighSpeedLog', 'format' ] returnables = [ 'forward_to', 'syslog_format' ] updatables = [ 'forward_to', 'syslog_format', 'type' ] # TODO(Remove in 2.12) class V1ModuleParameters(V1Parameters): @property def forward_to(self): if self._values['forward_to']: result = self._values['forward_to'] else: if self._values['syslog_settings'] is None: return None result = self._values['syslog_settings'].get('forward_to', None) if result: result = fq_name(self.partition, result) return result @property def syslog_format(self): if self._values['syslog_format']: result = self._values['syslog_format'] else: if self._values['syslog_settings'] is None: return None result = self._values['syslog_settings'].get('syslog_format', None) if result == 'syslog': result = 'rfc5424' if result == 'bsd-syslog': result = 'rfc3164' return result # TODO(Remove in 2.12) class V1ApiParameters(V1Parameters): @property def type(self): return 'remote-syslog' # TODO(Remove in 2.12) class V1Changes(V1Parameters): def to_return(self): result = {} try: for returnable in self.returnables: result[returnable] = getattr(self, returnable) result = self._filter_params(result) except Exception: pass return result # TODO(Remove in 2.12) class V1UsableChanges(V1Changes): pass # TODO(Remove in 2.12) class V1ReportableChanges(V1Changes): pass # TODO(Remove in 2.12) class V2Parameters(AnsibleF5Parameters): """Base Parameters for remote-high-speed-log """ api_map = { 'poolName': 'pool' } api_attributes = [ 'distribution', 'poolName', 'protocol' ] returnables = [ 'pool', 'distribution', 'protocol' ] updatables = [ 'pool', 'distribution', 'protocol', 'type' ] # TODO(Remove in 2.12) class V2ModuleParameters(V2Parameters): @property def pool(self): if self._values['pool']: result = self._values['pool'] else: if self._values['pool_settings'] is None: return None result = self._values['pool_settings'].get('pool', None) if result: result = fq_name(self.partition, result) return result @property def protocol(self): if self._values['protocol']: return self._values['protocol'] else: if self._values['pool_settings'] is None: return None return self._values['pool_settings'].get('protocol', None) @property def distribution(self): if self._values['distribution']: return self._values['distribution'] else: if self._values['pool_settings'] is None: return None return self._values['pool_settings'].get('distribution', None) # TODO(Remove in 2.12) class V2ApiParameters(V2Parameters): @property def type(self): return 'remote-high-speed-log' # TODO(Remove in 2.12) class V2Changes(V2Parameters): def to_return(self): result = {} try: for returnable in self.returnables: result[returnable] = getattr(self, returnable) result = self._filter_params(result) except Exception: pass return result # TODO(Remove in 2.12) class V2UsableChanges(V2Changes): pass # TODO(Remove in 2.12) class V2ReportableChanges(V2Changes): pass class V3Parameters(AnsibleF5Parameters): api_map = { 'forwardTo': 'forward_to', 'poolName': 'pool', 'remoteHighSpeedLog': 'forward_to', 'format': 'syslog_format', 'ipAddress': 'address', 'protocolVersion': 'protocol', 'templateDeleteDelay': 'template_delete_delay', 'templateRetransmitInterval': 'template_retransmit_interval', 'transportProfile': 'transport_profile', 'serversslProfile': 'server_ssl_profile', } api_attributes = [ 'forwardTo', 'distribution', 'poolName', 'protocol', 'remoteHighSpeedLog', 'format', 'ipAddress', 'port', 'serversslProfile', 'transportProfile', 'templateRetransmitInterval', 'templateDeleteDelay', 'protocolVersion', ] returnables = [ 'forward_to', 'pool', 'distribution', 'protocol', 'syslog_format', 'address', 'port', 'template_delete_delay', 'template_retransmit_interval', 'transport_profile', 'server_ssl_profile', ] updatables = [ 'forward_to', 'type', 'pool', 'distribution', 'protocol', 'syslog_format', 'address', 'port', 'template_delete_delay', 'template_retransmit_interval', 'transport_profile', 'server_ssl_profile', 'type', ] class V3ModuleParameters(V3Parameters): @property def forward_to(self): if self._values['forward_to'] is None: return None return fq_name(self.partition, self._values['forward_to']) @property def pool(self): if self._values['pool'] is None: return None return fq_name(self.partition, self._values['pool']) @property def syslog_format(self): if self._values['syslog_format'] is None: return None result = self._values['syslog_format'] if result == 'syslog': result = 'rfc5424' if result == 'bsd-syslog': result = 'rfc3164' return result @property def server_ssl_profile(self): if self._values['server_ssl_profile'] is None: return None elif self._values['server_ssl_profile'] in ['', 'none']: return '' return fq_name(self.partition, self._values['server_ssl_profile']) @property def transport_profile(self): if self._values['transport_profile'] is None: return None return fq_name(self.partition, self._values['transport_profile']) class V3ApiParameters(V3Parameters): pass class V3Changes(V3Parameters): def to_return(self): result = {} try: for returnable in self.returnables: result[returnable] = getattr(self, returnable) result = self._filter_params(result) except Exception: pass return result class V3UsableChanges(V3Changes): pass class V3ReportableChanges(V3Changes): pass class Difference(object): def __init__(self, want, have=None): self.want = want self.have = have def compare(self, param): try: result = getattr(self, param) return result except AttributeError: return self.__default(param) def __default(self, param): attr1 = getattr(self.want, param) try: attr2 = getattr(self.have, param) if attr1 != attr2: return attr1 except AttributeError: return attr1 @property def type(self): if self.want.type != self.have.type: raise F5ModuleError( "'type' cannot be changed once it is set." ) @property def server_ssl_profile(self): return cmp_str_with_none(self.want.server_ssl_profile, self.have.server_ssl_profile) @property def transport_profile(self): return cmp_str_with_none(self.want.transport_profile, self.have.transport_profile) class BaseManager(object): def __init__(self, *args, **kwargs): self.module = kwargs.get('module', None) self.client = kwargs.get('client', None) def _set_changed_options(self): changed = {} for key in self.get_returnables(): if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: self.changes = self.get_usable_changes(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) updatables = self.get_updatables() changed = dict() for k in updatables: change = diff.compare(k) if change is None: continue else: if isinstance(change, dict): changed.update(change) else: changed[k] = change if changed: self.changes = self.get_usable_changes(params=changed) return True return False def should_update(self): result = self._update_changed_options() if result: return True return False def exec_module(self): changed = False result = dict() state = self.want.state if state == "present": changed = self.present() elif state == "absent": changed = self.absent() reportable = self.get_reportable_changes(params=self.changes.to_return()) changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) self._announce_deprecations(result) return result def _announce_deprecations(self, result): warnings = result.pop('__warnings', []) for warning in warnings: self.client.module.deprecate( msg=warning['msg'], version=warning['version'] ) def _validate_creation_parameters(self): pass def present(self): if self.exists(): return self.update() else: return self.create() def update(self): self.have = self.read_current_from_device() if not self.should_update(): return False if self.module.check_mode: return True self.update_on_device() return True def remove(self): if self.module.check_mode: return True self.remove_from_device() if self.exists(): raise F5ModuleError("Failed to delete the resource.") return True def create(self): self._validate_creation_parameters() self._set_changed_options() if self.module.check_mode: return True self.create_on_device() return True def absent(self): if self.exists(): return self.remove() return False class V1Manager(BaseManager): """Manages remote-syslog settings """ def __init__(self, *args, **kwargs): super(V1Manager, self).__init__(*args, **kwargs) self.want = self.get_module_params(params=self.module.params) self.have = self.get_api_params() self.changes = self.get_usable_changes() def _validate_creation_parameters(self): if self.want.syslog_format is None: self.want.update({'syslog_format': 'bsd-syslog'}) if self.want.forward_to is None: raise F5ModuleError( "'forward_to' is required when creating a new remote-syslog destination." ) # TODO(In 2.12, these get_* methods should no longer be needed) def get_reportable_changes(self, params=None): if params: return V1ReportableChanges(params=params) return V1ReportableChanges() def get_usable_changes(self, params=None): if params: return V1UsableChanges(params=params) return V1UsableChanges() def get_returnables(self): return V1ApiParameters.returnables def get_updatables(self): return V1ApiParameters.updatables def get_module_params(self, params=None): if params: return V1ModuleParameters(params=params) return V1ModuleParameters() def get_api_params(self, params=None): if params: return V1ApiParameters(params=params) return V1ApiParameters() def exists(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-syslog/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError: return False if resp.status == 404 or 'code' in response and response['code'] == 404: return False return True def create_on_device(self): params = self.changes.api_params() params['name'] = self.want.name params['partition'] = self.want.partition uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-syslog/".format( self.client.provider['server'], self.client.provider['server_port'] ) resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def update_on_device(self): params = self.changes.api_params() uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-syslog/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.patch(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def remove_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-syslog/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.delete(uri) if resp.status == 200: return True def read_current_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-syslog/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) response['type'] = 'remote-syslog' return V1ApiParameters(params=response) class V2Manager(BaseManager): """Manages remote-high-speed-log settings """ def __init__(self, *args, **kwargs): super(V2Manager, self).__init__(*args, **kwargs) self.want = self.get_module_params(params=self.module.params) self.have = self.get_api_params() self.changes = self.get_usable_changes() def get_reportable_changes(self, params=None): if params: return V2ReportableChanges(params=params) return V2ReportableChanges() def get_usable_changes(self, params=None): if params: return V2UsableChanges(params=params) return V2UsableChanges() def _validate_creation_parameters(self): if self.want.protocol is None: self.want.update({'protocol': 'tcp'}) if self.want.distribution is None: self.want.update({'distribution': 'adaptive'}) if self.want.pool is None: raise F5ModuleError( "'pool' is required when creating a new remote-high-speed-log destination." ) def get_returnables(self): return V2ApiParameters.returnables def get_updatables(self): return V2ApiParameters.updatables def get_module_params(self, params=None): if params: return V2ModuleParameters(params=params) return V2ModuleParameters() def get_api_params(self, params=None): if params: return V2ApiParameters(params=params) return V2ApiParameters() def exists(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-high-speed-log/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError: return False if resp.status == 404 or 'code' in response and response['code'] == 404: return False return True def create_on_device(self): params = self.changes.api_params() params['name'] = self.want.name params['partition'] = self.want.partition uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-high-speed-log/".format( self.client.provider['server'], self.client.provider['server_port'] ) resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def update_on_device(self): params = self.changes.api_params() uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-high-speed-log/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.patch(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def remove_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-high-speed-log/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.delete(uri) if resp.status == 200: return True def read_current_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/remote-high-speed-log/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) response['type'] = 'remote-high-speed-log' return V2ApiParameters(params=response) class V3Manager(BaseManager): def __init__(self, *args, **kwargs): super(V3Manager, self).__init__(*args, **kwargs) self.want = self.get_module_params(params=self.module.params) self.have = self.get_api_params() self.changes = self.get_usable_changes() def get_reportable_changes(self, params=None): if params: return V3ReportableChanges(params=params) return V3ReportableChanges() def get_usable_changes(self, params=None): if params: return V3UsableChanges(params=params) return V3UsableChanges() def _validate_creation_parameters(self): if self.want.forward_to is None: raise F5ModuleError( "'forward_to' is required when creating a new arcsight destination." ) def get_returnables(self): return V3ApiParameters.returnables def get_updatables(self): return V3ApiParameters.updatables def get_module_params(self, params=None): if params: return V3ModuleParameters(params=params) return V3ModuleParameters() def get_api_params(self, params=None): if params: return V3ApiParameters(params=params) return V3ApiParameters() def exists(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/arcsight/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError: return False if resp.status == 404 or 'code' in response and response['code'] == 404: return False return True def create_on_device(self): params = self.changes.api_params() params['name'] = self.want.name params['partition'] = self.want.partition uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/arcsight/".format( self.client.provider['server'], self.client.provider['server_port'] ) resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def update_on_device(self): params = self.changes.api_params() uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/arcsight/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.patch(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def remove_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/arcsight/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.delete(uri) if resp.status == 200: return True def read_current_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/arcsight/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) response['type'] = 'arcsight' return V3ApiParameters(params=response) class V4Manager(BaseManager): """Manager for Splunk Do not worry about the usage of V3 classes in this V4 manager. In Ansible 2.12, the Parameter classes will undergo a rename because of parameters being deprecated. The correct Parameter classes to use in this class are the V3 Parameter classes. """ def __init__(self, *args, **kwargs): super(V4Manager, self).__init__(*args, **kwargs) self.want = self.get_module_params(params=self.module.params) self.have = self.get_api_params() self.changes = self.get_usable_changes() def get_reportable_changes(self, params=None): if params: return V3ReportableChanges(params=params) return V3ReportableChanges() def get_usable_changes(self, params=None): if params: return V3UsableChanges(params=params) return V3UsableChanges() def _validate_creation_parameters(self): if self.want.forward_to is None: raise F5ModuleError( "'forward_to' is required when creating a new splunk destination." ) def get_returnables(self): return V3ApiParameters.returnables def get_updatables(self): return V3ApiParameters.updatables def get_module_params(self, params=None): if params: return V3ModuleParameters(params=params) return V3ModuleParameters() def get_api_params(self, params=None): if params: return V3ApiParameters(params=params) return V3ApiParameters() def exists(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/splunk/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError: return False if resp.status == 404 or 'code' in response and response['code'] == 404: return False return True def create_on_device(self): params = self.changes.api_params() params['name'] = self.want.name params['partition'] = self.want.partition uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/splunk/".format( self.client.provider['server'], self.client.provider['server_port'] ) resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def update_on_device(self): params = self.changes.api_params() uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/splunk/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.patch(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def remove_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/splunk/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.delete(uri) if resp.status == 200: return True def read_current_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/splunk/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) response['type'] = 'splunk' return V3ApiParameters(params=response) class V5Manager(BaseManager): """Manager for Management Port Do not worry about the usage of V3 classes in this V5 manager. In Ansible 2.12, the Parameter classes will undergo a rename because of parameters being deprecated. The correct Parameter classes to use in this class are the V3 Parameter classes. """ def __init__(self, *args, **kwargs): super(V5Manager, self).__init__(*args, **kwargs) self.want = self.get_module_params(params=self.module.params) self.have = self.get_api_params() self.changes = self.get_usable_changes() def get_reportable_changes(self, params=None): if params: return V3ReportableChanges(params=params) return V3ReportableChanges() def get_usable_changes(self, params=None): if params: return V3UsableChanges(params=params) return V3UsableChanges() def _validate_creation_parameters(self): if self.want.address is None: raise F5ModuleError( "'address' is required when creating a new management-port destination." ) if self.want.port is None: raise F5ModuleError( "'port' is required when creating a new management-port destination." ) def get_returnables(self): return V3ApiParameters.returnables def get_updatables(self): return V3ApiParameters.updatables def get_module_params(self, params=None): if params: return V3ModuleParameters(params=params) return V3ModuleParameters() def get_api_params(self, params=None): if params: return V3ApiParameters(params=params) return V3ApiParameters() def exists(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/management-port/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError: return False if resp.status == 404 or 'code' in response and response['code'] == 404: return False return True def create_on_device(self): params = self.changes.api_params() params['name'] = self.want.name params['partition'] = self.want.partition uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/management-port/".format( self.client.provider['server'], self.client.provider['server_port'] ) resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def update_on_device(self): params = self.changes.api_params() uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/management-port/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.patch(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def remove_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/management-port/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.delete(uri) if resp.status == 200: return True def read_current_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/management-port/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) response['type'] = 'management-port' return V3ApiParameters(params=response) class V6Manager(BaseManager): """Manager for IPFIX Do not worry about the usage of V3 classes in this V6 manager. In Ansible 2.12, the Parameter classes will undergo a rename because of parameters being deprecated. The correct Parameter classes to use in this class are the V3 Parameter classes. """ def __init__(self, *args, **kwargs): super(V6Manager, self).__init__(*args, **kwargs) self.want = self.get_module_params(params=self.module.params) self.have = self.get_api_params() self.changes = self.get_usable_changes() def get_reportable_changes(self, params=None): if params: return V3ReportableChanges(params=params) return V3ReportableChanges() def get_usable_changes(self, params=None): if params: return V3UsableChanges(params=params) return V3UsableChanges() def _validate_creation_parameters(self): if self.want.protocol is None: raise F5ModuleError( "'protocol' is required when creating a new ipfix destination." ) if self.want.pool is None: raise F5ModuleError( "'port' is required when creating a new ipfix destination." ) if self.want.transport_profile is None: raise F5ModuleError( "'transport_profile' is required when creating a new ipfix destination." ) def get_returnables(self): return V3ApiParameters.returnables def get_updatables(self): return V3ApiParameters.updatables def get_module_params(self, params=None): if params: return V3ModuleParameters(params=params) return V3ModuleParameters() def get_api_params(self, params=None): if params: return V3ApiParameters(params=params) return V3ApiParameters() def exists(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/ipfix/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError: return False if resp.status == 404 or 'code' in response and response['code'] == 404: return False return True def create_on_device(self): params = self.changes.api_params() params['name'] = self.want.name params['partition'] = self.want.partition uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/ipfix/".format( self.client.provider['server'], self.client.provider['server_port'] ) resp = self.client.api.post(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] in [400, 403]: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def update_on_device(self): params = self.changes.api_params() uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/ipfix/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.patch(uri, json=params) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) def remove_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/ipfix/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.delete(uri) if resp.status == 200: return True def read_current_from_device(self): uri = "https://{0}:{1}/mgmt/tm/sys/log-config/destination/ipfix/{2}".format( self.client.provider['server'], self.client.provider['server_port'], transform_name(self.want.partition, self.want.name) ) resp = self.client.api.get(uri) try: response = resp.json() except ValueError as ex: raise F5ModuleError(str(ex)) if 'code' in response and response['code'] == 400: if 'message' in response: raise F5ModuleError(response['message']) else: raise F5ModuleError(resp.content) response['type'] = 'ipfix' return V3ApiParameters(params=response) class ModuleManager(object): def __init__(self, *args, **kwargs): self.kwargs = kwargs self.module = kwargs.get('module', None) def exec_module(self): if self.module.params['type'] == 'remote-syslog': manager = self.get_manager('v1') elif self.module.params['type'] == 'remote-high-speed-log': manager = self.get_manager('v2') elif self.module.params['type'] == 'arcsight': manager = self.get_manager('v3') elif self.module.params['type'] == 'splunk': manager = self.get_manager('v4') elif self.module.params['type'] == 'management-port': manager = self.get_manager('v5') elif self.module.params['type'] == 'ipfix': manager = self.get_manager('v6') else: raise F5ModuleError( "Unknown type specified." ) result = manager.exec_module() return result def get_manager(self, type): if type == 'v1': return V1Manager(**self.kwargs) elif type == 'v2': return V2Manager(**self.kwargs) elif type == 'v3': return V3Manager(**self.kwargs) elif type == 'v4': return V4Manager(**self.kwargs) elif type == 'v5': return V5Manager(**self.kwargs) elif type == 'v6': return V6Manager(**self.kwargs) class ArgumentSpec(object): def __init__(self): self.supports_check_mode = True argument_spec = dict( name=dict(required=True), type=dict( required=True, choices=[ 'arcsight', 'remote-high-speed-log', 'remote-syslog', 'splunk', 'management-port', 'ipfix', ] ), description=dict(), syslog_format=dict( choices=[ 'bsd-syslog', 'syslog', 'legacy-bigip', 'rfc5424', 'rfc3164' ] ), forward_to=dict(), pool=dict(), protocol=dict( choices=['tcp', 'udp', 'ipfix', 'netflow-9'] ), distribution=dict( choices=[ 'adaptive', 'balanced', 'replicated', ] ), state=dict( default='present', choices=['present', 'absent'] ), partition=dict( default='Common', fallback=(env_fallback, ['F5_PARTITION']) ), address=dict(), port=dict(type='int'), transport_profile=dict(), server_ssl_profile=dict(), template_retransmit_interval=dict(type='int'), template_delete_delay=dict(type='int'), # Deprecated settings pool_settings=dict( type='dict', suboptions=dict( pool=dict(), protocol=dict( choices=['tcp', 'udp'] ), distribution=dict( choices=[ 'adaptive', 'balanced', 'replicated', ] ) ), removed_in_version=2.12, ), syslog_settings=dict( type='dict', suboptions=dict( syslog_format=dict( choices=[ 'bsd-syslog', 'syslog', 'legacy-bigip', 'rfc5424', 'rfc3164' ] ), forward_to=dict() ), removed_in_version=2.12, ), ) self.argument_spec = {} self.argument_spec.update(f5_argument_spec) self.argument_spec.update(argument_spec) self.mutually_exclusive = [ ['syslog_settings', 'syslog_format'], ['syslog_settings', 'forward_to'], ['pool_settings', 'pool'], ['pool_settings', 'protocol'], ['pool_settings', 'distribution'], ] def main(): spec = ArgumentSpec() module = AnsibleModule( argument_spec=spec.argument_spec, supports_check_mode=spec.supports_check_mode ) client = F5RestClient(**module.params) try: mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) exit_json(module, results, client) except F5ModuleError as ex: cleanup_tokens(client) fail_json(module, ex, client) if __name__ == '__main__': main()