mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 21:00:22 -07:00
refactored most binaries
added AnsibleOptionsError removed pulicate parser error class
This commit is contained in:
parent
900b992ba9
commit
cdefeb6d84
8 changed files with 754 additions and 687 deletions
|
@ -40,13 +40,15 @@ def get_config(p, section, key, env_var, default, boolean=False, integer=False,
|
|||
''' return a configuration variable with casting '''
|
||||
value = _get_config(p, section, key, env_var, default)
|
||||
if boolean:
|
||||
return mk_boolean(value)
|
||||
if value and integer:
|
||||
return int(value)
|
||||
if value and floating:
|
||||
return float(value)
|
||||
if value and islist:
|
||||
return [x.strip() for x in value.split(',')]
|
||||
value = mk_boolean(value)
|
||||
if value:
|
||||
if integer:
|
||||
value = int(value)
|
||||
if floating:
|
||||
value = float(value)
|
||||
if islist:
|
||||
if isinstance(value, basestring):
|
||||
value = [x.strip() for x in value.split(',')]
|
||||
return value
|
||||
|
||||
def _get_config(p, section, key, env_var, default):
|
||||
|
@ -104,7 +106,7 @@ DEFAULTS='defaults'
|
|||
|
||||
# configurable things
|
||||
DEFAULT_DEBUG = get_config(p, DEFAULTS, 'debug', 'ANSIBLE_DEBUG', False, boolean=True)
|
||||
DEFAULT_HOST_LIST = shell_expand_path(get_config(p, DEFAULTS, 'inventory', 'ANSIBLE_INVENTORY', get_config(p, DEFAULTS,'hostfile','ANSIBLE_HOSTS', '/etc/ansible/hosts')))
|
||||
DEFAULT_HOST_LIST = shell_expand_path(get_config(p, DEFAULTS, 'hostfile', 'ANSIBLE_HOSTS', get_config(p, DEFAULTS,'inventory','ANSIBLE_INVENTORY', '/etc/ansible/hosts')))
|
||||
DEFAULT_MODULE_PATH = get_config(p, DEFAULTS, 'library', 'ANSIBLE_LIBRARY', None)
|
||||
DEFAULT_ROLES_PATH = shell_expand_path(get_config(p, DEFAULTS, 'roles_path', 'ANSIBLE_ROLES_PATH', '/etc/ansible/roles'))
|
||||
DEFAULT_REMOTE_TMP = get_config(p, DEFAULTS, 'remote_tmp', 'ANSIBLE_REMOTE_TEMP', '$HOME/.ansible/tmp')
|
||||
|
@ -212,6 +214,7 @@ GALAXY_SCMS = get_config(p, 'galaxy', 'scms', 'ANSIBLE_GALAXY
|
|||
DEFAULT_PASSWORD_CHARS = ascii_letters + digits + ".,:-_"
|
||||
|
||||
# non-configurable things
|
||||
MODULE_REQUIRE_ARGS = ['command', 'shell', 'raw', 'script']
|
||||
DEFAULT_BECOME_PASS = None
|
||||
DEFAULT_SUDO_PASS = None
|
||||
DEFAULT_REMOTE_PASS = None
|
||||
|
|
|
@ -140,6 +140,10 @@ class AnsibleError(Exception):
|
|||
|
||||
return error_message
|
||||
|
||||
class AnsibleOptionsError(AnsibleError):
|
||||
''' bad or incomplete options passed '''
|
||||
pass
|
||||
|
||||
class AnsibleParserError(AnsibleError):
|
||||
''' something was detected early that is wrong about a playbook or data file '''
|
||||
pass
|
||||
|
@ -164,6 +168,14 @@ class AnsibleFilterError(AnsibleRuntimeError):
|
|||
''' a templating failure '''
|
||||
pass
|
||||
|
||||
class AnsibleLookupError(AnsibleRuntimeError):
|
||||
''' a lookup failure '''
|
||||
pass
|
||||
|
||||
class AnsibleCallbackError(AnsibleRuntimeError):
|
||||
''' a callback failure '''
|
||||
pass
|
||||
|
||||
class AnsibleUndefinedVariable(AnsibleRuntimeError):
|
||||
''' a templating failure '''
|
||||
pass
|
||||
|
@ -171,7 +183,3 @@ class AnsibleUndefinedVariable(AnsibleRuntimeError):
|
|||
class AnsibleFileNotFound(AnsibleRuntimeError):
|
||||
''' a file missing failure '''
|
||||
pass
|
||||
|
||||
class AnsibleParserError(AnsibleRuntimeError):
|
||||
''' a parser error '''
|
||||
pass
|
||||
|
|
|
@ -36,6 +36,8 @@ class GalaxyRole(object):
|
|||
SUPPORTED_SCMS = set(['git', 'hg'])
|
||||
META_MAIN = os.path.join('meta', 'main.yml')
|
||||
META_INSTALL = os.path.join('meta', '.galaxy_install_info')
|
||||
ROLE_DIRS = ('defaults','files','handlers','meta','tasks','templates','vars')
|
||||
|
||||
|
||||
def __init__(self, galaxy, role_name, role_version=None, role_url=None):
|
||||
|
||||
|
@ -45,13 +47,13 @@ class GalaxyRole(object):
|
|||
self.name = role_name
|
||||
self.meta_data = None
|
||||
self.install_info = None
|
||||
self.role_path = (os.path.join(self.roles_path, self.name))
|
||||
self.path = (os.path.join(galaxy.roles_path, self.name))
|
||||
|
||||
# TODO: possibly parse version and url from role_name
|
||||
self.version = role_version
|
||||
self.url = role_url
|
||||
if self.url is None and '://' in self.name:
|
||||
self.url = self.name
|
||||
if self.url is None:
|
||||
self._spec_parse()
|
||||
|
||||
if C.GALAXY_SCMS:
|
||||
self.scms = self.SUPPORTED_SCMS.intersection(set(C.GALAXY_SCMS))
|
||||
|
@ -62,7 +64,7 @@ class GalaxyRole(object):
|
|||
self.display.warning("No valid SCMs configured for Galaxy.")
|
||||
|
||||
|
||||
def fetch_from_scm_archive(self, scm, role_url, role_version):
|
||||
def fetch_from_scm_archive(self):
|
||||
|
||||
# this can be configured to prevent unwanted SCMS but cannot add new ones unless the code is also updated
|
||||
if scm not in self.scms:
|
||||
|
@ -111,12 +113,21 @@ class GalaxyRole(object):
|
|||
return temp_file.name
|
||||
|
||||
|
||||
def get_metadata(self):
|
||||
"""
|
||||
Returns role metadata
|
||||
"""
|
||||
if self.meta_data is None:
|
||||
self._read_metadata
|
||||
|
||||
def read_metadata(self):
|
||||
return self.meta_data
|
||||
|
||||
|
||||
def _read_metadata(self):
|
||||
"""
|
||||
Reads the metadata as YAML, if the file 'meta/main.yml' exists
|
||||
"""
|
||||
meta_path = os.path.join(self.role_path, self.META_MAIN)
|
||||
meta_path = os.path.join(self.path, self.META_MAIN)
|
||||
if os.path.isfile(meta_path):
|
||||
try:
|
||||
f = open(meta_path, 'r')
|
||||
|
@ -127,15 +138,24 @@ class GalaxyRole(object):
|
|||
finally:
|
||||
f.close()
|
||||
|
||||
return True
|
||||
|
||||
def read_galaxy_install_info(self):
|
||||
def get_galaxy_install_info(self):
|
||||
"""
|
||||
Returns role install info
|
||||
"""
|
||||
if self.install_info is None:
|
||||
self._read_galaxy_isntall_info()
|
||||
|
||||
return self.install_info
|
||||
|
||||
|
||||
def _read_galaxy_install_info(self):
|
||||
"""
|
||||
Returns the YAML data contained in 'meta/.galaxy_install_info',
|
||||
if it exists.
|
||||
"""
|
||||
|
||||
info_path = os.path.join(self.role_path, self.META_INSTALL)
|
||||
info_path = os.path.join(self.path, self.META_INSTALL)
|
||||
if os.path.isfile(info_path):
|
||||
try:
|
||||
f = open(info_path, 'r')
|
||||
|
@ -146,9 +166,7 @@ class GalaxyRole(object):
|
|||
finally:
|
||||
f.close()
|
||||
|
||||
return True
|
||||
|
||||
def write_galaxy_install_info(self):
|
||||
def _write_galaxy_install_info(self):
|
||||
"""
|
||||
Writes a YAML-formatted file to the role's meta/ directory
|
||||
(named .galaxy_install_info) which contains some information
|
||||
|
@ -159,7 +177,7 @@ class GalaxyRole(object):
|
|||
version=self.version,
|
||||
install_date=datetime.datetime.utcnow().strftime("%c"),
|
||||
)
|
||||
info_path = os.path.join(self.role_path, self.META_INSTALL)
|
||||
info_path = os.path.join(self.path, self.META_INSTALL)
|
||||
try:
|
||||
f = open(info_path, 'w+')
|
||||
self.install_info = yaml.safe_dump(info, f)
|
||||
|
@ -178,7 +196,7 @@ class GalaxyRole(object):
|
|||
"""
|
||||
if self.read_metadata():
|
||||
try:
|
||||
rmtree(self.role_path)
|
||||
rmtree(self.path)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
@ -213,7 +231,7 @@ class GalaxyRole(object):
|
|||
self.display.error("failed to download the file.")
|
||||
return False
|
||||
|
||||
def install(self, role_version, role_filename):
|
||||
def install(self, role_filename):
|
||||
# the file is a tar, so open it that way and extract it
|
||||
# to the specified (or default) roles directory
|
||||
|
||||
|
@ -246,10 +264,10 @@ class GalaxyRole(object):
|
|||
# we strip off the top-level directory for all of the files contained within
|
||||
# the tar file here, since the default is 'github_repo-target', and change it
|
||||
# to the specified role's name
|
||||
self.display.display("- extracting %s to %s" % (self.name, self.role_path))
|
||||
self.display.display("- extracting %s to %s" % (self.name, self.path))
|
||||
try:
|
||||
if os.path.exists(self.role_path):
|
||||
if not os.path.isdir(self.role_path):
|
||||
if os.path.exists(self.path):
|
||||
if not os.path.isdir(self.path):
|
||||
self.display.error("the specified roles path exists and is not a directory.")
|
||||
return False
|
||||
elif not getattr(self.options, "force", False):
|
||||
|
@ -258,13 +276,13 @@ class GalaxyRole(object):
|
|||
else:
|
||||
# using --force, remove the old path
|
||||
if not self.remove():
|
||||
self.display.error("%s doesn't appear to contain a role." % self.role_path)
|
||||
self.display.error("%s doesn't appear to contain a role." % self.path)
|
||||
self.display.error(" please remove this directory manually if you really want to put the role here.")
|
||||
return False
|
||||
else:
|
||||
os.makedirs(self.role_path)
|
||||
os.makedirs(self.path)
|
||||
|
||||
# now we do the actual extraction to the role_path
|
||||
# now we do the actual extraction to the path
|
||||
for member in members:
|
||||
# we only extract files, and remove any relative path
|
||||
# bits that might be in the file for security purposes
|
||||
|
@ -276,15 +294,62 @@ class GalaxyRole(object):
|
|||
if part != '..' and '~' not in part and '$' not in part:
|
||||
final_parts.append(part)
|
||||
member.name = os.path.join(*final_parts)
|
||||
role_tar_file.extract(member, self.role_path)
|
||||
role_tar_file.extract(member, self.path)
|
||||
|
||||
# write out the install info file for later use
|
||||
self.version = role_version
|
||||
self.write_galaxy_install_info()
|
||||
self._write_galaxy_install_info()
|
||||
except OSError as e:
|
||||
self.display.error("Could not update files in %s: %s" % (self.role_path, str(e)))
|
||||
self.display.error("Could not update files in %s: %s" % (self.path, str(e)))
|
||||
return False
|
||||
|
||||
# return the parsed yaml metadata
|
||||
self.display.display("- %s was installed successfully" % self.role_name)
|
||||
self.display.display("- %s was installed successfully" % self.name)
|
||||
return True
|
||||
|
||||
def get_spec(self):
|
||||
"""
|
||||
Returns role spec info
|
||||
{
|
||||
'scm': 'git',
|
||||
'src': 'http://git.example.com/repos/repo.git',
|
||||
'version': 'v1.0',
|
||||
'name': 'repo'
|
||||
}
|
||||
"""
|
||||
if self.scm is None and self.url is None:
|
||||
self._read_galaxy_isntall_info()
|
||||
|
||||
return dict(scm=self.scm, src=self.url, version=self.version, role_name=self.name)
|
||||
|
||||
def _spec_parse(self):
|
||||
''' creates separated parts of role spec '''
|
||||
default_role_versions = dict(git='master', hg='tip')
|
||||
|
||||
if not self.url and '://' in self.name:
|
||||
role_spec = self.name.strip()
|
||||
|
||||
if role_spec == "" or role_spec.startswith("#"):
|
||||
return
|
||||
|
||||
tokens = [s.strip() for s in role_spec.split(',')]
|
||||
|
||||
# assume https://github.com URLs are git+https:// URLs and not tarballs unless they end in '.zip'
|
||||
if 'github.com/' in tokens[0] and not tokens[0].startswith("git+") and not tokens[0].endswith('.tar.gz'):
|
||||
tokens[0] = 'git+' + tokens[0]
|
||||
|
||||
if '+' in tokens[0]:
|
||||
(self.scm, self.url) = tokens[0].split('+')
|
||||
else:
|
||||
self.scm = None
|
||||
self.url = tokens[0]
|
||||
|
||||
if len(tokens) >= 2:
|
||||
self.version = tokens[1]
|
||||
|
||||
if len(tokens) == 3:
|
||||
self.name = tokens[2]
|
||||
else:
|
||||
self.name = self._repo_url_to_role_name(tokens[0])
|
||||
|
||||
if self.scm and not self.version:
|
||||
self.version = default_role_versions.get(scm, '')
|
||||
|
|
|
@ -28,6 +28,7 @@ import getpass
|
|||
|
||||
from ansible import __version__
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.utils.unicode import to_bytes
|
||||
|
||||
# FIXME: documentation for methods here, which have mostly been
|
||||
|
@ -40,141 +41,286 @@ class SortedOptParser(optparse.OptionParser):
|
|||
self.option_list.sort(key=operator.methodcaller('get_opt_string'))
|
||||
return optparse.OptionParser.format_help(self, formatter=None)
|
||||
|
||||
def base_parser(usage="", output_opts=False, runas_opts=False, meta_opts=False,
|
||||
async_opts=False, connect_opts=False, subset_opts=False, check_opts=False, diff_opts=False):
|
||||
''' create an options parser for any ansible script '''
|
||||
#TODO: move many cli only functions in this file into the CLI class
|
||||
class CLI(object):
|
||||
''' code behind bin/ansible* programs '''
|
||||
|
||||
parser = SortedOptParser(usage, version=version("%prog"))
|
||||
VALID_ACTIONS = ['No Actions']
|
||||
|
||||
parser.add_option('-u', '--user', default=C.DEFAULT_REMOTE_USER, dest='remote_user',
|
||||
help='connect as this user (default=%s)' % C.DEFAULT_REMOTE_USER)
|
||||
parser.add_option('-v','--verbose', dest='verbosity', default=0, action="count",
|
||||
help="verbose mode (-vvv for more, -vvvv to enable connection debugging)")
|
||||
parser.add_option('-f','--forks', dest='forks', default=C.DEFAULT_FORKS, type='int',
|
||||
help="specify number of parallel processes to use (default=%s)" % C.DEFAULT_FORKS)
|
||||
parser.add_option('-i', '--inventory-file', dest='inventory',
|
||||
help="specify inventory host file (default=%s)" % C.DEFAULT_HOST_LIST,
|
||||
default=C.DEFAULT_HOST_LIST)
|
||||
parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true',
|
||||
help='ask for connection password')
|
||||
parser.add_option('--private-key', default=C.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file',
|
||||
help='use this file to authenticate the connection')
|
||||
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
|
||||
help='ask for vault password')
|
||||
parser.add_option('--vault-password-file', default=C.DEFAULT_VAULT_PASSWORD_FILE,
|
||||
dest='vault_password_file', help="vault password file")
|
||||
parser.add_option('--list-hosts', dest='listhosts', action='store_true',
|
||||
help='outputs a list of matching hosts; does not execute anything else')
|
||||
parser.add_option('-M', '--module-path', dest='module_path',
|
||||
help="specify path(s) to module library (default=%s)" % C.DEFAULT_MODULE_PATH,
|
||||
default=None)
|
||||
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
|
||||
help="set additional variables as key=value or YAML/JSON", default=[])
|
||||
def __init__(self, args, display=None):
|
||||
"""
|
||||
Base init method for all command line programs
|
||||
"""
|
||||
|
||||
if subset_opts:
|
||||
parser.add_option('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset',
|
||||
help='further limit selected hosts to an additional pattern')
|
||||
parser.add_option('-t', '--tags', dest='tags', default='all',
|
||||
help="only run plays and tasks tagged with these values")
|
||||
parser.add_option('--skip-tags', dest='skip_tags',
|
||||
help="only run plays and tasks whose tags do not match these values")
|
||||
self.args = args
|
||||
self.options = None
|
||||
self.parser = None
|
||||
self.action = None
|
||||
|
||||
if output_opts:
|
||||
parser.add_option('-o', '--one-line', dest='one_line', action='store_true',
|
||||
help='condense output')
|
||||
parser.add_option('-t', '--tree', dest='tree', default=None,
|
||||
help='log output to this directory')
|
||||
if display is None:
|
||||
self.display = Display()
|
||||
else:
|
||||
self.display = display
|
||||
|
||||
if runas_opts:
|
||||
# priv user defaults to root later on to enable detecting when this option was given here
|
||||
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
|
||||
help='ask for sudo password (deprecated, use become)')
|
||||
parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true',
|
||||
help='ask for su password (deprecated, use become)')
|
||||
parser.add_option("-s", "--sudo", default=C.DEFAULT_SUDO, action="store_true", dest='sudo',
|
||||
help="run operations with sudo (nopasswd) (deprecated, use become)")
|
||||
parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None,
|
||||
help='desired sudo user (default=root) (deprecated, use become)')
|
||||
parser.add_option('-S', '--su', default=C.DEFAULT_SU, action='store_true',
|
||||
help='run operations with su (deprecated, use become)')
|
||||
parser.add_option('-R', '--su-user', default=None,
|
||||
help='run operations with su as this user (default=%s) (deprecated, use become)' % C.DEFAULT_SU_USER)
|
||||
def set_action(self):
|
||||
"""
|
||||
Get the action the user wants to execute from the sys argv list.
|
||||
"""
|
||||
for i in range(0,len(self.args)):
|
||||
arg = self.args[i]
|
||||
if arg in self.VALID_ACTIONS:
|
||||
self.action = arg
|
||||
del self.args[i]
|
||||
break
|
||||
|
||||
# consolidated privilege escalation (become)
|
||||
parser.add_option("-b", "--become", default=C.DEFAULT_BECOME, action="store_true", dest='become',
|
||||
help="run operations with become (nopasswd implied)")
|
||||
parser.add_option('--become-method', dest='become_method', default=C.DEFAULT_BECOME_METHOD, type='string',
|
||||
help="privilege escalation method to use (default=%s), valid choices: [ %s ]" % (C.DEFAULT_BECOME_METHOD, ' | '.join(C.BECOME_METHODS)))
|
||||
parser.add_option('--become-user', default=None, dest='become_user', type='string',
|
||||
help='run operations as this user (default=%s)' % C.DEFAULT_BECOME_USER)
|
||||
parser.add_option('--ask-become-pass', default=False, dest='become_ask_pass', action='store_true',
|
||||
help='ask for privilege escalation password')
|
||||
if not self.action:
|
||||
self.parser.print_help()
|
||||
raise AnsibleError("Missing required action")
|
||||
|
||||
def execute(self):
|
||||
"""
|
||||
Actually runs a child defined method using the execute_<action> pattern
|
||||
"""
|
||||
fn = getattr(self, "execute_%s" % self.action)
|
||||
fn()
|
||||
|
||||
def parse(self):
|
||||
raise Exception("Need to implement!")
|
||||
|
||||
def run(self):
|
||||
raise Exception("Need to implement!")
|
||||
|
||||
@staticmethod
|
||||
def ask_vault_passwords(ask_vault_pass=False, ask_new_vault_pass=False, confirm_vault=False, confirm_new=False):
|
||||
|
||||
vault_pass = None
|
||||
new_vault_pass = None
|
||||
|
||||
if ask_vault_pass:
|
||||
vault_pass = getpass.getpass(prompt="Vault password: ")
|
||||
|
||||
if ask_vault_pass and confirm_vault:
|
||||
vault_pass2 = getpass.getpass(prompt="Confirm Vault password: ")
|
||||
if vault_pass != vault_pass2:
|
||||
raise errors.AnsibleError("Passwords do not match")
|
||||
|
||||
if ask_new_vault_pass:
|
||||
new_vault_pass = getpass.getpass(prompt="New Vault password: ")
|
||||
|
||||
if ask_new_vault_pass and confirm_new:
|
||||
new_vault_pass2 = getpass.getpass(prompt="Confirm New Vault password: ")
|
||||
if new_vault_pass != new_vault_pass2:
|
||||
raise errors.AnsibleError("Passwords do not match")
|
||||
|
||||
# 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()
|
||||
|
||||
return vault_pass, new_vault_pass
|
||||
|
||||
|
||||
if connect_opts:
|
||||
parser.add_option('-c', '--connection', dest='connection', default=C.DEFAULT_TRANSPORT,
|
||||
help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT)
|
||||
parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', dest='timeout',
|
||||
help="override the connection timeout in seconds (default=%s)" % C.DEFAULT_TIMEOUT)
|
||||
def ask_passwords(self):
|
||||
|
||||
op = self.options
|
||||
sshpass = None
|
||||
becomepass = None
|
||||
become_prompt = ''
|
||||
|
||||
if op.ask_pass:
|
||||
sshpass = getpass.getpass(prompt="SSH password: ")
|
||||
become_prompt = "%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: " % op.become_method.upper()
|
||||
|
||||
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)
|
||||
|
||||
return (sshpass, becomepass)
|
||||
|
||||
|
||||
if async_opts:
|
||||
parser.add_option('-P', '--poll', default=C.DEFAULT_POLL_INTERVAL, type='int',
|
||||
dest='poll_interval',
|
||||
help="set the poll interval if using -B (default=%s)" % C.DEFAULT_POLL_INTERVAL)
|
||||
parser.add_option('-B', '--background', dest='seconds', type='int', default=0,
|
||||
help='run asynchronously, failing after X seconds (default=N/A)')
|
||||
def normalize_become_options(self):
|
||||
''' this keeps backwards compatibility with sudo/su self.options '''
|
||||
self.options.become_ask_pass = self.options.become_ask_pass or self.options.ask_sudo_pass or self.options.ask_su_pass or C.DEFAULT_BECOME_ASK_PASS
|
||||
self.options.become_user = self.options.become_user or self.options.sudo_user or self.options.su_user or C.DEFAULT_BECOME_USER
|
||||
|
||||
if check_opts:
|
||||
parser.add_option("-C", "--check", default=False, dest='check', action='store_true',
|
||||
help="don't make any changes; instead, try to predict some of the changes that may occur")
|
||||
parser.add_option('--syntax-check', dest='syntax', action='store_true',
|
||||
help="perform a syntax check on the playbook, but do not execute it")
|
||||
|
||||
if diff_opts:
|
||||
parser.add_option("-D", "--diff", default=False, dest='diff', action='store_true',
|
||||
help="when changing (small) files and templates, show the differences in those files; works great with --check"
|
||||
)
|
||||
|
||||
if meta_opts:
|
||||
parser.add_option('--force-handlers', dest='force_handlers', action='store_true',
|
||||
help="run handlers even if a task fails")
|
||||
parser.add_option('--flush-cache', dest='flush_cache', action='store_true',
|
||||
help="clear the fact cache")
|
||||
|
||||
return parser
|
||||
|
||||
def version(prog):
|
||||
result = "{0} {1}".format(prog, __version__)
|
||||
gitinfo = _gitinfo()
|
||||
if gitinfo:
|
||||
result = result + " {0}".format(gitinfo)
|
||||
result = result + "\n configured module search path = %s" % C.DEFAULT_MODULE_PATH
|
||||
return result
|
||||
|
||||
def version_info(gitinfo=False):
|
||||
if gitinfo:
|
||||
# expensive call, user with care
|
||||
ansible_version_string = version('')
|
||||
else:
|
||||
ansible_version_string = __version__
|
||||
ansible_version = ansible_version_string.split()[0]
|
||||
ansible_versions = ansible_version.split('.')
|
||||
for counter in range(len(ansible_versions)):
|
||||
if ansible_versions[counter] == "":
|
||||
ansible_versions[counter] = 0
|
||||
try:
|
||||
ansible_versions[counter] = int(ansible_versions[counter])
|
||||
except:
|
||||
if self.options.become:
|
||||
pass
|
||||
if len(ansible_versions) < 3:
|
||||
for counter in range(len(ansible_versions), 3):
|
||||
ansible_versions.append(0)
|
||||
return {'string': ansible_version_string.strip(),
|
||||
'full': ansible_version,
|
||||
'major': ansible_versions[0],
|
||||
'minor': ansible_versions[1],
|
||||
'revision': ansible_versions[2]}
|
||||
elif self.options.sudo:
|
||||
self.options.become = True
|
||||
self.options.become_method = 'sudo'
|
||||
elif self.options.su:
|
||||
self.options.become = True
|
||||
options.become_method = 'su'
|
||||
|
||||
|
||||
def validate_conflicts(self):
|
||||
|
||||
op = self.options
|
||||
|
||||
# Check for vault related conflicts
|
||||
if (op.ask_vault_pass and op.vault_password_file):
|
||||
self.parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
||||
|
||||
|
||||
# Check for privilege escalation conflicts
|
||||
if (op.su or op.su_user or op.ask_su_pass) and \
|
||||
(op.sudo or op.sudo_user or op.ask_sudo_pass) or \
|
||||
(op.su or op.su_user or op.ask_su_pass) and \
|
||||
(op.become or op.become_user or op.become_ask_pass) or \
|
||||
(op.sudo or op.sudo_user or op.ask_sudo_pass) and \
|
||||
(op.become or op.become_user or op.become_ask_pass):
|
||||
|
||||
self.parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
||||
"and su arguments ('-su', '--su-user', and '--ask-su-pass') "
|
||||
"and become arguments ('--become', '--become-user', and '--ask-become-pass')"
|
||||
" are exclusive of each other")
|
||||
|
||||
@staticmethod
|
||||
def base_parser(usage="", output_opts=False, runas_opts=False, meta_opts=False,
|
||||
async_opts=False, connect_opts=False, subset_opts=False, check_opts=False, diff_opts=False):
|
||||
''' create an options parser for any ansible script '''
|
||||
|
||||
parser = SortedOptParser(usage, version=CLI.version("%prog"))
|
||||
|
||||
parser.add_option('-u', '--user', default=C.DEFAULT_REMOTE_USER, dest='remote_user',
|
||||
help='connect as this user (default=%s)' % C.DEFAULT_REMOTE_USER)
|
||||
parser.add_option('-v','--verbose', dest='verbosity', default=0, action="count",
|
||||
help="verbose mode (-vvv for more, -vvvv to enable connection debugging)")
|
||||
parser.add_option('-f','--forks', dest='forks', default=C.DEFAULT_FORKS, type='int',
|
||||
help="specify number of parallel processes to use (default=%s)" % C.DEFAULT_FORKS)
|
||||
parser.add_option('-i', '--inventory-file', dest='inventory',
|
||||
help="specify inventory host file (default=%s)" % C.DEFAULT_HOST_LIST,
|
||||
default=C.DEFAULT_HOST_LIST)
|
||||
parser.add_option('-k', '--ask-pass', default=False, dest='ask_pass', action='store_true',
|
||||
help='ask for connection password')
|
||||
parser.add_option('--private-key', default=C.DEFAULT_PRIVATE_KEY_FILE, dest='private_key_file',
|
||||
help='use this file to authenticate the connection')
|
||||
parser.add_option('--ask-vault-pass', default=False, dest='ask_vault_pass', action='store_true',
|
||||
help='ask for vault password')
|
||||
parser.add_option('--vault-password-file', default=C.DEFAULT_VAULT_PASSWORD_FILE,
|
||||
dest='vault_password_file', help="vault password file")
|
||||
parser.add_option('--list-hosts', dest='listhosts', action='store_true',
|
||||
help='outputs a list of matching hosts; does not execute anything else')
|
||||
parser.add_option('-M', '--module-path', dest='module_path',
|
||||
help="specify path(s) to module library (default=%s)" % C.DEFAULT_MODULE_PATH,
|
||||
default=None)
|
||||
parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
|
||||
help="set additional variables as key=value or YAML/JSON", default=[])
|
||||
|
||||
if subset_opts:
|
||||
parser.add_option('-l', '--limit', default=C.DEFAULT_SUBSET, dest='subset',
|
||||
help='further limit selected hosts to an additional pattern')
|
||||
parser.add_option('-t', '--tags', dest='tags', default='all',
|
||||
help="only run plays and tasks tagged with these values")
|
||||
parser.add_option('--skip-tags', dest='skip_tags',
|
||||
help="only run plays and tasks whose tags do not match these values")
|
||||
|
||||
if output_opts:
|
||||
parser.add_option('-o', '--one-line', dest='one_line', action='store_true',
|
||||
help='condense output')
|
||||
parser.add_option('-t', '--tree', dest='tree', default=None,
|
||||
help='log output to this directory')
|
||||
|
||||
if runas_opts:
|
||||
# priv user defaults to root later on to enable detecting when this option was given here
|
||||
parser.add_option('-K', '--ask-sudo-pass', default=False, dest='ask_sudo_pass', action='store_true',
|
||||
help='ask for sudo password (deprecated, use become)')
|
||||
parser.add_option('--ask-su-pass', default=False, dest='ask_su_pass', action='store_true',
|
||||
help='ask for su password (deprecated, use become)')
|
||||
parser.add_option("-s", "--sudo", default=C.DEFAULT_SUDO, action="store_true", dest='sudo',
|
||||
help="run operations with sudo (nopasswd) (deprecated, use become)")
|
||||
parser.add_option('-U', '--sudo-user', dest='sudo_user', default=None,
|
||||
help='desired sudo user (default=root) (deprecated, use become)')
|
||||
parser.add_option('-S', '--su', default=C.DEFAULT_SU, action='store_true',
|
||||
help='run operations with su (deprecated, use become)')
|
||||
parser.add_option('-R', '--su-user', default=None,
|
||||
help='run operations with su as this user (default=%s) (deprecated, use become)' % C.DEFAULT_SU_USER)
|
||||
|
||||
# consolidated privilege escalation (become)
|
||||
parser.add_option("-b", "--become", default=C.DEFAULT_BECOME, action="store_true", dest='become',
|
||||
help="run operations with become (nopasswd implied)")
|
||||
parser.add_option('--become-method', dest='become_method', default=C.DEFAULT_BECOME_METHOD, type='string',
|
||||
help="privilege escalation method to use (default=%s), valid choices: [ %s ]" % (C.DEFAULT_BECOME_METHOD, ' | '.join(C.BECOME_METHODS)))
|
||||
parser.add_option('--become-user', default=None, dest='become_user', type='string',
|
||||
help='run operations as this user (default=%s)' % C.DEFAULT_BECOME_USER)
|
||||
parser.add_option('--ask-become-pass', default=False, dest='become_ask_pass', action='store_true',
|
||||
help='ask for privilege escalation password')
|
||||
|
||||
|
||||
if connect_opts:
|
||||
parser.add_option('-c', '--connection', dest='connection', default=C.DEFAULT_TRANSPORT,
|
||||
help="connection type to use (default=%s)" % C.DEFAULT_TRANSPORT)
|
||||
parser.add_option('-T', '--timeout', default=C.DEFAULT_TIMEOUT, type='int', dest='timeout',
|
||||
help="override the connection timeout in seconds (default=%s)" % C.DEFAULT_TIMEOUT)
|
||||
|
||||
|
||||
if async_opts:
|
||||
parser.add_option('-P', '--poll', default=C.DEFAULT_POLL_INTERVAL, type='int',
|
||||
dest='poll_interval',
|
||||
help="set the poll interval if using -B (default=%s)" % C.DEFAULT_POLL_INTERVAL)
|
||||
parser.add_option('-B', '--background', dest='seconds', type='int', default=0,
|
||||
help='run asynchronously, failing after X seconds (default=N/A)')
|
||||
|
||||
if check_opts:
|
||||
parser.add_option("-C", "--check", default=False, dest='check', action='store_true',
|
||||
help="don't make any changes; instead, try to predict some of the changes that may occur")
|
||||
parser.add_option('--syntax-check', dest='syntax', action='store_true',
|
||||
help="perform a syntax check on the playbook, but do not execute it")
|
||||
|
||||
if diff_opts:
|
||||
parser.add_option("-D", "--diff", default=False, dest='diff', action='store_true',
|
||||
help="when changing (small) files and templates, show the differences in those files; works great with --check"
|
||||
)
|
||||
|
||||
if meta_opts:
|
||||
parser.add_option('--force-handlers', dest='force_handlers', action='store_true',
|
||||
help="run handlers even if a task fails")
|
||||
parser.add_option('--flush-cache', dest='flush_cache', action='store_true',
|
||||
help="clear the fact cache")
|
||||
|
||||
return parser
|
||||
|
||||
@staticmethod
|
||||
def version(prog):
|
||||
result = "{0} {1}".format(prog, __version__)
|
||||
gitinfo = _gitinfo()
|
||||
if gitinfo:
|
||||
result = result + " {0}".format(gitinfo)
|
||||
result = result + "\n configured module search path = %s" % C.DEFAULT_MODULE_PATH
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def version_info(gitinfo=False):
|
||||
if gitinfo:
|
||||
# expensive call, user with care
|
||||
ansible_version_string = version('')
|
||||
else:
|
||||
ansible_version_string = __version__
|
||||
ansible_version = ansible_version_string.split()[0]
|
||||
ansible_versions = ansible_version.split('.')
|
||||
for counter in range(len(ansible_versions)):
|
||||
if ansible_versions[counter] == "":
|
||||
ansible_versions[counter] = 0
|
||||
try:
|
||||
ansible_versions[counter] = int(ansible_versions[counter])
|
||||
except:
|
||||
pass
|
||||
if len(ansible_versions) < 3:
|
||||
for counter in range(len(ansible_versions), 3):
|
||||
ansible_versions.append(0)
|
||||
return {'string': ansible_version_string.strip(),
|
||||
'full': ansible_version,
|
||||
'major': ansible_versions[0],
|
||||
'minor': ansible_versions[1],
|
||||
'revision': ansible_versions[2]}
|
||||
|
||||
def _git_repo_info(repo_path):
|
||||
''' returns a string containing git branch, commit id and commit date '''
|
||||
|
@ -234,69 +380,3 @@ def _gitinfo():
|
|||
result += "\n {0}: {1}".format(submodule_path, submodule_info)
|
||||
f.close()
|
||||
return result
|
||||
|
||||
|
||||
def ask_passwords(options):
|
||||
sshpass = None
|
||||
becomepass = None
|
||||
vaultpass = None
|
||||
become_prompt = ''
|
||||
|
||||
if options.ask_pass:
|
||||
sshpass = getpass.getpass(prompt="SSH password: ")
|
||||
become_prompt = "%s password[defaults to SSH password]: " % options.become_method.upper()
|
||||
if sshpass:
|
||||
sshpass = to_bytes(sshpass, errors='strict', nonstring='simplerepr')
|
||||
else:
|
||||
become_prompt = "%s password: " % options.become_method.upper()
|
||||
|
||||
if options.become_ask_pass:
|
||||
becomepass = getpass.getpass(prompt=become_prompt)
|
||||
if options.ask_pass and becomepass == '':
|
||||
becomepass = sshpass
|
||||
if becomepass:
|
||||
becomepass = to_bytes(becomepass)
|
||||
|
||||
if options.ask_vault_pass:
|
||||
vaultpass = getpass.getpass(prompt="Vault password: ")
|
||||
if vaultpass:
|
||||
vaultpass = to_bytes(vaultpass, errors='strict', nonstring='simplerepr').strip()
|
||||
|
||||
return (sshpass, becomepass, vaultpass)
|
||||
|
||||
|
||||
def normalize_become_options(options):
|
||||
''' this keeps backwards compatibility with sudo/su options '''
|
||||
options.become_ask_pass = options.become_ask_pass or options.ask_sudo_pass or options.ask_su_pass or C.DEFAULT_BECOME_ASK_PASS
|
||||
options.become_user = options.become_user or options.sudo_user or options.su_user or C.DEFAULT_BECOME_USER
|
||||
|
||||
if options.become:
|
||||
pass
|
||||
elif options.sudo:
|
||||
options.become = True
|
||||
options.become_method = 'sudo'
|
||||
elif options.su:
|
||||
options.become = True
|
||||
options.become_method = 'su'
|
||||
|
||||
|
||||
def validate_conflicts(parser, options):
|
||||
|
||||
# Check for vault related conflicts
|
||||
if (options.ask_vault_pass and options.vault_password_file):
|
||||
parser.error("--ask-vault-pass and --vault-password-file are mutually exclusive")
|
||||
|
||||
|
||||
# Check for privilege escalation conflicts
|
||||
if (options.su or options.su_user or options.ask_su_pass) and \
|
||||
(options.sudo or options.sudo_user or options.ask_sudo_pass) or \
|
||||
(options.su or options.su_user or options.ask_su_pass) and \
|
||||
(options.become or options.become_user or options.become_ask_pass) or \
|
||||
(options.sudo or options.sudo_user or options.ask_sudo_pass) and \
|
||||
(options.become or options.become_user or options.become_ask_pass):
|
||||
|
||||
parser.error("Sudo arguments ('--sudo', '--sudo-user', and '--ask-sudo-pass') "
|
||||
"and su arguments ('-su', '--su-user', and '--ask-su-pass') "
|
||||
"and become arguments ('--become', '--become-user', and '--ask-become-pass')"
|
||||
" are exclusive of each other")
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue