def send_msg(self, attachments): headers = { 'Content-type': 'application/json', } payload = { 'channel': self.channel, 'username': self.username, 'attachments': attachments, 'parse': 'none', 'icon_url': ('http://cdn2.hubspot.net/hub/330046/' 'file-449187601-png/ansible_badge.png'), } data = json.dumps(payload) self._display.debug(data) self._display.debug(self.webhook_url) try: response = open_url(self.webhook_url, data=data, validate_certs=self.validate_certs, headers=headers) return response.read() except Exception as e: self._display.warning(u'Could not submit message to Slack: %s' % to_text(e))
def _fetch_conjur_variable(conjur_variable, token, conjur_url, account): token = b64encode(token) headers = {'Authorization': 'Token token="{0}"'.format(token)} display.vvvv('Header: {0}'.format(headers)) url = '{0}/secrets/{1}/variable/{2}'.format(conjur_url, account, quote_plus(conjur_variable)) display.vvvv('Conjur Variable URL: {0}'.format(url)) response = open_url(url, headers=headers, method='GET') if response.getcode() == 200: display.vvvv('Conjur variable {0} was successfully retrieved'.format( conjur_variable)) return [response.read()] if response.getcode() == 401: raise AnsibleError( 'Conjur request has invalid authorization credentials') if response.getcode() == 403: raise AnsibleError( 'The controlling host\'s Conjur identity does not have authorization to retrieve {0}' .format(conjur_variable)) if response.getcode() == 404: raise AnsibleError( 'The variable {0} does not exist'.format(conjur_variable)) return {}
def get_clients(self, realm='master', filter=None): """ Obtains client representations for clients in a realm :param realm: realm to be queried :param filter: if defined, only the client with clientId specified in the filter is returned :return: list of dicts of client representations """ clientlist_url = URL_CLIENTS.format(url=self.baseurl, realm=realm) if filter is not None: clientlist_url += '?clientId=%s' % filter try: return json.load( open_url(clientlist_url, method='GET', headers=self.restheaders, validate_certs=self.validate_certs)) except ValueError as e: self.module.fail_json( msg= 'API returned incorrect JSON when trying to obtain list of clients for realm %s: %s' % (realm, str(e))) except Exception as e: self.module.fail_json( msg='Could not obtain list of clients for realm %s: %s' % (realm, str(e)))
def get_token(base_url, validate_certs, auth_realm, client_id, auth_username, auth_password, client_secret): auth_url = URL_TOKEN.format(url=base_url, realm=auth_realm) temp_payload = { 'grant_type': 'password', 'client_id': client_id, 'client_secret': client_secret, 'username': auth_username, 'password': auth_password, } # Remove empty items, for instance missing client_secret payload = dict((k, v) for k, v in temp_payload.items() if v is not None) try: r = json.load( open_url(auth_url, method='POST', validate_certs=validate_certs, data=urlencode(payload))) except ValueError as e: raise KeycloakError( 'API returned invalid JSON when trying to obtain access token from %s: %s' % (auth_url, str(e))) except Exception as e: raise KeycloakError('Could not obtain access token from %s: %s' % (auth_url, str(e))) try: return { 'Authorization': 'Bearer ' + r['access_token'], 'Content-Type': 'application/json' } except KeyError: raise KeycloakError('Could not obtain access token from %s' % auth_url)
def send_msg_v2(self, msg, msg_format='text', color='yellow', notify=False): """Method for sending a message to HipChat""" headers = { 'Authorization': 'Bearer %s' % self.token, 'Content-Type': 'application/json' } body = {} body['room_id'] = self.room body['from'] = self.from_name[:15] # max length is 15 body['message'] = msg body['message_format'] = msg_format body['color'] = color body['notify'] = self.allow_notify and notify data = json.dumps(body) url = self.API_V2_URL + "room/{room_id}/notification".format( room_id=self.room) try: response = open_url(url, data=data, headers=headers, method='POST') return response.read() except Exception as ex: self._display.warning( 'Could not submit message to hipchat: {0}'.format(ex))
def run(self, terms, variables, **kwargs): try: resp = open_url('https://ip-ranges.amazonaws.com/ip-ranges.json') amazon_response = json.load(resp)['prefixes'] except getattr(json.decoder, 'JSONDecodeError', ValueError) as e: # on Python 3+, json.decoder.JSONDecodeError is raised for bad # JSON. On 2.x it's a ValueError raise AnsibleError("Could not decode AWS IP ranges: %s" % to_native(e)) except HTTPError as e: raise AnsibleError( "Received HTTP error while pulling IP ranges: %s" % to_native(e)) except SSLValidationError as e: raise AnsibleError( "Error validating the server's certificate for: %s" % to_native(e)) except URLError as e: raise AnsibleError("Failed look up IP range service: %s" % to_native(e)) except ConnectionError as e: raise AnsibleError("Error connecting to IP range service: %s" % to_native(e)) if 'region' in kwargs: region = kwargs['region'] amazon_response = (item for item in amazon_response if item['region'] == region) if 'service' in kwargs: service = str.upper(kwargs['service']) amazon_response = (item for item in amazon_response if item['service'] == service) return [item['ip_prefix'] for item in amazon_response]
def get(self, key): url = "%s/%s?recursive=true" % (self.baseurl, key) data = None value = {} try: r = open_url(url, validate_certs=self.validate_certs) data = r.read() except Exception: return None try: # I will not support Version 1 of etcd for folder parsing item = json.loads(data) if self.version == 'v1': # When ETCD are working with just v1 if 'value' in item: value = item['value'] else: if 'node' in item: # When a usual result from ETCD value = self._parse_node(item['node']) if 'errorCode' in item: # Here return an error when an unknown entry responds value = "ENOENT" except Exception: raise return value
def _fetch_information(token, url): results = [] paginated_url = url while True: try: response = open_url(paginated_url, headers={ 'X-Auth-Token': token, 'Content-type': 'application/json' }) except Exception as e: raise AnsibleError("Error while fetching %s: %s" % (url, to_native(e))) try: raw_json = json.loads(response.read()) except ValueError: raise AnsibleError("Incorrect JSON payload") try: results.extend(raw_json["servers"]) except KeyError: raise AnsibleError( "Incorrect format from the Scaleway API response") link = response.headers['Link'] if not link: return results relations = parse_pagination_link(link) if 'next' not in relations: return results paginated_url = urllib_parse.urljoin(paginated_url, relations['next'])
def run(self, terms, variables=None, **kwargs): self.set_options(direct=kwargs) ret = [] for term in terms: display.vvvv("url lookup connecting to %s" % term) try: response = open_url( term, validate_certs=self.get_option('validate_certs'), use_proxy=self.get_option('use_proxy'), url_username=self.get_option('username'), url_password=self.get_option('password'), headers=self.get_option('headers')) except HTTPError as e: raise AnsibleError("Received HTTP error for %s : %s" % (term, to_native(e))) except URLError as e: raise AnsibleError("Failed lookup url for %s : %s" % (term, to_native(e))) except SSLValidationError as e: raise AnsibleError( "Error validating the server's certificate for %s: %s" % (term, to_native(e))) except ConnectionError as e: raise AnsibleError("Error connecting to %s: %s" % (term, to_native(e))) if self.get_option('split_lines'): for line in response.read().splitlines(): ret.append(to_text(line)) else: ret.append(to_text(response.read())) return ret
def grafana_switch_organisation(self, headers): try: r = open_url('%s/api/user/using/%s' % (self.grafana_url, self.grafana_org_id), headers=headers, method='POST') except HTTPError as e: raise GrafanaAPIException('Unable to switch to organization %s : %s' % (self.grafana_org_id, to_native(e))) if r.getcode() != 200: raise GrafanaAPIException('Unable to switch to organization %s : %s' % (self.grafana_org_id, str(r.getcode())))
def get_group_by_groupid(self, gid, realm="master"): """ Fetch a keycloak group from the provided realm using the group's unique ID. If the group does not exist, None is returned. gid is a UUID provided by the Keycloak API :param gid: UUID of the group to be returned :param realm: Realm in which the group resides; default 'master'. """ groups_url = URL_GROUP.format(url=self.baseurl, realm=realm, groupid=gid) try: return json.load( open_url(groups_url, method="GET", headers=self.restheaders, validate_certs=self.validate_certs)) except HTTPError as e: if e.code == 404: return None else: self.module.fail_json( msg="Could not fetch group %s in realm %s: %s" % (gid, realm, str(e))) except Exception as e: self.module.fail_json( msg="Could not fetch group %s in realm %s: %s" % (gid, realm, str(e)))
def get_client_by_id(self, id, realm='master'): """ Obtain client representation by id :param id: id (not clientId) of client to be queried :param realm: client from this realm :return: dict of client representation or None if none matching exist """ client_url = URL_CLIENT.format(url=self.baseurl, realm=realm, id=id) try: return json.load( open_url(client_url, method='GET', headers=self.restheaders, validate_certs=self.validate_certs)) except HTTPError as e: if e.code == 404: return None else: self.module.fail_json( msg='Could not obtain client %s for realm %s: %s' % (id, realm, str(e))) except ValueError as e: self.module.fail_json( msg= 'API returned incorrect JSON when trying to obtain client %s for realm %s: %s' % (id, realm, str(e))) except Exception as e: self.module.fail_json( msg='Could not obtain client %s for realm %s: %s' % (id, realm, str(e)))
def run(self, terms, variables=None, **kwargs): self.set_options(direct=kwargs) ret = [] for term in terms: auth = to_text( base64.b64encode( to_bytes('{0}:{1}'.format(self.get_option('cpm_username'), self.get_option('cpm_password')), errors='surrogate_or_strict'))) if self.get_option('use_https') is True: protocol = "https://" else: protocol = "http://" if (term == 'temperature'): fullurl = ("%s%s/api/v2/status/temperature" % (protocol, self.get_option('cpm_url'))) elif (term == 'firmware'): fullurl = ("%s%s/api/v2/status/firmware" % (protocol, self.get_option('cpm_url'))) elif (term == 'status'): fullurl = ("%s%s/api/v2/status/status" % (protocol, self.get_option('cpm_url'))) elif (term == 'alarms'): fullurl = ("%s%s/api/v2/status/alarms" % (protocol, self.get_option('cpm_url'))) else: raise AnsibleError("Status command not recognized %s " % (term)) try: response = open_url( fullurl, validate_certs=self.get_option('validate_certs'), use_proxy=self.get_option('use_proxy'), headers={ 'Content-Type': 'application/json', 'Authorization': "Basic %s" % auth }) except HTTPError as e: raise AnsibleError("Received HTTP error for %s : %s" % (fullurl, to_native(e))) except URLError as e: raise AnsibleError("Failed lookup url for %s : %s" % (fullurl, to_native(e))) except SSLValidationError as e: raise AnsibleError( "Error validating the server's certificate for %s: %s" % (fullurl, to_native(e))) except ConnectionError as e: raise AnsibleError("Error connecting to %s: %s" % (fullurl, to_native(e))) ret.append(to_text(response.read())) return ret
def send_event(self, url, authtoken, state, result, runtime): if result._task_fields['args'].get('_ansible_check_mode') is True: self.ansible_check_mode = True if result._task_fields['args'].get('_ansible_version'): self.ansible_version = \ result._task_fields['args'].get('_ansible_version') if result._task._role: ansible_role = str(result._task._role) else: ansible_role = None if 'args' in result._task_fields: del result._task_fields['args'] data = {} data['uuid'] = result._task._uuid data['session'] = self.session data['status'] = state data['timestamp'] = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S ' '+0000') data['host'] = self.host data['ip_address'] = self.ip_address data['user'] = self.user data['runtime'] = runtime data['ansible_version'] = self.ansible_version data['ansible_check_mode'] = self.ansible_check_mode data['ansible_host'] = result._host.name data['ansible_playbook'] = self.ansible_playbook data['ansible_role'] = ansible_role data['ansible_task'] = result._task_fields data['ansible_result'] = result._result # This wraps the json payload in and outer json event needed by Splunk jsondata = json.dumps(data, cls=AnsibleJSONEncoder, sort_keys=True) jsondata = '{"event":' + jsondata + "}" open_url(url, jsondata, headers={ 'Content-type': 'application/json', 'Authorization': 'Splunk ' + authtoken }, method='POST')
def request(self, api, endpoint, *args, **kwargs): """ Send a request to API backend and pre-process a response. :param api: API to send a request to :type api: str :param endpoint: API endpoint to fetch data from :type endpoint: str :param args: other args for open_url :param kwargs: other kwargs for open_url :return: server response. JSON response is automatically deserialized. :rtype: dict | list | str """ default_headers = { 'Authorization': "Bearer {0}".format(self._token), 'Accept': "*/*" # Otherwise server doesn't set content-type header } url = self.base_url.format(api=api, endpoint=endpoint) headers = default_headers arg_headers = kwargs.pop('headers', None) if arg_headers: headers.update(arg_headers) try: display.vvvv('manifold lookup connecting to {0}'.format(url)) response = open_url(url, headers=headers, http_agent=self.http_agent, *args, **kwargs) data = response.read() if response.headers.get('content-type') == 'application/json': data = json.loads(data) return data except ValueError: raise ApiError( 'JSON response can\'t be parsed while requesting {url}:\n{json}' .format(json=data, url=url)) except HTTPError as e: raise ApiError( 'Server returned: {err} while requesting {url}:\n{response}'. format(err=str(e), url=url, response=e.read())) except URLError as e: raise ApiError('Failed lookup url for {url} : {err}'.format( url=url, err=str(e))) except SSLValidationError as e: raise ApiError( 'Error validating the server\'s certificate for {url}: {err}'. format(url=url, err=str(e))) except ConnectionError as e: raise ApiError('Error connecting to {url}: {err}'.format( url=url, err=str(e)))
def send_event(self, url, state, result, runtime): if result._task_fields['args'].get('_ansible_check_mode') is True: self.ansible_check_mode = True if result._task_fields['args'].get('_ansible_version'): self.ansible_version = \ result._task_fields['args'].get('_ansible_version') if result._task._role: ansible_role = str(result._task._role) else: ansible_role = None if 'args' in result._task_fields: del result._task_fields['args'] data = {} data['uuid'] = result._task._uuid data['session'] = self.session data['status'] = state data['timestamp'] = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S ' '+0000') data['host'] = self.host data['ip_address'] = self.ip_address data['user'] = self.user data['runtime'] = runtime data['ansible_version'] = self.ansible_version data['ansible_check_mode'] = self.ansible_check_mode data['ansible_host'] = result._host.name data['ansible_playbook'] = self.ansible_playbook data['ansible_role'] = ansible_role data['ansible_task'] = result._task_fields data['ansible_result'] = result._result open_url(url, data=json.dumps(data, cls=AnsibleJSONEncoder, sort_keys=True), headers={ 'Content-type': 'application/json', 'X-Sumo-Host': data['ansible_host'] }, method='POST')
def grafana_list_dashboards(self): # define http headers headers = self.grafana_headers() dashboard_list = [] try: if self.search: r = open_url('%s/api/search?query=%s' % (self.grafana_url, self.search), headers=headers, method='GET') else: r = open_url('%s/api/search/' % self.grafana_url, headers=headers, method='GET') except HTTPError as e: raise GrafanaAPIException('Unable to search dashboards : %s' % to_native(e)) if r.getcode() == 200: try: dashboard_list = json.loads(r.read()) except Exception as e: raise GrafanaAPIException('Unable to parse json list %s' % to_native(e)) else: raise GrafanaAPIException('Unable to list grafana dashboards : %s' % str(r.getcode())) return dashboard_list
def send(self, path, data, **kwargs): ''' Sends the command to the device over api ''' url_kwargs = dict( timeout=self.get_option('persistent_command_timeout'), validate_certs=self.get_option('validate_certs'), use_proxy=self.get_option("use_proxy"), headers={}, ) url_kwargs.update(kwargs) if self._auth: # Avoid modifying passed-in headers headers = dict(kwargs.get('headers', {})) headers.update(self._auth) url_kwargs['headers'] = headers else: url_kwargs['force_basic_auth'] = True url_kwargs['url_username'] = self.get_option('remote_user') url_kwargs['url_password'] = self.get_option('password') try: url = self._url + path self._log_messages("send url '%s' with data '%s' and kwargs '%s'" % (url, data, url_kwargs)) response = open_url(url, data=data, **url_kwargs) except HTTPError as exc: is_handled = self.handle_httperror(exc) if is_handled is True: return self.send(path, data, **kwargs) elif is_handled is False: raise else: response = is_handled except URLError as exc: raise AnsibleConnectionFailure( 'Could not connect to {0}: {1}'.format(self._url + path, exc.reason)) response_buffer = BytesIO() resp_data = response.read() self._log_messages("received response: '%s'" % resp_data) response_buffer.write(resp_data) # Try to assign a new auth token if one is given self._auth = self.update_auth(response, response_buffer) or self._auth response_buffer.seek(0) return response, response_buffer
def _send_annotation(self, annotation): try: response = open_url(self.grafana_url, data=json.dumps(annotation), headers=self.headers, method="POST", validate_certs=self.validate_grafana_certs, url_username=self.grafana_user, url_password=self.grafana_password, http_agent=self.http_agent, force_basic_auth=self.force_basic_auth) except Exception as e: self._display.error(u'Could not submit message to Grafana: %s' % to_text(e))
def memset_api_call(api_key, api_method, payload=None): ''' Generic function which returns results back to calling function. Requires an API key and an API method to assemble the API URL. Returns response text to be analysed. ''' # instantiate a response object response = Response() # if we've already started preloading the payload then copy it # and use that, otherwise we need to isntantiate it. if payload is None: payload = dict() else: payload = payload.copy() # set some sane defaults has_failed = False msg = None data = urlencode(payload) headers = {'Content-Type': 'application/x-www-form-urlencoded'} api_uri_base = 'https://api.memset.com/v1/json/' api_uri = '{0}{1}/' . format(api_uri_base, api_method) try: resp = open_url(api_uri, data=data, headers=headers, method="POST", force_basic_auth=True, url_username=api_key) response.content = resp.read().decode('utf-8') response.status_code = resp.getcode() except urllib_error.HTTPError as e: try: errorcode = e.code except AttributeError: errorcode = None has_failed = True response.content = e.read().decode('utf8') response.status_code = errorcode if response.status_code is not None: msg = "Memset API returned a {0} response ({1}, {2})." . format(response.status_code, response.json()['error_type'], response.json()['error']) else: msg = "Memset API returned an error ({0}, {1})." . format(response.json()['error_type'], response.json()['error']) if msg is None: msg = response.json() return(has_failed, msg, response)
def _fetch_conjur_token(conjur_url, account, username, api_key): conjur_url = '{0}/authn/{1}/{2}/authenticate'.format( conjur_url, account, username) display.vvvv( 'Authentication request to Conjur at: {0}, with user: {1}'.format( conjur_url, username)) response = open_url(conjur_url, data=api_key, method='POST') code = response.getcode() if code != 200: raise AnsibleError( 'Failed to authenticate as \'{0}\' (got {1} response)'.format( username, code)) return response.read()
def _fetch_information(self, url): try: response = open_url(url, headers=self.headers) except Exception as e: self.display.warning("An error happened while fetching: %s" % url) return None try: raw_data = to_text(response.read(), errors='surrogate_or_strict') except UnicodeError: raise AnsibleError("Incorrect encoding of fetched payload from Online servers") try: return json.loads(raw_data) except ValueError: raise AnsibleError("Incorrect JSON payload")
def _fetch_information(self, url): results = None cache_key = self.get_cache_key(url) # get the user's cache option to see if we should save the cache if it is changing user_cache_setting = self.get_option('cache') # read if the user has caching enabled and the cache isn't being refreshed attempt_to_read_cache = user_cache_setting and self.use_cache # attempt to read the cache if inventory isn't being refreshed and the user has caching enabled if attempt_to_read_cache: try: results = self._cache[cache_key] need_to_fetch = False except KeyError: # occurs if the cache_key is not in the cache or if the cache_key expired # we need to fetch the URL now need_to_fetch = True else: # not reading from cache so do fetch need_to_fetch = True if need_to_fetch: self.display.v("Fetching: " + url) response = open_url(url, headers=self.headers, timeout=self.timeout, validate_certs=self.validate_certs) try: raw_data = to_text(response.read(), errors='surrogate_or_strict') except UnicodeError: raise AnsibleError( "Incorrect encoding of fetched payload from NetBox API.") try: results = json.loads(raw_data) except ValueError: raise AnsibleError("Incorrect JSON payload: %s" % raw_data) # put result in cache if enabled if user_cache_setting: self._cache[cache_key] = results return results
def create_group(self, grouprep, realm="master"): """ Create a Keycloak group. :param grouprep: a GroupRepresentation of the group to be created. Must contain at minimum the field name. :return: HTTPResponse object on success """ groups_url = URL_GROUPS.format(url=self.baseurl, realm=realm) try: return open_url(groups_url, method='POST', headers=self.restheaders, data=json.dumps(grouprep), validate_certs=self.validate_certs) except Exception as e: self.module.fail_json( msg="Could not create group %s in realm %s: %s" % (grouprep['name'], realm, str(e)))
def delete_client_template(self, id, realm="master"): """ Delete a client template from Keycloak :param id: id (not name) of client to be deleted :param realm: realm of client template to be deleted :return: HTTPResponse object on success """ url = URL_CLIENTTEMPLATE.format(url=self.baseurl, realm=realm, id=id) try: return open_url(url, method='DELETE', headers=self.restheaders, validate_certs=self.validate_certs) except Exception as e: self.module.fail_json( msg='Could not delete client template %s in realm %s: %s' % (id, realm, str(e)))
def create_client_template(self, clienttrep, realm="master"): """ Create a client in keycloak :param clienttrep: Client template representation of client template to be created. Must at least contain field name :param realm: realm for client template to be created in :return: HTTPResponse object on success """ url = URL_CLIENTTEMPLATES.format(url=self.baseurl, realm=realm) try: return open_url(url, method='POST', headers=self.restheaders, data=json.dumps(clienttrep), validate_certs=self.validate_certs) except Exception as e: self.module.fail_json( msg='Could not create client template %s in realm %s: %s' % (clienttrep['clientId'], realm, str(e)))
def delete_group(self, name=None, groupid=None, realm="master"): """ Delete a group. One of name or groupid must be provided. Providing the group ID is preferred as it avoids a second lookup to convert a group name to an ID. :param name: The name of the group. A lookup will be performed to retrieve the group ID. :param groupid: The ID of the group (preferred to name). :param realm: The realm in which this group resides, default "master". """ if groupid is None and name is None: # prefer an exception since this is almost certainly a programming error in the module itself. raise Exception( "Unable to delete group - one of group ID or name must be provided." ) # only lookup the name if groupid isn't provided. # in the case that both are provided, prefer the ID, since it's one # less lookup. if groupid is None and name is not None: for group in self.get_groups(realm=realm): if group['name'] == name: groupid = group['id'] break # if the group doesn't exist - no problem, nothing to delete. if groupid is None: return None # should have a good groupid by here. group_url = URL_GROUP.format(realm=realm, groupid=groupid, url=self.baseurl) try: return open_url(group_url, method='DELETE', headers=self.restheaders, validate_certs=self.validate_certs) except Exception as e: self.module.fail_json(msg="Unable to delete group %s: %s" % (groupid, str(e)))
def update_client_template(self, id, clienttrep, realm="master"): """ Update an existing client template :param id: id (not name) of client template to be updated in Keycloak :param clienttrep: corresponding (partial/full) client template representation with updates :param realm: realm the client template is in :return: HTTPResponse object on success """ url = URL_CLIENTTEMPLATE.format(url=self.baseurl, realm=realm, id=id) try: return open_url(url, method='PUT', headers=self.restheaders, data=json.dumps(clienttrep), validate_certs=self.validate_certs) except Exception as e: self.module.fail_json( msg='Could not update client template %s in realm %s: %s' % (id, realm, str(e)))
def get_groups(self, realm="master"): """ Fetch the name and ID of all groups on the Keycloak server. To fetch the full data of the group, make a subsequent call to get_group_by_groupid, passing in the ID of the group you wish to return. :param realm: Return the groups of this realm (default "master"). """ groups_url = URL_GROUPS.format(url=self.baseurl, realm=realm) try: return json.load( open_url(groups_url, method="GET", headers=self.restheaders, validate_certs=self.validate_certs)) except Exception as e: self.module.fail_json( msg="Could not fetch list of groups in realm %s: %s" % (realm, str(e)))
def update_group(self, grouprep, realm="master"): """ Update an existing group. :param grouprep: A GroupRepresentation of the updated group. :return HTTPResponse object on success """ group_url = URL_GROUP.format(url=self.baseurl, realm=realm, groupid=grouprep['id']) try: return open_url(group_url, method='PUT', headers=self.restheaders, data=json.dumps(grouprep), validate_certs=self.validate_certs) except Exception as e: self.module.fail_json( msg='Could not update group %s in realm %s: %s' % (grouprep['name'], realm, str(e)))