2014-11-14 22:14:08 +00:00
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Make coding more python3-ish
from __future__ import ( absolute_import , division , print_function )
__metaclass__ = type
import operator
import optparse
import os
2015-05-01 02:54:38 +00:00
import sys
2014-11-14 22:14:08 +00:00
import time
import yaml
2015-05-01 02:54:38 +00:00
import re
2015-03-21 05:19:07 +00:00
import getpass
2015-05-01 02:54:38 +00:00
import subprocess
2014-11-14 22:14:08 +00:00
from ansible import __version__
from ansible import constants as C
2015-06-02 15:02:40 +00:00
from ansible . errors import AnsibleError , AnsibleOptionsError
2015-03-21 05:19:07 +00:00
from ansible . utils . unicode import to_bytes
2015-06-02 19:11:16 +00:00
from ansible . utils . display import Display
2015-07-11 18:24:45 +00:00
from ansible . utils . path import is_executable
2014-11-14 22:14:08 +00:00
class SortedOptParser ( optparse . OptionParser ) :
''' Optparser which sorts the options by opt before outputting --help '''
2015-05-01 01:22:23 +00:00
#FIXME: epilog parsing: OptionParser.format_epilog = lambda self, formatter: self.epilog
def format_help ( self , formatter = None , epilog = None ) :
2014-11-14 22:14:08 +00:00
self . option_list . sort ( key = operator . methodcaller ( ' get_opt_string ' ) )
return optparse . OptionParser . format_help ( self , formatter = None )
2015-04-27 11:31:41 +00:00
class CLI ( object ) :
''' code behind bin/ansible* programs '''
2014-11-14 22:14:08 +00:00
2015-04-27 11:31:41 +00:00
VALID_ACTIONS = [ ' No Actions ' ]
2015-05-01 02:54:38 +00:00
_ITALIC = re . compile ( r " I \ (([^)]+) \ ) " )
_BOLD = re . compile ( r " B \ (([^)]+) \ ) " )
_MODULE = re . compile ( r " M \ (([^)]+) \ ) " )
_URL = re . compile ( r " U \ (([^)]+) \ ) " )
_CONST = re . compile ( r " C \ (([^)]+) \ ) " )
PAGER = ' less '
LESS_OPTS = ' FRSX ' # -F (quit-if-one-screen) -R (allow raw ansi control chars)
# -S (chop long lines) -X (disable termcap init and de-init)
2015-04-27 11:31:41 +00:00
def __init__ ( self , args , display = None ) :
"""
Base init method for all command line programs
"""
self . args = args
self . options = None
self . parser = None
self . action = None
if display is None :
self . display = Display ( )
else :
self . display = display
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
if not self . action :
2015-05-01 01:22:23 +00:00
raise AnsibleOptionsError ( " Missing required action " )
2015-04-27 11:31:41 +00:00
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 ) :
2015-07-04 14:23:30 +00:00
if self . options . verbosity > 0 :
self . display . display ( " Using %s as config file " % C . CONFIG_FILE )
2015-04-27 11:31:41 +00:00
@staticmethod
def ask_vault_passwords ( ask_vault_pass = False , ask_new_vault_pass = False , confirm_vault = False , confirm_new = False ) :
2015-04-30 22:43:53 +00:00
''' prompt for vault password and/or password change '''
2015-04-27 11:31:41 +00:00
vault_pass = None
new_vault_pass = None
2015-07-01 09:21:46 +00:00
try :
if ask_vault_pass :
vault_pass = getpass . getpass ( prompt = " Vault password: " )
2015-04-27 11:31:41 +00:00
2015-07-01 09:21:46 +00:00
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 " )
2015-04-27 11:31:41 +00:00
2015-07-01 09:21:46 +00:00
if ask_new_vault_pass :
new_vault_pass = getpass . getpass ( prompt = " New Vault password: " )
2015-04-27 11:31:41 +00:00
2015-07-01 09:21:46 +00:00
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 " )
except EOFError :
pass
2015-04-27 11:31:41 +00:00
# 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
def ask_passwords ( self ) :
2015-04-30 22:43:53 +00:00
''' prompt for connection and become passwords if needed '''
2015-04-27 11:31:41 +00:00
op = self . options
sshpass = None
becomepass = None
become_prompt = ' '
2015-07-01 09:21:46 +00:00
try :
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 )
except EOFError :
pass
2015-04-27 11:31:41 +00:00
return ( sshpass , becomepass )
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 self . options . become :
2014-11-14 22:14:08 +00:00
pass
2015-04-27 11:31:41 +00:00
elif self . options . sudo :
self . options . become = True
self . options . become_method = ' sudo '
elif self . options . su :
self . options . become = True
2015-07-01 04:16:01 +00:00
self . options . become_method = ' su '
2015-04-27 11:31:41 +00:00
2015-06-09 21:24:06 +00:00
def validate_conflicts ( self , vault_opts = False , runas_opts = False ) :
2015-04-30 22:43:53 +00:00
''' check for conflicting options '''
2015-04-27 11:31:41 +00:00
op = self . options
2015-06-09 21:24:06 +00:00
if vault_opts :
# 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 " )
2015-04-27 11:31:41 +00:00
2015-06-09 21:24:06 +00:00
if runas_opts :
# 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 " )
2015-04-27 11:31:41 +00:00
2015-07-01 16:38:56 +00:00
@staticmethod
def expand_tilde ( option , opt , value , parser ) :
setattr ( parser . values , option . dest , os . path . expanduser ( value ) )
2015-04-27 11:31:41 +00:00
@staticmethod
2015-05-01 01:22:23 +00:00
def base_parser ( usage = " " , output_opts = False , runas_opts = False , meta_opts = False , runtask_opts = False , vault_opts = False ,
2015-06-09 21:24:06 +00:00
async_opts = False , connect_opts = False , subset_opts = False , check_opts = False , diff_opts = False , epilog = None , fork_opts = False ) :
2015-04-30 22:43:53 +00:00
''' create an options parser for most ansible scripts '''
2015-04-27 11:31:41 +00:00
2015-05-01 01:22:23 +00:00
#FIXME: implemente epilog parsing
#OptionParser.format_epilog = lambda self, formatter: self.epilog
2015-04-27 11:31:41 +00:00
2015-05-01 01:22:23 +00:00
# base opts
parser = SortedOptParser ( usage , version = CLI . version ( " % prog " ) )
2015-04-27 11:31:41 +00:00
parser . add_option ( ' -v ' , ' --verbose ' , dest = ' verbosity ' , default = 0 , action = " count " ,
help = " verbose mode (-vvv for more, -vvvv to enable connection debugging) " )
2015-05-01 01:22:23 +00:00
if runtask_opts :
parser . add_option ( ' -i ' , ' --inventory-file ' , dest = ' inventory ' ,
help = " specify inventory host file (default= %s ) " % C . DEFAULT_HOST_LIST ,
2015-07-01 16:38:56 +00:00
default = C . DEFAULT_HOST_LIST , action = " callback " , callback = CLI . expand_tilde , type = str )
2015-05-01 01:22:23 +00:00
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 ' ,
2015-07-01 16:38:56 +00:00
help = " specify path(s) to module library (default= %s ) " % C . DEFAULT_MODULE_PATH , default = None ,
action = " callback " , callback = CLI . expand_tilde , type = str )
2015-05-01 01:22:23 +00:00
parser . add_option ( ' -e ' , ' --extra-vars ' , dest = " extra_vars " , action = " append " ,
help = " set additional variables as key=value or YAML/JSON " , default = [ ] )
2015-06-09 21:24:06 +00:00
if fork_opts :
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 )
2015-06-09 21:35:19 +00:00
parser . add_option ( ' -l ' , ' --limit ' , default = C . DEFAULT_SUBSET , dest = ' subset ' ,
help = ' further limit selected hosts to an additional pattern ' )
2015-06-09 21:24:06 +00:00
2015-05-01 01:22:23 +00:00
if vault_opts :
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 ,
2015-07-01 16:38:56 +00:00
dest = ' vault_password_file ' , help = " vault password file " , action = " callback " ,
callback = CLI . expand_tilde , type = str )
2015-04-27 11:31:41 +00:00
if subset_opts :
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
2015-05-04 20:44:54 +00:00
parser . add_option ( ' -K ' , ' --ask-sudo-pass ' , default = C . DEFAULT_ASK_SUDO_PASS , dest = ' ask_sudo_pass ' , action = ' store_true ' ,
2015-04-27 11:31:41 +00:00
help = ' ask for sudo password (deprecated, use become) ' )
2015-05-04 20:44:54 +00:00
parser . add_option ( ' --ask-su-pass ' , default = C . DEFAULT_ASK_SU_PASS , dest = ' ask_su_pass ' , action = ' store_true ' ,
2015-04-27 11:31:41 +00:00
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 :
2015-05-01 01:22:23 +00:00
parser . add_option ( ' -k ' , ' --ask-pass ' , default = False , dest = ' ask_pass ' , action = ' store_true ' ,
help = ' ask for connection password ' )
2015-06-09 21:24:06 +00:00
parser . add_option ( ' --private-key ' , ' --key-file ' , default = C . DEFAULT_PRIVATE_KEY_FILE , dest = ' private_key_file ' ,
2015-05-01 01:22:23 +00:00
help = ' use this file to authenticate the connection ' )
parser . add_option ( ' -u ' , ' --user ' , default = C . DEFAULT_REMOTE_USER , dest = ' remote_user ' ,
help = ' connect as this user (default= %s ) ' % C . DEFAULT_REMOTE_USER )
2015-04-27 11:31:41 +00:00
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 :
2015-07-10 05:53:59 +00:00
parser . add_option ( ' --force-handlers ' , default = C . DEFAULT_FORCE_HANDLERS , dest = ' force_handlers ' , action = ' store_true ' ,
2015-04-27 11:31:41 +00:00
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 ) :
2015-04-30 22:43:53 +00:00
''' return ansible version '''
2015-04-27 11:31:41 +00:00
result = " {0} {1} " . format ( prog , __version__ )
2015-05-01 01:22:23 +00:00
gitinfo = CLI . _gitinfo ( )
2015-04-27 11:31:41 +00:00
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 ) :
2015-04-30 22:43:53 +00:00
''' return full ansible version info '''
2015-04-27 11:31:41 +00:00
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 ] }
2014-11-14 22:14:08 +00:00
2015-04-30 22:43:53 +00:00
@staticmethod
def _git_repo_info ( repo_path ) :
''' returns a string containing git branch, commit id and commit date '''
result = None
if os . path . exists ( repo_path ) :
# Check if the .git is a file. If it is a file, it means that we are in a submodule structure.
if os . path . isfile ( repo_path ) :
try :
gitdir = yaml . safe_load ( open ( repo_path ) ) . get ( ' gitdir ' )
# There is a possibility the .git file to have an absolute path.
if os . path . isabs ( gitdir ) :
repo_path = gitdir
else :
repo_path = os . path . join ( repo_path [ : - 4 ] , gitdir )
except ( IOError , AttributeError ) :
return ' '
f = open ( os . path . join ( repo_path , " HEAD " ) )
branch = f . readline ( ) . split ( ' / ' ) [ - 1 ] . rstrip ( " \n " )
2014-11-14 22:14:08 +00:00
f . close ( )
2015-04-30 22:43:53 +00:00
branch_path = os . path . join ( repo_path , " refs " , " heads " , branch )
if os . path . exists ( branch_path ) :
f = open ( branch_path )
commit = f . readline ( ) [ : 10 ]
f . close ( )
else :
# detached HEAD
commit = branch [ : 10 ]
branch = ' detached HEAD '
branch_path = os . path . join ( repo_path , " HEAD " )
date = time . localtime ( os . stat ( branch_path ) . st_mtime )
if time . daylight == 0 :
offset = time . timezone
else :
offset = time . altzone
result = " ( {0} {1} ) last updated {2} (GMT {3:+04d} ) " . format ( branch , commit ,
time . strftime ( " % Y/ % m/ %d % H: % M: % S " , date ) , int ( offset / - 36 ) )
2014-11-14 22:14:08 +00:00
else :
2015-04-30 22:43:53 +00:00
result = ' '
return result
@staticmethod
def _gitinfo ( ) :
basedir = os . path . join ( os . path . dirname ( __file__ ) , ' .. ' , ' .. ' , ' .. ' )
repo_path = os . path . join ( basedir , ' .git ' )
2015-05-01 01:22:23 +00:00
result = CLI . _git_repo_info ( repo_path )
2015-04-30 22:43:53 +00:00
submodules = os . path . join ( basedir , ' .gitmodules ' )
if not os . path . exists ( submodules ) :
return result
f = open ( submodules )
for line in f :
tokens = line . strip ( ) . split ( ' ' )
if tokens [ 0 ] == ' path ' :
submodule_path = tokens [ 2 ]
2015-05-01 01:22:23 +00:00
submodule_info = CLI . _git_repo_info ( os . path . join ( basedir , submodule_path , ' .git ' ) )
2015-04-30 22:43:53 +00:00
if not submodule_info :
submodule_info = ' not found - use git submodule update --init ' + submodule_path
result + = " \n {0} : {1} " . format ( submodule_path , submodule_info )
f . close ( )
return result
2015-05-01 02:54:38 +00:00
@staticmethod
def pager ( text ) :
''' find reasonable way to display text '''
# this is a much simpler form of what is in pydoc.py
if not sys . stdout . isatty ( ) :
2015-06-16 13:19:37 +00:00
print ( text )
2015-05-01 02:54:38 +00:00
elif ' PAGER ' in os . environ :
if sys . platform == ' win32 ' :
2015-06-16 13:19:37 +00:00
print ( text )
2015-05-01 02:54:38 +00:00
else :
CLI . pager_pipe ( text , os . environ [ ' PAGER ' ] )
elif subprocess . call ( ' (less --version) 2> /dev/null ' , shell = True ) == 0 :
CLI . pager_pipe ( text , ' less ' )
else :
2015-06-16 13:19:37 +00:00
print ( text )
2015-05-01 02:54:38 +00:00
@staticmethod
def pager_pipe ( text , cmd ) :
''' pipe text through a pager '''
if ' LESS ' not in os . environ :
2015-05-12 16:26:20 +00:00
os . environ [ ' LESS ' ] = CLI . LESS_OPTS
2015-05-01 02:54:38 +00:00
try :
cmd = subprocess . Popen ( cmd , shell = True , stdin = subprocess . PIPE , stdout = sys . stdout )
cmd . communicate ( input = text )
except IOError :
pass
except KeyboardInterrupt :
pass
@classmethod
def tty_ify ( self , text ) :
t = self . _ITALIC . sub ( " ` " + r " \ 1 " + " ' " , text ) # I(word) => `word'
t = self . _BOLD . sub ( " * " + r " \ 1 " + " * " , t ) # B(word) => *word*
t = self . _MODULE . sub ( " [ " + r " \ 1 " + " ] " , t ) # M(word) => [word]
t = self . _URL . sub ( r " \ 1 " , t ) # U(word) => word
t = self . _CONST . sub ( " ` " + r " \ 1 " + " ' " , t ) # C(word) => `word'
return t
2015-07-11 18:24:45 +00:00
@staticmethod
def read_vault_password_file ( 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 ( os . path . expanduser ( vault_password_file ) )
if not os . path . exists ( this_path ) :
raise AnsibleError ( " The vault password file %s was not found " % this_path )
if 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 ) , e ) )
stdout , stderr = p . communicate ( )
vault_pass = stdout . strip ( ' \r \n ' )
else :
try :
f = open ( this_path , " rb " )
vault_pass = f . read ( ) . strip ( )
f . close ( )
except ( OSError , IOError ) as e :
raise AnsibleError ( " Could not read vault password file %s : %s " % ( this_path , e ) )
return vault_pass