From 4e4149db97c215d2a96febc871058b687d4ce665 Mon Sep 17 00:00:00 2001 From: Martin Magr Date: Tue, 7 May 2013 15:39:53 +0200 Subject: [PATCH] Refactored packstack.installer.setup_sequences Change-Id: Ie4b039ed9c4996240c20a033d91b936c8113214b --- packstack/installer/basedefs.py | 5 - packstack/installer/core/__init__.py | 0 packstack/installer/core/sequences.py | 105 ++++++++++++++ packstack/installer/exceptions.py | 6 + packstack/installer/setup_controller.py | 30 +++- packstack/installer/setup_sequences.py | 163 ---------------------- packstack/installer/utils/__init__.py | 4 +- packstack/installer/utils/strings.py | 7 +- packstack/plugins/cinder_250.py | 8 +- packstack/plugins/dashboard_500.py | 2 +- packstack/plugins/glance_200.py | 4 +- packstack/plugins/keystone_100.py | 2 +- packstack/plugins/mysql_001.py | 2 +- packstack/plugins/nagios_910.py | 4 +- packstack/plugins/nova_300.py | 18 +-- packstack/plugins/openstack_client_400.py | 2 +- packstack/plugins/postscript_949.py | 2 +- packstack/plugins/prescript_000.py | 4 +- packstack/plugins/puppet_950.py | 8 +- packstack/plugins/qpid_002.py | 2 +- packstack/plugins/serverprep_901.py | 2 +- packstack/plugins/sshkeys_000.py | 2 +- packstack/plugins/swift_600.py | 10 +- tests/installer/test_sequences.py | 98 +++++++++++++ 24 files changed, 274 insertions(+), 216 deletions(-) create mode 100644 packstack/installer/core/__init__.py create mode 100644 packstack/installer/core/sequences.py delete mode 100644 packstack/installer/setup_sequences.py create mode 100644 tests/installer/test_sequences.py diff --git a/packstack/installer/basedefs.py b/packstack/installer/basedefs.py index f51451344..62bd0f6c2 100644 --- a/packstack/installer/basedefs.py +++ b/packstack/installer/basedefs.py @@ -41,10 +41,5 @@ EXEC_CHKCONFIG = "chkconfig" EXEC_SERVICE = "service" EXEC_IP = "ip" -# text colors -NO_COLOR = "\033[0m" -COLORS = {'red': "\033[0;31m", 'green': "\033[92m", 'blue': "\033[94m", - 'yellow': "\033[93m"} - # space len size for color print SPACE_LEN = 70 diff --git a/packstack/installer/core/__init__.py b/packstack/installer/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packstack/installer/core/sequences.py b/packstack/installer/core/sequences.py new file mode 100644 index 000000000..932f846d1 --- /dev/null +++ b/packstack/installer/core/sequences.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +""" +Base class for steps & sequences +""" +import re +import sys +import logging +import traceback + +from .. import utils +from ..exceptions import SequenceError + + +class Step(object): + """ + Wrapper for function representing single setup step. + """ + def __init__(self, name, function, title=None): + self.name = name + self.title = title or ('Step: %s' % name) + + # process step function + if function and not callable(function): + raise SequenceError("Function object have to be callable. " + "Object %s is not callable." % function) + self.function = function + + def run(self, config=None): + config = config or {} + # TO-DO: complete logger name when logging will be setup correctly + logger = logging.getLogger() + logger.debug('Running step %s.' % self.name) + sys.stdout.write('%s...' % self.title) + sys.stdout.flush() + + # count space needed for title print + title = self.title + for color in utils.COLORS.itervalues(): + title = re.sub(re.escape(color), '', title) + space = 70 - len(title) + + # execute and report state + state_fmt = '[ %s ]\n' + try: + self.function(config) + except Exception, ex: + logger.debug(traceback.format_exc()) + state = state_fmt % utils.color_text('ERROR', 'red') + sys.stdout.write(state.rjust(space)) + sys.stdout.flush() + raise SequenceError(str(ex)) + else: + state = state_fmt % utils.color_text('DONE', 'green') + sys.stdout.write(state.rjust(space)) + sys.stdout.flush() + + + +class Sequence(object): + """ + Wrapper for sequence of setup steps. + """ + def __init__(self, name, steps, title=None, condition=None, + cond_match=None): + self.name = name + self.title = title + self.condition = condition + self.cond_match = cond_match + + # process sequence steps + self.steps = utils.SortedDict() + for step in steps: + name, func = step['name'], step['function'] + self.steps[name] = Step(name, func, title=step.get('title')) + + def validate_condition(self, config): + """ + Returns True if config option condition has value given + in cond_match. Otherwise returns False. + """ + if not self.condition: + return True + result = config.get(self.condition) + return result == self.cond_match + + def run(self, config=None, step=None): + """ + Runs sequence of steps. Runs only specific step if step's name + is given via 'step' parameter. + """ + config = config or {} + if not self.validate_condition(config): + return + if step: + self.steps[step].run(config=config) + return + + logger = logging.getLogger() + logger.debug('Running sequence %s.' % self.name) + if self.title: + sys.stdout.write('%s\n' % self.title) + sys.stdout.flush() + for step in self.steps.itervalues(): + step.run(config=config) diff --git a/packstack/installer/exceptions.py b/packstack/installer/exceptions.py index aa6c80292..fd6e679e4 100644 --- a/packstack/installer/exceptions.py +++ b/packstack/installer/exceptions.py @@ -64,5 +64,11 @@ class ScriptRuntimeError(PackStackError): """ pass + class ExecuteRuntimeError(PackStackError): """Raised when utils.execute does not end successfully.""" + + +class SequenceError(PackStackError): + """Exception for errors during setup sequence run.""" + pass diff --git a/packstack/installer/setup_controller.py b/packstack/installer/setup_controller.py index bd4884fb7..42c3a1f2d 100644 --- a/packstack/installer/setup_controller.py +++ b/packstack/installer/setup_controller.py @@ -3,7 +3,15 @@ Controller class is a SINGLETON which handles all groups, params, sequences, steps and replaces the CONF dictionary. """ from setup_params import Group -from setup_sequences import Sequence +from .core.sequences import Sequence + + +def steps_new_format(steplist): + # we have to duplicate title to name parameter and also only sigle + # function is allowed in new step + return [{'name': i['title'], 'title': i['title'], + 'function': i['functions'][0]} for i in steplist] + class Controller(object): @@ -64,27 +72,32 @@ class Controller(object): # Sequences and steps def addSequence(self, desc, cond, cond_match, steps): - self.__SEQUENCES.append(Sequence(desc, cond, cond_match, steps)) + self.__SEQUENCES.append(Sequence(desc, steps_new_format(steps), + condition=cond, + cond_match=cond_match)) def insertSequence(self, desc, cond, cond_match, steps, index=0): - self.__SEQUENCES.insert(index, Sequence(desc, cond, cond_match, steps)) + self.__SEQUENCES.insert(index, Sequence(desc, + steps_new_format(steps), + condition=cond, + cond_match=cond_match)) def getAllSequences(self): return self.__SEQUENCES def runAllSequences(self): for sequence in self.__SEQUENCES: - sequence.run() + sequence.run(self.CONF) def getSequenceByDesc(self, desc): for sequence in self.getAllSequences(): - if sequence.getDescription() == desc: + if sequence.name == desc: return sequence return None def __getSequenceIndexByDesc(self, desc): for sequence in self.getAllSequences(): - if sequence.getDescription() == desc: + if sequence.name == desc: return self.__SEQUENCES.index(sequence) return None @@ -97,7 +110,10 @@ class Controller(object): index = self.__getSequenceIndexByDesc(sequenceName) if index == None: index = len(self.getAllSequences()) - self.__SEQUENCES.insert(index, Sequence(desc, cond, cond_match, steps)) + self.__SEQUENCES.insert(index, Sequence(desc, + steps_new_format(steps), + condition=cond, + cond_match=cond_match)) # Groups and params def addGroup(self, group, params): diff --git a/packstack/installer/setup_sequences.py b/packstack/installer/setup_sequences.py deleted file mode 100644 index 159bf404c..000000000 --- a/packstack/installer/setup_sequences.py +++ /dev/null @@ -1,163 +0,0 @@ -""" -Base class for steps & sequences -""" -import logging -import sys -import re -import string -import traceback -import basedefs -import output_messages -from . import utils - -class Step(object): - def __init__(self, title=None, functions=[]): - self.__TITLE = None - self.__FUNCTIONS = [] - if title: - if not isinstance(title, str): - raise TypeError("step's title should be of string type instead of %s" % type(title)) - if not isinstance(functions, list): - raise TypeError("step's function should be of list type instead of %s" % type(functions)) - for function in functions: - if not callable(function): - raise TypeError("All parameters which pass as functions should be callable. %s is not callable" % function) - - self.setTitle(title) - for function in functions: - self.addFunction(function) - - def setTitle(self, title): - self.__TITLE = title - - def getTitle(self): - return self.__TITLE - - def addFunction(self, function): - self.__FUNCTIONS.append(function) - - def removeFunction(self, function): - self.__FUNCTIONS.remove(function) - - def getFunctions(self): - return self.__FUNCTIONS - - def run(self): - # keep relative space - # allow newline chars in title. This is useful for plugins - alignedTitle = self.getTitle() - if re.search('\n', alignedTitle): - alignedTitle = self.getTitle().split('\n')[-1] - for color in basedefs.COLORS: - if color in alignedTitle: - alignedTitle = string.replace(alignedTitle, color, '') - spaceLen = basedefs.SPACE_LEN - len(alignedTitle) - print "%s..."%(self.getTitle()), - sys.stdout.flush() - for function in self.getFunctions(): - try: - logging.debug("running %s"%(function.func_name)) - function() - except: - logging.debug(traceback.format_exc()) - raise - print ("[ " + utils.color_text(output_messages.INFO_DONE, 'green') + " ]").rjust(spaceLen) - -class Sequence(object): - """ - Gets 4 parameters: - description, condition's name/function, condition's expected result and steps - steps should be a list of dictionaries, example: - [ { 'title' : 'step1's title', - 'functions' : [ func1, func2, func3 ] }, - { 'title' : 'step2's tittle', - 'functions' : [ func4, func6 ] } ] - """ - def __init__(self, desc=None, cond=[], cond_match=[], steps=[]): - self.__DESCRIPTION = None - self.__CONDITION = None - self.__COND_MATCH = None - self.__STEPS = [] - - self.setDescription(desc) - self.setCondition(cond, cond_match) - for step in steps: - if not isinstance(step, dict): - raise TypeError("step should be of dictionary type instead of %s" % type(step)) - self.addStep(step['title'], step['functions']) - - def addStep(self, title, functions): - self.__STEPS.append(Step(title, functions)) - - def setDescription(self, desc): - self.__DESCRIPTION = desc - - def getDescription(self): - return self.__DESCRIPTION - - def getSteps(self): - return self.__STEPS - - def getStepByTitle(self, stepTitle): - for step in self.__STEPS: - if step.getTitle == stepTitle: - return step - return None - - def setCondition(self, cond, cond_match): - for item in [cond, cond_match]: - if not isinstance(item, list): - raise TypeError("supplied parameter should be of list type instead of %s" % type(item)) - - self.__CONDITION = cond - self.__COND_MATCH = cond_match - - def __validateCondition(self): - """ - Both _CONDITION & _COND_MATCH are lists. - if any of them is a function that needs to be run, the first member - of the list is the function and the rest of the members in that list - are the params for the said function - i.e. self._CONDITION = [function, arg1, arg2] - will be executed as function(arg1, arg2) - if the first member of the list is not a function. we handle it - as anything else (i.e. string/bool etc) - """ - if len(self.__CONDITION) < 1 and len(self.__COND_MATCH) < 1: - return True - - condResult = None - condMatchResult = None - - if callable(self.__CONDITION[0]): - condResult = self.__CONDITION[0](*self.__CONDITION[1:]) - else: - condResult = self.__CONDITION[0] - - if callable(self.__COND_MATCH[0]): - condMatchResult = self.__COND_MATCH[0](*self.__COND_MATCH[1:]) - else: - condMatchResult = self.__COND_MATCH[0] - - if condResult == condMatchResult: - return True - - return False - - def removeStepByTitle(self, stepTitle): - self.__STEPS.remove(stepTitle) - - def run(self): - if self.__validateCondition(): - for step in self.__STEPS: - step.run() - - def runStepByTitle(self, stepTitle): - step = self.getStepByTitle(stepTitle) - step.run() - - def listStepsByTitle(self): - output = [] - for step in self.__STEPS: - output.append(step.getTitle()) - return output diff --git a/packstack/installer/utils/__init__.py b/packstack/installer/utils/__init__.py index 1cd2234f2..8cc44043f 100644 --- a/packstack/installer/utils/__init__.py +++ b/packstack/installer/utils/__init__.py @@ -4,11 +4,11 @@ from .datastructures import SortedDict from .decorators import retry from .network import get_localhost_ip, host2ip, force_ip, device_from_ip from .shell import ScriptRunner, execute -from .strings import color_text, mask_string +from .strings import COLORS, color_text, mask_string __all__ = ('SortedDict', 'retry', 'ScriptRunner', 'execute', 'get_localhost_ip', 'host2ip', 'force_ip', 'device_from_ip', - 'color_text', 'mask_string') + 'COLORS', 'color_text', 'mask_string') diff --git a/packstack/installer/utils/strings.py b/packstack/installer/utils/strings.py index af47325c2..0c944197b 100644 --- a/packstack/installer/utils/strings.py +++ b/packstack/installer/utils/strings.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -from .. import basedefs - STR_MASK = '*' * 8 +COLORS = {'nocolor': "\033[0m", 'red': "\033[0;31m", + 'green': "\033[92m", 'blue': "\033[94m", + 'yellow': "\033[93m"} def color_text(text, color): @@ -11,7 +12,7 @@ def color_text(text, color): Returns given text string with appropriate color tag. Allowed values for color parameter are 'red', 'blue', 'green' and 'yellow'. """ - return '%s%s%s' % (basedefs.COLORS[color], text, basedefs.NO_COLOR) + return '%s%s%s' % (COLORS[color], text, COLORS['nocolor']) def mask_string(unmasked, mask_list=None, replace_list=None): diff --git a/packstack/plugins/cinder_250.py b/packstack/plugins/cinder_250.py index c0b0c0290..c294b2451 100644 --- a/packstack/plugins/cinder_250.py +++ b/packstack/plugins/cinder_250.py @@ -136,12 +136,12 @@ def initSequences(controller): ] controller.addSequence("Installing OpenStack Cinder", [], [], cinder_steps) -def install_cinder_deps(): +def install_cinder_deps(config): server = utils.ScriptRunner(controller.CONF['CONFIG_CINDER_HOST']) server.append("rpm -q %(package)s || yum install -y %(package)s" % {'package': "lvm2"}) server.execute() -def check_cinder_vg(): +def check_cinder_vg(config): cinders_volume = 'cinder-volumes' @@ -211,13 +211,13 @@ def check_cinder_vg(): raise exceptions.MissingRequirements(err) -def create_keystone_manifest(): +def create_keystone_manifest(config): manifestfile = "%s_keystone.pp" % controller.CONF['CONFIG_KEYSTONE_HOST'] manifestdata = getManifestTemplate("keystone_cinder.pp") appendManifestFile(manifestfile, manifestdata) -def create_manifest(): +def create_manifest(config): manifestfile = "%s_cinder.pp" % controller.CONF['CONFIG_CINDER_HOST'] manifestdata = getManifestTemplate("cinder.pp") appendManifestFile(manifestfile, manifestdata) diff --git a/packstack/plugins/dashboard_500.py b/packstack/plugins/dashboard_500.py index 0bbfd4afa..9d8c2bd20 100644 --- a/packstack/plugins/dashboard_500.py +++ b/packstack/plugins/dashboard_500.py @@ -109,7 +109,7 @@ def initSequences(controller): controller.addSequence("Installing OpenStack Horizon", [], [], steps) -def createmanifest(): +def createmanifest(config): controller.CONF["CONFIG_HORIZON_SECRET_KEY"] = uuid.uuid4().hex horizon_host = controller.CONF['CONFIG_HORIZON_HOST'] manifestfile = "%s_horizon.pp" % horizon_host diff --git a/packstack/plugins/glance_200.py b/packstack/plugins/glance_200.py index 804b49e77..6c9ab0963 100644 --- a/packstack/plugins/glance_200.py +++ b/packstack/plugins/glance_200.py @@ -87,12 +87,12 @@ def initSequences(controller): ] controller.addSequence("Installing OpenStack Glance", [], [], glancesteps) -def createkeystonemanifest(): +def createkeystonemanifest(config): manifestfile = "%s_keystone.pp" % controller.CONF['CONFIG_KEYSTONE_HOST'] manifestdata = getManifestTemplate("keystone_glance.pp") appendManifestFile(manifestfile, manifestdata) -def createmanifest(): +def createmanifest(config): manifestfile = "%s_glance.pp" % controller.CONF['CONFIG_GLANCE_HOST'] manifestdata = getManifestTemplate("glance.pp") appendManifestFile(manifestfile, manifestdata) diff --git a/packstack/plugins/keystone_100.py b/packstack/plugins/keystone_100.py index 8e6273b3c..8eea25570 100644 --- a/packstack/plugins/keystone_100.py +++ b/packstack/plugins/keystone_100.py @@ -92,7 +92,7 @@ def initSequences(controller): ] controller.addSequence("Installing OpenStack Keystone", [], [], keystonesteps) -def createmanifest(): +def createmanifest(config): manifestfile = "%s_keystone.pp"%controller.CONF['CONFIG_KEYSTONE_HOST'] manifestdata = getManifestTemplate("keystone.pp") appendManifestFile(manifestfile, manifestdata) diff --git a/packstack/plugins/mysql_001.py b/packstack/plugins/mysql_001.py index bb30727c4..7ac786c0d 100644 --- a/packstack/plugins/mysql_001.py +++ b/packstack/plugins/mysql_001.py @@ -81,7 +81,7 @@ def initSequences(controller): controller.addSequence("Installing MySQL", [], [], mysqlsteps) -def createmanifest(): +def createmanifest(config): host = controller.CONF['CONFIG_MYSQL_HOST'] manifestfile = "%s_mysql.pp" % host manifestdata = [getManifestTemplate("mysql.pp")] diff --git a/packstack/plugins/nagios_910.py b/packstack/plugins/nagios_910.py index 3a0437111..1f90a0b64 100644 --- a/packstack/plugins/nagios_910.py +++ b/packstack/plugins/nagios_910.py @@ -99,7 +99,7 @@ def nagios_host(hostname, **kwargs): return "%s}\n" % out -def createmanifest(): +def createmanifest(config): manifest_entries = '' # I should be adding service entries with nagios_service but it appears to be broken # http://projects.puppetlabs.com/issues/3420 @@ -167,7 +167,7 @@ def createmanifest(): manifestdata = getManifestTemplate("nagios_server.pp") appendManifestFile(manifestfile, manifestdata) -def createnrpemanifests(): +def createnrpemanifests(config): for hostname in gethostlist(controller.CONF): controller.CONF['CONFIG_NRPE_HOST'] = hostname manifestfile = "%s_nagios_nrpe.pp" % hostname diff --git a/packstack/plugins/nova_300.py b/packstack/plugins/nova_300.py index bc5ec9dc9..2f5901dc3 100644 --- a/packstack/plugins/nova_300.py +++ b/packstack/plugins/nova_300.py @@ -272,25 +272,25 @@ def initSequences(controller): controller.addSequence("Installing OpenStack Nova API", [], [], novaapisteps) -def createapimanifest(): +def createapimanifest(config): manifestfile = "%s_api_nova.pp"%controller.CONF['CONFIG_NOVA_API_HOST'] manifestdata = getManifestTemplate("nova_api.pp") appendManifestFile(manifestfile, manifestdata, 'novaapi') -def createkeystonemanifest(): +def createkeystonemanifest(config): manifestfile = "%s_keystone.pp"%controller.CONF['CONFIG_KEYSTONE_HOST'] manifestdata = getManifestTemplate("keystone_nova.pp") appendManifestFile(manifestfile, manifestdata) -def createcertmanifest(): +def createcertmanifest(config): manifestfile = "%s_nova.pp"%controller.CONF['CONFIG_NOVA_CERT_HOST'] manifestdata = getManifestTemplate("nova_cert.pp") appendManifestFile(manifestfile, manifestdata) -def createconductormanifest(): +def createconductormanifest(config): manifestfile = "%s_nova.pp"%controller.CONF['CONFIG_NOVA_CONDUCTOR_HOST'] manifestdata = getManifestTemplate("nova_conductor.pp") appendManifestFile(manifestfile, manifestdata) @@ -328,7 +328,7 @@ def bring_up_ifcfg(host, device): raise ScriptRuntimeError(msg) -def createcomputemanifest(): +def createcomputemanifest(config): for host in controller.CONF["CONFIG_NOVA_COMPUTE_HOSTS"].split(","): controller.CONF["CONFIG_NOVA_COMPUTE_HOST"] = host manifestdata = getManifestTemplate("nova_compute.pp") @@ -347,7 +347,7 @@ def createcomputemanifest(): appendManifestFile(manifestfile, manifestdata + "\n" + nova_config_options.getManifestEntry()) -def createnetworkmanifest(): +def createnetworkmanifest(config): host = controller.CONF['CONFIG_NOVA_NETWORK_HOST'] for i in ('CONFIG_NOVA_NETWORK_PRIVIF', 'CONFIG_NOVA_NETWORK_PUBIF'): check_ifcfg(host, controller.CONF[i]) @@ -372,19 +372,19 @@ def createnetworkmanifest(): appendManifestFile(manifestfile, manifestdata) -def createschedmanifest(): +def createschedmanifest(config): manifestfile = "%s_nova.pp"%controller.CONF['CONFIG_NOVA_SCHED_HOST'] manifestdata = getManifestTemplate("nova_sched.pp") appendManifestFile(manifestfile, manifestdata) -def createvncproxymanifest(): +def createvncproxymanifest(config): manifestfile = "%s_nova.pp"%controller.CONF['CONFIG_NOVA_VNCPROXY_HOST'] manifestdata = getManifestTemplate("nova_vncproxy.pp") appendManifestFile(manifestfile, manifestdata) -def createcommonmanifest(): +def createcommonmanifest(config): for manifestfile, marker in manifestfiles.getFiles(): if manifestfile.endswith("_nova.pp"): data = getManifestTemplate("nova_common.pp") diff --git a/packstack/plugins/openstack_client_400.py b/packstack/plugins/openstack_client_400.py index 65ff62f21..e1c603211 100644 --- a/packstack/plugins/openstack_client_400.py +++ b/packstack/plugins/openstack_client_400.py @@ -57,7 +57,7 @@ def initSequences(controller): ] controller.addSequence("Installing OpenStack Client", [], [], osclientsteps) -def createmanifest(): +def createmanifest(config): client_host = controller.CONF['CONFIG_OSCLIENT_HOST'].strip() manifestfile = "%s_osclient.pp" % client_host manifestdata = getManifestTemplate("openstack_client.pp") diff --git a/packstack/plugins/postscript_949.py b/packstack/plugins/postscript_949.py index 1740a2218..0fcebf904 100644 --- a/packstack/plugins/postscript_949.py +++ b/packstack/plugins/postscript_949.py @@ -42,7 +42,7 @@ def initSequences(controller): ] controller.addSequence("Running post install scripts", [], [], osclientsteps) -def createmanifest(): +def createmanifest(config): for hostname in gethostlist(controller.CONF): manifestfile = "%s_postscript.pp" % hostname manifestdata = getManifestTemplate("postscript.pp") diff --git a/packstack/plugins/prescript_000.py b/packstack/plugins/prescript_000.py index 6c4dea73f..ed6cb3418 100644 --- a/packstack/plugins/prescript_000.py +++ b/packstack/plugins/prescript_000.py @@ -146,13 +146,13 @@ def initSequences(controller): 'instances might be problem for ' 'some OpenStack components.') -def createmanifest(): +def createmanifest(config): for hostname in gethostlist(controller.CONF): manifestfile = "%s_prescript.pp" % hostname manifestdata = getManifestTemplate("prescript.pp") appendManifestFile(manifestfile, manifestdata) -def create_ntp_manifest(): +def create_ntp_manifest(config): servers = '' for srv in controller.CONF['CONFIG_NTP_SERVERS'].split(','): srv = srv.strip() diff --git a/packstack/plugins/puppet_950.py b/packstack/plugins/puppet_950.py index 2009e29ee..9f0c1ebf2 100644 --- a/packstack/plugins/puppet_950.py +++ b/packstack/plugins/puppet_950.py @@ -59,13 +59,13 @@ def initSequences(controller): controller.addSequence("Puppet", [], [], puppetsteps) -def runCleanup(): +def runCleanup(config): localserver = utils.ScriptRunner() localserver.append("rm -rf %s/*pp" % basedefs.PUPPET_MANIFEST_DIR) localserver.execute() -def installdeps(): +def installdeps(config): for hostname in gethostlist(controller.CONF): server = utils.ScriptRunner(hostname) for package in ("puppet", "openssh-clients", "tar"): @@ -73,7 +73,7 @@ def installdeps(): server.execute() -def copyPuppetModules(): +def copyPuppetModules(config): os_modules = ' '.join(('apache', 'cinder', 'concat', 'create_resources', 'firewall', 'glance', 'horizon', 'inifile', @@ -165,7 +165,7 @@ def waitforpuppet(currently_running): print ("[ " + utils.color_text(output_messages.INFO_DONE, 'green') + " ]") -def applyPuppetManifest(): +def applyPuppetManifest(config): print currently_running = [] lastmarker = None diff --git a/packstack/plugins/qpid_002.py b/packstack/plugins/qpid_002.py index 1ea2c04f1..ae4d2a2d4 100644 --- a/packstack/plugins/qpid_002.py +++ b/packstack/plugins/qpid_002.py @@ -57,7 +57,7 @@ def initSequences(controller): ] controller.addSequence("Installing QPID", [], [], qpidsteps) -def createmanifest(): +def createmanifest(config): manifestfile = "%s_qpid.pp"%controller.CONF['CONFIG_QPID_HOST'] manifestdata = getManifestTemplate("qpid.pp") appendManifestFile(manifestfile, manifestdata, 'pre') diff --git a/packstack/plugins/serverprep_901.py b/packstack/plugins/serverprep_901.py index 403e3c311..cb6d2c2bc 100644 --- a/packstack/plugins/serverprep_901.py +++ b/packstack/plugins/serverprep_901.py @@ -373,7 +373,7 @@ def initSequences(controller): controller.addSequence("Preparing servers", [], [], preparesteps) -def serverprep(): +def serverprep(config): config = controller.CONF rh_username = None diff --git a/packstack/plugins/sshkeys_000.py b/packstack/plugins/sshkeys_000.py index 0fab872d8..a81a1c0e6 100644 --- a/packstack/plugins/sshkeys_000.py +++ b/packstack/plugins/sshkeys_000.py @@ -61,7 +61,7 @@ def initSequences(controller): controller.addSequence("Setting up ssh keys", [], [], puppetsteps) -def installKeys(): +def installKeys(config): with open(controller.CONF["CONFIG_SSH_KEY"]) as fp: sshkeydata = fp.read().strip() for hostname in gethostlist(controller.CONF): diff --git a/packstack/plugins/swift_600.py b/packstack/plugins/swift_600.py index c4a09cb87..44280045d 100644 --- a/packstack/plugins/swift_600.py +++ b/packstack/plugins/swift_600.py @@ -130,7 +130,7 @@ def initSequences(controller): controller.addSequence("Installing OpenStack Swift", [], [], steps) -def createkeystonemanifest(): +def createkeystonemanifest(config): manifestfile = "%s_keystone.pp"%controller.CONF['CONFIG_KEYSTONE_HOST'] controller.CONF['CONFIG_SWIFT_PROXY'] = controller.CONF['CONFIG_SWIFT_PROXY_HOSTS'].split(',')[0] manifestdata = getManifestTemplate("keystone_swift.pp") @@ -158,7 +158,7 @@ def parse_devices(config_swift_storage_hosts): # The ring file should be built and distributed befor the storage services # come up. Specifically the replicator crashes if the ring isn't present -def createbuildermanifest(): +def createbuildermanifest(config): # TODO : put this on the proxy server, will need to change this later controller.CONF['CONFIG_SWIFT_BUILDER_HOST'] = controller.CONF['CONFIG_SWIFT_PROXY_HOSTS'].split(',')[0] manifestfile = "%s_ring_swift.pp"%controller.CONF['CONFIG_SWIFT_BUILDER_HOST'] @@ -178,7 +178,7 @@ def createbuildermanifest(): appendManifestFile(manifestfile, manifestdata, 'swiftbuilder') -def createproxymanifest(): +def createproxymanifest(config): manifestfile = "%s_swift.pp"%controller.CONF['CONFIG_SWIFT_PROXY_HOSTS'] manifestdata = getManifestTemplate("swift_proxy.pp") # If the proxy server is also a storage server then swift::ringsync will be included for the storage server @@ -212,7 +212,7 @@ def check_device(host, device): return False -def createstoragemanifest(): +def createstoragemanifest(config): # this need to happen once per storage host for host in set([device['host'] for device in devices]): @@ -238,7 +238,7 @@ def createstoragemanifest(): appendManifestFile(manifestfile, manifestdata) -def createcommonmanifest(): +def createcommonmanifest(config): for manifestfile, marker in manifestfiles.getFiles(): if manifestfile.endswith("_swift.pp"): data = getManifestTemplate("swift_common.pp") diff --git a/tests/installer/test_sequences.py b/tests/installer/test_sequences.py new file mode 100644 index 000000000..fb8d64654 --- /dev/null +++ b/tests/installer/test_sequences.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013, Red Hat, Inc. +# +# 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 sys +import StringIO +from unittest import TestCase + +from packstack.installer import utils +from packstack.installer.core.sequences import * + +from ..test_base import PackstackTestCaseMixin + + +class StepTestCase(PackstackTestCaseMixin, TestCase): + def setUp(self): + super(StepTestCase, self).setUp() + self._stdout = sys.stdout + sys.stdout = StringIO.StringIO() + + def tearDown(self): + super(StepTestCase, self).tearDown() + sys.stdout = self._stdout + + def test_run(self): + """ + Test packstack.instaler.core.sequences.Step run. + """ + def func(config): + if 'test' not in config: + raise AssertionError('Missing config value.') + + step = Step('test', func, title='Running test') + step.run(config={'test': 'test'}) + contents = sys.stdout.getvalue() + + state = '[ %s ]\n' % utils.color_text('DONE', 'green') + if not contents.startswith('Running test') or \ + not contents.endswith(state): + raise AssertionError('Step run test failed: %s' % contents) + + +class SequenceTestCase(PackstackTestCaseMixin, TestCase): + def setUp(self): + super(SequenceTestCase, self).setUp() + self._stdout = sys.stdout + sys.stdout = StringIO.StringIO() + + self.steps = [{'name': '1', 'function': lambda x: True, + 'title': 'Step 1'}, + {'name': '2', 'function': lambda x: True, + 'title': 'Step 2'}, + {'name': '3', 'function': lambda x: True, + 'title': 'Step 3'}] + + self.seq = Sequence('test', self.steps, condition='test', + cond_match='test') + + def tearDown(self): + super(SequenceTestCase, self).tearDown() + sys.stdout = self._stdout + + def test_run(self): + """ + Test packstack.instaler.core.sequences.Sequence run. + """ + self.seq.run() + contents = sys.stdout.getvalue() + self.assertEqual(contents, '') + + self.seq.run(config={'test': 'test'}, step='2') + contents = sys.stdout.getvalue() + assert contents.startswith('Step 2') + + output = [] + state_fmt = '[ %s ]\n' + self.steps.insert(0, {'title': 'Step 2'}) + for i in self.steps: + space = 70 - len(i['title']) + title = '[ %s ]\n' % utils.color_text('DONE', 'green') + output.append('%s...%s' % (i['title'], title.rjust(space))) + + self.seq.run(config={'test': 'test'}) + contents = sys.stdout.getvalue() + self.assertEqual(contents, ''.join(output))