def fetch_file(self, in_path, out_path): ''' fetch a file from lxc to local ''' super(Connection, self).fetch_file(in_path, out_path) self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.container_name) in_path = to_bytes(in_path, errors='surrogate_or_strict') out_path = to_bytes(out_path, errors='surrogate_or_strict') try: dst_file = open(out_path, "wb") except IOError: traceback.print_exc() msg = "failed to open output file %s" % out_path raise errors.AnsibleError(msg) try: def write_file(args): try: with open(in_path, 'rb') as src_file: shutil.copyfileobj(src_file, dst_file) finally: # this is needed in the lxc child process # to flush internal python buffers dst_file.close() try: self.container.attach_wait(write_file, None) except IOError: traceback.print_exc() msg = "failed to transfer file from %s to %s" % (in_path, out_path) raise errors.AnsibleError(msg) finally: dst_file.close()
def ask_vault_passwords(ask_new_vault_pass=False, rekey=False): ''' prompt for vault password and/or password change ''' vault_pass = None new_vault_pass = None try: if rekey or not ask_new_vault_pass: vault_pass = getpass.getpass(prompt="Vault password: "******"New Vault password: "******"Confirm New Vault password: "******"Passwords do not match") except EOFError: pass # enforce no newline chars at the end of passwords if vault_pass: vault_pass = to_bytes(vault_pass, errors='strict', nonstring='simplerepr').strip() if new_vault_pass: new_vault_pass = to_bytes(new_vault_pass, errors='strict', nonstring='simplerepr').strip() if ask_new_vault_pass and not rekey: vault_pass = new_vault_pass return vault_pass, new_vault_pass
def put_file(self, in_path, out_path): ''' transfer a file from local to lxc ''' super(Connection, self).put_file(in_path, out_path) self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.container_name) in_path = to_bytes(in_path, errors='surrogate_or_strict') out_path = to_bytes(out_path, errors='surrogate_or_strict') if not os.path.exists(in_path): msg = "file or module does not exist: %s" % in_path raise errors.AnsibleFileNotFound(msg) try: src_file = open(in_path, "rb") except IOError: traceback.print_exc() raise errors.AnsibleError("failed to open input file to %s" % in_path) try: def write_file(args): with open(out_path, 'wb+') as dst_file: shutil.copyfileobj(src_file, dst_file) try: self.container.attach_wait(write_file, None) except IOError: traceback.print_exc() msg = "failed to transfer file to %s" % out_path raise errors.AnsibleError(msg) finally: src_file.close()
def test_plugins_connection_ssh_fetch_file(self): pc = PlayContext() new_stdin = StringIO() conn = ssh.Connection(pc, new_stdin) conn._build_command = MagicMock() conn._run = MagicMock() conn._build_command.return_value = 'some command to run' conn._run.return_value = (0, '', '') conn.host = "some_host" # test with C.DEFAULT_SCP_IF_SSH enabled C.DEFAULT_SCP_IF_SSH = True conn.fetch_file('/path/to/in/file', '/path/to/dest/file') conn._run.assert_called_with('some command to run', None) conn.fetch_file(u'/path/to/in/file/with/unicode-fö〩', u'/path/to/dest/file/with/unicode-fö〩') conn._run.assert_called_with('some command to run', None) # test with C.DEFAULT_SCP_IF_SSH disabled C.DEFAULT_SCP_IF_SSH = False expected_in_data = b' '.join((b'get', to_bytes(pipes.quote('/path/to/in/file')), to_bytes(pipes.quote('/path/to/dest/file')))) + b'\n' conn.fetch_file('/path/to/in/file', '/path/to/dest/file') conn._run.assert_called_with('some command to run', expected_in_data) expected_in_data = b' '.join((b'get', to_bytes(pipes.quote('/path/to/in/file/with/unicode-fö〩')), to_bytes(pipes.quote('/path/to/dest/file/with/unicode-fö〩')))) + b'\n' conn.fetch_file(u'/path/to/in/file/with/unicode-fö〩', u'/path/to/dest/file/with/unicode-fö〩') conn._run.assert_called_with('some command to run', expected_in_data) # test that a non-zero rc raises an error conn._run.return_value = (1, 'stdout', 'some errors') self.assertRaises(AnsibleError, conn.fetch_file, '/path/to/bad/file', '/remote/path/to/file')
def encrypt(self, b_plaintext, b_password): b_salt = os.urandom(32) b_key1, b_key2, b_iv = self._gen_key_initctr(b_password, b_salt) # PKCS#7 PAD DATA http://tools.ietf.org/html/rfc5652#section-6.3 bs = AES.block_size padding_length = (bs - len(b_plaintext) % bs) or bs b_plaintext += to_bytes(padding_length * chr(padding_length), encoding='ascii', errors='strict') # COUNTER.new PARAMETERS # 1) nbits (integer) - Length of the counter, in bits. # 2) initial_value (integer) - initial value of the counter. "iv" from _gen_key_initctr ctr = Counter.new(128, initial_value=int(b_iv, 16)) # AES.new PARAMETERS # 1) AES key, must be either 16, 24, or 32 bytes long -- "key" from _gen_key_initctr # 2) MODE_CTR, is the recommended mode # 3) counter=<CounterObject> cipher = AES.new(b_key1, AES.MODE_CTR, counter=ctr) # ENCRYPT PADDED DATA b_ciphertext = cipher.encrypt(b_plaintext) # COMBINE SALT, DIGEST AND DATA hmac = HMAC.new(b_key2, b_ciphertext, SHA256) b_vaulttext = b'\n'.join([hexlify(b_salt), to_bytes(hmac.hexdigest()), hexlify(b_ciphertext)]) b_vaulttext = hexlify(b_vaulttext) return b_vaulttext
def test_edit_file_with_vault_id(self, mock_sp_call): self._test_dir = self._create_test_dir() src_contents = to_bytes("some info in a file\nyup.") src_file_path = self._create_file(self._test_dir, 'src_file', content=src_contents) new_src_contents = to_bytes("The info is different now.") def faux_editor(editor_args): self._faux_editor(editor_args, new_src_contents) mock_sp_call.side_effect = faux_editor ve = self._vault_editor() ve.encrypt_file(src_file_path, self.vault_secret, vault_id='vault_secrets') ve.edit_file(src_file_path) new_src_file = open(src_file_path, 'rb') new_src_file_contents = new_src_file.read() self.assertTrue(b'$ANSIBLE_VAULT;1.2;AES256;vault_secrets' in new_src_file_contents) src_file_plaintext = ve.vault.decrypt(new_src_file_contents) self.assertEqual(src_file_plaintext, new_src_contents)
def test_edit_file_symlink(self, mock_sp_call): self._test_dir = self._create_test_dir() src_contents = to_bytes("some info in a file\nyup.") src_file_path = self._create_file(self._test_dir, 'src_file', content=src_contents) new_src_contents = to_bytes("The info is different now.") def faux_editor(editor_args): self._faux_editor(editor_args, new_src_contents) mock_sp_call.side_effect = faux_editor ve = self._vault_editor() ve.encrypt_file(src_file_path, self.vault_secret) src_file_link_path = os.path.join(self._test_dir, 'a_link_to_dest_file') os.symlink(src_file_path, src_file_link_path) ve.edit_file(src_file_link_path) new_src_file = open(src_file_path, 'rb') new_src_file_contents = new_src_file.read() src_file_plaintext = ve.vault.decrypt(new_src_file_contents) self._assert_file_is_link(src_file_link_path, src_file_path) self.assertEqual(src_file_plaintext, new_src_contents)
def rekey_file(self, filename, new_vault_secret, new_vault_id=None): # follow the symlink filename = self._real_path(filename) prev = os.stat(filename) b_vaulttext = self.read_data(filename) vaulttext = to_text(b_vaulttext) try: plaintext = self.vault.decrypt(vaulttext) except AnsibleError as e: raise AnsibleError("%s for %s" % (to_bytes(e), to_bytes(filename))) # This is more or less an assert, see #18247 if new_vault_secret is None: raise AnsibleError('The value for the new_password to rekey %s with is not valid' % filename) # FIXME: VaultContext...? could rekey to a different vault_id in the same VaultSecrets # Need a new VaultLib because the new vault data can be a different # vault lib format or cipher (for ex, when we migrate 1.0 style vault data to # 1.1 style data we change the version and the cipher). This is where a VaultContext might help # the new vault will only be used for encrypting, so it doesn't need the vault secrets # (we will pass one in directly to encrypt) new_vault = VaultLib(secrets={}) b_new_vaulttext = new_vault.encrypt(plaintext, new_vault_secret, vault_id=new_vault_id) self.write_data(b_new_vaulttext, filename) # preserve permissions os.chmod(filename, prev.st_mode) os.chown(filename, prev.st_uid, prev.st_gid)
def ask_passwords(self): """ prompt for connection and become passwords if needed """ op = self.options sshpass = None becomepass = None become_prompt = "" try: if op.ask_pass: sshpass = getpass.getpass(prompt="SSH password: "******"%s password[defaults to SSH password]: " % op.become_method.upper() if sshpass: sshpass = to_bytes(sshpass, errors="strict", nonstring="simplerepr") else: become_prompt = "%s password: "******"": becomepass = sshpass if becomepass: becomepass = to_bytes(becomepass) except EOFError: pass return (sshpass, becomepass)
def edit_file(self, filename): # follow the symlink filename = self._real_path(filename) b_vaulttext = self.read_data(filename) # vault or yaml files are always utf8 vaulttext = to_text(b_vaulttext) try: # vaulttext gets converted back to bytes, but alas plaintext = self.vault.decrypt(vaulttext) except AnsibleError as e: raise AnsibleError("%s for %s" % (to_bytes(e), to_bytes(filename))) # Figure out the vault id from the file, to select the right secret to re-encrypt it # (duplicates parts of decrypt, but alas...) dummy, dummy, cipher_name, vault_id = parse_vaulttext_envelope(b_vaulttext) # if we could decrypt, the vault_id should be in secrets # though we could have multiple secrets for a given vault_id, pick the first one secrets = match_secrets(self.vault.secrets, [vault_id]) secret = secrets[0][1] if cipher_name not in CIPHER_WRITE_WHITELIST: # we want to get rid of files encrypted with the AES cipher self._edit_file_helper(filename, secret, existing_data=plaintext, force_save=True) else: self._edit_file_helper(filename, secret, existing_data=plaintext, force_save=False)
def _encrypt_pycrypto(b_plaintext, b_key1, b_key2, b_iv): # PKCS#7 PAD DATA http://tools.ietf.org/html/rfc5652#section-6.3 bs = AES_pycrypto.block_size padding_length = (bs - len(b_plaintext) % bs) or bs b_plaintext += to_bytes(padding_length * chr(padding_length), encoding='ascii', errors='strict') # COUNTER.new PARAMETERS # 1) nbits (integer) - Length of the counter, in bits. # 2) initial_value (integer) - initial value of the counter. "iv" from _gen_key_initctr ctr = Counter_pycrypto.new(128, initial_value=int(b_iv, 16)) # AES.new PARAMETERS # 1) AES key, must be either 16, 24, or 32 bytes long -- "key" from _gen_key_initctr # 2) MODE_CTR, is the recommended mode # 3) counter=<CounterObject> cipher = AES_pycrypto.new(b_key1, AES_pycrypto.MODE_CTR, counter=ctr) # ENCRYPT PADDED DATA b_ciphertext = cipher.encrypt(b_plaintext) # COMBINE SALT, DIGEST AND DATA hmac = HMAC_pycrypto.new(b_key2, b_ciphertext, SHA256_pycrypto) return to_bytes(hmac.hexdigest(), errors='surrogate_or_strict'), hexlify(b_ciphertext)
def fetch_file(self, in_path, out_path): """ Fetch a file from container to local. """ super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr) in_path = self._prefix_login_path(in_path) out_dir = os.path.dirname(out_path) # kubectl doesn't have native support for fetching files from # running containers, so we use kubectl exec to implement this args = self._build_exec_cmd([self._play_context.executable, "-c", "dd if=%s bs=%s" % (in_path, BUFSIZE)]) args = [to_bytes(i, errors='surrogate_or_strict') for i in args] actual_out_path = os.path.join(out_dir, os.path.basename(in_path)) with open(to_bytes(actual_out_path, errors='surrogate_or_strict'), 'wb') as out_file: try: p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=out_file, stderr=subprocess.PIPE) except OSError: raise AnsibleError( "{0} connection requires dd command in the container to fetch files".format(self.transport) ) stdout, stderr = p.communicate() if p.returncode != 0: raise AnsibleError("failed to fetch file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr)) if actual_out_path != out_path: os.rename(to_bytes(actual_out_path, errors='strict'), to_bytes(out_path, errors='strict'))
def exec_command(self, request): """Sends the request to the node and returns the reply The method accepts two forms of request. The first form is as a byte string that represents xml string be send over netconf session. The second form is a json-rpc (2.0) byte string. """ try: obj = json.loads(to_text(request, errors='surrogate_or_strict')) if 'jsonrpc' in obj: if self._netconf: out = self._exec_rpc(obj) else: out = self.internal_error("netconf plugin is not supported for network_os %s" % self._play_context.network_os) return 0, to_bytes(out, errors='surrogate_or_strict'), b'' else: err = self.invalid_request(obj) return 1, b'', to_bytes(err, errors='surrogate_or_strict') except (ValueError, TypeError): # to_ele operates on native strings request = to_native(request, errors='surrogate_or_strict') req = to_ele(request) if req is None: return 1, b'', b'unable to parse request' try: reply = self._manager.rpc(req) except RPCError as exc: return 1, b'', to_bytes(to_xml(exc.xml), errors='surrogate_or_strict') return 0, to_bytes(reply.data_xml, errors='surrogate_or_strict'), b''
def ask_passwords(self): ''' prompt for connection and become passwords if needed ''' op = self.options sshpass = None becomepass = None become_prompt = '' become_prompt_method = "BECOME" if C.AGNOSTIC_BECOME_PROMPT else op.become_method.upper() try: if op.ask_pass: sshpass = getpass.getpass(prompt="SSH password: "******"%s password[defaults to SSH password]: " % become_prompt_method if sshpass: sshpass = to_bytes(sshpass, errors='strict', nonstring='simplerepr') else: become_prompt = "%s password: " % become_prompt_method if op.become_ask_pass: becomepass = getpass.getpass(prompt=become_prompt) if op.ask_pass and becomepass == '': becomepass = sshpass if becomepass: becomepass = to_bytes(becomepass) except EOFError: pass return (sshpass, becomepass)
def test_file_encrypted(self): vault_password = "******" text_secret = TextVaultSecret(vault_password) vault_secrets = [('foo', text_secret)] password = '******' # 'some password' encrypted with 'test-ansible-password' password_file_content = '''$ANSIBLE_VAULT;1.1;AES256 61393863643638653437313566313632306462383837303132346434616433313438353634613762 3334363431623364386164616163326537366333353663650a663634306232363432626162353665 39623061353266373631636331643761306665343731376633623439313138396330346237653930 6432643864346136640a653364386634666461306231353765636662316335613235383565306437 3737 ''' tmp_file = tempfile.NamedTemporaryFile(delete=False) tmp_file.write(to_bytes(password_file_content)) tmp_file.close() fake_loader = DictDataLoader({tmp_file.name: 'sdfadf'}) fake_loader._vault.secrets = vault_secrets secret = vault.FileVaultSecret(loader=fake_loader, filename=tmp_file.name) secret.load() os.unlink(tmp_file.name) self.assertEqual(secret.bytes, to_bytes(password))
def user_should_we_change_password(current_role_attrs, user, password, encrypted): """Check if we should change the user's password. Compare the proposed password with the existing one, comparing hashes if encrypted. If we can't access it assume yes. """ if current_role_attrs is None: # on some databases, E.g. AWS RDS instances, there is no access to # the pg_authid relation to check the pre-existing password, so we # just assume password is different return True # Do we actually need to do anything? pwchanging = False if password is not None: # 32: MD5 hashes are represented as a sequence of 32 hexadecimal digits # 3: The size of the 'md5' prefix # When the provided password looks like a MD5-hash, value of # 'encrypted' is ignored. if ((password.startswith('md5') and len(password) == 32 + 3) or encrypted == 'UNENCRYPTED'): if password != current_role_attrs['rolpassword']: pwchanging = True elif encrypted == 'ENCRYPTED': hashed_password = '******'.format(md5(to_bytes(password) + to_bytes(user)).hexdigest()) if hashed_password != current_role_attrs['rolpassword']: pwchanging = True return pwchanging
def __init__(self, conf_file=None, defs_file=None): self._base_defs = {} self._plugins = {} self._parsers = {} self._config_file = conf_file self.data = ConfigData() if defs_file is None: # Create configuration definitions from source b_defs_file = to_bytes('%s/base.yml' % os.path.dirname(__file__)) else: b_defs_file = to_bytes(defs_file) # consume definitions if os.path.exists(b_defs_file): with open(b_defs_file, 'rb') as config_def: self._base_defs = yaml_load(config_def, Loader=SafeLoader) else: raise AnsibleError("Missing base configuration definition file (bad install?): %s" % to_native(b_defs_file)) if self._config_file is None: # set config using ini self._config_file = find_ini_config_file() # consume configuration if self._config_file: if os.path.exists(self._config_file): # initialize parser and read config self._parse_config_file() # update constants self.update_config_data()
def run(self, terms, variables, **kwargs): ret = [] for term in terms: relpath, params = _parse_parameters(term) path = self._loader.path_dwim(relpath) b_path = to_bytes(path, errors='surrogate_or_strict') chars = _gen_candidate_chars(params['chars']) changed = False content = _read_password_file(b_path) if content is None or b_path == to_bytes('/dev/null'): plaintext_password = random_password(params['length'], chars) salt = None changed = True else: plaintext_password, salt = _parse_content(content) if params['encrypt'] and not salt: changed = True salt = _random_salt() if changed and b_path != to_bytes('/dev/null'): content = _format_content(plaintext_password, salt, encrypt=params['encrypt']) _write_password_file(b_path, content) if params['encrypt']: password = do_encrypt(plaintext_password, params['encrypt'], salt=salt) ret.append(password) else: ret.append(plaintext_password) return ret
def find_vars_files(self, path, name, extensions=None, allow_dir=True): """ Find vars files in a given path with specified name. This will find files in a dir named <name>/ or a file called <name> ending in known extensions. """ b_path = to_bytes(os.path.join(path, name)) found = [] if extensions is None: # Look for file with no extension first to find dir before file extensions = [''] + C.YAML_FILENAME_EXTENSIONS # add valid extensions to name for ext in extensions: if '.' in ext: full_path = b_path + to_bytes(ext) elif ext: full_path = b'.'.join([b_path, to_bytes(ext)]) else: full_path = b_path if self.path_exists(full_path): if self.is_directory(full_path): if allow_dir: found.extend(self._get_dir_vars_files(to_text(full_path), extensions)) else: next else: found.append(full_path) break return found
def put_file(self, in_path, out_path): """ Transfer a file from local to docker container """ super(Connection, self).put_file(in_path, out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr) out_path = self._prefix_login_path(out_path) if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')): raise AnsibleFileNotFound( "file or module does not exist: %s" % in_path) out_path = shlex_quote(out_path) # Older docker doesn't have native support for copying files into # running containers, so we use docker exec to implement this # Although docker version 1.8 and later provide support, the # owner and group of the files are always set to root args = self._build_exec_cmd([self._play_context.executable, "-c", "dd of=%s bs=%s" % (out_path, BUFSIZE)]) args = [to_bytes(i, errors='surrogate_or_strict') for i in args] with open(to_bytes(in_path, errors='surrogate_or_strict'), 'rb') as in_file: try: p = subprocess.Popen(args, stdin=in_file, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError: raise AnsibleError("docker connection requires dd command in the container to put files") stdout, stderr = p.communicate() if p.returncode != 0: raise AnsibleError("failed to transfer file %s to %s:\n%s\n%s" % (in_path, out_path, stdout, stderr))
def _check_subject(csr): subject = [(OpenSSL._util.lib.OBJ_txt2nid(to_bytes(sub[0])), to_bytes(sub[1])) for sub in self.subject] current_subject = [(OpenSSL._util.lib.OBJ_txt2nid(to_bytes(sub[0])), to_bytes(sub[1])) for sub in csr.get_subject().get_components()] if not set(subject) == set(current_subject): return False return True
def _find_vars_files(self, path, name): """ Find {group,host}_vars files """ b_path = to_bytes(os.path.join(path, name)) found = [] # first look for w/o extensions if os.path.exists(b_path): if os.path.isdir(b_path): found.extend(self._get_dir_files(to_text(b_path))) else: found.append(b_path) else: # add valid extensions to name for ext in C.YAML_FILENAME_EXTENSIONS: if '.' in ext: full_path = b_path + to_bytes(ext) elif ext: full_path = b'.'.join([b_path, to_bytes(ext)]) else: full_path = b_path if os.path.exists(full_path) and os.path.isfile(full_path): found.append(full_path) break return found
def read_vault_password_file(self, vault_password_file): """ Read a vault password from a file or if executable, execute the script and retrieve password from STDOUT """ this_path = os.path.realpath(to_bytes(os.path.expanduser(vault_password_file), errors='surrogate_or_strict')) if not os.path.exists(to_bytes(this_path, errors='surrogate_or_strict')): raise AnsibleFileNotFound("The vault password file %s was not found" % this_path) if self.is_executable(this_path): try: # STDERR not captured to make it easier for users to prompt for input in their scripts p = subprocess.Popen(this_path, stdout=subprocess.PIPE) except OSError as e: raise AnsibleError("Problem running vault password script %s (%s)." " If this is not a script, remove the executable bit from the file." % (' '.join(this_path), to_native(e))) stdout, stderr = p.communicate() self.set_vault_password(stdout.strip('\r\n')) else: try: f = open(this_path, "rb") self.set_vault_password(f.read().strip()) f.close() except (OSError, IOError) as e: raise AnsibleError("Could not read vault password file %s: %s" % (this_path, e))
def modify_module(module_name, module_path, module_args, task_vars=None, module_compression='ZIP_STORED', async_timeout=0, become=False, become_method=None, become_user=None, become_password=None, environment=None): """ Used to insert chunks of code into modules before transfer rather than doing regular python imports. This allows for more efficient transfer in a non-bootstrapping scenario by not moving extra files over the wire and also takes care of embedding arguments in the transferred modules. This version is done in such a way that local imports can still be used in the module code, so IDEs don't have to be aware of what is going on. Example: from ansible.module_utils.basic import * ... will result in the insertion of basic.py into the module from the module_utils/ directory in the source tree. For powershell, this code effectively no-ops, as the exec wrapper requires access to a number of properties not available here. """ task_vars = {} if task_vars is None else task_vars environment = {} if environment is None else environment with open(module_path, 'rb') as f: # read in the module source b_module_data = f.read() (b_module_data, module_style, shebang) = _find_module_utils(module_name, b_module_data, module_path, module_args, task_vars, module_compression, async_timeout=async_timeout, become=become, become_method=become_method, become_user=become_user, become_password=become_password, environment=environment) if module_style == 'binary': return (b_module_data, module_style, to_text(shebang, nonstring='passthru')) elif shebang is None: lines = b_module_data.split(b"\n", 1) if lines[0].startswith(b"#!"): shebang = lines[0].strip() args = shlex.split(str(shebang[2:])) interpreter = args[0] interpreter = to_bytes(interpreter) new_shebang = to_bytes(_get_shebang(interpreter, task_vars, args[1:])[0], errors='surrogate_or_strict', nonstring='passthru') if new_shebang: lines[0] = shebang = new_shebang if os.path.basename(interpreter).startswith(b'python'): lines.insert(1, to_bytes(ENCODING_STRING)) else: # No shebang, assume a binary module? pass b_module_data = b"\n".join(lines) else: shebang = to_bytes(shebang, errors='surrogate_or_strict') return (b_module_data, module_style, to_text(shebang, nonstring='passthru'))
def exec_command(self, cmd, in_data=None, sudoable=True): super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) cmd_parts = self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False) # TODO: display something meaningful here display.vvv("EXEC (via pipeline wrapper)") stdin_iterator = None if in_data: stdin_iterator = self._wrapper_payload_stream(in_data) result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True, stdin_iterator=stdin_iterator) result.std_out = to_bytes(result.std_out) result.std_err = to_bytes(result.std_err) # parse just stderr from CLIXML output if self.is_clixml(result.std_err): try: result.std_err = self.parse_clixml_stream(result.std_err) except Exception: # unsure if we're guaranteed a valid xml doc- use raw output in case of error pass return (result.status_code, result.std_out, result.std_err)
def test_is_equal_non_ascii_unequal(self): utf8_data = to_bytes(u'私はガラスを食べられます。それは私を傷つけません。') utf8_data2 = to_bytes(u'Pot să mănânc sticlă și ea nu mă rănește.') # Test for the len optimization path self.assertFalse(self.vault_cipher._is_equal(utf8_data, utf8_data2)) # Test for the slower, char by char comparison path self.assertFalse(self.vault_cipher._is_equal(utf8_data, utf8_data[:-1] + b'P'))
def get_host(self, macaddr): msg = OmapiMessage.open(to_bytes("host", errors='surrogate_or_strict')) msg.obj.append((to_bytes("hardware-address", errors='surrogate_or_strict'), pack_mac(macaddr))) msg.obj.append((to_bytes("hardware-type", errors='surrogate_or_strict'), struct.pack("!I", 1))) response = self.omapi.query_server(msg) if response.opcode != OMAPI_OP_UPDATE: return None return response
def fabric_ansible_display(self, msg, color=None, stderr=False, screen_only=False, log_only=False): """ Display a message to the user Note: msg *must* be a unicode string to prevent UnicodeError tracebacks. """ nocolor = msg if color: msg = stringc(msg, color) if not log_only: if not msg.endswith(u'\n'): msg2 = msg + u'\n' else: msg2 = msg msg2 = to_bytes(msg2, encoding=self._output_encoding(stderr=stderr)) if sys.version_info >= (3,): # Convert back to text string on python3 # We first convert to a byte string so that we get rid of # characters that are invalid in the user's locale msg2 = to_text(msg2, self._output_encoding(stderr=stderr), errors='replace') # Note: After Display() class is refactored need to update the # log capture code in 'bin/ansible-connection' (and other # relevant places). if not stderr: fileobj = sys.stdout else: fileobj = sys.stderr fileobj.write(msg2) try: fileobj.flush() except IOError as e: # Ignore EPIPE in case fileobj has been prematurely closed, eg. # when piping to "head -n1" if e.errno != errno.EPIPE: raise if logger: msg2 = nocolor.lstrip(u'\n') msg2 = to_bytes(msg2) if sys.version_info >= (3,): # Convert back to text string on python3 # We first convert to a byte string so that we get rid of # characters that are invalid in the user's locale msg2 = to_text(msg2, self._output_encoding(stderr=stderr)) if color == CONST.COLOR_ERROR: logger.error(msg2) else: logger.info(msg2)
def print_modules(module, category_file, deprecated, options, env, template, outputname, module_map, aliases): modstring = module if modstring.startswith('_'): modstring = module[1:] modname = modstring if module in deprecated: modstring = modstring + DEPRECATED category_file.write(" %s - %s <%s_module>\n" % (to_bytes(modstring), to_bytes(rst_ify(module_map[module][1])), to_bytes(modname)))
def absent(module, dest, regexp, line, backup): b_dest = to_bytes(dest, errors='surrogate_or_strict') if not os.path.exists(b_dest): module.exit_json(changed=False, msg="file not present") msg = '' diff = {'before': '', 'after': '', 'before_header': '%s (content)' % dest, 'after_header': '%s (content)' % dest} f = open(b_dest, 'rb') b_lines = f.readlines() f.close() if module._diff: diff['before'] = to_native(b('').join(b_lines)) if regexp is not None: bre_c = re.compile(to_bytes(regexp, errors='surrogate_or_strict')) found = [] b_line = to_bytes(line, errors='surrogate_or_strict') def matcher(b_cur_line): if regexp is not None: match_found = bre_c.search(b_cur_line) else: match_found = b_line == b_cur_line.rstrip(b('\r\n')) if match_found: found.append(b_cur_line) return not match_found b_lines = [l for l in b_lines if matcher(l)] changed = len(found) > 0 if module._diff: diff['after'] = to_native(b('').join(b_lines)) backupdest = "" if changed and not module.check_mode: if backup: backupdest = module.backup_local(dest) write_changes(module, b_lines, dest) if changed: msg = "%s line(s) removed" % len(found) attr_diff = {} msg, changed = check_file_attrs(module, changed, msg, attr_diff) attr_diff['before_header'] = '%s (file attributes)' % dest attr_diff['after_header'] = '%s (file attributes)' % dest difflist = [diff, attr_diff] module.exit_json(changed=changed, found=len(found), msg=msg, backup=backupdest, diff=difflist)
def set_module_args(args): """prepare arguments so that they will be picked up during module creation""" args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
def detect_su_prompt(b_data): b_password_string = b"|".join([b'(\w+\'s )?' + x for x in b_SU_PROMPT_LOCALIZATIONS]) # Colon or unicode fullwidth colon b_password_string = b_password_string + to_bytes(u' ?(:|:) ?') b_SU_PROMPT_LOCALIZATIONS_RE = re.compile(b_password_string, flags=re.IGNORECASE) return bool(b_SU_PROMPT_LOCALIZATIONS_RE.match(b_data))
def _generate_csr(self): req = crypto.X509Req() req.set_version(self.version - 1) subject = req.get_subject() for entry in self.subject: if entry[1] is not None: # Workaround for https://github.com/pyca/pyopenssl/issues/165 nid = OpenSSL._util.lib.OBJ_txt2nid(to_bytes(entry[0])) if nid == 0: raise CertificateSigningRequestError( 'Unknown subject field identifier "{0}"'.format( entry[0])) res = OpenSSL._util.lib.X509_NAME_add_entry_by_NID( subject._name, nid, OpenSSL._util.lib.MBSTRING_UTF8, to_bytes(entry[1]), -1, -1, 0) if res == 0: raise CertificateSigningRequestError( 'Invalid value for subject field identifier "{0}": {1}' .format(entry[0], entry[1])) extensions = [] if self.subjectAltName: altnames = ', '.join(self.subjectAltName) try: extensions.append( crypto.X509Extension(b"subjectAltName", self.subjectAltName_critical, altnames.encode('ascii'))) except OpenSSL.crypto.Error as e: raise CertificateSigningRequestError( 'Error while parsing Subject Alternative Names {0} (check for missing type prefix, such as "DNS:"!): {1}' .format( ', '.join([ "{0}".format(san) for san in self.subjectAltName ]), str(e))) if self.keyUsage: usages = ', '.join(self.keyUsage) extensions.append( crypto.X509Extension(b"keyUsage", self.keyUsage_critical, usages.encode('ascii'))) if self.extendedKeyUsage: usages = ', '.join(self.extendedKeyUsage) extensions.append( crypto.X509Extension(b"extendedKeyUsage", self.extendedKeyUsage_critical, usages.encode('ascii'))) if self.basicConstraints: usages = ', '.join(self.basicConstraints) extensions.append( crypto.X509Extension(b"basicConstraints", self.basicConstraints_critical, usages.encode('ascii'))) if self.ocspMustStaple: extensions.append( crypto.X509Extension(OPENSSL_MUST_STAPLE_NAME, self.ocspMustStaple_critical, OPENSSL_MUST_STAPLE_VALUE)) if extensions: req.add_extensions(extensions) req.set_pubkey(self.privatekey) req.sign(self.privatekey, self.digest) self.request = req return crypto.dump_certificate_request(crypto.FILETYPE_PEM, self.request)
def _bare_run(self, cmd, in_data, sudoable=True, checkrc=True): ''' Starts the command and communicates with it until it ends. ''' display_cmd = list(map(shlex_quote, map(to_text, cmd))) display.vvv(u'SSH: EXEC {0}'.format(u' '.join(display_cmd)), host=self.host) # Start the given command. If we don't need to pipeline data, we can try # to use a pseudo-tty (ssh will have been invoked with -tt). If we are # pipelining data, or can't create a pty, we fall back to using plain # old pipes. p = None if isinstance(cmd, (text_type, binary_type)): cmd = to_bytes(cmd) else: cmd = list(map(to_bytes, cmd)) if not in_data: try: # Make sure stdin is a proper pty to avoid tcgetattr errors master, slave = pty.openpty() if PY3 and self._play_context.password: p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, pass_fds=self.sshpass_pipe) else: p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'wb', 0) os.close(slave) except (OSError, IOError): p = None if not p: if PY3 and self._play_context.password: p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, pass_fds=self.sshpass_pipe) else: p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = p.stdin # If we are using SSH password authentication, write the password into # the pipe we opened in _build_command. if self._play_context.password: os.close(self.sshpass_pipe[0]) try: os.write(self.sshpass_pipe[1], to_bytes(self._play_context.password) + b'\n') except OSError as e: # Ignore broken pipe errors if the sshpass process has exited. if e.errno != errno.EPIPE or p.poll() is None: raise os.close(self.sshpass_pipe[1]) # # SSH state machine # # Now we read and accumulate output from the running process until it # exits. Depending on the circumstances, we may also need to write an # escalation password and/or pipelined input to the process. states = [ 'awaiting_prompt', 'awaiting_escalation', 'ready_to_send', 'awaiting_exit' ] # Are we requesting privilege escalation? Right now, we may be invoked # to execute sftp/scp with sudoable=True, but we can request escalation # only when using ssh. Otherwise we can send initial data straightaway. state = states.index('ready_to_send') if b'ssh' in cmd: if self._play_context.prompt: # We're requesting escalation with a password, so we have to # wait for a password prompt. state = states.index('awaiting_prompt') display.debug(u'Initial state: %s: %s' % (states[state], self._play_context.prompt)) elif self._play_context.become and self._play_context.success_key: # We're requesting escalation without a password, so we have to # detect success/failure before sending any initial data. state = states.index('awaiting_escalation') display.debug(u'Initial state: %s: %s' % (states[state], self._play_context.success_key)) # We store accumulated stdout and stderr output from the process here, # but strip any privilege escalation prompt/confirmation lines first. # Output is accumulated into tmp_*, complete lines are extracted into # an array, then checked and removed or copied to stdout or stderr. We # set any flags based on examining the output in self._flags. b_stdout = b_stderr = b'' b_tmp_stdout = b_tmp_stderr = b'' self._flags = dict(become_prompt=False, become_success=False, become_error=False, become_nopasswd_error=False) # select timeout should be longer than the connect timeout, otherwise # they will race each other when we can't connect, and the connect # timeout usually fails timeout = 2 + self._play_context.timeout for fd in (p.stdout, p.stderr): fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) # TODO: bcoca would like to use SelectSelector() when open # filehandles is low, then switch to more efficient ones when higher. # select is faster when filehandles is low. selector = selectors.DefaultSelector() selector.register(p.stdout, selectors.EVENT_READ) selector.register(p.stderr, selectors.EVENT_READ) # If we can send initial data without waiting for anything, we do so # before we start polling if states[state] == 'ready_to_send' and in_data: self._send_initial_data(stdin, in_data) state += 1 try: while True: poll = p.poll() events = selector.select(timeout) # We pay attention to timeouts only while negotiating a prompt. if not events: # We timed out if state <= states.index('awaiting_escalation'): # If the process has already exited, then it's not really a # timeout; we'll let the normal error handling deal with it. if poll is not None: break self._terminate_process(p) raise AnsibleError( 'Timeout (%ds) waiting for privilege escalation prompt: %s' % (timeout, to_native(b_stdout))) # Read whatever output is available on stdout and stderr, and stop # listening to the pipe if it's been closed. for key, event in events: if key.fileobj == p.stdout: b_chunk = p.stdout.read() if b_chunk == b'': # stdout has been closed, stop watching it selector.unregister(p.stdout) # When ssh has ControlMaster (+ControlPath/Persist) enabled, the # first connection goes into the background and we never see EOF # on stderr. If we see EOF on stdout, lower the select timeout # to reduce the time wasted selecting on stderr if we observe # that the process has not yet existed after this EOF. Otherwise # we may spend a long timeout period waiting for an EOF that is # not going to arrive until the persisted connection closes. timeout = 1 b_tmp_stdout += b_chunk display.debug("stdout chunk (state=%s):\n>>>%s<<<\n" % (state, to_text(b_chunk))) elif key.fileobj == p.stderr: b_chunk = p.stderr.read() if b_chunk == b'': # stderr has been closed, stop watching it selector.unregister(p.stderr) b_tmp_stderr += b_chunk display.debug("stderr chunk (state=%s):\n>>>%s<<<\n" % (state, to_text(b_chunk))) # We examine the output line-by-line until we have negotiated any # privilege escalation prompt and subsequent success/error message. # Afterwards, we can accumulate output without looking at it. if state < states.index('ready_to_send'): if b_tmp_stdout: b_output, b_unprocessed = self._examine_output( 'stdout', states[state], b_tmp_stdout, sudoable) b_stdout += b_output b_tmp_stdout = b_unprocessed if b_tmp_stderr: b_output, b_unprocessed = self._examine_output( 'stderr', states[state], b_tmp_stderr, sudoable) b_stderr += b_output b_tmp_stderr = b_unprocessed else: b_stdout += b_tmp_stdout b_stderr += b_tmp_stderr b_tmp_stdout = b_tmp_stderr = b'' # If we see a privilege escalation prompt, we send the password. # (If we're expecting a prompt but the escalation succeeds, we # didn't need the password and can carry on regardless.) if states[state] == 'awaiting_prompt': if self._flags['become_prompt']: display.debug( 'Sending become_pass in response to prompt') stdin.write( to_bytes(self._play_context.become_pass) + b'\n') self._flags['become_prompt'] = False state += 1 elif self._flags['become_success']: state += 1 # We've requested escalation (with or without a password), now we # wait for an error message or a successful escalation. if states[state] == 'awaiting_escalation': if self._flags['become_success']: display.debug('Escalation succeeded') self._flags['become_success'] = False state += 1 elif self._flags['become_error']: display.debug('Escalation failed') self._terminate_process(p) self._flags['become_error'] = False raise AnsibleError('Incorrect %s password' % self._play_context.become_method) elif self._flags['become_nopasswd_error']: display.debug('Escalation requires password') self._terminate_process(p) self._flags['become_nopasswd_error'] = False raise AnsibleError('Missing %s password' % self._play_context.become_method) elif self._flags['become_prompt']: # This shouldn't happen, because we should see the "Sorry, # try again" message first. display.debug('Escalation prompt repeated') self._terminate_process(p) self._flags['become_prompt'] = False raise AnsibleError('Incorrect %s password' % self._play_context.become_method) # Once we're sure that the privilege escalation prompt, if any, has # been dealt with, we can send any initial data and start waiting # for output. if states[state] == 'ready_to_send': if in_data: self._send_initial_data(stdin, in_data) state += 1 # Now we're awaiting_exit: has the child process exited? If it has, # and we've read all available output from it, we're done. if poll is not None: if not selector.get_map() or not events: break # We should not see further writes to the stdout/stderr file # descriptors after the process has closed, set the select # timeout to gather any last writes we may have missed. timeout = 0 continue # If the process has not yet exited, but we've already read EOF from # its stdout and stderr (and thus no longer watching any file # descriptors), we can just wait for it to exit. elif not selector.get_map(): p.wait() break # Otherwise there may still be outstanding data to read. finally: selector.close() # close stdin after process is terminated and stdout/stderr are read # completely (see also issue #848) stdin.close() if C.HOST_KEY_CHECKING: if cmd[0] == b"sshpass" and p.returncode == 6: raise AnsibleError( 'Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support ' 'this. Please add this host\'s fingerprint to your known_hosts file to manage this host.' ) controlpersisterror = b'Bad configuration option: ControlPersist' in b_stderr or b'unknown configuration option: ControlPersist' in b_stderr if p.returncode != 0 and controlpersisterror: raise AnsibleError( 'using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" ' '(or ssh_args in [ssh_connection] section of the config file) before running again' ) # If we find a broken pipe because of ControlPersist timeout expiring (see #16731), # we raise a special exception so that we can retry a connection. controlpersist_broken_pipe = b'mux_client_hello_exchange: write packet: Broken pipe' in b_stderr if p.returncode == 255 and controlpersist_broken_pipe: raise AnsibleControlPersistBrokenPipeError( 'SSH Error: data could not be sent because of ControlPersist broken pipe.' ) if p.returncode == 255 and in_data and checkrc: raise AnsibleConnectionFailure( 'SSH Error: data could not be sent to remote host "%s". Make sure this host can be reached over ssh' % self.host) return (p.returncode, b_stdout, b_stderr)
def get_user_data(self): user_data = self.module.params.get('user_data') if user_data is not None: user_data = to_text(base64.b64encode(to_bytes(user_data))) return user_data
def run_module(): # define the available arguments/parameters that a user can pass to # the module module_args = dict( instance=dict(default=None, type='str', required=True), username=dict(default=None, type='str', required=True, no_log=True), password=dict(default=None, type='str', required=True, no_log=True), table=dict(type='str', required=False, default='incident'), state=dict(choices=['present', 'absent'], type='str', required=True), number=dict(default=None, required=False, type='str'), data=dict(default=None, required=False, type='dict'), lookup_field=dict(default='number', required=False, type='str'), attachment=dict(default=None, required=False, type='str') ) module_required_if = [ ['state', 'absent', ['number']], ] module = AnsibleModule( argument_spec=module_args, supports_check_mode=True, required_if=module_required_if ) # check for pysnow if not HAS_PYSNOW: module.fail_json(msg='pysnow module required') params = module.params instance = params['instance'] username = params['username'] password = params['password'] table = params['table'] state = params['state'] number = params['number'] data = params['data'] lookup_field = params['lookup_field'] result = dict( changed=False, instance=instance, table=table, number=number, lookup_field=lookup_field ) # check for attachments if params['attachment'] is not None: attach = params['attachment'] b_attach = to_bytes(attach, errors='surrogate_or_strict') if not os.path.exists(b_attach): module.fail_json(msg="Attachment {0} not found".format(attach)) result['attachment'] = attach else: attach = None # Connect to ServiceNow try: conn = pysnow.Client(instance=instance, user=username, password=password) except Exception as detail: module.fail_json(msg='Could not connect to ServiceNow: {0}'.format(str(detail)), **result) # Deal with check mode if module.check_mode: # if we are in check mode and have no number, we would have created # a record. We can only partially simulate this if number is None: result['record'] = dict(data) result['changed'] = True # do we want to check if the record is non-existent? elif state == 'absent': try: record = conn.query(table=table, query={lookup_field: number}) res = record.get_one() result['record'] = dict(Success=True) result['changed'] = True except pysnow.exceptions.NoResults: result['record'] = None except Exception as detail: module.fail_json(msg="Unknown failure in query record: {0}".format(str(detail)), **result) # Let's simulate modification else: try: record = conn.query(table=table, query={lookup_field: number}) res = record.get_one() for key, value in data.items(): res[key] = value result['changed'] = True result['record'] = res except pysnow.exceptions.NoResults: snow_error = "Record does not exist" module.fail_json(msg=snow_error, **result) except Exception as detail: module.fail_json(msg="Unknown failure in query record: {0}".format(str(detail)), **result) module.exit_json(**result) # now for the real thing: (non-check mode) # are we creating a new record? if state == 'present' and number is None: try: record = conn.insert(table=table, payload=dict(data)) except pysnow.UnexpectedResponse as e: snow_error = "Failed to create record: {0}, details: {1}".format(e.error_summary, e.error_details) module.fail_json(msg=snow_error, **result) result['record'] = record result['changed'] = True # we are deleting a record elif state == 'absent': try: record = conn.query(table=table, query={lookup_field: number}) res = record.delete() except pysnow.exceptions.NoResults: res = dict(Success=True) except pysnow.exceptions.MultipleResults: snow_error = "Multiple record match" module.fail_json(msg=snow_error, **result) except pysnow.UnexpectedResponse as e: snow_error = "Failed to delete record: {0}, details: {1}".format(e.error_summary, e.error_details) module.fail_json(msg=snow_error, **result) except Exception as detail: snow_error = "Failed to delete record: {0}".format(str(detail)) module.fail_json(msg=snow_error, **result) result['record'] = res result['changed'] = True # We want to update a record else: try: record = conn.query(table=table, query={lookup_field: number}) if data is not None: res = record.update(dict(data)) result['record'] = res result['changed'] = True else: res = record.get_one() result['record'] = res if attach is not None: res = record.attach(b_attach) result['changed'] = True result['attached_file'] = res except pysnow.exceptions.MultipleResults: snow_error = "Multiple record match" module.fail_json(msg=snow_error, **result) except pysnow.exceptions.NoResults: snow_error = "Record does not exist" module.fail_json(msg=snow_error, **result) except pysnow.UnexpectedResponse as e: snow_error = "Failed to update record: {0}, details: {1}".format(e.error_summary, e.error_details) module.fail_json(msg=snow_error, **result) except Exception as detail: snow_error = "Failed to update record: {0}".format(str(detail)) module.fail_json(msg=snow_error, **result) module.exit_json(**result)
def check_missing_password(self, b_output): b_missing_password = to_bytes( gettext.dgettext( self._play_context.become_method, C.BECOME_MISSING_STRINGS[self._play_context.become_method])) return b_missing_password and b_missing_password in b_output
def present(module, dest, regexp, line, insertafter, insertbefore, create, backup, backrefs, firstmatch): diff = {'before': '', 'after': '', 'before_header': '%s (content)' % dest, 'after_header': '%s (content)' % dest} b_dest = to_bytes(dest, errors='surrogate_or_strict') if not os.path.exists(b_dest): if not create: module.fail_json(rc=257, msg='Destination %s does not exist !' % dest) b_destpath = os.path.dirname(b_dest) if not os.path.exists(b_destpath) and not module.check_mode: try: os.makedirs(b_destpath) except Exception as e: module.fail_json(msg='Error creating %s Error code: %s Error description: %s' % (b_destpath, e[0], e[1])) b_lines = [] else: f = open(b_dest, 'rb') b_lines = f.readlines() f.close() if module._diff: diff['before'] = to_native(b('').join(b_lines)) if regexp is not None: bre_m = re.compile(to_bytes(regexp, errors='surrogate_or_strict')) if insertafter not in (None, 'BOF', 'EOF'): bre_ins = re.compile(to_bytes(insertafter, errors='surrogate_or_strict')) elif insertbefore not in (None, 'BOF'): bre_ins = re.compile(to_bytes(insertbefore, errors='surrogate_or_strict')) else: bre_ins = None # index[0] is the line num where regexp has been found # index[1] is the line num where insertafter/inserbefore has been found index = [-1, -1] m = None b_line = to_bytes(line, errors='surrogate_or_strict') for lineno, b_cur_line in enumerate(b_lines): if regexp is not None: match_found = bre_m.search(b_cur_line) else: match_found = b_line == b_cur_line.rstrip(b('\r\n')) if match_found: index[0] = lineno m = match_found elif bre_ins is not None and bre_ins.search(b_cur_line): if insertafter: # + 1 for the next line index[1] = lineno + 1 if firstmatch: break if insertbefore: # index[1] for the previous line index[1] = lineno if firstmatch: break msg = '' changed = False # Regexp matched a line in the file b_linesep = to_bytes(os.linesep, errors='surrogate_or_strict') if index[0] != -1: if backrefs: b_new_line = m.expand(b_line) else: # Don't do backref expansion if not asked. b_new_line = b_line if not b_new_line.endswith(b_linesep): b_new_line += b_linesep # If a regexp is specified and a match is found anywhere in the file, do # not insert the line before or after. if regexp is None and m: # Insert lines if insertafter and insertafter != 'EOF': # Ensure there is a line separator after the found string # at the end of the file. if b_lines and not b_lines[-1][-1:] in (b('\n'), b('\r')): b_lines[-1] = b_lines[-1] + b_linesep # If the line to insert after is at the end of the file # use the appropriate index value. if len(b_lines) == index[1]: if b_lines[index[1] - 1].rstrip(b('\r\n')) != b_line: b_lines.append(b_line + b_linesep) msg = 'line added' changed = True elif b_lines[index[1]].rstrip(b('\r\n')) != b_line: b_lines.insert(index[1], b_line + b_linesep) msg = 'line added' changed = True elif insertbefore: # If the line to insert before is at the beginning of the file # use the appropriate index value. if index[1] == 0: if b_lines[index[1]].rstrip(b('\r\n')) != b_line: b_lines.insert(index[1], b_line + b_linesep) msg = 'line replaced' changed = True elif b_lines[index[1] - 1].rstrip(b('\r\n')) != b_line: b_lines.insert(index[1], b_line + b_linesep) msg = 'line replaced' changed = True elif b_lines[index[0]] != b_new_line: b_lines[index[0]] = b_new_line msg = 'line replaced' changed = True elif backrefs: # Do absolutely nothing, since it's not safe generating the line # without the regexp matching to populate the backrefs. pass # Add it to the beginning of the file elif insertbefore == 'BOF' or insertafter == 'BOF': b_lines.insert(0, b_line + b_linesep) msg = 'line added' changed = True # Add it to the end of the file if requested or # if insertafter/insertbefore didn't match anything # (so default behaviour is to add at the end) elif insertafter == 'EOF' or index[1] == -1: # If the file is not empty then ensure there's a newline before the added line if b_lines and not b_lines[-1][-1:] in (b('\n'), b('\r')): b_lines.append(b_linesep) b_lines.append(b_line + b_linesep) msg = 'line added' changed = True # insert matched, but not the regexp else: b_lines.insert(index[1], b_line + b_linesep) msg = 'line added' changed = True if module._diff: diff['after'] = to_native(b('').join(b_lines)) backupdest = "" if changed and not module.check_mode: if backup and os.path.exists(b_dest): backupdest = module.backup_local(dest) write_changes(module, b_lines, dest) if module.check_mode and not os.path.exists(b_dest): module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=diff) attr_diff = {} msg, changed = check_file_attrs(module, changed, msg, attr_diff) attr_diff['before_header'] = '%s (file attributes)' % dest attr_diff['after_header'] = '%s (file attributes)' % dest difflist = [diff, attr_diff] module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=difflist)
def __init__(self, vm_model, inventory_client, vmss=None): self._inventory_client = inventory_client self._vm_model = vm_model self._vmss = vmss self._instanceview = None self._powerstate = "unknown" self.nics = [] # Azure often doesn't provide a globally-unique filename, so use resource name + a chunk of ID hash self.default_inventory_hostname = '{0}_{1}'.format(vm_model['name'], hashlib.sha1(to_bytes(vm_model['id'])).hexdigest()[0:4]) self._hostvars = {} inventory_client._enqueue_get(url="{0}/instanceView".format(vm_model['id']), api_version=self._inventory_client._compute_api_version, handler=self._on_instanceview_response) nic_refs = vm_model['properties']['networkProfile']['networkInterfaces'] for nic in nic_refs: # single-nic instances don't set primary, so figure it out... is_primary = nic.get('properties', {}).get('primary', len(nic_refs) == 1) inventory_client._enqueue_get(url=nic['id'], api_version=self._inventory_client._network_api_version, handler=self._on_nic_response, handler_args=dict(is_primary=is_primary))
def display(self, msg, color=None, stderr=False, screen_only=False, log_only=False): """ Display a message to the user Note: msg *must* be a unicode string to prevent UnicodeError tracebacks. """ nocolor = msg if color: msg = stringc(msg, color) if not log_only: if not msg.endswith(u'\n'): msg2 = msg + u'\n' else: msg2 = msg msg2 = to_bytes(msg2, encoding=self._output_encoding(stderr=stderr)) if sys.version_info >= (3, ): # Convert back to text string on python3 # We first convert to a byte string so that we get rid of # characters that are invalid in the user's locale msg2 = to_text(msg2, self._output_encoding(stderr=stderr), errors='replace') # Note: After Display() class is refactored need to update the log capture # code in 'bin/ansible-connection' (and other relevant places). if not stderr: fileobj = sys.stdout else: fileobj = sys.stderr fileobj.write(msg2) try: fileobj.flush() except IOError as e: # Ignore EPIPE in case fileobj has been prematurely closed, eg. # when piping to "head -n1" if e.errno != errno.EPIPE: raise if logger and not screen_only: msg2 = nocolor.lstrip(u'\n') msg2 = to_bytes(msg2) if sys.version_info >= (3, ): # Convert back to text string on python3 # We first convert to a byte string so that we get rid of # characters that are invalid in the user's locale msg2 = to_text(msg2, self._output_encoding(stderr=stderr)) if color == C.COLOR_ERROR: logger.error(msg2) else: logger.info(msg2)
def run(self, tmp=None, task_vars=None): ''' handler for file transfer operations ''' if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect try: creates = self._task.args.get('creates') if creates: # do not run the command if the line contains creates=filename # and the filename already exists. This allows idempotence # of command executions. if self._remote_file_exists(creates): raise AnsibleActionSkip( "%s exists, matching creates option" % creates) removes = self._task.args.get('removes') if removes: # do not run the command if the line contains removes=filename # and the filename does not exist. This allows idempotence # of command executions. if not self._remote_file_exists(removes): raise AnsibleActionSkip( "%s does not exist, matching removes option" % removes) # The chdir must be absolute, because a relative path would rely on # remote node behaviour & user config. chdir = self._task.args.get('chdir') if chdir: # Powershell is the only Windows-path aware shell if self._connection._shell.SHELL_FAMILY == 'powershell' and \ not self.windows_absolute_path_detection.matches(chdir): raise AnsibleActionFail( 'chdir %s must be an absolute path for a Windows remote node' % chdir) # Every other shell is unix-path-aware. if self._connection._shell.SHELL_FAMILY != 'powershell' and not chdir.startswith( '/'): raise AnsibleActionFail( 'chdir %s must be an absolute path for a Unix-aware remote node' % chdir) # Split out the script as the first item in raw_params using # shlex.split() in order to support paths and files with spaces in the name. # Any arguments passed to the script will be added back later. raw_params = to_native(self._task.args.get('_raw_params', ''), errors='surrogate_or_strict') parts = [ to_text(s, errors='surrogate_or_strict') for s in shlex.split(raw_params.strip()) ] source = parts[0] # Support executable paths and files with spaces in the name. executable = to_native(self._task.args.get('executable', ''), errors='surrogate_or_strict') try: source = self._loader.get_real_file( self._find_needle('files', source), decrypt=self._task.args.get('decrypt', True)) except AnsibleError as e: raise AnsibleActionFail(to_native(e)) # now we execute script, always assume changed. result['changed'] = True if not self._play_context.check_mode: # transfer the file to a remote tmp location tmp_src = self._connection._shell.join_path( self._connection._shell.tmpdir, os.path.basename(source)) # Convert raw_params to text for the purpose of replacing the script since # parts and tmp_src are both unicode strings and raw_params will be different # depending on Python version. # # Once everything is encoded consistently, replace the script path on the remote # system with the remainder of the raw_params. This preserves quoting in parameters # that would have been removed by shlex.split(). target_command = to_text(raw_params).strip().replace( parts[0], tmp_src) self._transfer_file(source, tmp_src) # set file permissions, more permissive when the copy is done as a different user self._fixup_perms2((self._connection._shell.tmpdir, tmp_src), execute=True) # add preparation steps to one ssh roundtrip executing the script env_dict = dict() env_string = self._compute_environment_string(env_dict) if executable: script_cmd = ' '.join( [env_string, executable, target_command]) else: script_cmd = ' '.join([env_string, target_command]) if self._play_context.check_mode: raise _AnsibleActionDone() script_cmd = self._connection._shell.wrap_for_exec(script_cmd) exec_data = None # PowerShell runs the script in a special wrapper to enable things # like become and environment args if self._connection._shell.SHELL_FAMILY == "powershell": # FIXME: use a more public method to get the exec payload pc = self._play_context exec_data = _create_powershell_wrapper(to_bytes(script_cmd), {}, env_dict, self._task.async_val, pc.become, pc.become_method, pc.become_user, pc.become_pass, pc.become_flags, scan_dependencies=False) script_cmd = "-" result.update( self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir)) if 'rc' in result and result['rc'] != 0: raise AnsibleActionFail('non-zero return code') except AnsibleAction as e: result.update(e.result) finally: self._remove_tmp_path(self._connection._shell.tmpdir) return result
def set_module_args(args): args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) basic._ANSIBLE_ARGS = to_bytes(args)
def main(): argument_spec = ec2_argument_spec() argument_spec.update(dict( name=dict(required=True), key_material=dict(required=False), force = dict(required=False, type='bool', default=True), state = dict(default='present', choices=['present', 'absent']), wait = dict(type='bool', default=False), wait_timeout = dict(default=300), ) ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, ) if not HAS_BOTO: module.fail_json(msg='boto required for this module') name = module.params['name'] state = module.params.get('state') key_material = module.params.get('key_material') force = module.params.get('force') wait = module.params.get('wait') wait_timeout = int(module.params.get('wait_timeout')) changed = False ec2 = ec2_connect(module) # find the key if present key = ec2.get_key_pair(name) # Ensure requested key is absent if state == 'absent': if key: '''found a match, delete it''' if not module.check_mode: try: key.delete() if wait: start = time.time() action_complete = False while (time.time() - start) < wait_timeout: if not ec2.get_key_pair(name): action_complete = True break time.sleep(1) if not action_complete: module.fail_json(msg="timed out while waiting for the key to be removed") except Exception as e: module.fail_json(msg="Unable to delete key pair '%s' - %s" % (key, e)) key = None changed = True # Ensure requested key is present elif state == 'present': if key: # existing key found if key_material and force: # EC2's fingerprints are non-trivial to generate, so push this key # to a temporary name and make ec2 calculate the fingerprint for us. # # http://blog.jbrowne.com/?p=23 # https://forums.aws.amazon.com/thread.jspa?messageID=352828 # find an unused name test = 'empty' while test: randomchars = [random.choice(string.ascii_letters + string.digits) for x in range(0,10)] tmpkeyname = "ansible-" + ''.join(randomchars) test = ec2.get_key_pair(tmpkeyname) # create tmp key tmpkey = ec2.import_key_pair(tmpkeyname, to_bytes(key_material)) # get tmp key fingerprint tmpfingerprint = tmpkey.fingerprint # delete tmp key tmpkey.delete() if key.fingerprint != tmpfingerprint: if not module.check_mode: key.delete() key = ec2.import_key_pair(name, to_bytes(key_material)) if wait: start = time.time() action_complete = False while (time.time() - start) < wait_timeout: if ec2.get_key_pair(name): action_complete = True break time.sleep(1) if not action_complete: module.fail_json(msg="timed out while waiting for the key to be re-created") changed = True pass # if the key doesn't exist, create it now else: '''no match found, create it''' if not module.check_mode: if key_material: '''We are providing the key, need to import''' key = ec2.import_key_pair(name, to_bytes(key_material)) else: ''' No material provided, let AWS handle the key creation and retrieve the private key ''' key = ec2.create_key_pair(name) if wait: start = time.time() action_complete = False while (time.time() - start) < wait_timeout: if ec2.get_key_pair(name): action_complete = True break time.sleep(1) if not action_complete: module.fail_json(msg="timed out while waiting for the key to be created") changed = True if key: data = { 'name': key.name, 'fingerprint': key.fingerprint } if key.material: data.update({'private_key': key.material}) module.exit_json(changed=changed, key=data) else: module.exit_json(changed=changed, key=None)
def exec_command(self, cmd, in_data=None, sudoable=True): ''' run a command on the ssm host ''' super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) display.vvv(u"EXEC {0}".format(to_text(cmd)), host=self.host) session = self._session mark_begin = "".join([random.choice(string.ascii_letters) for i in xrange(self.MARK_LENGTH)]) if self.is_windows: mark_start = mark_begin + " $LASTEXITCODE" else: mark_start = mark_begin mark_end = "".join([random.choice(string.ascii_letters) for i in xrange(self.MARK_LENGTH)]) # Wrap command in markers accordingly for the shell used cmd = self._wrap_command(cmd, sudoable, mark_start, mark_end) self._flush_stderr(session) for chunk in chunks(cmd, 1024): session.stdin.write(to_bytes(chunk, errors='surrogate_or_strict')) # Read stdout between the markers stdout = '' win_line = '' begin = False stop_time = int(round(time.time())) + self.get_option('ssm_timeout') while session.poll() is None: remaining = stop_time - int(round(time.time())) if remaining < 1: self._timeout = True display.vvvv(u"EXEC timeout stdout: {0}".format(to_text(stdout)), host=self.host) raise AnsibleConnectionFailure("SSM exec_command timeout on host: %s" % self.instance_id) if self._poll_stdout.poll(1000): line = self._filter_ansi(self._stdout.readline()) display.vvvv(u"EXEC stdout line: {0}".format(to_text(line)), host=self.host) else: display.vvvv(u"EXEC remaining: {0}".format(remaining), host=self.host) continue if not begin and self.is_windows: win_line = win_line + line line = win_line if mark_start in line: begin = True if not line.startswith(mark_start): stdout = '' continue if begin: if mark_end in line: display.vvvv(u"POST_PROCESS: {0}".format(to_text(stdout)), host=self.host) returncode, stdout = self._post_process(stdout, mark_begin) break else: stdout = stdout + line stderr = self._flush_stderr(session) return (returncode, stdout, stderr)
from ansible.module_utils.six import string_types from ansible.module_utils._text import to_bytes, to_text from ansible.parsing.utils.addresses import parse_address from ansible.plugins import PluginLoader from ansible.utils.path import unfrackpath try: from __main__ import display except ImportError: from ansible.utils.display import Display display = Display() HOSTS_PATTERNS_CACHE = {} IGNORED_ALWAYS = [b"^\.", b"^host_vars$", b"^group_vars$", b"^vars_plugins$"] IGNORED_PATTERNS = [to_bytes(x) for x in C.INVENTORY_IGNORE_PATTERNS] IGNORED_EXTS = [ b'%s$' % to_bytes(re.escape(x)) for x in C.INVENTORY_IGNORE_EXTS ] IGNORED = re.compile(b'|'.join(IGNORED_ALWAYS + IGNORED_PATTERNS + IGNORED_EXTS)) def order_patterns(patterns): ''' takes a list of patterns and reorders them by modifier to apply them consistently ''' # FIXME: this goes away if we apply patterns incrementally or by groups pattern_regular = [] pattern_intersection = [] pattern_exclude = []
def _parse_pipeline_result(self, pipeline): """ PSRP doesn't have the same concept as other protocols with its output. We need some extra logic to convert the pipeline streams and host output into the format that Ansible understands. :param pipeline: The finished PowerShell pipeline that invoked our commands :return: rc, stdout, stderr based on the pipeline output """ # we try and get the rc from our host implementation, this is set if # exit or $host.SetShouldExit() is called in our pipeline, if not we # set to 0 if the pipeline had not errors and 1 if it did rc = self.host.rc or (1 if pipeline.had_errors else 0) # TODO: figure out a better way of merging this with the host output stdout_list = [] for output in pipeline.output: # Not all pipeline outputs are a string or contain a __str__ value, # we will create our own output based on the properties of the # complex object if that is the case. if isinstance(output, GenericComplexObject) and output.to_string is None: obj_lines = output.property_sets for key, value in output.adapted_properties.items(): obj_lines.append(u"%s: %s" % (key, value)) for key, value in output.extended_properties.items(): obj_lines.append(u"%s: %s" % (key, value)) output_msg = u"\n".join(obj_lines) else: output_msg = to_text(output, nonstring='simplerepr') stdout_list.append(output_msg) if len(self.host.ui.stdout) > 0: stdout_list += self.host.ui.stdout stdout = u"\r\n".join(stdout_list) stderr_list = [] for error in pipeline.streams.error: # the error record is not as fully fleshed out like we usually get # in PS, we will manually create it here command_name = "%s : " % error.command_name if error.command_name else '' position = "%s\r\n" % error.invocation_position_message if error.invocation_position_message else '' error_msg = "%s%s\r\n%s" \ " + CategoryInfo : %s\r\n" \ " + FullyQualifiedErrorId : %s" \ % (command_name, str(error), position, error.message, error.fq_error) stacktrace = error.script_stacktrace if self._play_context.verbosity >= 3 and stacktrace is not None: error_msg += "\r\nStackTrace:\r\n%s" % stacktrace stderr_list.append(error_msg) if len(self.host.ui.stderr) > 0: stderr_list += self.host.ui.stderr stderr = u"\r\n".join([to_text(o) for o in stderr_list]) display.vvvvv("PSRP RC: %d" % rc, host=self._psrp_host) display.vvvvv("PSRP STDOUT: %s" % stdout, host=self._psrp_host) display.vvvvv("PSRP STDERR: %s" % stderr, host=self._psrp_host) # reset the host back output back to defaults, needed if running # multiple pipelines on the same RunspacePool self.host.rc = 0 self.host.ui.stdout = [] self.host.ui.stderr = [] return rc, to_bytes(stdout, encoding='utf-8'), to_bytes(stderr, encoding='utf-8')
def check_become_success(self, b_output): b_success_key = to_bytes(self._play_context.success_key) for b_line in b_output.splitlines(True): if b_success_key == b_line.rstrip(): return True return False
def check_incorrect_password(self, b_output): b_incorrect_password = to_bytes( gettext.dgettext( self._play_context.become_method, C.BECOME_ERROR_STRINGS[self._play_context.become_method])) return b_incorrect_password and b_incorrect_password in b_output
def _connect(self): if not HAS_NCCLIENT: raise AnsibleError("%s: %s" % ( missing_required_lib("ncclient"), to_native(NCCLIENT_IMP_ERR), )) self.queue_message("log", "ssh connection done, starting ncclient") allow_agent = True if self._play_context.password is not None: allow_agent = False setattr(self._play_context, "allow_agent", allow_agent) self.key_filename = (self._play_context.private_key_file or self.get_option("private_key_file")) if self.key_filename: self.key_filename = str(os.path.expanduser(self.key_filename)) self._ssh_config = self.get_option("netconf_ssh_config") if self._ssh_config in BOOLEANS_TRUE: self._ssh_config = True elif self._ssh_config in BOOLEANS_FALSE: self._ssh_config = None # Try to guess the network_os if the network_os is set to auto if self._network_os == "auto": for cls in netconf_loader.all(class_only=True): network_os = cls.guess_network_os(self) if network_os: self.queue_message("vvv", "discovered network_os %s" % network_os) self._network_os = network_os # If we have tried to detect the network_os but were unable to i.e. network_os is still 'auto' # then use default as the network_os if self._network_os == "auto": # Network os not discovered. Set it to default self.queue_message( "vvv", "Unable to discover network_os. Falling back to default.", ) self._network_os = "default" try: ncclient_device_handler = self.netconf.get_option( "ncclient_device_handler") except KeyError: ncclient_device_handler = "default" self.queue_message( "vvv", "identified ncclient device handler: %s." % ncclient_device_handler, ) device_params = {"name": ncclient_device_handler} try: port = self._play_context.port or 830 self.queue_message( "vvv", "ESTABLISH NETCONF SSH CONNECTION FOR USER: %s on PORT %s TO %s WITH SSH_CONFIG = %s" % ( self._play_context.remote_user, port, self._play_context.remote_addr, self._ssh_config, ), ) self._manager = manager.connect( host=self._play_context.remote_addr, port=port, username=self._play_context.remote_user, password=self._play_context.password, key_filename=self.key_filename, hostkey_verify=self.get_option("host_key_checking"), look_for_keys=self.get_option("look_for_keys"), device_params=device_params, allow_agent=self._play_context.allow_agent, timeout=self.get_option("persistent_connect_timeout"), ssh_config=self._ssh_config, ) self._manager._timeout = self.get_option( "persistent_command_timeout") except SSHUnknownHostError as exc: raise AnsibleConnectionFailure(to_native(exc)) except ImportError: raise AnsibleError( "connection=netconf is not supported on {0}".format( self._network_os)) if not self._manager.connected: return 1, b"", b"not connected" self.queue_message("log", "ncclient manager object created successfully") self._connected = True super(Connection, self)._connect() return ( 0, to_bytes(self._manager.session_id, errors="surrogate_or_strict"), b"", )
def find_ini_config_file(warnings=None): ''' Load INI Config File order(first found is used): ENV, CWD, HOME, /etc/ansible ''' # FIXME: eventually deprecate ini configs if warnings is None: # Note: In this case, warnings does nothing warnings = set() # A value that can never be a valid path so that we can tell if ANSIBLE_CONFIG was set later # We can't use None because we could set path to None. SENTINEL = object potential_paths = [] # Environment setting path_from_env = os.getenv("ANSIBLE_CONFIG", SENTINEL) if path_from_env is not SENTINEL: path_from_env = unfrackpath(path_from_env, follow=False) if os.path.isdir(to_bytes(path_from_env)): path_from_env = os.path.join(path_from_env, "ansible.cfg") potential_paths.append(path_from_env) # Current working directory warn_cmd_public = False try: cwd = os.getcwd() perms = os.stat(cwd) cwd_cfg = os.path.join(cwd, "ansible.cfg") if perms.st_mode & stat.S_IWOTH: # Working directory is world writable so we'll skip it. # Still have to look for a file here, though, so that we know if we have to warn if os.path.exists(cwd_cfg): warn_cmd_public = True else: potential_paths.append(cwd_cfg) except OSError: # If we can't access cwd, we'll simply skip it as a possible config source pass # Per user location potential_paths.append(unfrackpath("~/.ansible.cfg", follow=False)) # System location potential_paths.append("/etc/ansible/ansible.cfg") for path in potential_paths: if os.path.exists(to_bytes(path)): break else: path = None # Emit a warning if all the following are true: # * We did not use a config from ANSIBLE_CONFIG # * There's an ansible.cfg in the current working directory that we skipped if path_from_env != path and warn_cmd_public: warnings.add(u"Ansible is being run in a world writable directory (%s)," u" ignoring it as an ansible.cfg source." u" For more information see" u" https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir" % to_text(cwd)) return path
def _file_transport_command(self, in_path, out_path, sftp_action): # scp and sftp require square brackets for IPv6 addresses, but # accept them for hostnames and IPv4 addresses too. host = '[%s]' % self.host # Transfer methods to try methods = [] # Use the transfer_method option if set, otherwise use scp_if_ssh ssh_transfer_method = self._play_context.ssh_transfer_method if ssh_transfer_method is not None: if not (ssh_transfer_method in ('smart', 'sftp', 'scp', 'piped')): raise AnsibleOptionsError( 'transfer_method needs to be one of [smart|sftp|scp|piped]' ) if ssh_transfer_method == 'smart': methods = ['sftp', 'scp', 'piped'] else: methods = [ssh_transfer_method] else: # since this can be a non-bool now, we need to handle it correctly scp_if_ssh = C.DEFAULT_SCP_IF_SSH if not isinstance(scp_if_ssh, bool): scp_if_ssh = scp_if_ssh.lower() if scp_if_ssh in BOOLEANS: scp_if_ssh = boolean(scp_if_ssh, strict=False) elif scp_if_ssh != 'smart': raise AnsibleOptionsError( 'scp_if_ssh needs to be one of [smart|True|False]') if scp_if_ssh == 'smart': methods = ['sftp', 'scp', 'piped'] elif scp_if_ssh is True: methods = ['scp'] else: methods = ['sftp'] for method in methods: returncode = stdout = stderr = None if method == 'sftp': cmd = self._build_command('sftp', to_bytes(host)) in_data = u"{0} {1} {2}\n".format(sftp_action, shlex_quote(in_path), shlex_quote(out_path)) in_data = to_bytes(in_data, nonstring='passthru') (returncode, stdout, stderr) = self._bare_run(cmd, in_data, checkrc=False) elif method == 'scp': if sftp_action == 'get': cmd = self._build_command( 'scp', u'{0}:{1}'.format(host, shlex_quote(in_path)), out_path) else: cmd = self._build_command( 'scp', in_path, u'{0}:{1}'.format(host, shlex_quote(out_path))) in_data = None (returncode, stdout, stderr) = self._bare_run(cmd, in_data, checkrc=False) elif method == 'piped': if sftp_action == 'get': # we pass sudoable=False to disable pty allocation, which # would end up mixing stdout/stderr and screwing with newlines (returncode, stdout, stderr) = self.exec_command( 'dd if=%s bs=%s' % (in_path, BUFSIZE), sudoable=False) out_file = open( to_bytes(out_path, errors='surrogate_or_strict'), 'wb+') out_file.write(stdout) out_file.close() else: in_data = open( to_bytes(in_path, errors='surrogate_or_strict'), 'rb').read() in_data = to_bytes(in_data, nonstring='passthru') (returncode, stdout, stderr) = self.exec_command( 'dd of=%s bs=%s' % (out_path, BUFSIZE), in_data=in_data) # Check the return code and rollover to next method if failed if returncode == 0: return (returncode, stdout, stderr) else: # If not in smart mode, the data will be printed by the raise below if len(methods) > 1: display.warning( msg= '%s transfer mechanism failed on %s. Use ANSIBLE_DEBUG=1 to see detailed information' % (method, host)) display.debug(msg='%s' % to_native(stdout)) display.debug(msg='%s' % to_native(stderr)) if returncode == 255: raise AnsibleConnectionFailure( "Failed to connect to the host via %s: %s" % (method, to_native(stderr))) else: raise AnsibleError("failed to transfer file to %s %s:\n%s\n%s" % (to_native(in_path), to_native(out_path), to_native(stdout), to_native(stderr)))
def ensure_type(value, value_type, origin=None): ''' return a configuration variable with casting :arg value: The value to ensure correct typing of :kwarg value_type: The type of the value. This can be any of the following strings: :boolean: sets the value to a True or False value :bool: Same as 'boolean' :integer: Sets the value to an integer or raises a ValueType error :int: Same as 'integer' :float: Sets the value to a float or raises a ValueType error :list: Treats the value as a comma separated list. Split the value and return it as a python list. :none: Sets the value to None :path: Expands any environment variables and tilde's in the value. :tmppath: Create a unique temporary directory inside of the directory specified by value and return its path. :temppath: Same as 'tmppath' :tmp: Same as 'tmppath' :pathlist: Treat the value as a typical PATH string. (On POSIX, this means colon separated strings.) Split the value and then expand each part for environment variables and tildes. :pathspec: Treat the value as a PATH string. Expands any environment variables tildes's in the value. :str: Sets the value to string types. :string: Same as 'str' ''' basedir = None if origin and os.path.isabs(origin) and os.path.exists(to_bytes(origin)): basedir = origin if value_type: value_type = value_type.lower() if value_type in ('boolean', 'bool'): value = boolean(value, strict=False) elif value is not None: if value_type in ('integer', 'int'): value = int(value) elif value_type == 'float': value = float(value) elif value_type == 'list': if isinstance(value, string_types): value = [x.strip() for x in value.split(',')] elif value_type == 'none': if value == "None": value = None elif value_type == 'path': value = resolve_path(value, basedir=basedir) elif value_type in ('tmp', 'temppath', 'tmppath'): value = resolve_path(value, basedir=basedir) if not os.path.exists(value): makedirs_safe(value, 0o700) prefix = 'ansible-local-%s' % os.getpid() value = tempfile.mkdtemp(prefix=prefix, dir=value) elif value_type == 'pathspec': if isinstance(value, string_types): value = value.split(os.pathsep) value = [resolve_path(x, basedir=basedir) for x in value] elif value_type == 'pathlist': if isinstance(value, string_types): value = value.split(',') value = [resolve_path(x, basedir=basedir) for x in value] elif value_type in ('str', 'string'): value = unquote(to_text(value, errors='surrogate_or_strict')) # defaults to string type elif isinstance(value, string_types): value = unquote(value) return to_text(value, errors='surrogate_or_strict', nonstring='passthru')
def _build_command(self, binary, *other_args): ''' Takes a binary (ssh, scp, sftp) and optional extra arguments and returns a command line as an array that can be passed to subprocess.Popen. ''' b_command = [] # # First, the command to invoke # # If we want to use password authentication, we have to set up a pipe to # write the password to sshpass. if self._play_context.password: if not self._sshpass_available(): raise AnsibleError( "to use the 'ssh' connection type with passwords, you must install the sshpass program" ) self.sshpass_pipe = os.pipe() b_command += [ b'sshpass', b'-d' + to_bytes(self.sshpass_pipe[0], nonstring='simplerepr', errors='surrogate_or_strict') ] if binary == 'ssh': b_command += [ to_bytes(self._play_context.ssh_executable, errors='surrogate_or_strict') ] else: b_command += [to_bytes(binary, errors='surrogate_or_strict')] # # Next, additional arguments based on the configuration. # # sftp batch mode allows us to correctly catch failed transfers, but can # be disabled if the client side doesn't support the option. However, # sftp batch mode does not prompt for passwords so it must be disabled # if not using controlpersist and using sshpass if binary == 'sftp' and C.DEFAULT_SFTP_BATCH_MODE: if self._play_context.password: b_args = [b'-o', b'BatchMode=no'] self._add_args(b_command, b_args, u'disable batch mode for sshpass') b_command += [b'-b', b'-'] if self._play_context.verbosity > 3: b_command.append(b'-vvv') # # Next, we add [ssh_connection]ssh_args from ansible.cfg. # if self._play_context.ssh_args: b_args = [ to_bytes(a, errors='surrogate_or_strict') for a in self._split_ssh_args(self._play_context.ssh_args) ] self._add_args(b_command, b_args, u"ansible.cfg set ssh_args") # Now we add various arguments controlled by configuration file settings # (e.g. host_key_checking) or inventory variables (ansible_ssh_port) or # a combination thereof. if not C.HOST_KEY_CHECKING: b_args = (b"-o", b"StrictHostKeyChecking=no") self._add_args( b_command, b_args, u"ANSIBLE_HOST_KEY_CHECKING/host_key_checking disabled") if self._play_context.port is not None: b_args = (b"-o", b"Port=" + to_bytes(self._play_context.port, nonstring='simplerepr', errors='surrogate_or_strict')) self._add_args( b_command, b_args, u"ANSIBLE_REMOTE_PORT/remote_port/ansible_port set") key = self._play_context.private_key_file if key: b_args = (b"-o", b'IdentityFile="' + to_bytes( os.path.expanduser(key), errors='surrogate_or_strict') + b'"') self._add_args( b_command, b_args, u"ANSIBLE_PRIVATE_KEY_FILE/private_key_file/ansible_ssh_private_key_file set" ) if not self._play_context.password: self._add_args(b_command, ( b"-o", b"KbdInteractiveAuthentication=no", b"-o", b"PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey", b"-o", b"PasswordAuthentication=no"), u"ansible_password/ansible_ssh_pass not set") user = self._play_context.remote_user if user: self._add_args( b_command, (b"-o", b"User="******"ANSIBLE_REMOTE_USER/remote_user/ansible_user/user/-u set") self._add_args( b_command, (b"-o", b"ConnectTimeout=" + to_bytes(self._play_context.timeout, errors='surrogate_or_strict', nonstring='simplerepr')), u"ANSIBLE_TIMEOUT/timeout set") # Add in any common or binary-specific arguments from the PlayContext # (i.e. inventory or task settings or overrides on the command line). for opt in (u'ssh_common_args', u'{0}_extra_args'.format(binary)): attr = getattr(self._play_context, opt, None) if attr is not None: b_args = [ to_bytes(a, errors='surrogate_or_strict') for a in self._split_ssh_args(attr) ] self._add_args(b_command, b_args, u"PlayContext set %s" % opt) # Check if ControlPersist is enabled and add a ControlPath if one hasn't # already been set. controlpersist, controlpath = self._persistence_controls(b_command) if controlpersist: self._persistent = True if not controlpath: cpdir = unfrackpath(self.control_path_dir) b_cpdir = to_bytes(cpdir, errors='surrogate_or_strict') # The directory must exist and be writable. makedirs_safe(b_cpdir, 0o700) if not os.access(b_cpdir, os.W_OK): raise AnsibleError("Cannot write to ControlPath %s" % to_native(cpdir)) if not self.control_path: self.control_path = self._create_control_path( self.host, self.port, self.user) b_args = (b"-o", b"ControlPath=" + to_bytes(self.control_path % dict(directory=cpdir), errors='surrogate_or_strict')) self._add_args( b_command, b_args, u"found only ControlPersist; added ControlPath") # Finally, we add any caller-supplied extras. if other_args: b_command += [to_bytes(a) for a in other_args] return b_command
def parse_source(self, source, cache=False): ''' Generate or update inventory for the source provided ''' parsed = False display.debug(u'Examining possible inventory source: %s' % source) b_source = to_bytes(source) # process directories as a collection of inventories if os.path.isdir(b_source): display.debug(u'Searching for inventory files in directory: %s' % source) for i in sorted(os.listdir(b_source)): display.debug(u'Considering %s' % i) # Skip hidden files and stuff we explicitly ignore if IGNORED.search(i): continue # recursively deal with directory entries fullpath = os.path.join(b_source, i) parsed_this_one = self.parse_source(to_native(fullpath), cache=cache) display.debug(u'parsed %s as %s' % (fullpath, parsed_this_one)) if not parsed: parsed = parsed_this_one else: # left with strings or files, let plugins figure it out # set so new hosts can use for inventory_file/dir vasr self._inventory.current_source = source # get inventory plugins if needed, there should always be at least one generator if not self._inventory_plugins: self._setup_inventory_plugins() # try source with each plugin failures = [] for plugin in self._inventory_plugins: plugin_name = to_native(getattr(plugin, '_load_name', getattr(plugin, '_original_path', ''))) display.debug(u'Attempting to use plugin %s (%s)' % (plugin_name, plugin._original_path)) # initialize and figure out if plugin wants to attempt parsing this file try: plugin_wants = bool(plugin.verify_file(source)) except Exception: plugin_wants = False if plugin_wants: try: # in case plugin fails 1/2 way we dont want partial inventory plugin.parse(self._inventory, self._loader, source, cache=cache) parsed = True display.vvv('Parsed %s inventory source with %s plugin' % (to_text(source), plugin_name)) break except AnsibleParserError as e: display.debug('%s was not parsable by %s' % (to_text(source), plugin_name)) failures.append({'src': source, 'plugin': plugin_name, 'exc': e}) except Exception as e: display.debug('%s failed to parse %s' % (plugin_name, to_text(source))) failures.append({'src': source, 'plugin': plugin_name, 'exc': e}) else: display.debug('%s did not meet %s requirements' % (to_text(source), plugin_name)) else: if not parsed and failures: # only if no plugin processed files should we show errors. for fail in failures: display.warning(u'\n* Failed to parse %s with %s plugin: %s' % (to_text(fail['src']), fail['plugin'], to_text(fail['exc']))) if hasattr(fail['exc'], 'tb'): display.vvv(to_text(fail['exc'].tb)) if not parsed: display.warning("Unable to parse %s as an inventory source" % to_text(source)) # clear up, jic self._inventory.current_source = None return parsed
def set_module_args(args): if 'provider' not in args: args['provider'] = {'transport': args.get('transport') or 'cli'} args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) basic._ANSIBLE_ARGS = to_bytes(args)
sudo = ('ansible_sudo',), sudo_user = ('ansible_sudo_user',), sudo_pass = ('ansible_sudo_password', 'ansible_sudo_pass'), sudo_exe = ('ansible_sudo_exe',), sudo_flags = ('ansible_sudo_flags',), su = ('ansible_su',), su_user = ('ansible_su_user',), su_pass = ('ansible_su_password', 'ansible_su_pass'), su_exe = ('ansible_su_exe',), su_flags = ('ansible_su_flags',), executable = ('ansible_shell_executable',), module_compression = ('ansible_module_compression',), ) b_SU_PROMPT_LOCALIZATIONS = [ to_bytes('Password'), to_bytes('암호'), to_bytes('パスワード'), to_bytes('Adgangskode'), to_bytes('Contraseña'), to_bytes('Contrasenya'), to_bytes('Hasło'), to_bytes('Heslo'), to_bytes('Jelszó'), to_bytes('Lösenord'), to_bytes('Mật khẩu'), to_bytes('Mot de passe'), to_bytes('Parola'), to_bytes('Parool'), to_bytes('Pasahitza'), to_bytes('Passord'),
def _put_file_new(self, in_path, out_path): copy_script = '''begin { $ErrorActionPreference = "Stop" $WarningPreference = "Continue" $path = $MyInvocation.UnboundArguments[0] $fd = [System.IO.File]::Create($path) $algo = [System.Security.Cryptography.SHA1CryptoServiceProvider]::Create() $bytes = @() $bindingFlags = [System.Reflection.BindingFlags]'NonPublic, Instance' Function Get-Property { <# .SYNOPSIS Gets the private/internal property specified of the object passed in. #> Param ( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [System.Object] $Object, [Parameter(Mandatory=$true, Position=1)] [System.String] $Name ) $Object.GetType().GetProperty($Name, $bindingFlags).GetValue($Object, $null) } Function Set-Property { <# .SYNOPSIS Sets the private/internal property specified on the object passed in. #> Param ( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [System.Object] $Object, [Parameter(Mandatory=$true, Position=1)] [System.String] $Name, [Parameter(Mandatory=$true, Position=2)] [AllowNull()] [System.Object] $Value ) $Object.GetType().GetProperty($Name, $bindingFlags).SetValue($Object, $Value, $null) } Function Get-Field { <# .SYNOPSIS Gets the private/internal field specified of the object passed in. #> Param ( [Parameter(Mandatory=$true, ValueFromPipeline=$true)] [System.Object] $Object, [Parameter(Mandatory=$true, Position=1)] [System.String] $Name ) $Object.GetType().GetField($Name, $bindingFlags).GetValue($Object) } # MaximumAllowedMemory is required to be set to so we can send input data that exceeds the limit on a PS # Runspace. We use reflection to access/set this property as it is not accessible publicly. This is not ideal # but works on all PowerShell versions I've tested with. We originally used WinRS to send the raw bytes to the # host but this falls flat if someone is using a custom PS configuration name so this is a workaround. This # isn't required for smaller files so if it fails we ignore the error and hope it wasn't needed. # https://github.com/PowerShell/PowerShell/blob/c8e72d1e664b1ee04a14f226adf655cced24e5f0/src/System.Management.Automation/engine/serialization.cs#L325 try { $Host | Get-Property 'ExternalHost' | ` Get-Field '_transportManager' | ` Get-Property 'Fragmentor' | ` Get-Property 'DeserializationContext' | ` Set-Property 'MaximumAllowedMemory' $null } catch {} } process { $bytes = [System.Convert]::FromBase64String($input) $algo.TransformBlock($bytes, 0, $bytes.Length, $bytes, 0) > $null $fd.Write($bytes, 0, $bytes.Length) } end { $fd.Close() $algo.TransformFinalBlock($bytes, 0, 0) > $null $hash = [System.BitConverter]::ToString($algo.Hash).Replace('-', '').ToLowerInvariant() Write-Output -InputObject "{`"sha1`":`"$hash`"}" } ''' # Get the buffer size of each fragment to send, subtract 82 for the fragment, message, and other header info # fields that PSRP adds. Adjust to size of the base64 encoded bytes length. buffer_size = int((self.runspace.connection.max_payload_size - 82) / 4 * 3) sha1_hash = sha1() b_in_path = to_bytes(in_path, errors='surrogate_or_strict') if not os.path.exists(b_in_path): raise AnsibleFileNotFound('file or module does not exist: "%s"' % to_native(in_path)) def read_gen(): offset = 0 with open(b_in_path, 'rb') as src_fd: for b_data in iter((lambda: src_fd.read(buffer_size)), b""): data_len = len(b_data) offset += data_len sha1_hash.update(b_data) # PSRP technically supports sending raw bytes but that method requires a larger CLIXML message. # Sending base64 is still more efficient here. display.vvvvv("PSRP PUT %s to %s (offset=%d, size=%d" % (in_path, out_path, offset, data_len), host=self._psrp_host) b64_data = base64.b64encode(b_data) yield [to_text(b64_data)] if offset == 0: # empty file yield [""] rc, stdout, stderr = self._exec_psrp_script(copy_script, read_gen(), arguments=[out_path], force_stop=True) return rc, stdout, stderr, sha1_hash.hexdigest()
def fetch_file(self, in_path, out_path): super(Connection, self).fetch_file(in_path, out_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._psrp_host) in_path = self._shell._unquote(in_path) out_path = out_path.replace('\\', '/') # because we are dealing with base64 data we need to get the max size # of the bytes that the base64 size would equal max_b64_size = int(self.runspace.connection.max_payload_size - (self.runspace.connection.max_payload_size / 4 * 3)) buffer_size = max_b64_size - (max_b64_size % 1024) # setup the file stream with read only mode setup_script = '''$ErrorActionPreference = "Stop" $path = '%s' if (Test-Path -Path $path -PathType Leaf) { $fs = New-Object -TypeName System.IO.FileStream -ArgumentList @( $path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read ) $buffer_size = %d } elseif (Test-Path -Path $path -PathType Container) { Write-Output -InputObject "[DIR]" } else { Write-Error -Message "$path does not exist" $host.SetShouldExit(1) }''' % (self._shell._escape(in_path), buffer_size) # read the file stream at the offset and return the b64 string read_script = '''$ErrorActionPreference = "Stop" $fs.Seek(%d, [System.IO.SeekOrigin]::Begin) > $null $buffer = New-Object -TypeName byte[] -ArgumentList $buffer_size $bytes_read = $fs.Read($buffer, 0, $buffer_size) if ($bytes_read -gt 0) { $bytes = $buffer[0..($bytes_read - 1)] Write-Output -InputObject ([System.Convert]::ToBase64String($bytes)) }''' # need to run the setup script outside of the local scope so the # file stream stays active between fetch operations rc, stdout, stderr = self._exec_psrp_script(setup_script, use_local_scope=False, force_stop=True) if rc != 0: raise AnsibleError("failed to setup file stream for fetch '%s': %s" % (out_path, to_native(stderr))) elif stdout.strip() == '[DIR]': # to be consistent with other connection plugins, we assume the caller has created the target dir return b_out_path = to_bytes(out_path, errors='surrogate_or_strict') # to be consistent with other connection plugins, we assume the caller has created the target dir offset = 0 with open(b_out_path, 'wb') as out_file: while True: display.vvvvv("PSRP FETCH %s to %s (offset=%d" % (in_path, out_path, offset), host=self._psrp_host) rc, stdout, stderr = self._exec_psrp_script(read_script % offset, force_stop=True) if rc != 0: raise AnsibleError("failed to transfer file to '%s': %s" % (out_path, to_native(stderr))) data = base64.b64decode(stdout.strip()) out_file.write(data) if len(data) < buffer_size: break offset += len(data) rc, stdout, stderr = self._exec_psrp_script("$fs.Close()", force_stop=True) if rc != 0: display.warning("failed to close remote file stream of file " "'%s': %s" % (in_path, to_native(stderr)))
def main(): # the command module is the one ansible module that does not take key=value args # hence don't copy this one if you are looking to build others! # NOTE: ensure splitter.py is kept in sync for exceptions module = AnsibleModule( argument_spec=dict( _raw_params=dict(), _uses_shell=dict(type='bool', default=False), argv=dict(type='list', elements='str'), chdir=dict(type='path'), executable=dict(), creates=dict(type='path'), removes=dict(type='path'), # The default for this really comes from the action plugin warn=dict(type='bool', default=False, removed_in_version='2.14', removed_from_collection='ansible.builtin'), stdin=dict(required=False), stdin_add_newline=dict(type='bool', default=True), strip_empty_ends=dict(type='bool', default=True), ), supports_check_mode=True, ) shell = module.params['_uses_shell'] chdir = module.params['chdir'] executable = module.params['executable'] args = module.params['_raw_params'] argv = module.params['argv'] creates = module.params['creates'] removes = module.params['removes'] warn = module.params['warn'] stdin = module.params['stdin'] stdin_add_newline = module.params['stdin_add_newline'] strip = module.params['strip_empty_ends'] # we promissed these in 'always' ( _lines get autoaded on action plugin) r = { 'changed': False, 'stdout': '', 'stderr': '', 'rc': None, 'cmd': None, 'start': None, 'end': None, 'delta': None, 'msg': '' } if not shell and executable: module.warn( "As of Ansible 2.4, the parameter 'executable' is no longer supported with the 'command' module. Not using '%s'." % executable) executable = None if (not args or args.strip() == '') and not argv: r['rc'] = 256 r['msg'] = "no command given" module.fail_json(**r) if args and argv: r['rc'] = 256 r['msg'] = "only command or argv can be given, not both" module.fail_json(**r) if not shell and args: args = shlex.split(args) args = args or argv # All args must be strings if is_iterable(args, include_strings=False): args = [ to_native(arg, errors='surrogate_or_strict', nonstring='simplerepr') for arg in args ] r['cmd'] = args if warn: # nany telling you to use module instead! check_command(module, args) if chdir: chdir = to_bytes(chdir, errors='surrogate_or_strict') try: os.chdir(chdir) except (IOError, OSError) as e: r['msg'] = 'Unable to change directory before execution: %s' % to_text( e) module.fail_json(**r) # check_mode partial support, since it only really works in checking creates/removes if module.check_mode: shoulda = "Would" else: shoulda = "Did" # special skips for idempotence if file exists (assumes command creates) if creates: if glob.glob(creates): r['msg'] = "%s not run command since '%s' exists" % (shoulda, creates) r['stdout'] = "skipped, since %s exists" % creates # TODO: deprecate r['rc'] = 0 # special skips for idempotence if file does not exist (assumes command removes) if not r['msg'] and removes: if not glob.glob(removes): r['msg'] = "%s not run command since '%s' does not exist" % ( shoulda, removes) r['stdout'] = "skipped, since %s does not exist" % removes # TODO: deprecate r['rc'] = 0 if r['msg']: module.exit_json(**r) # actually executes command (or not ...) if not module.check_mode: r['start'] = datetime.datetime.now() r['rc'], r['stdout'], r['stderr'] = module.run_command( args, executable=executable, use_unsafe_shell=shell, encoding=None, data=stdin, binary_data=(not stdin_add_newline)) r['end'] = datetime.datetime.now() else: # this is partial check_mode support, since we end up skipping if we get here r['rc'] = 0 r['msg'] = "Command would have run if not in check mode" if creates is None and removes is None: r['skipped'] = True r['changed'] = True # convert to text for jsonization and usability if r['start'] is not None and r['end'] is not None: # these are datetime objects, but need them as strings to pass back r['delta'] = to_text(r['end'] - r['start']) r['end'] = to_text(r['end']) r['start'] = to_text(r['start']) if strip: r['stdout'] = to_text(r['stdout']).rstrip("\r\n") r['stderr'] = to_text(r['stderr']).rstrip("\r\n") if r['rc'] != 0: r['msg'] = 'non-zero return code' module.fail_json(**r) module.exit_json(**r)
def exec_command(self, cmd, in_data=None, sudoable=True): ''' run a command on the remote host ''' super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) if in_data: raise AnsibleError( "Internal Error: this module does not support optimized module pipelining" ) bufsize = 4096 try: self.ssh.get_transport().set_keepalive(5) chan = self.ssh.get_transport().open_session() except Exception as e: text_e = to_text(e) msg = u"Failed to open session" if text_e: msg += u": %s" % text_e raise AnsibleConnectionFailure(to_native(msg)) # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one by default (pty=True in ansible.cfg), and we try # to initialise from the calling environment when sudoable is enabled if self.get_option('pty') and sudoable: chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) display.vvv("EXEC %s" % cmd, host=self._play_context.remote_addr) cmd = to_bytes(cmd, errors='surrogate_or_strict') no_prompt_out = b'' no_prompt_err = b'' become_output = b'' try: chan.exec_command(cmd) if self._play_context.prompt: passprompt = False become_sucess = False while not (become_sucess or passprompt): display.debug('Waiting for Privilege Escalation input') chunk = chan.recv(bufsize) display.debug("chunk is: %s" % chunk) if not chunk: if b'unknown user' in become_output: raise AnsibleError('user %s does not exist' % self._play_context.become_user) else: break # raise AnsibleError('ssh connection closed waiting for password prompt') become_output += chunk # need to check every line because we might get lectured # and we might get the middle of a line in a chunk for l in become_output.splitlines(True): if self.check_become_success(l): become_sucess = True break elif self.check_password_prompt(l): passprompt = True break if passprompt: if self._play_context.become and self._play_context.become_pass: chan.sendall( to_bytes(self._play_context.become_pass) + b'\n') else: raise AnsibleError( "A password is required but none was supplied") else: no_prompt_out += become_output no_prompt_err += become_output except socket.timeout: raise AnsibleError( 'ssh timed out waiting for privilege escalation.\n' + become_output) stdout = b''.join(chan.makefile('rb', bufsize)) stderr = b''.join(chan.makefile_stderr('rb', bufsize)) return (chan.recv_exit_status(), no_prompt_out + stdout, no_prompt_out + stderr)