12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046 |
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- # implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- import ConfigParser
- import copy
- import datetime
- import getpass
- import logging
- import os
- import re
- import sys
- from StringIO import StringIO
- import traceback
- import types
- import textwrap
- from optparse import OptionGroup
- from optparse import OptionParser
- import basedefs
- import validators
- from . import utils
- import processors
- import output_messages
- from .exceptions import FlagValidationError
- from .exceptions import ParamValidationError
- from packstack.modules.common import filtered_hosts
- from packstack.version import version_info
- from setup_controller import Controller
- controller = Controller()
- 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):
- try:
- logFile = os.path.join(basedefs.DIR_LOG, basedefs.FILE_LOG)
- # Create the log file with specific permissions, puppet has a habbit of putting
- # passwords in logs
- os.close(os.open(logFile, os.O_CREAT | os.O_EXCL, 0o600))
- hdlr = logging.FileHandler(filename=logFile, mode='w')
- if (debug):
- level = logging.DEBUG
- else:
- level = logging.INFO
- fmts = '%(asctime)s::%(levelname)s::%(module)s::%(lineno)d::%(name)s:: %(message)s'
- dfmt = '%Y-%m-%d %H:%M:%S'
- fmt = logging.Formatter(fmts, dfmt)
- hdlr.setFormatter(fmt)
- logging.root.handlers = []
- logging.root.addHandler(hdlr)
- logging.root.setLevel(level)
- except:
- logging.error(traceback.format_exc())
- raise Exception(output_messages.ERR_EXP_FAILED_INIT_LOGGER)
- return logFile
- def _getInputFromUser(param):
- """
- this private func reads the data from the user
- for the given param
- """
- loop = True
- userInput = None
- try:
- if param.USE_DEFAULT:
- logging.debug("setting default value (%s) for key (%s)" % (mask(param.DEFAULT_VALUE), param.CONF_NAME))
- controller.CONF[param.CONF_NAME] = param.DEFAULT_VALUE
- else:
- while loop:
- # If the value was not supplied by the command line flags
- if param.CONF_NAME not in commandLineValues:
- message = StringIO()
- message.write(param.PROMPT)
- val_list = param.VALIDATORS or []
- if(validators.validate_regexp not in val_list
- and param.OPTION_LIST):
- message.write(" [%s]" % "|".join(param.OPTION_LIST))
- if param.DEFAULT_VALUE:
- message.write(" [%s] " % (str(param.DEFAULT_VALUE)))
- message.write(": ")
- message.seek(0)
- # mask password or hidden fields
- if (param.MASK_INPUT):
- userInput = getpass.getpass("%s :" % (param.PROMPT))
- else:
- userInput = raw_input(message.read())
- else:
- userInput = commandLineValues[param.CONF_NAME]
- # If DEFAULT_VALUE is set and user did not input anything
- if userInput == "" and len(str(param.DEFAULT_VALUE)) > 0:
- userInput = param.DEFAULT_VALUE
- # Param processing
- userInput = process_param_value(param, userInput)
- # If param requires validation
- try:
- validate_param_value(param, userInput)
- controller.CONF[param.CONF_NAME] = userInput
- loop = False
- except ParamValidationError:
- if param.LOOSE_VALIDATION:
- # If validation failed but LOOSE_VALIDATION is true, ask user
- answer = _askYesNo("User input failed validation, "
- "do you still wish to use it")
- loop = not answer
- if answer:
- controller.CONF[param.CONF_NAME] = userInput
- continue
- else:
- if param.CONF_NAME in commandLineValues:
- del commandLineValues[param.CONF_NAME]
- else:
- # Delete value from commandLineValues so that we will prompt the user for input
- if param.CONF_NAME in commandLineValues:
- del commandLineValues[param.CONF_NAME]
- loop = True
- except KeyboardInterrupt:
- # add the new line so messages wont be displayed in the same line as the question
- print("")
- raise
- except:
- logging.error(traceback.format_exc())
- raise Exception(output_messages.ERR_EXP_READ_INPUT_PARAM % (param.CONF_NAME))
- def input_param(param):
- """
- this func will read input from user
- and ask confirmation if needed
- """
- # We need to check if a param needs confirmation, (i.e. ask user twice)
- # Do not validate if it was given from the command line
- if param.NEED_CONFIRM and param.CONF_NAME not in commandLineValues:
- # create a copy of the param so we can call it twice
- confirmedParam = copy.deepcopy(param)
- confirmedParamName = param.CONF_NAME + "_CONFIRMED"
- confirmedParam.CONF_NAME = confirmedParamName
- confirmedParam.PROMPT = output_messages.INFO_CONF_PARAMS_PASSWD_CONFIRM_PROMPT
- # Now get both values from user (with existing validations)
- while True:
- _getInputFromUser(param)
- _getInputFromUser(confirmedParam)
- if controller.CONF[param.CONF_NAME] == controller.CONF[confirmedParamName]:
- logging.debug("Param confirmation passed, value for both questions is identical")
- break
- else:
- print(output_messages.INFO_VAL_PASSWORD_DONT_MATCH)
- else:
- _getInputFromUser(param)
- return param
- def _askYesNo(question=None):
- message = StringIO()
- while True:
- askString = "\r%s? (yes|no): " % (question)
- logging.debug("asking user: %s" % askString)
- message.write(askString)
- message.seek(0)
- raw = raw_input(message.read())
- if not len(raw):
- continue
- answer = raw[0].lower()
- logging.debug("user answered read: %s" % (answer))
- if answer not in 'yn':
- continue
- return answer == 'y'
- def _addDefaultsToMaskedValueSet():
- """
- For every param in conf_params
- that has MASK_INPUT enabled keep the default value
- in the 'masked_value_set'
- """
- global masked_value_set
- for group in controller.getAllGroups():
- for param in group.parameters.itervalues():
- # Keep default password values masked, but ignore default empty values
- if ((param.MASK_INPUT is True) and param.DEFAULT_VALUE != ""):
- masked_value_set.add(param.DEFAULT_VALUE)
- def _updateMaskedValueSet():
- """
- For every param in conf
- has MASK_INPUT enabled keep the user input
- in the 'masked_value_set'
- """
- global masked_value_set
- for confName in controller.CONF:
- # Add all needed values to masked_value_set
- if (controller.getParamKeyValue(confName, "MASK_INPUT") is True):
- masked_value_set.add(controller.CONF[confName])
- def mask(input):
- """
- Gets a dict/list/str and search maksked values in them.
- The list of masked values in is masked_value_set and is updated
- via the user input
- If it finds, it replaces them with '********'
- """
- output = copy.deepcopy(input)
- if isinstance(input, types.DictType):
- for key in input:
- if isinstance(input[key], types.StringType):
- output[key] = utils.mask_string(input[key],
- masked_value_set)
- if isinstance(input, types.ListType):
- for item in input:
- org = item
- orgIndex = input.index(org)
- if isinstance(item, types.StringType):
- item = utils.mask_string(item, masked_value_set)
- if item != org:
- output.remove(org)
- output.insert(orgIndex, item)
- if isinstance(input, types.StringType):
- output = utils.mask_string(input, masked_value_set)
- return output
- def removeMaskString(maskedString):
- """
- remove an element from masked_value_set
- we need to itterate over the set since
- calling set.remove() on an string that does not exit
- will raise an exception
- """
- global masked_value_set
- # Since we cannot remove an item from a set during itteration over
- # the said set, we only mark a flag and if the flag is set to True
- # we remove the string from the set.
- found = False
- for item in masked_value_set:
- if item == maskedString:
- found = True
- if found:
- masked_value_set.remove(maskedString)
- def validate_param_value(param, value):
- cname = param.CONF_NAME
- logging.debug("Validating parameter %s." % cname)
- val_list = param.VALIDATORS or []
- opt_list = param.OPTION_LIST
- for val_func in val_list:
- try:
- val_func(value, opt_list)
- except ParamValidationError as ex:
- print('Parameter %s failed validation: %s' % (cname, ex))
- raise
- def process_param_value(param, value):
- _value = value
- proclist = param.PROCESSORS or []
- for proc_func in proclist:
- is_silent = getattr(proc_func, 'silent', False)
- logging.debug("Processing value of parameter "
- "%s." % param.CONF_NAME)
- try:
- new_value = proc_func(_value, param.CONF_NAME, controller.CONF)
- if new_value != _value:
- if param.MASK_INPUT is False and not is_silent:
- msg = output_messages.INFO_CHANGED_VALUE
- print(msg % (_value, new_value))
- _value = new_value
- else:
- logging.debug("Processor returned the original "
- "value: %s" % _value)
- except processors.ParamProcessingError as ex:
- print("Value processing of parameter %s "
- "failed.\n%s" % (param.CONF_NAME, ex))
- raise
- return _value
- def _handleGroupCondition(config, conditionName, conditionValue):
- """
- handle params group pre/post condition
- checks if a group has a pre/post condition
- and validates the params related to the group
- """
- # If the post condition is a function
- if callable(conditionName):
- # Call the function conditionName with conf as the arg
- conditionValue = conditionName(controller.CONF)
- # If the condition is a string - just read it to global conf
- # We assume that if we get a string as a member it is the name of a member of conf_params
- elif isinstance(conditionName, types.StringType):
- conditionValue = _loadParamFromFile(config, "general", conditionName)
- else:
- # Any other type is invalid
- raise TypeError("%s type (%s) is not supported" % (conditionName, type(conditionName)))
- return conditionValue
- def _loadParamFromFile(config, section, param_name):
- """
- read param from file
- validate it
- and load to to global conf dict
- """
- param = controller.getParamByName(param_name)
- # Get value from answer file
- try:
- value = config.get(section, param_name)
- except ConfigParser.NoOptionError:
- value = None
- # Check for deprecated parameters
- deprecated = param.DEPRECATES if param.DEPRECATES is not None else []
- for old_name in deprecated:
- try:
- val = config.get(section, old_name)
- except ConfigParser.NoOptionError:
- continue
- if not val:
- # value is empty string
- continue
- if value is None:
- value = val
- if value != val:
- raise ValueError('Parameter %(param_name)s deprecates '
- 'following parameters:\n%(deprecated)s.\n'
- 'Please either use parameter %(param_name)s '
- 'or use same value for all deprecated '
- 'parameters.' % locals())
- if deprecated and value is not None:
- controller.MESSAGES.append('Deprecated parameter has been used '
- 'in answer file. Please use parameter '
- '%(param_name)s next time. This '
- 'parameter deprecates following '
- 'parameters: %(deprecated)s.'
- % locals())
- if value is None:
- # Let's use default value if we have one
- value = getattr(param, 'DEFAULT_VALUE', None)
- if value is None:
- raise KeyError('Parser cannot find option %s in answer file.'
- % param_name)
- # Validate param value using its validation func
- value = process_param_value(param, value)
- validate_param_value(param, value)
- # Keep param value in our never ending global conf
- controller.CONF[param.CONF_NAME] = value
- # Add message to controller.MESSAGES if defined in parameter
- if param.MESSAGE:
- _handleParamMessage(param, value)
- return value
- def _handleAnswerFileParams(answerFile):
- """
- handle loading and validating
- params from answer file
- supports reading single or group params
- """
- try:
- logging.debug("Starting to handle config file")
- # Read answer file
- fconf = ConfigParser.ConfigParser()
- fconf.read(answerFile)
- # Iterate all the groups and check the pre/post conditions
- for group in controller.getAllGroups():
- # Get all params per group
- # Handle pre conditions for group
- preConditionValue = True
- if group.PRE_CONDITION:
- preConditionValue = _handleGroupCondition(fconf, group.PRE_CONDITION, preConditionValue)
- # Handle pre condition match with case insensitive values
- if preConditionValue == group.PRE_CONDITION_MATCH:
- for param in group.parameters.itervalues():
- _loadParamFromFile(fconf, "general", param.CONF_NAME)
- # Handle post conditions for group only if pre condition passed
- postConditionValue = True
- if group.POST_CONDITION:
- postConditionValue = _handleGroupCondition(fconf, group.POST_CONDITION, postConditionValue)
- # Handle post condition match for group
- if postConditionValue != group.POST_CONDITION_MATCH:
- logging.error("The group condition (%s) returned: %s, which differs from the excpeted output: %s" %
- (group.GROUP_NAME, postConditionValue, group.POST_CONDITION_MATCH))
- raise ValueError(output_messages.ERR_EXP_GROUP_VALIDATION_ANS_FILE %
- (group.GROUP_NAME, postConditionValue, group.POST_CONDITION_MATCH))
- else:
- logging.debug("condition (%s) passed" % group.POST_CONDITION)
- else:
- logging.debug("no post condition check for group %s" % group.GROUP_NAME)
- else:
- logging.debug("skipping params group %s since value of group validation is %s" % (group.GROUP_NAME, preConditionValue))
- except Exception as e:
- logging.error(traceback.format_exc())
- raise Exception(output_messages.ERR_EXP_HANDLE_ANSWER_FILE % (e))
- def _getanswerfilepath():
- path = None
- msg = "Could not find a suitable path on which to create the 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, "packstack-answers-%s.txt" % ts))
- msg = "A new answerfile was created in: %s" % path
- 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()]))
- for group in controller.getAllGroups():
- preConditionValue = True
- logging.debug("going over group %s" % group.GROUP_NAME)
- # If pre_condition is set, get Value
- if group.PRE_CONDITION:
- preConditionValue = _getConditionValue(group.PRE_CONDITION)
- inputLoop = True
- # If we have a match, i.e. condition returned True, go over all params in the group
- if preConditionValue == group.PRE_CONDITION_MATCH:
- while inputLoop:
- for param in group.parameters.itervalues():
- if not param.CONDITION:
- input_param(param)
- # update password list, so we know to mask them
- _updateMaskedValueSet()
- postConditionValue = True
- # If group has a post condition, we check it after we get the input from
- # all the params in the group. if the condition returns False, we loop over the group again
- if group.POST_CONDITION:
- postConditionValue = _getConditionValue(group.POST_CONDITION)
- if postConditionValue == group.POST_CONDITION_MATCH:
- inputLoop = False
- else:
- # we clear the value of all params in the group
- # in order to re-input them by the user
- for param in group.parameters.itervalues():
- if param.CONF_NAME in controller.CONF:
- del controller.CONF[param.CONF_NAME]
- if param.CONF_NAME in commandLineValues:
- del commandLineValues[param.CONF_NAME]
- else:
- inputLoop = False
- else:
- logging.debug("no post condition check for group %s" % group.GROUP_NAME)
- _displaySummary()
- except KeyboardInterrupt:
- logging.error("keyboard interrupt caught")
- raise Exception(output_messages.ERR_EXP_KEYBOARD_INTERRUPT)
- except Exception:
- logging.error(traceback.format_exc())
- raise
- except:
- logging.error(traceback.format_exc())
- raise Exception(output_messages.ERR_EXP_HANDLE_PARAMS)
- def _handleParams(configFile):
- _addDefaultsToMaskedValueSet()
- if configFile:
- _handleAnswerFileParams(configFile)
- else:
- _handleInteractiveParams()
- def _getConditionValue(matchMember):
- returnValue = False
- if isinstance(matchMember, types.FunctionType):
- returnValue = matchMember(controller.CONF)
- elif isinstance(matchMember, types.StringType):
- # we assume that if we get a string as a member it is the name
- # of a member of conf_params
- if matchMember not in controller.CONF:
- param = controller.getParamByName(matchMember)
- input_param(param)
- returnValue = controller.CONF[matchMember]
- else:
- raise TypeError("%s type (%s) is not supported" % (matchMember, type(matchMember)))
- return returnValue
- def _displaySummary():
- print(output_messages.INFO_DSPLY_PARAMS)
- print("=" * (len(output_messages.INFO_DSPLY_PARAMS) - 1))
- logging.info("*** User input summary ***")
- for group in controller.getAllGroups():
- for param in group.parameters.itervalues():
- if not param.USE_DEFAULT and param.CONF_NAME in controller.CONF:
- cmdOption = param.CMD_OPTION
- l = 30 - len(cmdOption)
- maskParam = param.MASK_INPUT
- # Only call mask on a value if the param has MASK_INPUT set to True
- if maskParam:
- logging.info("%s: %s" % (cmdOption, mask(controller.CONF[param.CONF_NAME])))
- print("%s:" % (cmdOption) + " " * l + mask(controller.CONF[param.CONF_NAME]))
- else:
- # Otherwise, log & display it as it is
- logging.info("%s: %s" % (cmdOption, str(controller.CONF[param.CONF_NAME])))
- print("%s:" % (cmdOption) + " " * l + str(controller.CONF[param.CONF_NAME]))
- logging.info("*** User input summary ***")
- answer = _askYesNo(output_messages.INFO_USE_PARAMS)
- if not answer:
- logging.debug("user chose to re-enter the user parameters")
- for group in controller.getAllGroups():
- for param in group.parameters.itervalues():
- if param.CONF_NAME in controller.CONF:
- if not param.MASK_INPUT:
- param.DEFAULT_VALUE = controller.CONF[param.CONF_NAME]
- # Remove the string from mask_value_set in order
- # to remove values that might be over overwritten.
- removeMaskString(controller.CONF[param.CONF_NAME])
- del controller.CONF[param.CONF_NAME]
- if param.CONF_NAME in commandLineValues:
- del commandLineValues[param.CONF_NAME]
- print("")
- logging.debug("calling handleParams in interactive mode")
- return _handleParams(None)
- else:
- logging.debug("user chose to accept user parameters")
- def _printAdditionalMessages():
- if len(controller.MESSAGES) > 0:
- print(output_messages.INFO_ADDTIONAL_MSG)
- for msg in controller.MESSAGES:
- print(output_messages.INFO_ADDTIONAL_MSG_BULLET % (msg))
- def _addFinalInfoMsg(logFile):
- """
- add info msg to the user finalizing the
- successfull install of rhemv
- """
- controller.MESSAGES.append(output_messages.INFO_LOG_FILE_PATH % (logFile))
- controller.MESSAGES.append(
- output_messages.INFO_MANIFEST_PATH % (basedefs.PUPPET_MANIFEST_DIR))
- def _summaryParamsToLog():
- if len(controller.CONF) > 0:
- logging.debug("*** The following params were used as user input:")
- for group in controller.getAllGroups():
- for param in group.parameters.itervalues():
- if param.CONF_NAME in controller.CONF:
- maskedValue = mask(controller.CONF[param.CONF_NAME])
- logging.debug("%s: %s" % (param.CMD_OPTION, maskedValue))
- def _handleParamMessage(param, value):
- """
- add message to the information displayed at the end of the execution
- for parameters with MESSAGE option. if parameter has MESSAGE_VALUES
- option, message will be only displayed if the provided value is in
- MESSAGE_VALUES.
- """
- message_values = param.MESSAGE_VALUES if param.MESSAGE_VALUES is not None else None
- if not message_values or value in message_values:
- message = utils.color_text('Parameter %s: %s'
- % (param.CONF_NAME, param.MESSAGE), 'red')
- if message not in controller.MESSAGES:
- controller.MESSAGES.append(message)
- def runSequences():
- controller.runAllSequences()
- def _main(options, configFile=None, logFile=None):
- print(output_messages.INFO_HEADER)
- print("\n" + output_messages.INFO_LOG_FILE_PATH % logFile)
- # Get parameters
- _handleParams(configFile)
- # Generate answer file, only if no answer file was provided
- if not options.answer_file:
- path = _getanswerfilepath()
- if path:
- generateAnswerFile(path)
- # If an answer file was provided, some options may have been overriden
- # Overwrite answer file with updated options
- else:
- generateAnswerFile(options.answer_file)
- # Update masked_value_list with user input values
- _updateMaskedValueSet()
- # Print masked conf
- logging.debug(mask(controller.CONF))
- # Start configuration stage
- print("\n" + output_messages.INFO_INSTALL)
- # Initialize Sequences
- initPluginsSequences()
- # Run main setup logic
- runSequences()
- # Lock rhevm version
- # _lockRpmVersion()
- # Print info
- _addFinalInfoMsg(logFile)
- print(output_messages.INFO_INSTALL_SUCCESS)
- def remove_remote_var_dirs(options, config, messages):
- """
- Removes the temp directories on remote hosts,
- doesn't remove data on localhost
- """
- for host in filtered_hosts(config):
- try:
- host_dir = config['HOST_DETAILS'][host]['tmpdir']
- except KeyError:
- # Nothing was added to this host yet, so we have nothing to delete
- continue
- if options.debug:
- # we keep temporary directories on hosts in debug mode
- messages.append(
- 'Note temporary directory {host_dir} on host {host} was '
- 'not deleted for debugging purposes.'.format(**locals())
- )
- continue
- logging.debug(output_messages.INFO_REMOVE_REMOTE_VAR % (host_dir, host))
- server = utils.ScriptRunner(host)
- server.append('rm -rf %s' % host_dir)
- try:
- server.execute()
- except Exception as e:
- msg = output_messages.ERR_REMOVE_REMOTE_VAR % (host_dir, host)
- logging.error(msg)
- logging.exception(e)
- 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
- fmt = ("%(comment)s%(separator)s%(conf_name)s=%(default_value)s"
- "%(separator)s")
- outputFile = os.path.expanduser(outputFile)
- # Remove the answer file so it can be recreated as the current user with
- # the mode -rw-------
- if os.path.exists(outputFile):
- os.remove(outputFile)
- fd = os.open(outputFile, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
- with os.fdopen(fd, "w") as ans_file:
- ans_file.write("[general]%s" % os.linesep)
- for group in controller.getAllGroups():
- for param in group.parameters.itervalues():
- comm = param.USAGE or ''
- comm = textwrap.fill(comm,
- initial_indent='%s# ' % sep,
- subsequent_indent='# ',
- break_long_words=False)
- value = controller.CONF.get(param.CONF_NAME,
- param.DEFAULT_VALUE)
- args = {'comment': comm,
- 'separator': sep,
- 'default_value': overrides.get(param.CONF_NAME, value),
- 'conf_name': param.CONF_NAME}
- ans_file.write(fmt % args)
- def single_step_aio_install(options, logFile):
- """Installs an All in One host on this host."""
- options.install_hosts = utils.get_localhost_ip()
- single_step_install(options, logFile)
- def single_step_install(options, logFile):
- answerfilepath = _gettmpanswerfilepath()
- if not answerfilepath:
- _printAdditionalMessages()
- return
- # We're going to generate the answerfile and run Packstack in a single step
- # todo this we generate the answerfile and pass in some override variables to
- # override the default hosts
- overrides = {}
- hosts = options.install_hosts
- hosts = [host.strip() for host in hosts.split(',')]
- for group in controller.getAllGroups():
- for param in group.parameters.itervalues():
- # and directives that contain _HOST are set to the controller node
- if param.CONF_NAME.find("_HOST") != -1:
- overrides[param.CONF_NAME] = hosts[0]
- # If there are more than one host, all but the first are a compute nodes
- if len(hosts) > 1:
- overrides["CONFIG_COMPUTE_HOSTS"] = ','.join(hosts[1:])
- # We can also override defaults with command line options
- _set_command_line_values(options)
- for key, value in commandLineValues.items():
- overrides[key] = value
- generateAnswerFile(answerfilepath, overrides)
- _main(options, answerfilepath, logFile)
- def initCmdLineParser():
- """
- Initiate the optparse object, add all the groups and general command line flags
- and returns the optparse object
- """
- # Init parser and all general flags
- usage = "usage: %prog [options] [--help]"
- parser = OptionParser(usage=usage, version="%prog {0}".format(version_info.version_string()))
- parser.add_option("--gen-answer-file", help="Generate a template of an answer file.")
- parser.add_option("--answer-file", help="Runs the configuration in non-interactive mode, extracting all information from the"
- "configuration file. using this option excludes all other options")
- parser.add_option("--install-hosts", help="Install on a set of hosts in a single step. The format should be a comma separated list "
- "of hosts, the first is setup as a controller, and the others are setup as compute nodes."
- "if only a single host is supplied then it is setup as an all in one installation. An answerfile "
- "will also be generated and should be used if Packstack needs to be run a second time ")
- parser.add_option("--allinone", action="store_true", help="Shorthand for --install-hosts=<local ipaddr> --novanetwork-pubif=<dev> "
- "--novacompute-privif=lo --novanetwork-privif=lo --os-swift-install=y --nagios-install=y "
- ", this option can be used to install an all in one OpenStack on this host")
- parser.add_option("-t", "--timeout", default=300, help="The timeout for puppet Exec calls")
- parser.add_option("-o", "--options", action="store_true", dest="options", help="Print details on options available in answer file(rst format)")
- parser.add_option("-d", "--debug", action="store_true", default=False, help="Enable debug in logging")
- parser.add_option("-y", "--dry-run", action="store_true", default=False, help="Don't execute, just generate manifests")
- # For each group, create a group option
- for group in controller.getAllGroups():
- groupParser = OptionGroup(parser, group.DESCRIPTION)
- for param in group.parameters.itervalues():
- cmdOption = param.CMD_OPTION
- paramUsage = param.USAGE
- optionsList = param.OPTION_LIST
- useDefault = param.USE_DEFAULT
- if not useDefault:
- groupParser.add_option("--%s" % cmdOption, help=paramUsage)
- # Add group parser to main parser
- parser.add_option_group(groupParser)
- return parser
- def printOptions():
- """
- print and document the available options to the answer file (rst format)
- """
- # For each group, create a group option
- for group in controller.getAllGroups():
- print("%s" % group.DESCRIPTION)
- print("-" * len(group.DESCRIPTION) + "\n")
- for param in group.parameters.itervalues():
- cmdOption = param.CONF_NAME
- paramUsage = param.USAGE
- optionsList = param.OPTION_LIST or ""
- print("%s" % (("**%s**" % str(cmdOption)).ljust(30)))
- print(" %s" % paramUsage + "\n")
- def plugin_compare(x, y):
- """
- Used to sort the plugin file list
- according to the number at the end of the plugin module
- """
- x_match = re.search(".+\_(\d\d\d)", x)
- x_cmp = x_match.group(1)
- y_match = re.search(".+\_(\d\d\d)", y)
- y_cmp = y_match.group(1)
- return int(x_cmp) - int(y_cmp)
- def loadPlugins():
- """
- Load All plugins from ./plugins
- """
- sys.path.append(basedefs.DIR_PLUGINS)
- sys.path.append(basedefs.DIR_MODULES)
- fileList = [f for f in os.listdir(basedefs.DIR_PLUGINS) if f[0] != "_"]
- fileList = sorted(fileList, cmp=plugin_compare)
- for item in fileList:
- # Looking for files that end with ###.py, example: a_plugin_100.py
- match = re.search("^(.+\_\d\d\d)\.py$", item)
- if match:
- try:
- moduleToLoad = match.group(1)
- logging.debug("importing module %s, from file %s", moduleToLoad, item)
- moduleobj = __import__(moduleToLoad)
- moduleobj.__file__ = os.path.join(basedefs.DIR_PLUGINS, item)
- globals()[moduleToLoad] = moduleobj
- checkPlugin(moduleobj)
- controller.addPlugin(moduleobj)
- except:
- logging.error("Failed to load plugin from file %s", item)
- logging.error(traceback.format_exc())
- raise Exception("Failed to load plugin from file %s" % item)
- def checkPlugin(plugin):
- for funcName in ['initConfig', 'initSequences']:
- if not hasattr(plugin, funcName):
- raise ImportError("Plugin %s does not contain the %s function" % (plugin.__class__, funcName))
- def countCmdLineFlags(options, flag):
- """
- counts all command line flags that were supplied, excluding the supplied flag name
- """
- counter = 0
- # make sure only flag was supplied
- for key, value in options.__dict__.items():
- if key in (flag, 'debug', 'timeout', 'dry_run', 'default_password'):
- next
- # If anything but flag was called, increment
- elif value:
- counter += 1
- return counter
- def validateSingleFlag(options, flag):
- counter = countCmdLineFlags(options, flag)
- if counter > 0:
- flag = flag.replace("_", "-")
- msg = output_messages.ERR_ONLY_1_FLAG % ("--%s" % flag)
- raise FlagValidationError(msg)
- def initPluginsConfig():
- for plugin in controller.getAllPlugins():
- plugin.initConfig(controller)
- def initPluginsSequences():
- for plugin in controller.getAllPlugins():
- plugin.initSequences(controller)
- def _set_command_line_values(options):
- for key, value in options.__dict__.items():
- # Replace the _ with - in the string since optparse replace _ with -
- for group in controller.getAllGroups():
- param = group.search("CMD_OPTION", key.replace("_", "-"))
- if len(param) > 0 and value:
- commandLineValues[param[0].CONF_NAME] = value
- def main():
- options = ""
- try:
- # Load Plugins
- loadPlugins()
- initPluginsConfig()
- optParser = initCmdLineParser()
- # Do the actual command line parsing
- # Try/Except are here to catch the silly sys.exit(0) when calling rhevm-setup --help
- (options, args) = optParser.parse_args()
- if options.options:
- printOptions()
- raise SystemExit
- # Initialize logging
- logFile = initLogging(options.debug)
- # Parse parameters
- runConfiguration = True
- confFile = None
- controller.CONF['DEFAULT_EXEC_TIMEOUT'] = options.timeout
- controller.CONF['DRY_RUN'] = options.dry_run
- controller.CONF['DIR_LOG'] = basedefs.DIR_LOG
- # If --gen-answer-file was supplied, do not run main
- if options.gen_answer_file:
- answerfilepath = _gettmpanswerfilepath()
- if not answerfilepath:
- _printAdditionalMessages()
- return
- # We can also override defaults with command line options
- overrides = {}
- _set_command_line_values(options)
- for key, value in commandLineValues.items():
- overrides[key] = value
- generateAnswerFile(answerfilepath, overrides)
- _handleParams(answerfilepath)
- generateAnswerFile(options.gen_answer_file)
- # Are we installing an all in one
- elif options.allinone:
- if getattr(options, 'answer_file', None):
- msg = ('Please use either --allinone or --answer-file, '
- 'but not both.')
- raise FlagValidationError(msg)
- single_step_aio_install(options, logFile)
- # Are we installing in a single step
- elif options.install_hosts:
- single_step_install(options, logFile)
- # Otherwise, run main()
- else:
- # Make sure only --answer-file was supplied
- if options.answer_file:
- validateSingleFlag(options, "answer_file")
- # If using an answer file, setting a default password
- # does not really make sense
- if getattr(options, 'default_password', None):
- msg = ('Please do not set --default-password '
- 'when specifying an answer file.')
- raise FlagValidationError(msg)
- confFile = os.path.expanduser(options.answer_file)
- if not os.path.exists(confFile):
- raise Exception(output_messages.ERR_NO_ANSWER_FILE % confFile)
- else:
- _set_command_line_values(options)
- _main(options, confFile, logFile)
- except FlagValidationError as ex:
- optParser.error(str(ex))
- except Exception as e:
- logging.error(traceback.format_exc())
- print("\n" + utils.color_text("ERROR : " + str(e), 'red'))
- try:
- print(output_messages.ERR_CHECK_LOG_FILE_FOR_MORE_INFO % (logFile))
- except NameError:
- pass
- sys.exit(1)
- finally:
- remove_remote_var_dirs(options, controller.CONF, controller.MESSAGES)
- remove_temp_files()
- # Always print user params to log
- _printAdditionalMessages()
- _summaryParamsToLog()
- if __name__ == "__main__":
- main()
|