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 put(self, data): # Replace newlines with Unicode line separator # for multi-line events data = to_text(data, errors='surrogate_or_strict') multiline = data.replace(u'\n', self.LINE_SEP) multiline += u"\n" # Send data, reconnect if needed while True: try: self._conn.send( to_bytes(multiline, errors='surrogate_or_strict')) except socket.error: self.reopen_connection() continue break self.close_connection()
def legacy_plugin_dir_to_plugin_type(legacy_plugin_dir_name): """ Utility method to convert from a PluginLoader dir name to a plugin ref_type :param legacy_plugin_dir_name: PluginLoader dir name (eg, 'action_plugins', 'library') :return: the corresponding plugin ref_type (eg, 'action', 'role') """ legacy_plugin_dir_name = to_text(legacy_plugin_dir_name) plugin_type = legacy_plugin_dir_name.replace(u'_plugins', u'') if plugin_type == u'library': plugin_type = u'modules' if plugin_type not in AnsibleCollectionRef.VALID_REF_TYPES: raise ValueError('{0} cannot be mapped to a valid collection ref type'.format(to_native(legacy_plugin_dir_name))) return plugin_type
def is_valid_collection_name(collection_name): """ Validates if the given string is a well-formed collection name (does not look up the collection itself) :param collection_name: candidate collection name to validate (a valid name is of the form 'ns.collname') :return: True if the collection name passed is well-formed, False otherwise """ collection_name = to_text(collection_name) if collection_name.count(u'.') != 1: return False return all( # NOTE: keywords and identifiers are different in different Pythons not iskeyword(ns_or_name) and is_python_identifier(ns_or_name) for ns_or_name in collection_name.split(u'.') )
def get_cert_days(self, cert_filename=None, cert_content=None, now=None): ''' Return the days the certificate in cert_filename remains valid and -1 if the file was not found. If cert_filename contains more than one certificate, only the first one will be considered. If now is not specified, datetime.datetime.now() is used. ''' filename = cert_filename data = None if cert_content is not None: filename = '-' data = cert_content.encode('utf-8') cert_filename_suffix = '' elif cert_filename is not None: if not os.path.exists(cert_filename): return -1 cert_filename_suffix = ' in {0}'.format(cert_filename) else: return -1 openssl_cert_cmd = [ self.openssl_binary, "x509", "-in", filename, "-noout", "-text" ] dummy, out, dummy = self.module.run_command( openssl_cert_cmd, data=data, check_rc=True, binary_data=True, environ_update=_OPENSSL_ENVIRONMENT_UPDATE) try: not_after_str = re.search( r"\s+Not After\s*:\s+(.*)", to_text(out, errors='surrogate_or_strict')).group(1) not_after = datetime.datetime.strptime(not_after_str, '%b %d %H:%M:%S %Y %Z') except AttributeError: raise BackendException( "No 'Not after' date found{0}".format(cert_filename_suffix)) except ValueError: raise BackendException( "Failed to parse 'Not after' date{0}".format( cert_filename_suffix)) if now is None: now = datetime.datetime.now() return (not_after - now).days
def do_jq(value, expression, text=False, multiple=False): if not HAS_JQ: raise AnsibleFilterError( 'jq python module required for use of the |jq filter') # fire undefined error for v in (value, expression): if isinstance(v, Undefined): str(v) try: program = jq.compile(to_text(expression)) except ValueError as e: raise AnsibleFilterError('Error compile jq expression: %s' % to_native(e)) except AttributeError as e: raise AnsibleFilterError( 'Invalid type (%s) provided as an expression: %s' % (type(expression), to_native(e))) except Exception as e: raise AnsibleFilterError('Unknown error compiling jq expression: %s' % to_native(e)) kwargs = {} if isinstance(value, text_type): kwargs['text'] = value else: kwargs['text'] = json.dumps(value, cls=AnsibleJSONEncoder) try: ret = program.input(**kwargs) if text: return ret.text() elif multiple: return ret.all() else: return ret.first() except TypeError as e: raise AnsibleFilterError('Invalid type (%s) provided as value: %s' % (type(value), to_native(e))) except Exception as e: raise AnsibleFilterError( 'Unknown error evaluating jq expression against %r: %s' % (value, to_native(e)))
def gather_cloud_init_data_facts(module): res = {'cloud_init_data_facts': dict()} for i in ['result', 'status']: filter = module.params.get('filter') if filter is None or filter == i: res['cloud_init_data_facts'][i] = dict() json_file = os.path.join(CLOUD_INIT_PATH, i + '.json') if os.path.exists(json_file): f = open(json_file, 'rb') contents = to_text(f.read(), errors='surrogate_or_strict') f.close() if contents: res['cloud_init_data_facts'][i] = module.from_json( contents) return res
def invoke_api(module, url, method='GET', data=None, status_codes=None): status_codes = status_codes or [200] headers = {} if data: headers['Content-Type'] = 'application/json' data = json.dumps(data) resp, info = fetch_url(module, url, method=method, data=data, headers=headers) if info['status'] not in status_codes: module.fail_json(url=url, **info) data = to_text(resp.read()) if data: return json.loads(data)
def _is_valid(self): if self.original_data: try: original_time_parameters = OpensshCertificateTimeParameters( valid_from=self.original_data.valid_after, valid_to=self.original_data.valid_before) except ValueError as e: return self.module.fail_json(msg=to_native(e)) return all([ self.original_data.type == self.type, set(to_text(p) for p in self.original_data.principals) == set( self.principals), self.original_data.serial == self.serial_number if self.serial_number is not None else True, original_time_parameters == self.time_parameters, original_time_parameters.within_range(self.valid_at) ]) return False
def main(): module_args = dict(url=dict(type='str', required=True), ) module = AnsibleModule( argument_spec=module_args, supports_check_mode=True, ) result = { 'changed': False, 'cert': None, 'raw_cert': None, } req = Request().get(module.params['url']) try: cert = getpeercert(req) b_cert = getpeercert(req, binary_form=True) finally: req.close() if cert: processed_cert = { 'issuer': '', 'not_after': cert.get('notAfter', None), 'not_before': cert.get('notBefore', None), 'serial_number': cert.get('serialNumber', None), 'subject': '', 'version': cert.get('version', None), } for field in ['issuer', 'subject']: field_values = [] for x509_part in cert.get(field, []): field_values.append( get_x509_shorthand(x509_part[0][0], x509_part[0][1])) processed_cert[field] = ",".join(field_values) result['cert'] = processed_cert if b_cert: result['raw_cert'] = to_text(base64.b64encode(b_cert)) module.exit_json(**result)
def _compare_conn_params(self, conn_info, options): changed = False diff_before = dict() diff_after = dict() for key, value in options.items(): if not value: continue if key in conn_info: current_value = conn_info[key] if key == 'ipv4.routes' and current_value is not None: # ipv4.routes do not have same options and show_connection() format # options: ['10.11.0.0/24 10.10.0.2', '10.12.0.0/24 10.10.0.2 200'] # show_connection(): ['{ ip = 10.11.0.0/24, nh = 10.10.0.2 }', '{ ip = 10.12.0.0/24, nh = 10.10.0.2, mt = 200 }'] # Need to convert in order to compare both current_value = [re.sub(r'^{\s*ip\s*=\s*([^, ]+),\s*nh\s*=\s*([^} ]+),\s*mt\s*=\s*([^} ]+)\s*}', r'\1 \2 \3', route) for route in current_value] current_value = [re.sub(r'^{\s*ip\s*=\s*([^, ]+),\s*nh\s*=\s*([^} ]+)\s*}', r'\1 \2', route) for route in current_value] if key == self.mac_setting: # MAC addresses are case insensitive, nmcli always reports them in uppercase value = value.upper() # ensure current_value is also converted to uppercase in case nmcli changes behaviour current_value = current_value.upper() else: # parameter does not exist current_value = None if isinstance(current_value, list) and isinstance(value, list): # compare values between two lists if sorted(current_value) != sorted(value): changed = True else: if current_value != to_text(value): changed = True diff_before[key] = current_value diff_after[key] = value diff = { 'before': diff_before, 'after': diff_after, } return (changed, diff)
def decode_txt_value(value): """ Given an encoded TXT value, decodes it. Raises DNSConversionError in case of errors. """ value = to_bytes(value) state = _STATE_OUTSIDE index = 0 length = len(value) result = [] while index < length: letter = value[index:index + 1] index += 1 if letter == b' ': if state == _STATE_QUOTED_STRING: result.append(letter) else: state = _STATE_OUTSIDE elif letter == b'\\': if state != _STATE_QUOTED_STRING: state = _STATE_UNQUOTED_STRING letter, index = _parse_quoted(value, index) result.append(letter) elif letter == b'"': if state == _STATE_QUOTED_STRING: state = _STATE_OUTSIDE elif state == _STATE_OUTSIDE: state = _STATE_QUOTED_STRING else: raise DNSConversionError( u'Unexpected double quotation mark inside an unquoted block at position {index}' .format(index=index)) else: if state != _STATE_QUOTED_STRING: state = _STATE_UNQUOTED_STRING result.append(letter) if state == _STATE_QUOTED_STRING: raise DNSConversionError( u'Missing double quotation mark at the end of value') return to_text(b''.join(result))
def _has_value_changed(consul_client, key, target_value): """ Uses the given Consul client to determine if the value associated to the given key is different to the given target value. :param consul_client: Consul connected client :param key: key in Consul :param target_value: value to be associated to the key :return: tuple where the first element is the value of the "X-Consul-Index" header and the second is `True` if the value has changed (i.e. the stored value is not the target value) """ index, existing = consul_client.kv.get(key) if not existing: return index, True try: changed = to_text(existing['Value'], errors='surrogate_or_strict') != target_value return index, changed except UnicodeError: # Existing value was not decodable but all values we set are valid utf-8 return index, True
def _content_of_file_at_path(path): """Read file content. Try read content of file at specified path. :type path: str :param path: Full path to location of file which should be read'ed. :rtype: content :return: File content or 'None' """ content = None if path and os.path.exists(path): with open(path, mode="rt") as opened_file: b_content = opened_file.read() try: content = to_text(b_content, errors='surrogate_or_strict') except UnicodeError: pass return content
def run(self, terms, variables=None, **kwargs): if not isinstance(terms, list): terms = [terms] ret = [] for term in terms: paramvals = {"file": None, "key": None} params = term.split() try: for param in params: name, value = param.split('=') if name not in paramvals: raise AnsibleAssertionError('%s not in paramvals' % name) paramvals[name] = value except (ValueError, AssertionError) as e: # In case "file" or "key" are not present raise AnsibleError(e) key = paramvals['key'] # Search also in the role/files directory and in the playbook directory shelvefile = self.find_file_in_search_path(variables, 'files', paramvals['file']) if shelvefile: res = self.read_shelve(shelvefile, key) if res is None: raise AnsibleError("Key %s not found in shelve file %s" % (key, shelvefile)) # Convert the value read to string ret.append(to_text(res)) break else: raise AnsibleError( "Could not locate shelve file in lookup: %s" % paramvals['file']) return ret
def _run_command(self, args): if not self.DOCKER_MACHINE_PATH: try: self.DOCKER_MACHINE_PATH = get_bin_path('docker-machine') except ValueError as e: raise AnsibleError(to_native(e)) command = [self.DOCKER_MACHINE_PATH] command.extend(args) display.debug('Executing command {0}'.format(command)) try: result = subprocess.check_output(command) except subprocess.CalledProcessError as e: display.warning( 'Exception {0} caught while executing command {1}, this was the original exception: {2}' .format(type(e).__name__, command, e)) raise e return to_text(result).strip()
def _get_collection_resource_path(name, ref_type, collection_list=None): if ref_type == u'playbook': # they are handled a bit diff due to 'extension variance' and no collection_list return _get_collection_playbook_path(name) acr = AnsibleCollectionRef.try_parse_fqcr(name, ref_type) if acr: # looks like a valid qualified collection ref; skip the collection_list collection_list = [acr.collection] subdirs = acr.subdirs resource = acr.resource elif not collection_list: return None # not a FQ and no collection search list spec'd, nothing to do else: resource = name # treat as unqualified, loop through the collection search list to try and resolve subdirs = '' for collection_name in collection_list: try: acr = AnsibleCollectionRef(collection_name=collection_name, subdirs=subdirs, resource=resource, ref_type=ref_type) # FIXME: error handling/logging; need to catch any import failures and move along pkg = import_module(acr.n_python_package_name) if pkg is not None: # the package is now loaded, get the collection's package and ask where it lives path = os.path.dirname( to_bytes(sys.modules[acr.n_python_package_name].__file__, errors='surrogate_or_strict')) return resource, to_text( path, errors='surrogate_or_strict'), collection_name except (IOError, ModuleNotFoundError) as e: continue except Exception as ex: # FIXME: pick out typical import errors first, then error logging continue return None
def _print_host_or_item(self, host_or_item, changed, msg, diff, is_host, error, stdout, stderr): if is_host: indent_level = 0 name = colorize(host_or_item.name, 'not_so_bold') else: indent_level = 4 if isinstance(host_or_item, dict): if 'key' in host_or_item.keys(): host_or_item = host_or_item['key'] name = colorize(to_text(host_or_item), 'bold') if error: color = 'failed' change_string = colorize('FAILED!!!', color) else: color = 'changed' if changed else 'ok' change_string = colorize("changed={0}".format(changed), color) msg = colorize(msg, color) line_length = 120 spaces = ' ' * (40 - len(name) - indent_level) line = "{0} * {1}{2}- {3}".format(' ' * indent_level, name, spaces, change_string) if len(msg) < 50: line += ' -- {0}'.format(msg) print("{0} {1}---------".format(line, '-' * (line_length - len(line)))) else: print("{0} {1}".format(line, '-' * (line_length - len(line)))) print(self._indent_text(msg, indent_level + 4)) if diff: self._print_diff(diff, indent_level) if stdout: stdout = colorize(stdout, 'failed') print(self._indent_text(stdout, indent_level + 4)) if stderr: stderr = colorize(stderr, 'failed') print(self._indent_text(stderr, indent_level + 4))
def yaml_to_dict(yaml, content_id): """ Return a Python dict version of the provided YAML. Conversion is done in a subprocess since the current Python interpreter does not have access to PyYAML. """ if content_id in yaml_to_dict_cache: return yaml_to_dict_cache[content_id] try: cmd = [external_python, yaml_to_json_path] proc = subprocess.Popen([to_bytes(c) for c in cmd], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout_bytes, stderr_bytes = proc.communicate(to_bytes(yaml)) if proc.returncode != 0: raise Exception('command %s failed with return code %d: %s' % ([to_native(c) for c in cmd], proc.returncode, to_native(stderr_bytes))) data = yaml_to_dict_cache[content_id] = json.loads(to_text(stdout_bytes), object_hook=object_hook) return data except Exception as ex: raise Exception('internal importer error - failed to parse yaml: %s' % to_native(ex))
def execute(self, cmd, timeout=200, capture_output=True): """ Executes a HAProxy command by sending a message to a HAProxy's local UNIX socket and waiting up to 'timeout' milliseconds for the response. """ self.client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.client.connect(self.socket) self.client.sendall(to_bytes('%s\n' % cmd)) result = b'' buf = b'' buf = self.client.recv(RECV_SIZE) while buf: result += buf buf = self.client.recv(RECV_SIZE) result = to_text(result, errors='surrogate_or_strict') if capture_output: self.capture_command_output(cmd, result.strip()) self.client.close() return result
def perform_shutdown(self, task_vars, distribution): result = {} shutdown_result = {} shutdown_command = self.get_shutdown_command(task_vars, distribution) shutdown_command_args = self.get_shutdown_command_args(distribution) shutdown_command_exec = '{0} {1}'.format(shutdown_command, shutdown_command_args) self.cleanup(force=True) try: display.vvv("{action}: shutting down server...".format( action=self._task.action)) display.debug( "{action}: shutting down server with command '{command}'". format(action=self._task.action, command=shutdown_command_exec)) if self._play_context.check_mode: shutdown_result['rc'] = 0 else: shutdown_result = self._low_level_execute_command( shutdown_command_exec, sudoable=self.DEFAULT_SUDOABLE) except AnsibleConnectionFailure as e: # If the connection is closed too quickly due to the system being shutdown, carry on display.debug( '{action}: AnsibleConnectionFailure caught and handled: {error}' .format(action=self._task.action, error=to_text(e))) shutdown_result['rc'] = 0 if shutdown_result['rc'] != 0: result['failed'] = True result['shutdown'] = False result[ 'msg'] = "Shutdown command failed. Error was {stdout}, {stderr}".format( stdout=to_native(shutdown_result['stdout'].strip()), stderr=to_native(shutdown_result['stderr'].strip())) return result result['failed'] = False result['shutdown_command'] = shutdown_command_exec return result
def _prepare_attachment(filename, content=None, mime_type=None): def escape_quotes(s): return s.replace('"', '\\"') boundary = "".join( random.choice(string.digits + string.ascii_letters) for dummy in range(30)) name = to_native(os.path.basename(filename)) if not mime_type: try: mime_type = mimetypes.guess_type( filename or '', strict=False)[0] or 'application/octet-stream' except Exception: mime_type = 'application/octet-stream' main_type, sep, sub_type = mime_type.partition('/') if not content and filename: with open(to_bytes(filename, errors='surrogate_or_strict'), 'rb') as f: content = f.read() else: try: content = base64.b64decode(content) except binascii.Error as e: raise Exception("Unable to base64 decode file content: %s" % e) lines = [ "--{0}".format(boundary), 'Content-Disposition: form-data; name="file"; filename={0}'.format( escape_quotes(name)), "Content-Type: {0}".format("{0}/{1}".format(main_type, sub_type)), '', to_text(content), "--{0}--".format(boundary), "" ] return ("multipart/form-data; boundary={0}".format(boundary), "\r\n".join(lines))
def resolve(self, target, nxdomain_is_empty=True, **kwargs): dnsname = dns.name.from_unicode(to_text(target)) loop_catcher = set() while True: try: nameservers = self._lookup_ns(dnsname) except dns.resolver.NXDOMAIN: if nxdomain_is_empty: return {} raise cname = self.cache.get((str(dnsname), 'cname')) if cname is None: break dnsname = cname if dnsname in loop_catcher: raise ResolverError('Found CNAME loop starting at {0}'.format(target)) loop_catcher.add(dnsname) results = {} for nameserver in nameservers: results[nameserver] = None resolver = self._get_resolver(dnsname, [nameserver]) try: try: response = self._handle_timeout(resolver.resolve, dnsname, lifetime=self.timeout, **kwargs) except AttributeError: # For dnspython < 2.0.0 resolver.search = False try: response = self._handle_timeout(resolver.query, dnsname, lifetime=self.timeout, **kwargs) except TypeError: # For dnspython < 1.6.0 resolver.lifetime = self.timeout response = self._handle_timeout(resolver.query, dnsname, **kwargs) if response.rrset: results[nameserver] = response.rrset except dns.resolver.NoAnswer: pass return results
def _validate_query_params(self, query_params): """ Validate query_params that are passed in by users to make sure they're valid and return error if they're not valid. """ invalid_query_params = [] app = self._find_app(self.endpoint) nb_app = getattr(self.nb, app) nb_endpoint = getattr(nb_app, self.endpoint) # Fetch the OpenAPI spec to perform validation against base_url = self.nb.base_url junk, endpoint_url = nb_endpoint.url.split(base_url) response = open_url(base_url + "/docs/?format=openapi") try: raw_data = to_text(response.read(), errors="surrogate_or_strict") except UnicodeError: self._handle_errors( msg="Incorrect encoding of fetched payload from NetBox API." ) try: openapi = json.loads(raw_data) except ValueError: self._handle_errors(msg="Incorrect JSON payload returned: %s" % raw_data) valid_query_params = openapi["paths"][endpoint_url + "/"]["get"]["parameters"] # Loop over passed in params and add to invalid_query_params and then fail if non-empty for param in query_params: if param not in valid_query_params: invalid_query_params.append(param) if invalid_query_params: self._handle_errors( "The following query_params are invalid: {0}".format( ", ".join(invalid_query_params) ) )
def reopen_connection(self): self.close_connection() root_delay = self.MIN_DELAY while True: try: self.open_connection() return except Exception as e: self._display.vvvv(u"Unable to connect to Logentries: %s" % to_text(e)) root_delay *= 2 if root_delay > self.MAX_DELAY: root_delay = self.MAX_DELAY wait_for = root_delay + random.uniform(0, root_delay) try: self._display.vvvv("sleeping %s before retry" % wait_for) time.sleep(wait_for) except KeyboardInterrupt: raise
def run(self): result = { 'active_connections': None, 'accepts': None, 'handled': None, 'requests': None, 'reading': None, 'writing': None, 'waiting': None, 'data': None, } (response, info) = fetch_url(module=module, url=self.url, force=True, timeout=self.timeout) if not response: module.fail_json( msg= "No valid or no response from url %s within %s seconds (timeout)" % (self.url, self.timeout)) data = to_text(response.read(), errors='surrogate_or_strict') if not data: return result result['data'] = data expr = r'Active connections: ([0-9]+) \nserver accepts handled requests\n ([0-9]+) ([0-9]+) ([0-9]+) \n' \ r'Reading: ([0-9]+) Writing: ([0-9]+) Waiting: ([0-9]+)' match = re.match(expr, data, re.S) if match: result['active_connections'] = int(match.group(1)) result['accepts'] = int(match.group(2)) result['handled'] = int(match.group(3)) result['requests'] = int(match.group(4)) result['reading'] = int(match.group(5)) result['writing'] = int(match.group(6)) result['waiting'] = int(match.group(7)) return result
def remove_packages(module, packages): remove_c = 0 # Using a for loop in case of error, we can report the package that failed for package in packages: # Query the package first, to see if we even need to remove installed, updated = query_package(module, package) if not installed: continue cmd = "%s uninstall %s" % (_get_pear_path(module), package) rc, stdout, stderr = module.run_command(cmd, check_rc=False) if rc != 0: module.fail_json(msg="failed to remove %s: %s" % (package, to_text(stdout + stderr))) remove_c += 1 if remove_c > 0: module.exit_json(changed=True, msg="removed %s package(s)" % remove_c) module.exit_json(changed=False, msg="package(s) already absent")
def _process_result_output(self, result, msg): task_host = result._host.get_name() task_result = "%s %s" % (task_host, msg) if self._run_is_verbose(result): task_result = "%s %s: %s" % ( task_host, msg, self._dump_results(result._result, indent=4)) return task_result if self.delegated_vars: task_delegate_host = self.delegated_vars['ansible_host'] task_result = "%s -> %s %s" % (task_host, task_delegate_host, msg) if result._result.get( 'msg') and result._result.get('msg') != "All items completed": task_result += " | msg: " + to_text(result._result.get('msg')) if result._result.get('stdout'): task_result += " | stdout: " + result._result.get('stdout') if result._result.get('stderr'): task_result += " | stderr: " + result._result.get('stderr') return task_result
def _boolean_or_cacert(self): # This is needed because of this (https://hvac.readthedocs.io/en/stable/source/hvac_v1.html): # # # verify (Union[bool,str]) - Either a boolean to indicate whether TLS verification should # # be performed when sending requests to Vault, or a string pointing at the CA bundle to use for verification. # '''return a bool or cacert''' ca_cert = self._options.get_option('ca_cert') validate_certs = self._options.get_option('validate_certs') if validate_certs is None: # Validate certs option was not explicitly set # Check if VAULT_SKIP_VERIFY is set vault_skip_verify = os.environ.get('VAULT_SKIP_VERIFY') if vault_skip_verify is not None: # VAULT_SKIP_VERIFY is set try: # Check that we have a boolean value vault_skip_verify = check_type_bool(vault_skip_verify) except TypeError: # Not a boolean value fallback to default value (True) validate_certs = True else: # Use the inverse of VAULT_SKIP_VERIFY validate_certs = not vault_skip_verify else: validate_certs = True if not (validate_certs and ca_cert): self._options.set_option('ca_cert', validate_certs) else: self._options.set_option( 'ca_cert', to_text(ca_cert, errors='surrogate_or_strict'))
def send_msg(self, attachments): headers = { 'Content-type': 'application/json', } payload = { 'channel': self.channel, 'username': self.username, 'attachments': attachments, 'parse': 'none', 'icon_url': ('https://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))