123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- """
- Internal shared-state variables such as config settings and host lists.
- """
- import os
- import sys
- from optparse import make_option
- from fabric.network import HostConnectionCache, ssh
- from fabric.version import get_version
- from fabric.utils import _AliasDict, _AttributeDict
- #
- # Win32 flag
- #
- # Impacts a handful of platform specific behaviors. Note that Cygwin's Python
- # is actually close enough to "real" UNIXes that it doesn't need (or want!) to
- # use PyWin32 -- so we only test for literal Win32 setups (vanilla Python,
- # ActiveState etc) here.
- win32 = (sys.platform == 'win32')
- #
- # Environment dictionary - support structures
- #
- # By default, if the user (including code using Fabric as a library) doesn't
- # set the username, we obtain the currently running username and use that.
- def _get_system_username():
- """
- Obtain name of current system user, which will be default connection user.
- """
- import getpass
- username = None
- try:
- username = getpass.getuser()
- # getpass.getuser supported on both Unix and Windows systems.
- # getpass.getuser may call pwd.getpwuid which in turns may raise KeyError
- # if it cannot find a username for the given UID, e.g. on ep.io
- # and similar "non VPS" style services. Rather than error out, just keep
- # the 'default' username to None. Can check for this value later if needed.
- except KeyError:
- pass
- except ImportError:
- if win32:
- import win32api
- import win32security # noqa
- import win32profile # noqa
- username = win32api.GetUserName()
- return username
- def _rc_path():
- """
- Return platform-specific default file path for $HOME/.fabricrc.
- """
- rc_file = '.fabricrc'
- rc_path = '~/' + rc_file
- expanded_rc_path = os.path.expanduser(rc_path)
- if expanded_rc_path == rc_path and win32:
- from win32com.shell.shell import SHGetSpecialFolderPath
- from win32com.shell.shellcon import CSIDL_PROFILE
- expanded_rc_path = "%s/%s" % (
- SHGetSpecialFolderPath(0, CSIDL_PROFILE),
- rc_file
- )
- return expanded_rc_path
- default_port = '22' # hurr durr
- default_ssh_config_path = os.path.join(os.path.expanduser('~'), '.ssh', 'config')
- # Options/settings which exist both as environment keys and which can be set on
- # the command line, are defined here. When used via `fab` they will be added to
- # the optparse parser, and either way they are added to `env` below (i.e. the
- # 'dest' value becomes the environment key and the value, the env value).
- #
- # Keep in mind that optparse changes hyphens to underscores when automatically
- # deriving the `dest` name, e.g. `--reject-unknown-hosts` becomes
- # `reject_unknown_hosts`.
- #
- # Furthermore, *always* specify some sort of default to avoid ending up with
- # optparse.NO_DEFAULT (currently a two-tuple)! In general, None is a better
- # default than ''.
- #
- # User-facing documentation for these are kept in sites/docs/env.rst.
- env_options = [
- make_option('-a', '--no_agent',
- action='store_true',
- default=False,
- help="don't use the running SSH agent"
- ),
- make_option('-A', '--forward-agent',
- action='store_true',
- default=False,
- help="forward local agent to remote end"
- ),
- make_option('--abort-on-prompts',
- action='store_true',
- default=False,
- help="abort instead of prompting (for password, host, etc)"
- ),
- make_option('-c', '--config',
- dest='rcfile',
- default=_rc_path(),
- metavar='PATH',
- help="specify location of config file to use"
- ),
- make_option('--colorize-errors',
- action='store_true',
- default=False,
- help="Color error output",
- ),
- make_option('-D', '--disable-known-hosts',
- action='store_true',
- default=False,
- help="do not load user known_hosts file"
- ),
- make_option('-e', '--eagerly-disconnect',
- action='store_true',
- default=False,
- help="disconnect from hosts as soon as possible"
- ),
- make_option('-f', '--fabfile',
- default='fabfile',
- metavar='PATH',
- help="python module file to import, e.g. '../other.py'"
- ),
- make_option('-g', '--gateway',
- default=None,
- metavar='HOST',
- help="gateway host to connect through"
- ),
- make_option('--gss-auth',
- action='store_true',
- default=None,
- help="Use GSS-API authentication"
- ),
- make_option('--gss-deleg',
- action='store_true',
- default=None,
- help="Delegate GSS-API client credentials or not"
- ),
- make_option('--gss-kex',
- action='store_true',
- default=None,
- help="Perform GSS-API Key Exchange and user authentication"
- ),
- make_option('--hide',
- metavar='LEVELS',
- help="comma-separated list of output levels to hide"
- ),
- make_option('-H', '--hosts',
- default=[],
- help="comma-separated list of hosts to operate on"
- ),
- make_option('-i',
- action='append',
- dest='key_filename',
- metavar='PATH',
- default=None,
- help="path to SSH private key file. May be repeated."
- ),
- make_option('-k', '--no-keys',
- action='store_true',
- default=False,
- help="don't load private key files from ~/.ssh/"
- ),
- make_option('--keepalive',
- dest='keepalive',
- type=int,
- default=0,
- metavar="N",
- help="enables a keepalive every N seconds"
- ),
- make_option('--linewise',
- action='store_true',
- default=False,
- help="print line-by-line instead of byte-by-byte"
- ),
- make_option('-n', '--connection-attempts',
- type='int',
- metavar='M',
- dest='connection_attempts',
- default=1,
- help="make M attempts to connect before giving up"
- ),
- make_option('--no-pty',
- dest='always_use_pty',
- action='store_false',
- default=True,
- help="do not use pseudo-terminal in run/sudo"
- ),
- make_option('-p', '--password',
- default=None,
- help="password for use with authentication and/or sudo"
- ),
- make_option('-P', '--parallel',
- dest='parallel',
- action='store_true',
- default=False,
- help="default to parallel execution method"
- ),
- make_option('--port',
- default=default_port,
- help="SSH connection port"
- ),
- make_option('-r', '--reject-unknown-hosts',
- action='store_true',
- default=False,
- help="reject unknown hosts"
- ),
- make_option('--sudo-password',
- default=None,
- help="password for use with sudo only",
- ),
- make_option('--system-known-hosts',
- default=None,
- help="load system known_hosts file before reading user known_hosts"
- ),
- make_option('-R', '--roles',
- default=[],
- help="comma-separated list of roles to operate on"
- ),
- make_option('-s', '--shell',
- default='/bin/bash -l -c',
- help="specify a new shell, defaults to '/bin/bash -l -c'"
- ),
- make_option('--show',
- metavar='LEVELS',
- help="comma-separated list of output levels to show"
- ),
- make_option('--skip-bad-hosts',
- action="store_true",
- default=False,
- help="skip over hosts that can't be reached"
- ),
- make_option('--skip-unknown-tasks',
- action="store_true",
- default=False,
- help="skip over unknown tasks"
- ),
- make_option('--ssh-config-path',
- default=default_ssh_config_path,
- metavar='PATH',
- help="Path to SSH config file"
- ),
- make_option('-t', '--timeout',
- type='int',
- default=10,
- metavar="N",
- help="set connection timeout to N seconds"
- ),
- make_option('-T', '--command-timeout',
- dest='command_timeout',
- type='int',
- default=None,
- metavar="N",
- help="set remote command timeout to N seconds"
- ),
- make_option('-u', '--user',
- default=_get_system_username(),
- help="username to use when connecting to remote hosts"
- ),
- make_option('-w', '--warn-only',
- action='store_true',
- default=False,
- help="warn, instead of abort, when commands fail"
- ),
- make_option('-x', '--exclude-hosts',
- default=[],
- metavar='HOSTS',
- help="comma-separated list of hosts to exclude"
- ),
- make_option('-z', '--pool-size',
- dest='pool_size',
- type='int',
- metavar='INT',
- default=0,
- help="number of concurrent processes to use in parallel mode",
- ),
- ]
- #
- # Environment dictionary - actual dictionary object
- #
- # Global environment dict. Currently a catchall for everything: config settings
- # such as global deep/broad mode, host lists, username etc.
- # Most default values are specified in `env_options` above, in the interests of
- # preserving DRY: anything in here is generally not settable via the command
- # line.
- env = _AttributeDict({
- 'abort_exception': None,
- 'again_prompt': 'Sorry, try again.',
- 'all_hosts': [],
- 'combine_stderr': True,
- 'colorize_errors': False,
- 'command': None,
- 'command_prefixes': [],
- 'cwd': '', # Must be empty string, not None, for concatenation purposes
- 'dedupe_hosts': True,
- 'default_port': default_port,
- 'eagerly_disconnect': False,
- 'echo_stdin': True,
- 'effective_roles': [],
- 'exclude_hosts': [],
- 'gateway': None,
- 'gss_auth': None,
- 'gss_deleg': None,
- 'gss_kex': None,
- 'host': None,
- 'host_string': None,
- 'lcwd': '', # Must be empty string, not None, for concatenation purposes
- 'local_user': _get_system_username(),
- 'output_prefix': True,
- 'passwords': {},
- 'path': '',
- 'path_behavior': 'append',
- 'port': default_port,
- 'real_fabfile': None,
- 'remote_interrupt': None,
- 'roles': [],
- 'roledefs': {},
- 'shell_env': {},
- 'skip_bad_hosts': False,
- 'skip_unknown_tasks': False,
- 'ssh_config_path': default_ssh_config_path,
- 'sudo_passwords': {},
- 'ok_ret_codes': [0], # a list of return codes that indicate success
- # -S so sudo accepts passwd via stdin, -p with our known-value prompt for
- # later detection (thus %s -- gets filled with env.sudo_prompt at runtime)
- 'sudo_prefix': "sudo -S -p '%(sudo_prompt)s' ",
- 'sudo_prompt': 'sudo password:',
- 'sudo_user': None,
- 'tasks': [],
- 'prompts': {},
- 'use_exceptions_for': {'network': False},
- 'use_shell': True,
- 'use_ssh_config': False,
- 'user': None,
- 'version': get_version('short')
- })
- # Fill in exceptions settings
- exceptions = ['network']
- exception_dict = {}
- for e in exceptions:
- exception_dict[e] = False
- env.use_exceptions_for = _AliasDict(exception_dict,
- aliases={'everything': exceptions})
- # Add in option defaults
- for option in env_options:
- env[option.dest] = option.default
- #
- # Command dictionary
- #
- # Keys are the command/function names, values are the callables themselves.
- # This is filled in when main() runs.
- commands = {}
- #
- # Host connection dict/cache
- #
- connections = HostConnectionCache()
- def _open_session():
- return connections[env.host_string].get_transport().open_session()
- def default_channel():
- """
- Return a channel object based on ``env.host_string``.
- """
- try:
- chan = _open_session()
- except ssh.SSHException, err:
- if str(err) == 'SSH session not active':
- connections[env.host_string].close()
- del connections[env.host_string]
- chan = _open_session()
- else:
- raise
- chan.settimeout(0.1)
- chan.input_enabled = True
- return chan
- #
- # Output controls
- #
- # Keys are "levels" or "groups" of output, values are always boolean,
- # determining whether output falling into the given group is printed or not
- # printed.
- #
- # By default, everything except 'debug' is printed, as this is what the average
- # user, and new users, are most likely to expect.
- #
- # See docs/usage.rst for details on what these levels mean.
- output = _AliasDict({
- 'status': True,
- 'aborts': True,
- 'warnings': True,
- 'running': True,
- 'stdout': True,
- 'stderr': True,
- 'exceptions': False,
- 'debug': False,
- 'user': True
- }, aliases={
- 'everything': ['warnings', 'running', 'user', 'output', 'exceptions'],
- 'output': ['stdout', 'stderr'],
- 'commands': ['stdout', 'running']
- })
|