1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# https://github.com/ansible/ansible/issues/65816 4# https://github.com/PyCQA/pylint/issues/214 5 6# (c) 2018, Adam Miller (admiller@redhat.com) 7# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 8 9from __future__ import absolute_import, division, print_function 10 11__metaclass__ = type 12 13DOCUMENTATION = """ 14--- 15module: data_input_network 16short_description: Manage Splunk Data Inputs of type TCP or UDP 17description: 18 - This module allows for addition or deletion of TCP and UDP Data Inputs in Splunk. 19version_added: "1.0.0" 20options: 21 protocol: 22 description: 23 - Choose between tcp or udp 24 required: True 25 choices: 26 - 'tcp' 27 - 'udp' 28 type: str 29 connection_host: 30 description: 31 - Set the host for the remote server that is sending data. 32 - C(ip) sets the host to the IP address of the remote server sending data. 33 - C(dns) sets the host to the reverse DNS entry for the IP address of the remote server sending data. 34 - C(none) leaves the host as specified in inputs.conf, which is typically the Splunk system hostname. 35 default: "ip" 36 required: False 37 type: str 38 choices: 39 - "ip" 40 - "dns" 41 - "none" 42 state: 43 description: 44 - Enable, disable, create, or destroy 45 choices: 46 - "present" 47 - "absent" 48 - "enabled" 49 - "disable" 50 required: False 51 default: "present" 52 type: str 53 datatype: 54 description: > 55 Forwarders can transmit three types of data: raw, unparsed, or parsed. 56 C(cooked) data refers to parsed and unparsed formats. 57 choices: 58 - "cooked" 59 - "raw" 60 default: "raw" 61 required: False 62 type: str 63 host: 64 description: 65 - Host from which the indexer gets data. 66 required: False 67 type: str 68 index: 69 description: 70 - default Index to store generated events. 71 type: str 72 name: 73 description: 74 - The input port which receives raw data. 75 required: True 76 type: str 77 queue: 78 description: 79 - Specifies where the input processor should deposit the events it reads. Defaults to parsingQueue. 80 - Set queue to parsingQueue to apply props.conf and other parsing rules to your data. For more 81 information about props.conf and rules for timestamping and linebreaking, refer to props.conf and 82 the online documentation at "Monitor files and directories with inputs.conf" 83 - Set queue to indexQueue to send your data directly into the index. 84 choices: 85 - "parsingQueue" 86 - "indexQueue" 87 type: str 88 required: False 89 default: "parsingQueue" 90 rawTcpDoneTimeout: 91 description: 92 - Specifies in seconds the timeout value for adding a Done-key. 93 - If a connection over the port specified by name remains idle after receiving data for specified 94 number of seconds, it adds a Done-key. This implies the last event is completely received. 95 default: 10 96 type: int 97 required: False 98 restrictToHost: 99 description: 100 - Allows for restricting this input to only accept data from the host specified here. 101 required: False 102 type: str 103 ssl: 104 description: 105 - Enable or disble ssl for the data stream 106 required: False 107 type: bool 108 source: 109 description: 110 - Sets the source key/field for events from this input. Defaults to the input file path. 111 - > 112 Sets the source key initial value. The key is used during parsing/indexing, in particular to set 113 the source field during indexing. It is also the source field used at search time. As a convenience, 114 the chosen string is prepended with 'source::'. 115 - > 116 Note: Overriding the source key is generally not recommended. Typically, the input layer provides a 117 more accurate string to aid in problem analysis and investigation, accurately recording the file from 118 which the data was retrieved. Consider use of source types, tagging, and search wildcards before 119 overriding this value. 120 type: str 121 sourcetype: 122 description: 123 - Set the source type for events from this input. 124 - '"sourcetype=" is automatically prepended to <string>.' 125 - Defaults to audittrail (if signedaudit=True) or fschange (if signedaudit=False). 126 type: str 127 128author: Ansible Security Automation Team (@maxamillion) <https://github.com/ansible-security> 129""" 130 131EXAMPLES = """ 132- name: Example adding data input network with splunk.es.data_input_network 133 splunk.es.data_input_network: 134 name: "8099" 135 protocol: "tcp" 136 state: "present" 137""" 138 139 140from ansible.module_utils.basic import AnsibleModule 141from ansible.module_utils._text import to_text 142 143from ansible.module_utils.urls import Request 144from ansible.module_utils.six.moves.urllib.parse import urlencode, quote_plus 145from ansible.module_utils.six.moves.urllib.error import HTTPError 146from ansible_collections.splunk.es.plugins.module_utils.splunk import ( 147 SplunkRequest, 148 parse_splunk_args, 149) 150 151import copy 152 153 154def main(): 155 156 argspec = dict( 157 state=dict( 158 required=False, 159 choices=["present", "absent", "enabled", "disable"], 160 default="present", 161 type="str", 162 ), 163 connection_host=dict( 164 required=False, choices=["ip", "dns", "none"], default="ip", type="str" 165 ), 166 host=dict(required=False, type="str", default=None), 167 index=dict(required=False, type="str", default=None), 168 name=dict(required=True, type="str"), 169 protocol=dict(required=True, type="str", choices=["tcp", "udp"]), 170 queue=dict( 171 required=False, 172 type="str", 173 choices=["parsingQueue", "indexQueue"], 174 default="parsingQueue", 175 ), 176 rawTcpDoneTimeout=dict(required=False, type="int", default=10), 177 restrictToHost=dict(required=False, type="str", default=None), 178 ssl=dict(required=False, type="bool", default=None), 179 source=dict(required=False, type="str", default=None), 180 sourcetype=dict(required=False, type="str", default=None), 181 datatype=dict(required=False, choices=["cooked", "raw"], default="raw"), 182 ) 183 184 module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) 185 186 splunk_request = SplunkRequest( 187 module, 188 headers={"Content-Type": "application/x-www-form-urlencoded"}, 189 not_rest_data_keys=["state", "datatype", "protocol"], 190 ) 191 # This is where the splunk_* args are processed 192 request_data = splunk_request.get_data() 193 194 query_dict = splunk_request.get_by_path( 195 "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( 196 quote_plus(module.params["protocol"]), 197 quote_plus(module.params["datatype"]), 198 quote_plus(module.params["name"]), 199 ) 200 ) 201 202 if module.params["state"] in ["present", "enabled", "disabled"]: 203 _data = splunk_request.get_data() 204 if module.params["state"] in ["present", "enabled"]: 205 _data["disabled"] = False 206 else: 207 _data["disabled"] = True 208 if query_dict: 209 needs_change = False 210 for arg in request_data: 211 if arg in query_dict["entry"][0]["content"]: 212 if to_text(query_dict["entry"][0]["content"][arg]) != to_text( 213 request_data[arg] 214 ): 215 needs_change = True 216 if not needs_change: 217 module.exit_json( 218 changed=False, msg="Nothing to do.", splunk_data=query_dict 219 ) 220 if module.check_mode and needs_change: 221 module.exit_json( 222 changed=True, 223 msg="A change would have been made if not in check mode.", 224 splunk_data=query_dict, 225 ) 226 if needs_change: 227 splunk_data = splunk_request.create_update( 228 "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( 229 quote_plus(module.params["protocol"]), 230 quote_plus(module.params["datatype"]), 231 quote_plus(module.params["name"]), 232 data=urlencode(_data), 233 ) 234 ) 235 if module.params["state"] in ["present", "enabled"]: 236 module.exit_json( 237 changed=True, msg="{0} updated.", splunk_data=splunk_data 238 ) 239 else: 240 module.exit_json( 241 changed=True, msg="{0} disabled.", splunk_data=splunk_data 242 ) 243 else: 244 # Create it 245 splunk_data = splunk_request.create_update( 246 "servicesNS/nobody/search/data/inputs/{0}/{1}".format( 247 quote_plus(module.params["protocol"]), 248 quote_plus(module.params["datatype"]), 249 ), 250 data=urlencode(_data), 251 ) 252 module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) 253 elif module.params["state"] == "absent": 254 if query_dict: 255 splunk_data = splunk_request.delete_by_path( 256 "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( 257 quote_plus(module.params["protocol"]), 258 quote_plus(module.params["datatype"]), 259 quote_plus(module.params["name"]), 260 ) 261 ) 262 module.exit_json( 263 changed=True, 264 msg="Deleted {0}.".format(module.params["name"]), 265 splunk_data=splunk_data, 266 ) 267 268 module.exit_json(changed=False, msg="Nothing to do.", splunk_data={}) 269 270 271if __name__ == "__main__": 272 main() 273