def _connect(self): """ Obtains an access_token and saves it for use in API accesses """ self.baseurl = self.module.params.get('auth_keycloak_url') self.validate_certs = self.module.params.get('validate_certs') auth_url = URL_TOKEN.format(url=self.baseurl, realm=self.module.params.get('auth_realm')) payload = {'grant_type': 'password', 'client_id': self.module.params.get('auth_client_id'), 'client_secret': self.module.params.get('auth_client_secret'), 'username': self.module.params.get('auth_username'), 'password': self.module.params.get('auth_password')} # Remove empty items, for instance missing client_secret payload = dict((k, v) for k, v in payload.items() if v is not None) try: r = json.load(open_url(auth_url, method='POST', validate_certs=self.validate_certs, data=urlencode(payload))) except ValueError as e: self.module.fail_json(msg='API returned invalid JSON when trying to obtain access token from %s: %s' % (auth_url, str(e))) except Exception as e: self.module.fail_json(msg='Could not obtain access token from %s: %s' % (auth_url, str(e))) if 'access_token' in r: self.token = r['access_token'] self.restheaders = {'Authorization': 'Bearer ' + self.token, 'Content-Type': 'application/json'} else: self.module.fail_json(msg='Could not obtain access token from %s' % auth_url)
def send_msg_v1(module, token, room, msg_from, msg, msg_format='text', color='yellow', notify=False, api=MSG_URI_V1): '''sending message to hipchat v1 server''' params = {} params['room_id'] = room params['from'] = msg_from[:15] # max length is 15 params['message'] = msg params['message_format'] = msg_format params['color'] = color params['api'] = api params['notify'] = int(notify) url = api + MSG_URI_V1 + "?auth_token=%s" % (token) data = urlencode(params) if module.check_mode: # In check mode, exit before actually sending the message module.exit_json(changed=False) response, info = fetch_url(module, url, data=data) if info['status'] == 200: return response.read() else: module.fail_json(msg="failed to send message, return status=%s" % str(info['status']))
def get_dns_records(self,zone_name=None,type=None,record=None,value=''): if not zone_name: zone_name = self.zone if not type: type = self.type if not record: record = self.record # necessary because None as value means to override user # set module value if (not value) and (value is not None): value = self.value zone_id = self._get_zone_id() api_call = '/zones/{0}/dns_records'.format(zone_id) query = {} if type: query['type'] = type if record: query['name'] = record if value: query['content'] = value if query: api_call += '?' + urlencode(query) records,status = self._cf_api_call(api_call) return records
def post_sendgrid_api(module, username, password, from_address, to_addresses, subject, body, api_key=None, cc=None, bcc=None, attachments=None, html_body=False, from_name=None, headers=None): if not HAS_SENDGRID: SENDGRID_URI = "https://api.sendgrid.com/api/mail.send.json" AGENT = "Ansible" data = {'api_user': username, 'api_key':password, 'from':from_address, 'subject': subject, 'text': body} encoded_data = urlencode(data) to_addresses_api = '' for recipient in to_addresses: recipient = to_bytes(recipient, errors='surrogate_or_strict') to_addresses_api += '&to[]=%s' % recipient encoded_data += to_addresses_api headers = { 'User-Agent': AGENT, 'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'} return fetch_url(module, SENDGRID_URI, data=encoded_data, headers=headers, method='POST') else: if api_key: sg = sendgrid.SendGridClient(api_key) else: sg = sendgrid.SendGridClient(username, password) message = sendgrid.Mail() message.set_subject(subject) for recip in to_addresses: message.add_to(recip) if cc: for recip in cc: message.add_cc(recip) if bcc: for recip in bcc: message.add_bcc(recip) if headers: message.set_headers(headers) if attachments: for f in attachments: name = os.path.basename(f) message.add_attachment(name, f) if from_name: message.set_from('%s <%s.' % (from_name, from_address)) else: message.set_from(from_address) if html_body: message.set_html(body) else: message.set_text(body) return sg.send(message)
def get_zones(self,name=None): if not name: name = self.zone param = '' if name: param = '?' + urlencode({'name' : name}) zones,status = self._cf_api_call('/zones' + param) return zones
def checkID(module, params): data = urlencode(params) full_uri = API_BASE + API_ACTIONS['status'] + data req, info = fetch_url(module, full_uri) result = req.read() jsonresult = json.loads(result) req.close() return jsonresult
def authenticate(self, github_token): """ Retrieve an authentication token """ url = '%s/tokens/' % self.baseurl args = urlencode({"github_token": github_token}) resp = open_url(url, data=args, validate_certs=self._validate_certs, method="POST") data = json.loads(to_text(resp.read(), errors='surrogate_or_strict')) return data
def pauseMonitor(module, params): params['monitorStatus'] = 0 data = urlencode(params) full_uri = API_BASE + API_ACTIONS['editMonitor'] + data req, info = fetch_url(module, full_uri) result = req.read() jsonresult = json.loads(result) req.close() return jsonresult['stat']
def add_secret(self, source, github_user, github_repo, secret): url = "%s/notification_secrets/" % self.baseurl args = urlencode({ "source": source, "github_user": github_user, "github_repo": github_repo, "secret": secret }) data = self.__call_galaxy(url, args=args) return data
def get_plugin_info(module, plugin_manager_url, intellij_home, plugin_id): build_number = get_build_number(module, intellij_home) params = {'action': 'download', 'build': build_number, 'id': plugin_id} query_params = urlencode(params) url = '%s?%s' % (plugin_manager_url, query_params) for _ in range(0, 3): resp, info = fetch_url( module, url, method='HEAD', timeout=3, follow_redirects=False) if resp is not None: resp.close() status_code = info['status'] if status_code == 404: module.fail_json(msg='Unable to find plugin "%s" for build "%s"' % ( plugin_id, build_number)) if status_code > -1 and status_code < 400: break # 3 retries 5 seconds appart time.sleep(5) if status_code == -1 or status_code >= 400: module.fail_json(msg='Error querying url "%s": %s' % (url, info['msg'])) location = info.get('location') if location is None: location = info.get('Location') if location is None: module.fail_json(msg='Unsupported HTTP response for: %s (status=%s)' % ( url, status_code)) plugin_url = location jar_pattern = re.compile(r'/(?P<file_name>[^/]+\.jar)(?:\?.*)$') jar_matcher = jar_pattern.search(plugin_url) if jar_matcher: file_name = jar_matcher.group('file_name') else: versioned_pattern = re.compile( r'(?P<plugin_id>[0-9]+)/(?P<update_id>[0-9]+)/(?P<file_name>[^/]+)(?:\?.*)$' ) versioned_matcher = versioned_pattern.search(plugin_url) if versioned_matcher: file_name = '%s-%s-%s' % (versioned_matcher.group('plugin_id'), versioned_matcher.group('update_id'), versioned_matcher.group('file_name')) else: hash_object = hashlib.sha256(plugin_url) file_name = '%s-%s.zip' % (plugin_id, hash_object.hexdigest()) return plugin_url, file_name
def _pm_query(self, action, msg): url = "%s/pluginManager/plugin/%s/%s" % ( self.params['url'], self.params['name'], action) data = urlencode(self.crumb) # Send the request self._get_url_data( url, msg_status="Plugin not found. %s" % url, msg_exception="%s has failed." % msg, data=data)
def main(): module = AnsibleModule( argument_spec=dict( script=dict(required=True, type="str"), url=dict(required=False, type="str", default="http://localhost:8080"), validate_certs=dict(required=False, type="bool", default=True), user=dict(required=False, type="str", default=None), password=dict(required=False, no_log=True, type="str", default=None), timeout=dict(required=False, type="int", default=10), args=dict(required=False, type="dict", default=None) ) ) if module.params['user'] is not None: if module.params['password'] is None: module.fail_json(msg="password required when user provided", output='') module.params['url_username'] = module.params['user'] module.params['url_password'] = module.params['password'] module.params['force_basic_auth'] = True if module.params['args'] is not None: from string import Template try: script_contents = Template(module.params['script']).substitute(module.params['args']) except KeyError as err: module.fail_json(msg="Error with templating variable: %s" % err, output='') else: script_contents = module.params['script'] headers = {} if is_csrf_protection_enabled(module): crumb = get_crumb(module) headers = {crumb['crumbRequestField']: crumb['crumb']} resp, info = fetch_url(module, module.params['url'] + "/scriptText", data=urlencode({'script': script_contents}), headers=headers, method="POST", timeout=module.params['timeout']) if info["status"] != 200: module.fail_json(msg="HTTP error " + str(info["status"]) + " " + info["msg"], output='') result = to_native(resp.read()) if 'Exception:' in result and 'at java.lang.Thread' in result: module.fail_json(msg="script failed with stacktrace:\n " + result, output='') module.exit_json( output=result, )
def update_qsl(url, params): ''' Add or update a URL query string ''' if HAS_URLPARSE: url_parts = list(urlparse(url)) query = dict(parse_qsl(url_parts[4])) query.update(params) url_parts[4] = urlencode(query) return urlunparse(url_parts) elif '?' in url: return url + '&' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) else: return url + '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()])
def query(self, resource, method, data=None): url = self.baseurl + resource if data and not isinstance(data, string_types): data = urlencode(data) response, info = fetch_url(self.module, url, data=data, method=method, headers=self._headers()) if info['status'] not in (200, 201, 204): self.module.fail_json(msg="%s returned %s, with body: %s" % (url, info['status'], info['msg'])) try: return json.load(response) except Exception: return {}
def vmware_path(datastore, datacenter, path): ''' Constructs a URL path that VSphere accepts reliably ''' path = "/folder/%s" % path.lstrip("/") # Due to a software bug in vSphere, it fails to handle ampersand in datacenter names # The solution is to do what vSphere does (when browsing) and double-encode ampersands, maybe others ? datacenter = datacenter.replace('&', '%26') if not path.startswith("/"): path = "/" + path params = dict( dsName = datastore ) if datacenter: params["dcPath"] = datacenter params = urlencode(params) return "%s?%s" % (path, params)
def do_request(module, url, params, headers=None): data = urlencode(params) if headers is None: headers = dict() headers = dict(headers, **{ 'User-Agent': 'Ansible/typetalk module', }) r, info = fetch_url(module, url, data=data, headers=headers) if info['status'] != 200: exc = ConnectionError(info['msg']) exc.code = info['status'] raise exc return r
def do_notify_grove(module, channel_token, service, message, url=None, icon_url=None): my_url = BASE_URL % (channel_token,) my_data = dict(service=service, message=message) if url is not None: my_data['url'] = url if icon_url is not None: my_data['icon_url'] = icon_url data = urlencode(my_data) response, info = fetch_url(module, my_url, data=data) if info['status'] != 200: module.fail_json(msg="failed to send notification: %s" % info['msg'])
def __init__(self, module): self.module = module self.hostname = module.params['name'] self.rack = module.params['rack'] self.rank = module.params['rank'] self.appliance = module.params['appliance'] self.prim_intf = module.params['prim_intf'] self.prim_intf_ip = module.params['prim_intf_ip'] self.network = module.params['network'] self.prim_intf_mac = module.params['prim_intf_mac'] self.endpoint = module.params['stacki_endpoint'] auth_creds = {'USERNAME': module.params['stacki_user'], 'PASSWORD': module.params['stacki_password']} # Get Initial CSRF cred_a = self.do_request(self.module, self.endpoint, method="GET") cookie_a = cred_a.headers.get('Set-Cookie').split(';') init_csrftoken = None for c in cookie_a: if "csrftoken" in c: init_csrftoken = c.replace("csrftoken=", "") init_csrftoken = init_csrftoken.rstrip("\r\n") break # Make Header Dictionary with initial CSRF header = {'csrftoken': init_csrftoken, 'X-CSRFToken': init_csrftoken, 'Content-type': 'application/x-www-form-urlencoded', 'Cookie': cred_a.headers.get('Set-Cookie')} # Endpoint to get final authentication header login_endpoint = self.endpoint + "/login" # Get Final CSRF and Session ID login_req = self.do_request(self.module, login_endpoint, headers=header, payload=urlencode(auth_creds), method='POST') cookie_f = login_req.headers.get('Set-Cookie').split(';') csrftoken = None for f in cookie_f: if "csrftoken" in f: csrftoken = f.replace("csrftoken=", "") if "sessionid" in f: sessionid = c.split("sessionid=", 1)[-1] sessionid = sessionid.rstrip("\r\n") self.header = {'csrftoken': csrftoken, 'X-CSRFToken': csrftoken, 'sessionid': sessionid, 'Content-type': 'application/json', 'Cookie': login_req.headers.get('Set-Cookie')}
def main(): module = AnsibleModule( argument_spec=dict( token=dict(required=True, no_log=True), app_name=dict(required=False), application_id=dict(required=False), changelog=dict(required=False), description=dict(required=False), revision=dict(required=False), user=dict(required=False), appname=dict(required=False), environment=dict(required=False), validate_certs = dict(default='yes', type='bool'), ), required_one_of=[['app_name', 'application_id']], supports_check_mode=True ) # build list of params params = {} if module.params["app_name"] and module.params["application_id"]: module.fail_json(msg="only one of 'app_name' or 'application_id' can be set") if module.params["app_name"]: params["app_name"] = module.params["app_name"] elif module.params["application_id"]: params["application_id"] = module.params["application_id"] else: module.fail_json(msg="you must set one of 'app_name' or 'application_id'") for item in [ "changelog", "description", "revision", "user", "appname", "environment" ]: if module.params[item]: params[item] = module.params[item] # If we're in check mode, just exit pretending like we succeeded if module.check_mode: module.exit_json(changed=True) # Send the data to NewRelic url = "https://rpm.newrelic.com/deployments.xml" data = urlencode(params) headers = { 'x-api-key': module.params["token"], } response, info = fetch_url(module, url, data=data, headers=headers) if info['status'] in (200, 201): module.exit_json(changed=True) else: module.fail_json(msg="unable to update newrelic: %s" % info['msg'])
def rpc(self, action, params): """Make a call to the LogicMonitor RPC library and return the response""" self.module.debug("Running LogicMonitor.rpc") param_str = urlencode(params) creds = urlencode( {"c": self.company, "u": self.user, "p": self.password}) if param_str: param_str = param_str + "&" param_str = param_str + creds try: url = ("https://" + self.company + "." + self.lm_url + "/rpc/" + action + "?" + param_str) # Set custom LogicMonitor header with version headers = {"X-LM-User-Agent": self.__version__} # Set headers f = open_url(url, headers=headers) raw = f.read() resp = json.loads(raw) if resp["status"] == 403: self.module.debug("Authentication failed.") self.fail(msg="Error: " + resp["errmsg"]) else: return raw except IOError as ioe: self.fail(msg="Error: Exception making RPC call to " + "https://" + self.company + "." + self.lm_url + "/rpc/" + action + "\nException" + to_native(ioe))
def main(): module = AnsibleModule( argument_spec=dict( token=dict(required=True), environment=dict(required=True), revision=dict(required=True), user=dict(required=False), rollbar_user=dict(required=False), comment=dict(required=False), url=dict( required=False, default='https://api.rollbar.com/api/1/deploy/' ), validate_certs=dict(default='yes', type='bool'), ), supports_check_mode=True ) if module.check_mode: module.exit_json(changed=True) params = dict( access_token=module.params['token'], environment=module.params['environment'], revision=module.params['revision'] ) if module.params['user']: params['local_username'] = module.params['user'] if module.params['rollbar_user']: params['rollbar_username'] = module.params['rollbar_user'] if module.params['comment']: params['comment'] = module.params['comment'] url = module.params.get('url') try: data = urlencode(params) response, info = fetch_url(module, url, data=data) except Exception as e: module.fail_json(msg='Unable to notify Rollbar: %s' % to_native(e), exception=traceback.format_exc()) else: if info['status'] == 200: module.exit_json(changed=True) else: module.fail_json(msg='HTTP result code: %d connecting to %s' % (info['status'], url))
def main(): module = AnsibleModule( argument_spec=dict( token=dict(required=True, no_log=True), environment=dict(required=True), user=dict(required=False), repo=dict(required=False), revision=dict(required=False), url=dict(required=False, default='https://api.honeybadger.io/v1/deploys'), validate_certs=dict(default='yes', type='bool'), ), supports_check_mode=True ) params = {} if module.params["environment"]: params["deploy[environment]"] = module.params["environment"] if module.params["user"]: params["deploy[local_username]"] = module.params["user"] if module.params["repo"]: params["deploy[repository]"] = module.params["repo"] if module.params["revision"]: params["deploy[revision]"] = module.params["revision"] params["api_key"] = module.params["token"] url = module.params.get('url') # If we're in check mode, just exit pretending like we succeeded if module.check_mode: module.exit_json(changed=True) try: data = urlencode(params) response, info = fetch_url(module, url, data=data) except Exception as e: module.fail_json(msg='Unable to notify Honeybadger: %s' % to_native(e), exception=traceback.format_exc()) else: if info['status'] == 201: module.exit_json(changed=True) else: module.fail_json(msg="HTTP result code: %d connecting to %s" % (info['status'], url))
def _post(self, api_call, data=None): if data is not None: data = urlencode(data) resp, info = fetch_url(self._module, API_URL+api_call, headers = self._auth_header, method='POST', data=data) if info['status'] == 201: return json.loads(resp.read()) elif info['status'] == 204: return None else: self._module.fail_json(msg='Failure while calling the cloudscale.ch API with POST for ' '"%s": %s' % (api_call, info['body']))
def send_msg_v1(self, msg, msg_format='text', color='yellow', notify=False): """Method for sending a message to HipChat""" params = {} params['room_id'] = self.room params['from'] = self.from_name[:15] # max length is 15 params['message'] = msg params['message_format'] = msg_format params['color'] = color params['notify'] = int(self.allow_notify and notify) url = ('%s?auth_token=%s' % (self.API_V1_URL, self.token)) try: response = open_url(url, data=urlencode(params)) return response.read() except Exception as ex: self._display.warning('Could not submit message to hipchat: {0}'.format(ex))
def main(): module = AnsibleModule( argument_spec=dict( token=dict(required=True, no_log=True), environment=dict(required=True), user=dict(required=False), repo=dict(required=False), revision=dict(required=False), url=dict(required=False, default='https://api.airbrake.io/deploys.txt'), validate_certs=dict(default='yes', type='bool'), ), supports_check_mode=True ) # build list of params params = {} if module.params["environment"]: params["deploy[rails_env]"] = module.params["environment"] if module.params["user"]: params["deploy[local_username]"] = module.params["user"] if module.params["repo"]: params["deploy[scm_repository]"] = module.params["repo"] if module.params["revision"]: params["deploy[scm_revision]"] = module.params["revision"] params["api_key"] = module.params["token"] url = module.params.get('url') # If we're in check mode, just exit pretending like we succeeded if module.check_mode: module.exit_json(changed=True) # Send the data to airbrake data = urlencode(params) response, info = fetch_url(module, url, data=data) if info['status'] == 200: module.exit_json(changed=True) else: module.fail_json(msg="HTTP result code: %d connecting to %s" % (info['status'], url))
def run(self, priority, msg): ''' Do, whatever it is, we do. ''' url = '%s/1/messages.json' % (self.base_uri) # parse config options = dict(user=self.user, token=self.token, priority=priority, message=msg) data = urlencode(options) headers = { "Content-type": "application/x-www-form-urlencoded"} r, info = fetch_url(self.module, url, method='POST', data=data, headers=headers) if info['status'] != 200: raise Exception(info) return r.read()
def create_import_task(self, github_user, github_repo, reference=None, role_name=None): """ Post an import request """ url = '%s/imports/' % self.baseurl args = { "github_user": github_user, "github_repo": github_repo, "github_reference": reference if reference else "" } if role_name: args['alternate_role_name'] = role_name elif github_repo.startswith('ansible-role'): args['alternate_role_name'] = github_repo[len('ansible-role') + 1:] data = self.__call_galaxy(url, args=urlencode(args)) if data.get('results', None): return data['results'] return data
def http_request(self, api_endpoint, data_json=None): data_josn = {} if data_json is None else data_json request_url = self._nsc_protocol + '://' + self._nsc_host + self._nitro_base_url + api_endpoint data_json = urlencode(data_json) if not len(data_json): data_json = None auth = base64.b64encode(to_bytes('%s:%s' % (self._nsc_user, self._nsc_pass)).replace('\n', '').strip()) headers = { 'Authorization': 'Basic %s' % auth, 'Content-Type': 'application/x-www-form-urlencoded', } response, info = fetch_url(self.module, request_url, data=data_json, headers=headers) return json.load(response)
def post_twilio_api(module, account_sid, auth_token, msg, from_number, to_number, media_url=None): URI = "https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json" \ % (account_sid,) AGENT = "Ansible" data = {'From': from_number, 'To': to_number, 'Body': msg} if media_url: data['MediaUrl'] = media_url encoded_data = urlencode(data) headers = {'User-Agent': AGENT, 'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', } # Hack module params to have the Basic auth params that fetch_url expects module.params['url_username'] = account_sid.replace('\n', '') module.params['url_password'] = auth_token.replace('\n', '') return fetch_url(module, URI, data=encoded_data, headers=headers)
def send_msg(module): failed = list() responses = dict() msg = { 'api_key': module.params.get('api_key'), 'api_secret': module.params.get('api_secret'), 'from': module.params.get('src'), 'text': module.params.get('msg') } for number in module.params.get('dest'): msg['to'] = number url = "%s?%s" % (NEXMO_API, urlencode(msg)) headers = dict(Accept='application/json') response, info = fetch_url(module, url, headers=headers) if info['status'] != 200: failed.append(number) responses[number] = dict(failed=True) try: responses[number] = json.load(response) except: failed.append(number) responses[number] = dict(failed=True) else: for message in responses[number]['messages']: if int(message['status']) != 0: failed.append(number) responses[number] = dict(failed=True, **responses[number]) if failed: msg = 'One or messages failed to send' else: msg = '' module.exit_json(failed=bool(failed), msg=msg, changed=False, responses=responses)
def main(): argspec = dict( name=dict(required=True, type="str"), correlation_search_name=dict(required=True, type="str"), description=dict(required=True, type="str"), state=dict(choices=["present", "absent"], required=True), security_domain=dict( choices=[ "access", "endpoint", "network", "threat", "identity", "audit" ], required=False, default="threat", ), severity=dict( choices=[ "informational", "low", "medium", "high", "critical", "unknown" ], required=False, default="high", ), default_owner=dict(required=False, type="str"), default_status=dict( choices=[ "unassigned", "new", "in progress", "pending", "resolved", "closed", ], required=False, default="", ), drill_down_name=dict(required=False, type="str"), drill_down_search=dict(required=False, type="str"), drill_down_earliest_offset=dict(required=False, type="str", default="$info_min_time$"), drill_down_latest_offset=dict(required=False, type="str", default="$info_max_time$"), investigation_profiles=dict(required=False, type="str"), next_steps=dict(required=False, type="list", default=[]), recommended_actions=dict(required=False, type="list", default=[]), asset_extraction=dict( required=False, type="list", default=["src", "dest", "dvc", "orig_host"], choices=["src", "dest", "dvc", "orig_host"], ), identity_extraction=dict( required=False, type="list", default=["user", "src_user"], choices=["user", "src_user"], ), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, not_rest_data_keys=["state"], ) query_dict = splunk_request.get_by_path( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}". format(quote_plus(module.params["correlation_search_name"]))) # Have to custom craft the data here because they overload the saved searches # endpoint in the rest api and we want to hide the nuance from the user request_post_data = {} # FIXME need to figure out how to properly support these, the possible values appear to # be dynamically created based on what the search is indexing # request_post_data['action.notable.param.extract_assets'] = '[\"src\",\"dest\",\"dvc\",\"orig_host\"]' # request_post_data['action.notable.param.extract_identities'] = [\"src_user\",\"user\"] if module.params["next_steps"]: if len(module.params["next_steps"]) == 1: next_steps = "[[action|{0}]]".format( module.params["next_steps"][0]) else: next_steps = "" for next_step in module.params["next_steps"]: if next_steps: next_steps += "\n[[action|{0}]]".format(next_step) else: next_steps = "[[action|{0}]]".format(next_step) # NOTE: version:1 appears to be hard coded when you create this via the splunk web UI # but I don't know what it is/means because there's no docs on it next_steps_dict = {"version": 1, "data": next_steps} request_post_data["action.notable.param.next_steps"] = json.dumps( next_steps_dict) if module.params["recommended_actions"]: if len(module.params["recommended_actions"]) == 1: request_post_data[ "action.notable.param.recommended_actions"] = module.params[ "recommended_actions"][0] else: request_post_data[ "action.notable.param.recommended_actions"] = ",".join( module.params["recommended_actions"]) request_post_data["action.notable.param.rule_description"] = module.params[ "description"] request_post_data["action.notable.param.rule_title"] = module.params[ "name"] request_post_data["action.notable.param.security_domain"] = module.params[ "security_domain"] request_post_data["action.notable.param.severity"] = module.params[ "severity"] request_post_data["action.notable.param.asset_extraction"] = module.params[ "asset_extraction"] request_post_data[ "action.notable.param.identity_extraction"] = module.params[ "identity_extraction"] # NOTE: this field appears to be hard coded when you create this via the splunk web UI # but I don't know what it is/means because there's no docs on it request_post_data["action.notable.param.verbose"] = "0" if module.params["default_owner"]: request_post_data[ "action.notable.param.default_owner"] = module.params[ "default_owner"] if module.params["default_status"]: request_post_data[ "action.notable.param.default_status"] = module.params[ "default_status"] if query_dict: request_post_data["search"] = query_dict["entry"][0]["content"][ "search"] if "actions" in query_dict["entry"][0]["content"]: if query_dict["entry"][0]["content"]["actions"] == "notable": pass elif (len(query_dict["entry"][0]["content"]["actions"].split(",")) > 0 and "notable" not in query_dict["entry"][0]["content"]["actions"]): request_post_data["actions"] = ( query_dict["entry"][0]["content"]["actions"] + ", notable") else: request_post_data["actions"] = "notable" else: module.fail_json(msg="Unable to find correlation search: {0}", splunk_data=splunk_data) if module.params["state"] == "present": needs_change = False for arg in request_post_data: if arg in query_dict["entry"][0]["content"]: if to_text(query_dict["entry"][0]["content"][arg]) != to_text( request_post_data[arg]): needs_change = True if not needs_change: module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}" .format(quote_plus(module.params["correlation_search_name"])), data=urlencode(request_post_data), ) module.exit_json( changed=True, msg="{0} updated.".format( module.params["correlation_search_name"]), splunk_data=splunk_data, ) if module.params["state"] == "absent": # FIXME - need to figure out how to clear the action.notable.param fields from the api endpoint module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) for arg in request_post_data: if arg in query_dict["entry"][0]["content"]: needs_change = True del query_dict["entry"][0]["content"][arg] if not needs_change: module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}" .format(quote_plus(module.params["correlation_search_name"])), data=urlencode(request_post_data), ) module.exit_json( changed=True, msg="{0} updated.".format( module.params["correlation_search_name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict)
def get_url(self, url, params=None): if params: return self.api_version + url + '?' + urlencode(params) else: return self.api_version + url
def make_request(self, method, endpoint, *args, **kwargs): # In case someone is calling us directly; make sure we were given a method, let's not just assume a GET if not method: raise Exception("The HTTP method must be defined") # Make sure we start with /api/vX if not endpoint.startswith("/"): endpoint = "/{0}".format(endpoint) if not endpoint.startswith("/api/"): endpoint = "/api/v2{0}".format(endpoint) if not endpoint.endswith('/') and '?' not in endpoint: endpoint = "{0}/".format(endpoint) # Extract the headers, this will be used in a couple of places headers = kwargs.get('headers', {}) # Authenticate to Tower (if we don't have a token and if not already done so) if not self.oauth_token and not self.authenticated: # This method will set a cookie in the cookie jar for us and also an oauth_token self.authenticate(**kwargs) if self.oauth_token: # If we have a oauth token, we just use a bearer header headers['Authorization'] = 'Bearer {0}'.format(self.oauth_token) # Update the URL path with the endpoint self.url = self.url._replace(path=endpoint) if method in ['POST', 'PUT', 'PATCH']: headers.setdefault('Content-Type', 'application/json') kwargs['headers'] = headers elif kwargs.get('data'): self.url = self.url._replace(query=urlencode(kwargs.get('data'))) data = None # Important, if content type is not JSON, this should not be dict type if headers.get('Content-Type', '') == 'application/json': data = dumps(kwargs.get('data', {})) try: response = self.session.open(method, self.url.geturl(), headers=headers, validate_certs=self.verify_ssl, follow_redirects=True, data=data) except (SSLValidationError) as ssl_err: self.fail_json( msg= "Could not establish a secure connection to your host ({1}): {0}." .format(self.url.netloc, ssl_err)) except (ConnectionError) as con_err: self.fail_json( msg= "There was a network error of some kind trying to connect to your host ({1}): {0}." .format(self.url.netloc, con_err)) except (HTTPError) as he: # Sanity check: Did the server send back some kind of internal error? if he.code >= 500: self.fail_json( msg= 'The host sent back a server error ({1}): {0}. Please check the logs and try again later' .format(self.url.path, he)) # Sanity check: Did we fail to authenticate properly? If so, fail out now; this is always a failure. elif he.code == 401: self.fail_json( msg= 'Invalid Tower authentication credentials for {0} (HTTP 401).' .format(self.url.path)) # Sanity check: Did we get a forbidden response, which means that the user isn't allowed to do this? Report that. elif he.code == 403: self.fail_json( msg="You don't have permission to {1} to {0} (HTTP 403).". format(self.url.path, method)) # Sanity check: Did we get a 404 response? # Requests with primary keys will return a 404 if there is no response, and we want to consistently trap these. elif he.code == 404: if kwargs.get('return_none_on_404', False): return None self.fail_json( msg='The requested object could not be found at {0}.'. format(self.url.path)) # Sanity check: Did we get a 405 response? # A 405 means we used a method that isn't allowed. Usually this is a bad request, but it requires special treatment because the # API sends it as a logic error in a few situations (e.g. trying to cancel a job that isn't running). elif he.code == 405: self.fail_json( msg= "The Tower server says you can't make a request with the {0} method to this endpoing {1}" .format(method, self.url.path)) # Sanity check: Did we get some other kind of error? If so, write an appropriate error message. elif he.code >= 400: # We are going to return a 400 so the module can decide what to do with it page_data = he.read() try: return {'status_code': he.code, 'json': loads(page_data)} # JSONDecodeError only available on Python 3.5+ except ValueError: return {'status_code': he.code, 'text': page_data} elif he.code == 204 and method == 'DELETE': # A 204 is a normal response for a delete function pass else: self.fail_json( msg="Unexpected return code when calling {0}: {1}".format( self.url.geturl(), he)) except (Exception) as e: self.fail_json( msg= "There was an unknown error when trying to connect to {2}: {0} {1}" .format(type(e).__name__, e, self.url.geturl())) finally: self.url = self.url._replace(query=None) if not self.version_checked: # In PY2 we get back an HTTPResponse object but PY2 is returning an addinfourl # First try to get the headers in PY3 format and then drop down to PY2. try: tower_type = response.getheader('X-API-Product-Name', None) tower_version = response.getheader('X-API-Product-Version', None) except Exception: tower_type = response.info().getheader('X-API-Product-Name', None) tower_version = response.info().getheader( 'X-API-Product-Version', None) if self._COLLECTION_TYPE not in self.collection_to_version or self.collection_to_version[ self._COLLECTION_TYPE] != tower_type: self.warn( "You are using the {0} version of this collection but connecting to {1}" .format(self._COLLECTION_TYPE, tower_type)) elif self._COLLECTION_VERSION != tower_version: self.warn( "You are running collection version {0} but connecting to tower version {1}" .format(self._COLLECTION_VERSION, tower_version)) self.version_checked = True response_body = '' try: response_body = response.read() except (Exception) as e: self.fail_json(msg="Failed to read response body: {0}".format(e)) response_json = {} if response_body and response_body != '': try: response_json = loads(response_body) except (Exception) as e: self.fail_json( msg="Failed to parse the response json: {0}".format(e)) if PY2: status_code = response.getcode() else: status_code = response.status return {'status_code': status_code, 'json': response_json}
def main(): module = AnsibleModule( argument_spec=dict( token=dict(required=False, no_log=True), project_id=dict(required=False, no_log=True), project_key=dict(required=False, no_log=True), environment=dict(required=True), user=dict(required=False), repo=dict(required=False), revision=dict(required=False), url=dict(required=False, default='https://api.airbrake.io/api/v4/projects/'), validate_certs=dict(default='yes', type='bool'), ), supports_check_mode=True, required_together=[('project_id', 'project_key')], mutually_exclusive=[('project_id', 'token')], ) # Build list of params params = {} # If we're in check mode, just exit pretending like we succeeded if module.check_mode: module.exit_json(changed=True) if module.params["token"]: if module.params["environment"]: params["deploy[rails_env]"] = module.params["environment"] if module.params["user"]: params["deploy[local_username]"] = module.params["user"] if module.params["repo"]: params["deploy[scm_repository]"] = module.params["repo"] if module.params["revision"]: params["deploy[scm_revision]"] = module.params["revision"] module.deprecate( "Parameter 'token' is deprecated in 2.10. Please remove it and use 'project_id' and 'project_key' instead", version='2.14') params["api_key"] = module.params["token"] # Allow sending to Airbrake compliant v2 APIs if module.params["url"] == 'https://api.airbrake.io/api/v4/projects/': url = 'https://api.airbrake.io/deploys.txt' else: url = module.params["url"] # Send the data to airbrake data = urlencode(params) response, info = fetch_url(module, url, data=data) if module.params["project_id"] and module.params["project_key"]: if module.params["environment"]: params["environment"] = module.params["environment"] if module.params["user"]: params["username"] = module.params["user"] if module.params["repo"]: params["repository"] = module.params["repo"] if module.params["revision"]: params["revision"] = module.params["revision"] # Build deploy url url = module.params.get('url') + module.params[ "project_id"] + '/deploys?key=' + module.params["project_key"] json_body = module.jsonify(params) # Build header headers = {'Content-Type': 'application/json'} # Notify Airbrake of deploy response, info = fetch_url(module, url, data=json_body, headers=headers, method='POST') if info['status'] == 200 or info['status'] == 201: module.exit_json(changed=True) else: module.fail_json(msg="HTTP result code: %d connecting to %s" % (info['status'], url))
def post_sendgrid_api(module, username, password, from_address, to_addresses, subject, body, api_key=None, cc=None, bcc=None, attachments=None, html_body=False, from_name=None, headers=None): if not HAS_SENDGRID: SENDGRID_URI = "https://api.sendgrid.com/api/mail.send.json" AGENT = "Ansible" data = { 'api_user': username, 'api_key': password, 'from': from_address, 'subject': subject, 'text': body } encoded_data = urlencode(data) to_addresses_api = '' for recipient in to_addresses: if isinstance(recipient, unicode): recipient = recipient.encode('utf-8') to_addresses_api += '&to[]=%s' % recipient encoded_data += to_addresses_api headers = { 'User-Agent': AGENT, 'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' } return fetch_url(module, SENDGRID_URI, data=encoded_data, headers=headers, method='POST') else: if api_key: sg = sendgrid.SendGridClient(api_key) else: sg = sendgrid.SendGridClient(username, password) message = sendgrid.Mail() message.set_subject(subject) for recip in to_addresses: message.add_to(recip) if cc: for recip in cc: message.add_cc(recip) if bcc: for recip in bcc: message.add_bcc(recip) if headers: message.set_headers(headers) if attachments: for f in attachments: name = os.path.basename(f) message.add_attachment(name, f) if from_name: message.set_from('%s <%s.' % (from_name, from_address)) else: message.set_from(from_address) if html_body: message.set_html(body) else: message.set_text(body) return sg.send(message)
def _delete_profile(self): url = '/1.0/profiles/{0}'.format(self.name) if self.project: url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) self.client.do('DELETE', url) self.actions.append('delete')
def get_plugin_info(module, plugin_manager_url, intellij_home, plugin_id): build_number = get_build_number(module, intellij_home) params = {'action': 'download', 'build': build_number, 'id': plugin_id} query_params = urlencode(params) url = '%s?%s' % (plugin_manager_url, query_params) for _ in range(0, 3): resp, info = fetch_url(module, url, method='HEAD', timeout=3, follow_redirects=False) if resp is not None: resp.close() status_code = info['status'] if status_code == 404: module.fail_json(msg='Unable to find plugin "%s" for build "%s"' % (plugin_id, build_number)) if status_code > -1 and status_code < 400: break # 3 retries 5 seconds appart time.sleep(5) if status_code == -1 or status_code >= 400: module.fail_json(msg='Error querying url "%s": %s' % (url, info['msg'])) location = info.get('location') if location is None: location = info.get('Location') if location is None: module.fail_json(msg='Unsupported HTTP response for: %s (status=%s)' % (url, status_code)) if location.startswith('http'): plugin_url = location else: plugin_url = urljoin(plugin_manager_url, location) jar_pattern = re.compile(r'/(?P<file_name>[^/]+\.jar)(?:\?.*)$') jar_matcher = jar_pattern.search(plugin_url) if jar_matcher: file_name = jar_matcher.group('file_name') else: versioned_pattern = re.compile( r'(?P<plugin_id>[0-9]+)/(?P<update_id>[0-9]+)/' r'(?P<file_name>[^/]+)(?:\?.*)$') versioned_matcher = versioned_pattern.search(plugin_url) if versioned_matcher: file_name = '%s-%s-%s' % (versioned_matcher.group('plugin_id'), versioned_matcher.group('update_id'), versioned_matcher.group('file_name')) else: hash_object = hashlib.sha256(plugin_url) file_name = '%s-%s.zip' % (plugin_id, hash_object.hexdigest()) return plugin_url, file_name
def main(): argspec = dict( name=dict(required=True, type="str"), description=dict(required=True, type="str"), state=dict(choices=["present", "absent", "enabled", "disabled"], required=True), search=dict(required=True, type="str"), app=dict(type="str", required=False, default="SplunkEnterpriseSecuritySuite"), ui_dispatch_context=dict(type="str", required=False), time_earliest=dict(type="str", required=False, default="-24h"), time_latest=dict(type="str", required=False, default="now"), cron_schedule=dict(type="str", required=False, default="*/5 * * * *"), scheduling=dict( type="str", required=False, default="real-time", choices=["real-time", "continuous"], ), schedule_window=dict(type="str", required=False, default="0"), schedule_priority=dict( type="str", required=False, default="Default", choices=["Default", "Higher", "Highest"], ), trigger_alert_when=dict( type="str", required=False, default="number of events", choices=[ "number of events", "number of results", "number of hosts", "number of sources", ], ), trigger_alert_when_condition=dict( type="str", required=False, default="greater than", choices=[ "greater than", "less than", "equal to", "not equal to", "drops by", "rises by", ], ), trigger_alert_when_value=dict(type="str", required=False, default="10"), throttle_window_duration=dict(type="str", required=False), throttle_fields_to_group_by=dict(type="str", required=False), suppress_alerts=dict(type="bool", required=False, default=False), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) if module.params["state"] in ["present", "enabled"]: module_disabled_state = False else: module_disabled_state = True splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, not_rest_data_keys=["state"], ) try: query_dict = splunk_request.get_by_path( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}" .format(quote_plus(module.params["name"]))) except HTTPError as e: # the data monitor doesn't exist query_dict = {} # Have to custom craft the data here because they overload the saved searches # endpoint in the rest api and we want to hide the nuance from the user request_post_data = {} request_post_data["name"] = module.params["name"] request_post_data["action.correlationsearch.enabled"] = "1" request_post_data["is_scheduled"] = True request_post_data["dispatch.rt_backfill"] = True request_post_data["action.correlationsearch.label"] = module.params["name"] request_post_data["description"] = module.params["description"] request_post_data["search"] = module.params["search"] request_post_data["request.ui_dispatch_app"] = module.params["app"] if module.params["ui_dispatch_context"]: request_post_data["request.ui_dispatch_context"] = module.params[ "ui_dispatch_context"] request_post_data["dispatch.earliest_time"] = module.params[ "time_earliest"] request_post_data["dispatch.latest_time"] = module.params["time_latest"] request_post_data["cron_schedule"] = module.params["cron_schedule"] if module.params["scheduling"] == "real-time": request_post_data["realtime_schedule"] = True else: request_post_data["realtime_schedule"] = False request_post_data["schedule_window"] = module.params["schedule_window"] request_post_data["schedule_priority"] = module.params[ "schedule_priority"].lower() request_post_data["alert_type"] = module.params["trigger_alert_when"] request_post_data["alert_comparator"] = module.params[ "trigger_alert_when_condition"] request_post_data["alert_threshold"] = module.params[ "trigger_alert_when_value"] request_post_data["alert.suppress"] = module.params["suppress_alerts"] request_post_data["disabled"] = module_disabled_state if module.params["state"] in ["present", "enabled", "disabled"]: if query_dict: needs_change = False for arg in request_post_data: if arg in query_dict["entry"][0]["content"]: if to_text( query_dict["entry"][0]["content"][arg]) != to_text( request_post_data[arg]): needs_change = True if not needs_change: module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: # FIXME - need to find a reasonable way to deal with action.correlationsearch.enabled del request_post_data[ "name"] # If this is present, splunk assumes we're trying to create a new one wit the same name splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches/{0}" .format(quote_plus(module.params["name"])), data=urlencode(request_post_data), ) module.exit_json(changed=True, msg="{0} updated.", splunk_data=splunk_data) else: # Create it splunk_data = splunk_request.create_update( "servicesNS/nobody/SplunkEnterpriseSecuritySuite/saved/searches", data=urlencode(request_post_data), ) module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) elif module.params["state"] == "absent": if query_dict: splunk_data = splunk_request.delete_by_path( "services/saved/searches/{0}".format( quote_plus(module.params["name"]))) module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict)
def main(): argspec = dict( state=dict( required=False, choices=["present", "absent", "enabled", "disable"], default="present", type="str", ), connection_host=dict( required=False, choices=["ip", "dns", "none"], default="ip", type="str" ), host=dict(required=False, type="str", default=None), index=dict(required=False, type="str", default=None), name=dict(required=True, type="str"), protocol=dict(required=True, type="str", choices=["tcp", "udp"]), queue=dict( required=False, type="str", choices=["parsingQueue", "indexQueue"], default="parsingQueue", ), rawTcpDoneTimeout=dict(required=False, type="int", default=10), restrictToHost=dict(required=False, type="str", default=None), ssl=dict(required=False, type="bool", default=None), source=dict(required=False, type="str", default=None), sourcetype=dict(required=False, type="str", default=None), datatype=dict(required=False, choices=["cooked", "raw"], default="raw"), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, not_rest_data_keys=["state", "datatype", "protocol"], ) # This is where the splunk_* args are processed request_data = splunk_request.get_data() query_dict = splunk_request.get_by_path( "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), quote_plus(module.params["name"]), ) ) if module.params["state"] in ["present", "enabled", "disabled"]: _data = splunk_request.get_data() if module.params["state"] in ["present", "enabled"]: _data["disabled"] = False else: _data["disabled"] = True if query_dict: needs_change = False for arg in request_data: if arg in query_dict["entry"][0]["content"]: if to_text(query_dict["entry"][0]["content"][arg]) != to_text( request_data[arg] ): needs_change = True if not needs_change: module.exit_json( changed=False, msg="Nothing to do.", splunk_data=query_dict ) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), quote_plus(module.params["name"]), data=urlencode(_data), ) ) if module.params["state"] in ["present", "enabled"]: module.exit_json( changed=True, msg="{0} updated.", splunk_data=splunk_data ) else: module.exit_json( changed=True, msg="{0} disabled.", splunk_data=splunk_data ) else: # Create it splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/{0}/{1}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), ), data=urlencode(_data), ) module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) elif module.params["state"] == "absent": if query_dict: splunk_data = splunk_request.delete_by_path( "servicesNS/nobody/search/data/inputs/{0}/{1}/{2}".format( quote_plus(module.params["protocol"]), quote_plus(module.params["datatype"]), quote_plus(module.params["name"]), ) ) module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data={})
def restmethod(self, *args, **kwargs): """Do the hard work of making the request here""" # gather named path parameters and do substitution on the URL if self.parameters: path_parameters = {} body_parameters = {} query_parameters = {} for x in self.parameters: expected_location = x.get("in") key_name = x.get("name", None) key_value = kwargs.get(key_name, None) if expected_location == "path" and key_name and key_value: path_parameters.update({key_name: key_value}) elif expected_location == "body" and key_name and key_value: body_parameters.update({key_name: key_value}) elif expected_location == "query" and key_name and key_value: query_parameters.update({key_name: key_value}) if len(body_parameters.keys()) >= 1: body_parameters = body_parameters.get( list(body_parameters.keys())[0]) else: body_parameters = None else: path_parameters = {} query_parameters = {} body_parameters = None # This will fail if we have not set path parameters with a KeyError url = self.url.format(**path_parameters) if query_parameters: # modify the URL to add path parameters url = url + "?" + urlencode(query_parameters) try: if body_parameters: body_parameters_json = json.dumps(body_parameters) response = self.session.request.open(method=self.method, url=url, data=body_parameters_json) else: response = self.session.request.open(method=self.method, url=url) request_error = False except HTTPError as e: # An HTTPError has the same methods available as a valid response from request.open response = e request_error = True # Return the result if JSON and success ({} for empty responses) # Raise an exception if there was a failure. try: result_code = response.getcode() result = json.loads(response.read()) except ValueError: result = {} if result or result == {}: if result_code and result_code < 400: return result else: raise RestOperationException(result) # Raise a generic RestOperationException if this fails raise RestOperationException({ "status": result_code, "errors": [{ "message": "REST Operation Failed" }] })
def main(): argspec = dict( name=dict(required=True, type="str"), state=dict(choices=["present", "absent"], required=True), blacklist=dict(required=False, type="str", default=None), check_index=dict(required=False, type="bool", default=False), check_path=dict(required=False, type="bool", default=None), crc_salt=dict(required=False, type="str", default=None), disabled=dict(required=False, type="bool", default=False), followTail=dict(required=False, type="bool", default=False), host=dict(required=False, type="str", default=None), host_segment=dict(required=False, type="int", default=None), host_regex=dict(required=False, type="str", default=None), ignore_older_than=dict(required=False, type="str", default=None), index=dict(required=False, type="str", default=None), recursive=dict(required=False, type="bool", default=False), rename_source=dict(required=False, type="str", default=None), sourcetype=dict(required=False, type="str", default=None), time_before_close=dict(required=False, type="int", default=None), whitelist=dict(required=False, type="str", default=None), ) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) # map of keys for the splunk REST API that aren't pythonic so we have to # handle the substitutes keymap = { "check_index": "check-index", "check_path": "check-path", "crc_salt": "crc-salt", "ignore_older_than": "ignore-older-than", "rename_source": "rename-source", "time_before_close": "time-before-close", } splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, keymap=keymap, not_rest_data_keys=["state"], ) # This is where the splunk_* args are processed request_data = splunk_request.get_data() query_dict = splunk_request.get_by_path( "servicesNS/nobody/search/data/inputs/monitor/{0}".format( quote_plus(module.params["name"]))) if module.params["state"] == "present": if query_dict: needs_change = False for arg in request_data: if arg in query_dict["entry"][0]["content"]: if to_text( query_dict["entry"][0]["content"][arg]) != to_text( request_data[arg]): needs_change = True if not needs_change: module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict, ) if needs_change: splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/monitor/{0}".format( quote_plus(module.params["name"]))) module.exit_json(changed=True, msg="{0} updated.", splunk_data=splunk_data) else: # Create it _data = splunk_request.get_data() _data["name"] = module.params["name"] splunk_data = splunk_request.create_update( "servicesNS/nobody/search/data/inputs/monitor", data=urlencode(_data)) module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) if module.params["state"] == "absent": if query_dict: splunk_data = splunk_request.delete_by_path( "servicesNS/nobody/search/data/inputs/monitor/{0}".format( quote_plus(module.params["name"]))) module.exit_json( changed=True, msg="Deleted {0}.".format(module.params["name"]), splunk_data=splunk_data, ) module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict)
def install(self): changed = False plugin_file = ('%s/plugins/%s.jpi' % (self.params['jenkins_home'], self.params['name'])) if not self.is_installed and self.params['version'] in [ None, 'latest' ]: if not self.module.check_mode: # Install the plugin (with dependencies) install_script = ( 'd = Jenkins.instance.updateCenter.getPlugin("%s")' '.deploy(); d.get();' % self.params['name']) if self.params['with_dependencies']: install_script = ( 'Jenkins.instance.updateCenter.getPlugin("%s")' '.getNeededDependencies().each{it.deploy()}; %s' % (self.params['name'], install_script)) script_data = {'script': install_script} script_data.update(self.crumb) data = urlencode(script_data) # Send the installation request r = self._get_url_data( "%s/scriptText" % self.url, msg_status="Cannot install plugin.", msg_exception="Plugin installation has failed.", data=data) hpi_file = '%s/plugins/%s.hpi' % (self.params['jenkins_home'], self.params['name']) if os.path.isfile(hpi_file): os.remove(hpi_file) changed = True else: # Check if the plugin directory exists if not os.path.isdir(self.params['jenkins_home']): self.module.fail_json( msg="Jenkins home directory doesn't exist.") md5sum_old = None if os.path.isfile(plugin_file): # Make the checksum of the currently installed plugin with open(plugin_file, 'rb') as md5_plugin_fh: md5_plugin_content = md5_plugin_fh.read() md5sum_old = hashlib.md5(md5_plugin_content).hexdigest() if self.params['version'] in [None, 'latest']: # Take latest version plugin_url = ( "%s/latest/%s.hpi" % (self.params['updates_url'], self.params['name'])) else: # Take specific version plugin_url = ("{0}/download/plugins/" "{1}/{2}/{1}.hpi".format( self.params['updates_url'], self.params['name'], self.params['version'])) if (self.params['updates_expiration'] == 0 or self.params['version'] not in [None, 'latest'] or md5sum_old is None): # Download the plugin file directly r = self._download_plugin(plugin_url) # Write downloaded plugin into file if checksums don't match if md5sum_old is None: # No previously installed plugin if not self.module.check_mode: self._write_file(plugin_file, r) changed = True else: # Get data for the MD5 data = r.read() # Make new checksum md5sum_new = hashlib.md5(data).hexdigest() # If the checksum is different from the currently installed # plugin, store the new plugin if md5sum_old != md5sum_new: if not self.module.check_mode: self._write_file(plugin_file, data) changed = True elif self.params['version'] == 'latest': # Check for update from the updates JSON file plugin_data = self._download_updates() try: with open(plugin_file, 'rb') as sha1_plugin_fh: sha1_plugin_content = sha1_plugin_fh.read() sha1_old = hashlib.sha1(sha1_plugin_content) except Exception as e: self.module.fail_json( msg="Cannot calculate SHA1 of the old plugin.", details=to_native(e)) sha1sum_old = base64.b64encode(sha1_old.digest()) # If the latest version changed, download it if sha1sum_old != to_bytes(plugin_data['sha1']): if not self.module.check_mode: r = self._download_plugin(plugin_url) self._write_file(plugin_file, r) changed = True # Change file attributes if needed if os.path.isfile(plugin_file): params = {'dest': plugin_file} params.update(self.params) file_args = self.module.load_file_common_arguments(params) if not self.module.check_mode: # Not sure how to run this in the check mode changed = self.module.set_fs_attributes_if_different( file_args, changed) else: # See the comment above changed = True return changed
def __init__(self, module): self.module = module self.hostname = module.params['name'] self.rack = module.params['rack'] self.rank = module.params['rank'] self.appliance = module.params['appliance'] self.prim_intf = module.params['prim_intf'] self.prim_intf_ip = module.params['prim_intf_ip'] self.network = module.params['network'] self.prim_intf_mac = module.params['prim_intf_mac'] self.endpoint = module.params['stacki_endpoint'] auth_creds = { 'USERNAME': module.params['stacki_user'], 'PASSWORD': module.params['stacki_password'] } # Get Initial CSRF cred_a = self.do_request(self.module, self.endpoint, method="GET") cookie_a = cred_a.headers.get('Set-Cookie').split(';') init_csrftoken = None for c in cookie_a: if "csrftoken" in c: init_csrftoken = c.replace("csrftoken=", "") init_csrftoken = init_csrftoken.rstrip("\r\n") break # Make Header Dictionary with initial CSRF header = { 'csrftoken': init_csrftoken, 'X-CSRFToken': init_csrftoken, 'Content-type': 'application/x-www-form-urlencoded', 'Cookie': cred_a.headers.get('Set-Cookie') } # Endpoint to get final authentication header login_endpoint = self.endpoint + "/login" # Get Final CSRF and Session ID login_req = self.do_request(self.module, login_endpoint, headers=header, payload=urlencode(auth_creds), method='POST') cookie_f = login_req.headers.get('Set-Cookie').split(';') csrftoken = None for f in cookie_f: if "csrftoken" in f: csrftoken = f.replace("csrftoken=", "") if "sessionid" in f: sessionid = c.split("sessionid=", 1)[-1] sessionid = sessionid.rstrip("\r\n") self.header = { 'csrftoken': csrftoken, 'X-CSRFToken': csrftoken, 'sessionid': sessionid, 'Content-type': 'application/json', 'Cookie': login_req.headers.get('Set-Cookie') }
def main(): """ Module execution :return: """ argument_spec = keycloak_argument_spec() config_spec = dict( allowKerberosAuthentication=dict(type='bool', default=False), allowPasswordAuthentication=dict(type='bool'), authType=dict(type='str', choices=['none', 'simple'], default='none'), batchSizeForSync=dict(type='int', default=1000), bindCredential=dict(type='str', no_log=True), bindDn=dict(type='str'), cachePolicy=dict(type='str', choices=['DEFAULT', 'EVICT_DAILY', 'EVICT_WEEKLY', 'MAX_LIFESPAN', 'NO_CACHE'], default='DEFAULT'), changedSyncPeriod=dict(type='int', default=-1), connectionPooling=dict(type='bool', default=True), connectionPoolingAuthentication=dict(type='str', choices=['none', 'simple', 'DIGEST-MD5']), connectionPoolingDebug=dict(type='str'), connectionPoolingInitSize=dict(type='int'), connectionPoolingMaxSize=dict(type='int'), connectionPoolingPrefSize=dict(type='int'), connectionPoolingProtocol=dict(type='str'), connectionPoolingTimeout=dict(type='int'), connectionTimeout=dict(type='int'), connectionUrl=dict(type='str'), customUserSearchFilter=dict(type='str'), debug=dict(type='bool'), editMode=dict(type='str', choices=['READ_ONLY', 'WRITABLE', 'UNSYNCED']), enabled=dict(type='bool', default=True), evictionDay=dict(type='str'), evictionHour=dict(type='str'), evictionMinute=dict(type='str'), fullSyncPeriod=dict(type='int', default=-1), importEnabled=dict(type='bool', default=True), kerberosRealm=dict(type='str'), keyTab=dict(type='str', no_log=False), maxLifespan=dict(type='int'), pagination=dict(type='bool', default=True), priority=dict(type='int', default=0), rdnLDAPAttribute=dict(type='str'), readTimeout=dict(type='int'), searchScope=dict(type='str', choices=['1', '2'], default='1'), serverPrincipal=dict(type='str'), startTls=dict(type='bool', default=False), syncRegistrations=dict(type='bool', default=False), trustEmail=dict(type='bool', default=False), updateProfileFirstLogin=dict(type='bool'), useKerberosForPasswordAuthentication=dict(type='bool', default=False), usePasswordModifyExtendedOp=dict(type='bool', default=False, no_log=False), useTruststoreSpi=dict(type='str', choices=['always', 'ldapsOnly', 'never'], default='ldapsOnly'), userObjectClasses=dict(type='str'), usernameLDAPAttribute=dict(type='str'), usersDn=dict(type='str'), uuidLDAPAttribute=dict(type='str'), validatePasswordPolicy=dict(type='bool', default=False), vendor=dict(type='str'), ) mapper_spec = dict( id=dict(type='str'), name=dict(type='str'), parentId=dict(type='str'), providerId=dict(type='str'), providerType=dict(type='str'), config=dict(type='dict'), ) meta_args = dict( config=dict(type='dict', options=config_spec), state=dict(type='str', default='present', choices=['present', 'absent']), realm=dict(type='str', default='master'), id=dict(type='str'), name=dict(type='str'), provider_id=dict(type='str', aliases=['providerId'], choices=['ldap', 'kerberos']), provider_type=dict(type='str', aliases=['providerType'], default='org.keycloak.storage.UserStorageProvider'), parent_id=dict(type='str', aliases=['parentId']), mappers=dict(type='list', elements='dict', options=mapper_spec), ) argument_spec.update(meta_args) module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True, required_one_of=([['id', 'name'], ['token', 'auth_realm', 'auth_username', 'auth_password']]), required_together=([['auth_realm', 'auth_username', 'auth_password']])) result = dict(changed=False, msg='', diff={}, proposed={}, existing={}, end_state={}) # Obtain access token, initialize API try: connection_header = get_token(module.params) except KeycloakError as e: module.fail_json(msg=str(e)) kc = KeycloakAPI(module, connection_header) realm = module.params.get('realm') state = module.params.get('state') config = module.params.get('config') mappers = module.params.get('mappers') cid = module.params.get('id') name = module.params.get('name') # Keycloak API expects config parameters to be arrays containing a single string element if config is not None: module.params['config'] = dict((k, [str(v).lower() if not isinstance(v, str) else v]) for k, v in config.items() if config[k] is not None) if mappers is not None: for mapper in mappers: if mapper.get('config') is not None: mapper['config'] = dict((k, [str(v).lower() if not isinstance(v, str) else v]) for k, v in mapper['config'].items() if mapper['config'][k] is not None) # Filter and map the parameters names that apply comp_params = [x for x in module.params if x not in list(keycloak_argument_spec().keys()) + ['state', 'realm', 'mappers'] and module.params.get(x) is not None] # See if it already exists in Keycloak if cid is None: found = kc.get_components(urlencode(dict(type='org.keycloak.storage.UserStorageProvider', parent=realm, name=name)), realm) if len(found) > 1: module.fail_json(msg='No ID given and found multiple user federations with name `{name}`. Cannot continue.'.format(name=name)) before_comp = next(iter(found), None) if before_comp is not None: cid = before_comp['id'] else: before_comp = kc.get_component(cid, realm) if before_comp is None: before_comp = {} # if user federation exists, get associated mappers if cid is not None: before_comp['mappers'] = sorted(kc.get_components(urlencode(dict(parent=cid)), realm), key=lambda x: x.get('name')) # Build a proposed changeset from parameters given to this module changeset = {} for param in comp_params: new_param_value = module.params.get(param) old_value = before_comp[camel(param)] if camel(param) in before_comp else None if param == 'mappers': new_param_value = [dict((k, v) for k, v in x.items() if x[k] is not None) for x in new_param_value] if new_param_value != old_value: changeset[camel(param)] = new_param_value # special handling of mappers list to allow change detection if module.params.get('mappers') is not None: if module.params['provider_id'] == 'kerberos': module.fail_json(msg='Cannot configure mappers for Kerberos federations.') for change in module.params['mappers']: change = dict((k, v) for k, v in change.items() if change[k] is not None) if change.get('id') is None and change.get('name') is None: module.fail_json(msg='Either `name` or `id` has to be specified on each mapper.') if cid is None: old_mapper = {} elif change.get('id') is not None: old_mapper = kc.get_component(change['id'], realm) if old_mapper is None: old_mapper = {} else: found = kc.get_components(urlencode(dict(parent=cid, name=change['name'])), realm) if len(found) > 1: module.fail_json(msg='Found multiple mappers with name `{name}`. Cannot continue.'.format(name=change['name'])) if len(found) == 1: old_mapper = found[0] else: old_mapper = {} new_mapper = old_mapper.copy() new_mapper.update(change) if new_mapper != old_mapper: if changeset.get('mappers') is None: changeset['mappers'] = list() changeset['mappers'].append(new_mapper) # Prepare the desired values using the existing values (non-existence results in a dict that is save to use as a basis) desired_comp = before_comp.copy() desired_comp.update(changeset) result['proposed'] = sanitize(changeset) result['existing'] = sanitize(before_comp) # Cater for when it doesn't exist (an empty dict) if not before_comp: if state == 'absent': # Do nothing and exit if module._diff: result['diff'] = dict(before='', after='') result['changed'] = False result['end_state'] = {} result['msg'] = 'User federation does not exist; doing nothing.' module.exit_json(**result) # Process a creation result['changed'] = True if module._diff: result['diff'] = dict(before='', after=sanitize(desired_comp)) if module.check_mode: module.exit_json(**result) # create it desired_comp = desired_comp.copy() updated_mappers = desired_comp.pop('mappers', []) after_comp = kc.create_component(desired_comp, realm) for mapper in updated_mappers: if mapper.get('id') is not None: kc.update_component(mapper, realm) else: if mapper.get('parentId') is None: mapper['parentId'] = after_comp['id'] mapper = kc.create_component(mapper, realm) after_comp['mappers'] = updated_mappers result['end_state'] = sanitize(after_comp) result['msg'] = "User federation {id} has been created".format(id=after_comp['id']) module.exit_json(**result) else: if state == 'present': # Process an update # no changes if desired_comp == before_comp: result['changed'] = False result['end_state'] = sanitize(desired_comp) result['msg'] = "No changes required to user federation {id}.".format(id=cid) module.exit_json(**result) # doing an update result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_comp), after=sanitize(desired_comp)) if module.check_mode: module.exit_json(**result) # do the update desired_comp = desired_comp.copy() updated_mappers = desired_comp.pop('mappers', []) kc.update_component(desired_comp, realm) after_comp = kc.get_component(cid, realm) for mapper in updated_mappers: if mapper.get('id') is not None: kc.update_component(mapper, realm) else: if mapper.get('parentId') is None: mapper['parentId'] = desired_comp['id'] mapper = kc.create_component(mapper, realm) after_comp['mappers'] = updated_mappers result['end_state'] = sanitize(after_comp) result['msg'] = "User federation {id} has been updated".format(id=cid) module.exit_json(**result) elif state == 'absent': # Process a deletion result['changed'] = True if module._diff: result['diff'] = dict(before=sanitize(before_comp), after='') if module.check_mode: module.exit_json(**result) # delete it kc.delete_component(cid, realm) result['end_state'] = {} result['msg'] = "User federation {id} has been deleted".format(id=cid) module.exit_json(**result)
def main(): module = AnsibleModule(argument_spec=dict( token=dict(required=True, no_log=True), msg=dict(required=True), type=dict(required=True, choices=["inbox", "chat"]), external_user_name=dict(required=False), from_address=dict(required=False), source=dict(required=False), subject=dict(required=False), from_name=dict(required=False), reply_to=dict(required=False), project=dict(required=False), tags=dict(required=False), link=dict(required=False), validate_certs=dict(default='yes', type='bool'), ), supports_check_mode=True) type = module.params["type"] token = module.params["token"] if type == 'inbox': url = "https://api.flowdock.com/v1/messages/team_inbox/%s" % (token) else: url = "https://api.flowdock.com/v1/messages/chat/%s" % (token) params = {} # required params params['content'] = module.params["msg"] # required params for the 'chat' type if module.params['external_user_name']: if type == 'inbox': module.fail_json( msg="external_user_name is not valid for the 'inbox' type") else: params['external_user_name'] = module.params["external_user_name"] elif type == 'chat': module.fail_json( msg="external_user_name is required for the 'chat' type") # required params for the 'inbox' type for item in ['from_address', 'source', 'subject']: if module.params[item]: if type == 'chat': module.fail_json(msg="%s is not valid for the 'chat' type" % item) else: params[item] = module.params[item] elif type == 'inbox': module.fail_json(msg="%s is required for the 'inbox' type" % item) # optional params if module.params["tags"]: params['tags'] = module.params["tags"] # optional params for the 'inbox' type for item in ['from_name', 'reply_to', 'project', 'link']: if module.params[item]: if type == 'chat': module.fail_json(msg="%s is not valid for the 'chat' type" % item) else: params[item] = module.params[item] # If we're in check mode, just exit pretending like we succeeded if module.check_mode: module.exit_json(changed=False) # Send the data to Flowdock data = urlencode(params) response, info = fetch_url(module, url, data=data) if info['status'] != 200: module.fail_json(msg="unable to send msg: %s" % info['msg']) module.exit_json(changed=True, msg=module.params["msg"])
def post_sendgrid_api(module, username, password, from_address, to_addresses, subject, body, api_key=None, cc=None, bcc=None, attachments=None, html_body=False, from_name=None, headers=None): if not HAS_SENDGRID: SENDGRID_URI = "https://api.sendgrid.com/api/mail.send.json" AGENT = "Ansible" data = {'api_user': username, 'api_key': password, 'from': from_address, 'subject': subject, 'text': body} encoded_data = urlencode(data) to_addresses_api = '' for recipient in to_addresses: recipient = to_bytes(recipient, errors='surrogate_or_strict') to_addresses_api += '&to[]=%s' % recipient encoded_data += to_addresses_api headers = {'User-Agent': AGENT, 'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'} return fetch_url(module, SENDGRID_URI, data=encoded_data, headers=headers, method='POST') else: # Remove this check when adding Sendgrid API v3 support if LooseVersion(sendgrid.version.__version__) > LooseVersion("1.6.22"): module.fail_json(msg="Please install sendgrid==1.6.22 or lower since module uses Sendgrid V2 APIs.") if api_key: sg = sendgrid.SendGridClient(api_key) else: sg = sendgrid.SendGridClient(username, password) message = sendgrid.Mail() message.set_subject(subject) for recip in to_addresses: message.add_to(recip) if cc: for recip in cc: message.add_cc(recip) if bcc: for recip in bcc: message.add_bcc(recip) if headers: message.set_headers(headers) if attachments: for f in attachments: name = os.path.basename(f) message.add_attachment(name, f) if from_name: message.set_from('%s <%s.' % (from_name, from_address)) else: message.set_from(from_address) if html_body: message.set_html(body) else: message.set_text(body) return sg.send(message)
def main(): argspec = dict(state=dict( required=False, choices=['present', 'absent', 'enabled', 'disable'], default='present', type='str'), connection_host=dict(required=False, choices=['ip', 'dns', 'none'], default='none', type='str'), host=dict(required=False, type='str', default=None), index=dict(required=False, type='str', default=None), name=dict(required=True, type='str'), protocol=dict(required=True, type='str', choices=['tcp', 'udp']), queue=dict(required=False, type='str', choices=['parsingQueue', 'indexQueue'], default='parsingQueue'), rawTcpDoneTimeout=dict(required=False, type='int', default=10), restrictToHost=dict(required=False, type='str', default=None), ssl=dict(required=False, type='bool', default=None), source=dict(required=False, type='str', default=None), sourcetype=dict(required=False, type='str', default=None), datatype=dict(required=False, choices=["cooked", "raw"], default="raw")) module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) splunk_request = SplunkRequest( module, headers={"Content-Type": "application/x-www-form-urlencoded"}, not_rest_data_keys=['state', 'datatype', 'protocol']) # This is where the splunk_* args are processed request_data = splunk_request.get_data() query_dict = splunk_request.get_by_path( 'servicesNS/nobody/search/data/inputs/{0}/{1}/{2}'.format( quote_plus(module.params['protocol']), quote_plus(module.params['datatype']), quote_plus(module.params['name']), )) if module.params['state'] in ['present', 'enabled', 'disabled']: _data = splunk_request.get_data() if module.params['state'] in ['present', 'enabled']: _data['disabled'] = False else: _data['disabled'] = True if query_dict: needs_change = False for arg in request_data: if arg in query_dict['entry'][0]['content']: if to_text( query_dict['entry'][0]['content'][arg]) != to_text( request_data[arg]): needs_change = True if not needs_change: module.exit_json(changed=False, msg="Nothing to do.", splunk_data=query_dict) if module.check_mode and needs_change: module.exit_json( changed=True, msg="A change would have been made if not in check mode.", splunk_data=query_dict) if needs_change: splunk_data = splunk_request.create_update( 'servicesNS/nobody/search/data/inputs/{0}/{1}/{2}'.format( quote_plus(module.params['protocol']), quote_plus(module.params['datatype']), quote_plus(module.params['name']), data=urlencode(_data))) if module.params['state'] in ['present', 'enabled']: module.exit_json(changed=True, msg="{0} updated.", splunk_data=splunk_data) else: module.exit_json(changed=True, msg="{0} disabled.", splunk_data=splunk_data) else: # Create it splunk_data = splunk_request.create_update( 'servicesNS/nobody/search/data/inputs/{0}/{1}'.format( quote_plus(module.params['protocol']), quote_plus(module.params['datatype']), ), data=urlencode(_data)) module.exit_json(changed=True, msg="{0} created.", splunk_data=splunk_data) elif module.params['state'] == 'absent': if query_dict: splunk_data = splunk_request.delete_by_path( 'servicesNS/nobody/search/data/inputs/{0}/{1}/{2}'.format( quote_plus(module.params['protocol']), quote_plus(module.params['datatype']), quote_plus(module.params['name']), )) module.exit_json(changed=True, msg="Deleted {0}.".format(module.params['name']), splunk_data=splunk_data) module.exit_json(changed=False, msg="Nothing to do.", splunk_data={})
def _get_profile_json(self): url = '/1.0/profiles/{0}'.format(self.name) if self.project: url = '{0}?{1}'.format(url, urlencode(dict(project=self.project))) return self.client.do('GET', url, ok_error_codes=[404])
def intersight_call(self, http_method="", resource_path="", query_params=None, body=None, moid=None, name=None): """ Invoke the Intersight API :param resource_path: intersight resource path e.g. '/ntp/Policies' :param query_params: dictionary object with query string parameters as key/value pairs :param body: dictionary object with intersight data :param moid: intersight object moid :param name: intersight object name :return: json http response object """ target_host = urlparse(self.host).netloc target_path = urlparse(self.host).path query_path = "" method = http_method.upper() bodyString = "" # Verify an accepted HTTP verb was chosen if (method not in ['GET', 'POST', 'PATCH', 'DELETE']): raise ValueError( 'Please select a valid HTTP verb (GET/POST/PATCH/DELETE)') # Verify the resource path isn't empy & is a valid <str> object if (resource_path != "" and not (resource_path, str)): raise TypeError( 'The *resource_path* value is required and must be of type "<str>"' ) # Verify the query parameters isn't empy & is a valid <dict> object if (query_params is not None and not isinstance(query_params, dict)): raise TypeError( 'The *query_params* value must be of type "<dict>"') # Verify the body isn't empy & is a valid <dict> object if (body is not None and not isinstance(body, dict)): raise TypeError('The *body* value must be of type "<dict>"') # Verify the MOID is not null & of proper length if (moid is not None and len(moid.encode('utf-8')) != 24): raise ValueError('Invalid *moid* value!') # Check for query_params, encode, and concatenate onto URL if query_params: query_path = "?" + urlencode(query_params).replace('+', '%20') # Handle PATCH/DELETE by Object "name" instead of "moid" if (method == "PATCH" or method == "DELETE"): if moid is None: if name is not None: if isinstance(name, str): moid = self.get_moid_by_name(resource_path, name) else: raise TypeError( 'The *name* value must be of type "<str>"') else: raise ValueError( 'Must set either *moid* or *name* with "PATCH/DELETE!"' ) # Check for moid and concatenate onto URL if moid is not None: resource_path += "/" + moid # Check for GET request to properly form body if method != "GET": bodyString = json.dumps(body) # Concatenate URLs for headers target_url = self.host + resource_path + query_path request_target = method + " " + target_path + resource_path + query_path # Get the current GMT Date/Time cdate = get_gmt_date() # Generate the body digest body_digest = get_sha256_digest(bodyString) b64_body_digest = b64encode(body_digest.digest()) # Generate the authorization header auth_header = { 'Date': cdate, 'Host': target_host, 'Digest': "SHA-256=" + b64_body_digest.decode('ascii') } string_to_sign = prepare_str_to_sign(request_target, auth_header) b64_signed_msg = self.get_rsasig_b64encode(string_to_sign) auth_header = self.get_auth_header(auth_header, b64_signed_msg) # Generate the HTTP requests header request_header = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Host': '{0}'.format(target_host), 'Date': '{0}'.format(cdate), 'Digest': 'SHA-256={0}'.format(b64_body_digest.decode('ascii')), 'Authorization': '{0}'.format(auth_header), } response, info = fetch_url(self.module, target_url, data=bodyString, headers=request_header, method=method, use_proxy=self.module.params['use_proxy']) return response, info
def main(): argument_spec = dict( server_ip=dict(type='str', required=True), port=dict(type='str', default='main', choices=['main', 'kvm']), state=dict(type='str', default='present', choices=['present', 'absent']), whitelist_hos=dict(type='bool'), rules=dict(type='dict', options=dict(input=dict( type='list', elements='dict', options=dict( name=dict(type='str'), ip_version=dict(type='str', required=True, choices=['ipv4', 'ipv6']), dst_ip=dict(type='str'), dst_port=dict(type='str'), src_ip=dict(type='str'), src_port=dict(type='str'), protocol=dict(type='str'), tcp_flags=dict(type='str'), action=dict(type='str', required=True, choices=['accept', 'discard']), )), )), update_timeout=dict(type='int', default=30), wait_for_configured=dict(type='bool', default=True), wait_delay=dict(type='int', default=10), timeout=dict(type='int', default=180), ) argument_spec.update(HETZNER_DEFAULT_ARGUMENT_SPEC) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, ) # Sanitize input module.params['status'] = 'active' if (module.params['state'] == 'present') else 'disabled' if module.params['rules'] is None: module.params['rules'] = {} if module.params['rules'].get('input') is None: module.params['rules']['input'] = [] server_ip = module.params['server_ip'] # https://robot.your-server.de/doc/webservice/en.html#get-firewall-server-ip url = "{0}/firewall/{1}".format(BASE_URL, server_ip) if module.params['wait_for_configured']: try: result, error = fetch_url_json_with_retries( module, url, check_done_callback=firewall_configured, check_done_delay=module.params['wait_delay'], check_done_timeout=module.params['timeout'], ) except CheckDoneTimeoutException as dummy: module.fail_json( msg='Timeout while waiting for firewall to be configured.') else: result, error = fetch_url_json(module, url) if not firewall_configured(result, error): module.fail_json( msg= 'Firewall configuration cannot be read as it is not configured.' ) full_before = result['firewall'] if not full_before.get('rules'): full_before['rules'] = create_default_rules_object() before = restrict_firewall_config(full_before) # Build wanted (after) state and compare after = dict(before) changed = False changed |= update(before, after, module.params, 'port') changed |= update(before, after, module.params, 'status') changed |= update(before, after, module.params, 'whitelist_hos') after['rules'] = create_default_rules_object() if module.params['status'] == 'active': for ruleset in RULES: changed |= update_rules(before, after, module.params, ruleset) # Update if different construct_result = True construct_status = None if changed and not module.check_mode: # https://robot.your-server.de/doc/webservice/en.html#post-firewall-server-ip url = "{0}/firewall/{1}".format(BASE_URL, server_ip) headers = {"Content-type": "application/x-www-form-urlencoded"} data = dict(after) data['whitelist_hos'] = str(data['whitelist_hos']).lower() del data['rules'] for ruleset in RULES: encode_rule(data, ruleset, after) result, error = fetch_url_json( module, url, method='POST', timeout=module.params['update_timeout'], data=urlencode(data), headers=headers, ) if module.params['wait_for_configured'] and not firewall_configured( result, error): try: result, error = fetch_url_json_with_retries( module, url, check_done_callback=firewall_configured, check_done_delay=module.params['wait_delay'], check_done_timeout=module.params['timeout'], skip_first=True, ) except CheckDoneTimeoutException as e: result, error = e.result, e.error module.warn( 'Timeout while waiting for firewall to be configured.') full_after = result['firewall'] if not full_after.get('rules'): full_after['rules'] = create_default_rules_object() construct_status = full_after['status'] if construct_status != 'in process': # Only use result if configuration is done, so that diff will be ok after = restrict_firewall_config(full_after) construct_result = False if construct_result: # Construct result (used for check mode, and configuration still in process) full_after = dict(full_before) for k, v in after.items(): if k != 'rules': full_after[k] = after[k] if construct_status is not None: # We want 'in process' here full_after['status'] = construct_status full_after['rules'] = dict() for ruleset in RULES: full_after['rules'][ruleset] = after['rules'][ruleset] module.exit_json( changed=changed, diff=dict( before=before, after=after, ), firewall=full_after, )
def update_qs(params): ''' Append key-value pairs to self.filter_string ''' accepted_params = dict((k, v) for (k, v) in params.items() if v is not None) return '?' + urlencode(accepted_params)
def get_urlencoded_data(self): return urlencode(self.get_data())
def encode_url_params(self, params): """Encodes key value pairs for URL""" return "?{0}".format(urlencode(params))