Allow specifying of a global --password option
This fixes bz#1108742 by providing a new global parameter "--default-password", that will be the default for all other password parameters if set. Each individual password parameter can override the default global, and if none are set, a random password will be used as before. As part of the change, process_param_value() has been updated, to avoid leaking passwords when they are modified by a processor function. Change-Id: Ic5947567599c8b221b7a9e60acb4708429507741
This commit is contained in:
@@ -85,5 +85,5 @@ ERR_FAILURE="General failure"
|
||||
ERR_NO_ANSWER_FILE="Error: Could not find file %s"
|
||||
ERR_ONLY_1_FLAG="Error: The %s flag is mutually exclusive to all other command line options"
|
||||
ERR_REMOVE_REMOTE_VAR="Error: Failed to remove directory %s on %s, it contains sensitive data and should be removed"
|
||||
|
||||
ERR_REMOVE_TMP_FILE="Error: Failed to remove temporary file %s, it contains sensitive data and should be removed"
|
||||
#
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import netaddr
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from .utils import ScriptRunner, force_ip
|
||||
from .exceptions import ParamProcessingError, NetworkError
|
||||
@@ -11,7 +12,7 @@ __all__ = ('ParamProcessingError', 'process_cidr', 'process_host',
|
||||
'process_ssh_key')
|
||||
|
||||
|
||||
def process_cidr(param, process_args=None):
|
||||
def process_cidr(param, param_name, process_args=None):
|
||||
"""
|
||||
Corrects given CIDR if necessary.
|
||||
"""
|
||||
@@ -24,7 +25,7 @@ def process_cidr(param, process_args=None):
|
||||
raise ParamProcessingError(str(ex))
|
||||
|
||||
|
||||
def process_host(param, process_args=None):
|
||||
def process_host(param, param_name, process_args=None):
|
||||
"""
|
||||
Tries to change given parameter to IP address, if it is in hostname
|
||||
format
|
||||
@@ -37,7 +38,7 @@ def process_host(param, process_args=None):
|
||||
raise ParamProcessingError(str(ex))
|
||||
|
||||
|
||||
def process_ssh_key(param, process_args=None):
|
||||
def process_ssh_key(param, param_name, process_args=None):
|
||||
"""
|
||||
Generates SSH key if given key in param doesn't exist. In case param
|
||||
is an empty string it generates default SSH key ($HOME/.ssh/id_rsa).
|
||||
@@ -63,7 +64,7 @@ def process_ssh_key(param, process_args=None):
|
||||
return param
|
||||
|
||||
|
||||
def process_add_quotes_around_values(param, process_args=None):
|
||||
def process_add_quotes_around_values(param, param_name, process_args=None):
|
||||
"""
|
||||
Add a single quote character around each element of a comma
|
||||
separated list of values
|
||||
@@ -77,3 +78,33 @@ def process_add_quotes_around_values(param, process_args=None):
|
||||
params_list[index] = elem
|
||||
param = ','.join(params_list)
|
||||
return param
|
||||
|
||||
def process_password(param, param_name, process_args=None):
|
||||
"""
|
||||
Process passwords, checking the following:
|
||||
1- If there is a user-entered password, use it
|
||||
2- Otherwise, check for a global default password, and use it if available
|
||||
3- As a last resort, generate a random password
|
||||
"""
|
||||
if not hasattr(process_password,"pw_dict"):
|
||||
process_password.pw_dict = {}
|
||||
|
||||
if param == "PW_PLACEHOLDER":
|
||||
if process_args["CONFIG_DEFAULT_PASSWORD"] != "":
|
||||
param = process_args["CONFIG_DEFAULT_PASSWORD"]
|
||||
else:
|
||||
# We need to make sure we store the random password we provide
|
||||
# and return it once we are asked for it again
|
||||
if param_name.endswith("_CONFIRMED"):
|
||||
unconfirmed_param = param_name[:-10]
|
||||
if unconfirmed_param in process_password.pw_dict:
|
||||
param = process_password.pw_dict[unconfirmed_param]
|
||||
else:
|
||||
param = uuid.uuid4().hex[:16]
|
||||
process_password.pw_dict[unconfirmed_param] = param
|
||||
elif not param_name in process_password.pw_dict:
|
||||
param = uuid.uuid4().hex[:16]
|
||||
process_password.pw_dict[param_name] = param
|
||||
else:
|
||||
param = process_password.pw_dict[param_name]
|
||||
return param
|
||||
|
||||
@@ -30,7 +30,7 @@ commandLineValues = {}
|
||||
# List to hold all values to be masked in logging (i.e. passwords and sensitive data)
|
||||
#TODO: read default values from conf_param?
|
||||
masked_value_set = set()
|
||||
|
||||
tmpfiles = []
|
||||
|
||||
def initLogging (debug):
|
||||
global logFile
|
||||
@@ -147,8 +147,7 @@ def input_param(param):
|
||||
confirmedParamName = param.CONF_NAME + "_CONFIRMED"
|
||||
confirmedParam.CONF_NAME = confirmedParamName
|
||||
confirmedParam.PROMPT = output_messages.INFO_CONF_PARAMS_PASSWD_CONFIRM_PROMPT
|
||||
confirmedParam.VALIDATORS = [validators.validate_not_empty]
|
||||
# Now get both values from user (with existing validations
|
||||
# Now get both values from user (with existing validations)
|
||||
while True:
|
||||
_getInputFromUser(param)
|
||||
_getInputFromUser(confirmedParam)
|
||||
@@ -274,10 +273,11 @@ def process_param_value(param, value):
|
||||
logging.debug("Processing value of parameter "
|
||||
"%s." % param.CONF_NAME)
|
||||
try:
|
||||
new_value = proc_func(_value, controller.CONF)
|
||||
new_value = proc_func(_value, param.CONF_NAME, controller.CONF)
|
||||
if new_value != _value:
|
||||
msg = output_messages.INFO_CHANGED_VALUE
|
||||
print msg % (_value, new_value)
|
||||
if param.MASK_INPUT == False:
|
||||
msg = output_messages.INFO_CHANGED_VALUE
|
||||
print msg % (_value, new_value)
|
||||
_value = new_value
|
||||
else:
|
||||
logging.debug("Processor returned the original "
|
||||
@@ -429,6 +429,19 @@ def _getanswerfilepath():
|
||||
controller.MESSAGES.append(msg)
|
||||
return path
|
||||
|
||||
def _gettmpanswerfilepath():
|
||||
path = None
|
||||
msg = "Could not find a suitable path on which to create the temporary answerfile"
|
||||
|
||||
ts = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||
|
||||
p = os.path.expanduser("~/")
|
||||
if os.access(p, os.W_OK):
|
||||
path = os.path.abspath(os.path.join(p, "tmp-packstack-answers-%s.txt"%ts))
|
||||
tmpfiles.append(path)
|
||||
|
||||
return path
|
||||
|
||||
def _handleInteractiveParams():
|
||||
try:
|
||||
logging.debug("Groups: %s" % ', '.join([x.GROUP_NAME for x in controller.getAllGroups()]))
|
||||
@@ -474,13 +487,8 @@ def _handleInteractiveParams():
|
||||
else:
|
||||
logging.debug("no post condition check for group %s" % group.GROUP_NAME)
|
||||
|
||||
path = _getanswerfilepath()
|
||||
|
||||
_displaySummary()
|
||||
|
||||
if path:
|
||||
generateAnswerFile(path)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.error("keyboard interrupt caught")
|
||||
raise Exception(output_messages.ERR_EXP_KEYBOARD_INTERRUPT)
|
||||
@@ -589,6 +597,11 @@ def _main(configFile=None):
|
||||
# Get parameters
|
||||
_handleParams(configFile)
|
||||
|
||||
# Generate answer file
|
||||
path = _getanswerfilepath()
|
||||
if path:
|
||||
generateAnswerFile(path)
|
||||
|
||||
# Update masked_value_list with user input values
|
||||
_updateMaskedValueSet()
|
||||
|
||||
@@ -634,6 +647,20 @@ def remove_remote_var_dirs():
|
||||
logging.exception(e)
|
||||
controller.MESSAGES.append(utils.color_text(msg, 'red'))
|
||||
|
||||
def remove_temp_files():
|
||||
"""
|
||||
Removes any temporary files generated during
|
||||
configuration
|
||||
"""
|
||||
for myfile in tmpfiles:
|
||||
try:
|
||||
os.unlink(myfile)
|
||||
except Exception as e:
|
||||
msg = output_messages.ERR_REMOVE_TMP_FILE % (myfile)
|
||||
logging.error(msg)
|
||||
logging.exception(e)
|
||||
controller.MESSAGES.append(utils.color_text(msg, 'red'))
|
||||
|
||||
|
||||
def generateAnswerFile(outputFile, overrides={}):
|
||||
sep = os.linesep
|
||||
@@ -688,7 +715,7 @@ def single_step_aio_install(options):
|
||||
single_step_install(options)
|
||||
|
||||
def single_step_install(options):
|
||||
answerfilepath = _getanswerfilepath()
|
||||
answerfilepath = _gettmpanswerfilepath()
|
||||
if not answerfilepath:
|
||||
_printAdditionalMessages()
|
||||
return
|
||||
@@ -892,6 +919,12 @@ def main():
|
||||
if options.gen_answer_file:
|
||||
# Make sure only --gen-answer-file was supplied
|
||||
validateSingleFlag(options, "gen_answer_file")
|
||||
answerfilepath = _gettmpanswerfilepath()
|
||||
if not answerfilepath:
|
||||
_printAdditionalMessages()
|
||||
return
|
||||
generateAnswerFile(answerfilepath)
|
||||
_handleParams(answerfilepath)
|
||||
generateAnswerFile(options.gen_answer_file)
|
||||
# Are we installing an all in one
|
||||
elif options.allinone:
|
||||
@@ -926,6 +959,7 @@ def main():
|
||||
|
||||
finally:
|
||||
remove_remote_var_dirs()
|
||||
remove_temp_files()
|
||||
|
||||
# Always print user params to log
|
||||
_printAdditionalMessages()
|
||||
|
||||
Reference in New Issue
Block a user