def _load_private_key_file(filename, key_filename, key_password): exception = PyinfraError("Invalid key: {0}".format(filename)) for key_cls in (RSAKey, DSSKey, ECDSAKey, Ed25519Key): try: return key_cls.from_private_key_file(filename=filename, ) except PasswordRequiredException: if not key_password: # If password is not provided, but we're in CLI mode, ask for it. I'm not a # huge fan of having CLI specific code in here, but it doesn't really fit # anywhere else without duplicating lots of key related code into cli.py. if pyinfra.is_cli: key_password = getpass( "Enter password for private key: {0}: ".format( key_filename, ), ) # API mode and no password? We can't continue! else: raise PyinfraError( "Private key file ({0}) is encrypted, set ssh_key_password to " "use this key".format(key_filename), ) try: return key_cls.from_private_key_file( filename=filename, password=key_password, ) except SSHException as e: # key does not match key_cls type exception = e except SSHException as e: # key does not match key_cls type exception = e raise exception
def _get_private_key(state, key_filename, key_password): if key_filename in state.private_keys: return state.private_keys[key_filename] ssh_key_filenames = [ # Global from executed directory path.expanduser(key_filename), ] # Relative to the deploy if state.deploy_dir: ssh_key_filenames.append(path.join(state.deploy_dir, key_filename), ) for filename in ssh_key_filenames: if not path.isfile(filename): continue # First, lets try the key without a password try: key = RSAKey.from_private_key_file(filename=filename, ) break # Key is encrypted! except PasswordRequiredException: # If password is not provided, but we're in CLI mode, ask for it. I'm not a # huge fan of having CLI specific code in here, but it doesn't really fit # anywhere else without duplicating lots of key related code into cli.py. if not key_password: if pyinfra.is_cli: key_password = getpass( 'Enter password for private key: {0}: '.format( key_filename, ), ) # API mode and no password? We can't continue! else: raise PyinfraError( 'Private key file ({0}) is encrypted, set ssh_key_password to ' 'use this key'.format(key_filename), ) # Now, try opening the key with the password try: key = RSAKey.from_private_key_file( filename=filename, password=key_password, ) break except SSHException: raise PyinfraError( 'Incorrect password for private key: {0}'.format( key_filename, ), ) # No break, so no key found else: raise IOError('No such private key file: {0}'.format(key_filename)) state.private_keys[key_filename] = key return key
def _get_private_key(state, key_filename, key_password): if key_filename in state.private_keys: return state.private_keys[key_filename] ssh_key_filenames = [ # Global from executed directory path.expanduser(key_filename), ] # Relative to the deploy if state.deploy_dir: ssh_key_filenames.append(path.join(state.deploy_dir, key_filename), ) key = False key_file_exists = False for filename in ssh_key_filenames: if not path.isfile(filename): continue key_file_exists = True try: key = _load_private_key_file(filename, key_filename, key_password) break except SSHException: pass # No break, so no key found if not key: if not key_file_exists: raise PyinfraError( 'No such private key file: {0}'.format(key_filename)) # TODO: upgrade min paramiko version to 2.7 and remove this (pyinfra v2) extra_info = '' from pkg_resources import get_distribution, parse_version if get_distribution('paramiko').parsed_version < parse_version('2.7'): extra_info = ( '\n Paramiko versions under 2.7 do not support the latest OpenSSH key formats,' ' upgrading may fix this error.' '\n For more information, see this issue: ' 'https://github.com/Fizzadar/pyinfra/issues/548') raise PyinfraError('Invalid private key file: {0}{1}'.format( key_filename, extra_info)) # Load any certificate, names from OpenSSH: # https://github.com/openssh/openssh-portable/blob/049297de975b92adcc2db77e3fb7046c0e3c695d/ssh-keygen.c#L2453 # noqa: E501 for certificate_filename in ( '{0}-cert.pub'.format(key_filename), '{0}.pub'.format(key_filename), ): if path.isfile(certificate_filename): key.load_certificate(certificate_filename) state.private_keys[key_filename] = key return key
def _run_server_ops(state, host, progress=None): ''' Run all ops for a single server. ''' logger.debug('Running all ops on {0}'.format(host)) for op_hash in state.get_op_order(): op_meta = state.op_meta[op_hash] logger.info('--> {0} {1} on {2}'.format( click.style('--> Starting operation:', 'blue'), click.style(', '.join(op_meta['names']), bold=True), click.style(host.name, bold=True), )) result = _run_server_op(state, host, op_hash) # Trigger CLI progress if provided if progress: progress((host, op_hash)) if result is False: raise PyinfraError('Error in operation {0} on {1}'.format( ', '.join(op_meta['names']), host, )) if pyinfra.is_cli: print()
def get_file(state, host, remote_filename, filename_or_io, remote_temp_filename=None, **command_kwargs): raise PyinfraError("Not implemented")
def _run_server_ops(state, host, progress=None): name = host.name logger.debug('Running all ops on {0}'.format(name)) for op_hash in state.op_order: op_meta = state.op_meta[op_hash] logger.info('--> {0} {1} on {2}'.format( click.style('--> Starting operation:', 'blue'), click.style(', '.join(op_meta['names']), bold=True), click.style(name, bold=True), )) result = _run_op(state, host, op_hash) # Trigger CLI progress if provided if progress: progress() if result is False: raise PyinfraError('Error in operation {0} on {1}'.format( ', '.join(op_meta['names']), name, )) if state.print_lines: print()
def _get_private_key(state, key_filename, key_password): if key_filename in state.private_keys: return state.private_keys[key_filename] ssh_key_filenames = [ # Global from executed directory path.expanduser(key_filename), ] if state.cwd: # Relative to the CWD path.join(state.cwd, key_filename) key = False key_file_exists = False for filename in ssh_key_filenames: if not path.isfile(filename): continue key_file_exists = True try: key = _load_private_key_file(filename, key_filename, key_password) break except SSHException: pass # No break, so no key found if not key: if not key_file_exists: raise PyinfraError( "No such private key file: {0}".format(key_filename)) raise PyinfraError( "Invalid private key file: {0}".format(key_filename)) # Load any certificate, names from OpenSSH: # https://github.com/openssh/openssh-portable/blob/049297de975b92adcc2db77e3fb7046c0e3c695d/ssh-keygen.c#L2453 # noqa: E501 for certificate_filename in ( "{0}-cert.pub".format(key_filename), "{0}.pub".format(key_filename), ): if path.isfile(certificate_filename): key.load_certificate(certificate_filename) state.private_keys[key_filename] = key return key
def fake_docker_shell(command, splitlines=None): if command == 'docker run -d not-an-image sleep 10000': return ['containerid'] elif command == 'docker commit containerid': return ['sha256:blahsomerandomstringdata'] elif command == 'docker rm -f containerid': return [] raise PyinfraError('Invalid command: {0}'.format(command))
def fake_docker_shell(command, splitlines=None): if command == "docker run -d not-an-image tail -f /dev/null": return ["containerid"] elif command == "docker commit containerid": return ["sha256:blahsomerandomstringdata"] elif command == "docker rm -f containerid": return [] raise PyinfraError("Invalid command: {0}".format(command))
def fake_ssh_docker_shell( state, host, command, get_pty=False, timeout=None, stdin=None, success_exit_codes=None, print_output=False, print_input=False, return_combined_output=False, use_sudo_password=False, **command_kwargs, ): if host.data.ssh_hostname not in ("somehost", "anotherhost"): raise PyinfraError("Invalid host", host.data.ssh_hostname) if command == "docker run -d not-an-image tail -f /dev/null": return (True, ["containerid"], []) elif command == "docker commit containerid": return (True, ["sha256:blahsomerandomstringdata"], []) elif command == "docker rm -f containerid": return (True, [], []) elif str(command).startswith("rm -f"): return (True, [], []) # This is a bit messy. But it's easier than trying to swap out a mock # when it needs to be used... elif fake_ssh_docker_shell.custom_command: custom_command, status, stdout, stderr = fake_ssh_docker_shell.custom_command if str(command) == custom_command: fake_ssh_docker_shell.ran_custom_command = True return (status, stdout, stderr) raise PyinfraError("Invalid Command: {0}".format(command))
def include(filename): """ Executes a local python file within the ``pyinfra.state.cwd`` directory. """ if not pyinfra.is_cli: raise PyinfraError("local.include is only available in CLI mode.") filename = get_file_path(state, filename) logger.debug("Including local file: %s", filename) config_state = config.get_current_state() try: # Fixes a circular import because `pyinfra.local` is really a CLI # only thing (so should be `pyinfra_cli.local`). It is kept here # to maintain backwards compatibility and the nicer public import # (ideally users never need to import from `pyinfra_cli`). from pyinfra_cli.util import exec_file with host.deploy(path.relpath(filename, state.cwd), None, None, in_deploy=False): exec_file(filename) # One potential solution to the above is to add local as an actual # module, ie `pyinfra.operations.local`. except Exception as e: raise PyinfraError( "Could not include local file: {0}:\n{1}".format(filename, e), ) finally: config.set_current_state(config_state)
def shell(commands, splitlines=False, ignore_errors=False, print_output=False, print_input=False): """ Subprocess based implementation of pyinfra/api/ssh.py's ``run_shell_command``. Args: commands (string, list): command or list of commands to execute spltlines (bool): optionally have the output split by lines ignore_errors (bool): ignore errors when executing these commands """ if isinstance(commands, str): commands = [commands] all_stdout = [] # Checking for state context being set means this function works outside a deploy # e.g.: the vagrant connector. if ctx_state.isset(): print_output = state.print_output print_input = state.print_input for command in commands: print_prefix = "localhost: " if print_input: click.echo("{0}>>> {1}".format(print_prefix, command), err=True) return_code, combined_output = run_local_process( command, print_output=print_output, print_prefix=print_prefix, ) stdout, stderr = split_combined_output(combined_output) if return_code > 0 and not ignore_errors: raise PyinfraError( "Local command failed: {0}\n{1}".format(command, stderr), ) all_stdout.extend(stdout) if not splitlines: return "\n".join(all_stdout) return all_stdout
def _run_server_ops(state, hostname): logger.debug('Running all ops on {}'.format(hostname)) for op_hash in state.op_order: op_meta = state.op_meta[op_hash] logger.info('{0} {1} on {2}'.format( colored('Starting operation:', 'blue'), colored(', '.join(op_meta['names']), attrs=['bold']), colored(hostname, attrs=['bold']))) result = _run_op(state, hostname, op_hash) if result is False: raise PyinfraError('Error in operation {0} on {1}'.format( ', '.join(op_meta['names']), hostname)) if state.print_lines: print()
def put_file( state, host, filename_or_io, remote_filename, **command_kwargs ): raise PyinfraError('Not implemented')
def fake_chroot_shell(command, splitlines=None): if command == 'chroot /not-a-chroot ls': return True raise PyinfraError('Invalid command: {0}'.format(command))