From 931b896cc163ee933bf1f83895b680b9cec9022d Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 21 Mar 2012 17:30:41 -0400 Subject: [PATCH 1/2] Split the action runner into several subclasses and move the logic for each action into the appropriate subclass. --- devstack/distro.py | 5 +- devstack/opts.py | 3 +- devstack/progs/actions.py | 429 +++++++++++++++++++++++--------------- devstack/settings.py | 8 - stack | 23 +- 5 files changed, 277 insertions(+), 191 deletions(-) diff --git a/devstack/distro.py b/devstack/distro.py index d5040a7c..a1b77814 100644 --- a/devstack/distro.py +++ b/devstack/distro.py @@ -22,6 +22,7 @@ import shlex import yaml +from devstack.progs import actions from devstack import decorators from devstack import importer from devstack import log as logging @@ -140,7 +141,9 @@ class Distro(object): entry_point = component_info[action] cls = importer.import_entry_point(entry_point) # Knock all action class info (and any other keys) - key_deletions = [action] + settings.ACTIONS + # FIXME: This module shouldn't need to have knowledge + # of all available actions. + key_deletions = [action] + actions.get_action_names() for k in key_deletions: if k in component_info: del component_info[k] diff --git a/devstack/opts.py b/devstack/opts.py index 9425aceb..cee72b24 100644 --- a/devstack/opts.py +++ b/devstack/opts.py @@ -17,6 +17,7 @@ from optparse import IndentedHelpFormatter from optparse import OptionParser, OptionGroup +from devstack.progs import actions from devstack import log as logging from devstack import settings from devstack import version @@ -64,7 +65,7 @@ def parse(): type="string", dest="action", metavar="ACTION", - help="required action to perform: %s" % (_format_list(settings.ACTIONS))) + help="required action to perform: %s" % (_format_list(actions.get_action_names()))) base_group.add_option("-d", "--directory", action="store", type="string", diff --git a/devstack/progs/actions.py b/devstack/progs/actions.py index 3e1ed88f..5ba79e84 100644 --- a/devstack/progs/actions.py +++ b/devstack/progs/actions.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License.. +import abc + from devstack import env_rc from devstack import exceptions as excp from devstack import log as logging @@ -22,153 +24,66 @@ from devstack import shell as sh LOG = logging.getLogger("devstack.progs.actions") -# For actions in this list we will reverse the component order -REVERSE_ACTIONS = [settings.UNINSTALL, settings.STOP] - -# For these actions we will attempt to make an rc file if it does not exist -RC_FILE_MAKE_ACTIONS = [settings.INSTALL] - -# The order of which uninstalls happen + message of what is happening -# (before and after) -UNINSTALL_ORDERING = [ - ( - "Unconfiguring {name}.", - (lambda instance: (instance.unconfigure())), - None, - ), - ( - "Pre-uninstalling {name}.", - (lambda instance: (instance.pre_uninstall())), - None, - ), - ( - "Uninstalling {name}.", - (lambda instance: (instance.uninstall())), - None, - ), - ( - "Post-uninstalling {name}.", - (lambda instance: (instance.post_uninstall())), - None, - ), -] - -# The order of which starts happen + message of what is happening -# (before and after) -STARTS_ORDERING = [ - ( - "Configuring runner for {name}.", - (lambda instance: (instance.configure())), - None, - ), - ( - "Pre-starting {name}.", - (lambda instance: (instance.pre_start())), - None, - ), - ( - "Starting {name}.", - (lambda instance: (instance.start())), - "Started {result} applications.", - ), - ( - "Post-starting {name}.", - (lambda instance:(instance.post_start())), - None, - ), -] - -# The order of which stops happen + message of what is happening -# (before and after) -STOPS_ORDERING = [ - ( - "Stopping {name}.", - (lambda instance:(instance.stop())), - "Stopped {result} items.", - ), -] - -# The order of which install happen + message of what is happening -# (before and after) -INSTALL_ORDERING = [ - ( - "Downloading {name}.", - (lambda instance: (instance.download())), - "Performed {result} downloads.", - ), - ( - "Configuring {name}.", - (lambda instance: (instance.configure())), - "Configured {result} items.", - ), - ( - "Pre-installing {name}.", - (lambda instance: (instance.pre_install())), - None, - ), - ( - "Installing {name}.", - (lambda instance: (instance.install())), - "Finished install of {name} - check {result} for traces of what happened.", - ), - ( - "Post-installing {name}.", - (lambda instance: (instance.post_install())), - None, - ), -] - -# Map of action name to action order -ACTION_MP = { - settings.START: STARTS_ORDERING, - settings.STOP: STOPS_ORDERING, - settings.INSTALL: INSTALL_ORDERING, - settings.UNINSTALL: UNINSTALL_ORDERING, -} - -# These actions must have there prerequisite action accomplished (if -# determined by the boolean lambda to be needed) -PREQ_ACTIONS = { - settings.START: ((lambda instance: (not instance.is_installed())), settings.INSTALL), - settings.UNINSTALL: ((lambda instance: (instance.is_started())), settings.STOP), -} - class ActionRunner(object): - def __init__(self, distro, action, - cfg, pw_gen, pkg_manager, - **kargs): + __meta__ = abc.ABCMeta + + PREREQ = None + + def __init__(self, + distro, + cfg, + pw_gen, + pkg_manager, + **kargs): self.distro = distro - self.action = action self.cfg = cfg self.pw_gen = pw_gen self.pkg_manager = pkg_manager self.keep_old = kargs.get('keep_old', False) self.force = kargs.get('force', False) - def _apply_reverse(self, action, component_order): - adjusted_order = list(component_order) - if action in REVERSE_ACTIONS: - adjusted_order.reverse() - return adjusted_order + @abc.abstractmethod + def _instance_needs_prereq(self, instance): + """Determine if the instance will require our prereq to be invoked. - def _construct_instances(self, persona, action, root_dir): + Return boolean where True means invoke the prereq. + """ + return + + @abc.abstractmethod + def _run(self, persona, root_dir, component_order, instances): + """Run the phases of processing for this action. + + Subclasses are expected to override this method to + do something useful. + """ + + def _order_components(self, components): + """Returns the components in the order they should be processed. + """ + # Duplicate the list to avoid problems if it is updated later. + return components[:] + + def _construct_instances(self, persona, root_dir): + """Create component objects for each component in the persona. + """ components = persona.wanted_components desired_subsystems = persona.wanted_subsystems or dict() component_opts = persona.component_options or dict() instances = dict() for c in components: - (cls, my_info) = self.distro.extract_component(c, action) + (cls, my_info) = self.distro.extract_component(c, self.NAME) LOG.debug("Constructing class %s" % (cls)) cls_kvs = dict() cls_kvs['runner'] = self cls_kvs['component_dir'] = sh.joinpths(root_dir, c) - cls_kvs['subsystem_info'] = my_info.get('subsystems') or dict() + cls_kvs['subsystem_info'] = my_info.get('subsystems', {}) cls_kvs['all_instances'] = instances cls_kvs['name'] = c cls_kvs['keep_old'] = self.keep_old - cls_kvs['desired_subsystems'] = desired_subsystems.get(c) or set() - cls_kvs['options'] = component_opts.get(c) or dict() + cls_kvs['desired_subsystems'] = desired_subsystems.get(c, set()) + cls_kvs['options'] = component_opts.get(c, {}) # The above is not overrideable... for (k, v) in my_info.items(): if k not in cls_kvs: @@ -183,57 +98,239 @@ class ActionRunner(object): instance.verify() def _warm_components(self, component_order, instances): - LOG.info("Warming up your component configurations (ie so you won't be prompted later)") + LOG.info("Warming up component configurations") for c in component_order: instance = instances[c] instance.warm_configs() + def _run_phase(self, start_msg, functor, end_msg, component_order, instances): + """Run a given 'functor' across all of the components, in order. + """ + for c in component_order: + instance = instances[c] + if start_msg: + LOG.info(start_msg.format(name=c)) + try: + result = functor(instance) + if end_msg: + LOG.info(end_msg.format(name=c, result=result)) + except (excp.NoTraceException) as e: + if self.force: + LOG.debug("Skipping exception [%s]" % (e)) + else: + raise + + def _handle_prereq(self, persona, instances, root_dir): + if not self.PREREQ: + return + components_needing_prereq = [ + c + for (c, instance) in instances.items() + if self._instance_needs_prereq(instance) + ] + if components_needing_prereq: + LOG.info("Processing prerequisite action [%s] requested by (%s) components.", + self.PREREQ.NAME, ", ".join(components_needing_prereq)) + prereq = self.PREREQ(self.distro, + self.cfg, + self.pw_gen, + self.pkg_manager, + keep_old=self.keep_old, + force=self.force, + ) + prereq.run(persona, root_dir) + + def run(self, persona, root_dir): + instances = self._construct_instances(persona, root_dir) + self._handle_prereq(persona, instances, root_dir) + component_order = self._order_components(persona.wanted_components) + LOG.info("Processing components [%s] (in that order) for action [%s]", + "->".join(component_order), self.NAME) + self._verify_components(component_order, instances) + self._warm_components(component_order, instances) + self._run(persona, root_dir, component_order, instances) + return + + +class InstallRunner(ActionRunner): + NAME = 'install' + + def _instance_needs_prereq(self, instance): + return False + def _write_rc_file(self, root_dir): writer = env_rc.RcWriter(self.cfg, self.pw_gen, root_dir) if not sh.isfile(settings.OSRC_FN): - LOG.info("Generating a file at [%s] that will contain your environment settings." % (settings.OSRC_FN)) + LOG.info("Generating a file at [%s] that will contain your environment settings.", + settings.OSRC_FN) writer.write(settings.OSRC_FN) else: - LOG.info("Updating a file at [%s] that contains your environment settings." % (settings.OSRC_FN)) + LOG.info("Updating a file at [%s] that contains your environment settings.", + settings.OSRC_FN) am_upd = writer.update(settings.OSRC_FN) - LOG.info("Updated [%s] settings in rc file [%s]" % (am_upd, settings.OSRC_FN)) + LOG.info("Updated [%s] settings in rc file [%s]", + am_upd, settings.OSRC_FN) - def _run_instances(self, action, component_order, instances): - for (start_msg, functor, end_msg) in ACTION_MP[action]: - for c in component_order: - instance = instances[c] - if start_msg: - LOG.info(start_msg.format(name=c)) - try: - result = functor(instance) - if end_msg: - LOG.info(end_msg.format(name=c, result=result)) - except (excp.NoTraceException) as e: - if self.force: - LOG.debug("Skipping exception [%s]" % (e)) - else: - raise + def _run(self, persona, root_dir, component_order, instances): + self._write_rc_file(root_dir) + self._run_phase( + 'Downloading {name}', + lambda i: i.download(), + "Performed {result} downloads.", + component_order, + instances, + ) + self._run_phase( + 'Configuring {name}', + lambda i: i.configure(), + "Configured {result} items.", + component_order, + instances, + ) + self._run_phase( + 'Pre-installing {name}', + lambda i: i.pre_install(), + None, + component_order, + instances, + ) + self._run_phase( + 'Installing {name}', + lambda i: i.install(), + "Finished install of {name} - check {result} for traces of what happened.", + component_order, + instances, + ) + self._run_phase( + 'Post-installing {name}', + lambda i: i.post_install(), + None, + component_order, + instances, + ) - def _run_action(self, persona, action, root_dir): - instances = self._construct_instances(persona, action, root_dir) - if action in PREQ_ACTIONS: - (check_functor, preq_action) = PREQ_ACTIONS[action] - checks_passed_components = list() - for (c, instance) in instances.items(): - if check_functor(instance): - checks_passed_components.append(c) - if checks_passed_components: - LOG.info("Activating prerequisite action [%s] requested by (%s) components." - % (preq_action, ", ".join(checks_passed_components))) - self._run_action(persona, preq_action, root_dir) - component_order = self._apply_reverse(action, persona.wanted_components) - LOG.info("Activating components [%s] (in that order) for action [%s]" % - ("->".join(component_order), action)) - self._verify_components(component_order, instances) - self._warm_components(component_order, instances) - if action in RC_FILE_MAKE_ACTIONS: - self._write_rc_file(root_dir) - self._run_instances(action, component_order, instances) - def run(self, persona, root_dir): - self._run_action(persona, self.action, root_dir) +class StartRunner(ActionRunner): + NAME = 'start' + PREREQ = InstallRunner + + def _instance_needs_prereq(self, instance): + return not instance.is_installed() + + def _run(self, persona, root_dir, component_order, instances): + self._run_phase( + 'Configuring runner for {name}', + lambda i: i.configure(), + None, + component_order, + instances, + ) + self._run_phase( + 'Pre-starting {name}', + lambda i: i.pre_start(), + None, + component_order, + instances, + ) + self._run_phase( + 'Starting {name}', + lambda i: i.start(), + "Started {result} applications.", + component_order, + instances, + ) + self._run_phase( + 'Post-starting {name}', + lambda i: i.post_start(), + None, + component_order, + instances, + ) + + +class StopRunner(ActionRunner): + NAME = 'stop' + + def _instance_needs_prereq(self, instance): + return False + + def _order_components(self, components): + components = super(StopRunner, self)._order_components(components) + components.reverse() + return components + + def _run(self, persona, root_dir, component_order, instances): + self._run_phase( + 'Stopping {name}', + lambda i: i.stop(), + 'Stopped {result} items', + component_order, + instances, + ) + + +class UninstallRunner(ActionRunner): + NAME = 'uninstall' + PREREQ = StopRunner + + def _instance_needs_prereq(self, instance): + return instance.is_started() + + def _order_components(self, components): + components = super(UninstallRunner, self)._order_components(components) + components.reverse() + return components + + def _run(self, persona, root_dir, component_order, instances): + self._run_phase( + 'Unconfiguring {name}', + lambda i: i.unconfigure(), + None, + component_order, + instances, + ) + self._run_phase( + 'Pre-uninstalling {name}', + lambda i: i.pre_uninstall(), + None, + component_order, + instances, + ) + self._run_phase( + 'Uninstalling {name}', + lambda i: i.uninstall(), + None, + component_order, + instances, + ) + self._run_phase( + 'Post-uninstalling {name}', + lambda i: i.post_uninstall(), + None, + component_order, + instances, + ) + + + +_NAMES_TO_RUNNER = { + 'install': InstallRunner, + 'uninstall': UninstallRunner, + 'start': StartRunner, + 'stop': StopRunner, + } + + +def get_action_names(): + """Returns a list of the available action names. + """ + return sorted(_NAMES_TO_RUNNER.keys()) + + +def get_runner_factory(action): + """Given an action name, look up the factory for that action runner. + """ + try: + return _NAMES_TO_RUNNER[action] + except KeyError: + raise ValueError('Unrecognized action %s' % action) diff --git a/devstack/settings.py b/devstack/settings.py index 850433ae..968fcdb7 100644 --- a/devstack/settings.py +++ b/devstack/settings.py @@ -47,14 +47,6 @@ RC_FN_TEMPL = "os-%s.rc" LOCALRC_FN = RC_FN_TEMPL % ('local') OSRC_FN = RC_FN_TEMPL % ('core') -# Program -# actions -INSTALL = "install" -UNINSTALL = "uninstall" -START = "start" -STOP = "stop" -ACTIONS = [INSTALL, UNINSTALL, START, STOP] - # Where the configs and templates should be at. STACK_BIN_DIR = os.path.abspath(os.path.dirname(sys.argv[0])) STACK_CONFIG_DIR = os.path.join(STACK_BIN_DIR, "conf") diff --git a/stack b/stack index fe63ab91..afbb53cb 100755 --- a/stack +++ b/stack @@ -39,15 +39,6 @@ from devstack.progs import actions LOG = logging.getLogger("devstack.stack") -# This is used to map an action to a useful string for -# the welcome display -_WELCOME_MAP = { - settings.INSTALL: "INSTALLER", - settings.UNINSTALL: "UNINSTALLER", - settings.START: "STARTER", - settings.STOP: "STOPPER", -} - _CFG_GROUPS = { cfg_helpers.make_id('passwords', None): 'Passwords', cfg_helpers.make_id('db', None): 'Database info', @@ -116,7 +107,7 @@ def setup_root(root_dir): def run(args): action = args.pop("action", '').strip().lower() - if not (action in settings.ACTIONS): + if action not in actions.get_action_names(): print(utils.color_text("No valid action specified!", "red")) return False @@ -139,7 +130,7 @@ def run(args): persona_fn = sh.abspth(persona_fn) # Welcome! - (repeat_string, line_max_len) = utils.welcome(_WELCOME_MAP.get(action)) + (repeat_string, line_max_len) = utils.welcome(action.upper()) print(utils.center_text("Action Runner", repeat_string, line_max_len)) # !! @@ -162,10 +153,12 @@ def run(args): pkg_cls = dist.get_packager_factory() pkg_manager = pkg_cls(dist, args.get('keep_old', False)) - runner = actions.ActionRunner(dist, action, - config, pw_gen, - pkg_manager, - **args) + runner_factory = actions.get_runner_factory(action) + runner = runner_factory(dist, + config, + pw_gen, + pkg_manager, + **args) LOG.info("Starting action %r on %s for distro: %r" % (action, date.rcf8222date(), dist.name)) LOG.info("Using persona: %r" % (persona_fn)) From a8780f0dac9dab011788e869c4bf6d9191b6e3e8 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 21 Mar 2012 18:09:34 -0400 Subject: [PATCH 2/2] reorg yaml file to make it easier to not pass some component metadata to component constructors --- conf/distros/fedora-16.yaml | 19 +++++ conf/distros/rhel-6.yaml | 126 +++++++++++++++++-------------- conf/distros/ubuntu-oneiric.yaml | 126 +++++++++++++++++-------------- devstack/distro.py | 13 +--- 4 files changed, 163 insertions(+), 121 deletions(-) diff --git a/conf/distros/fedora-16.yaml b/conf/distros/fedora-16.yaml index 1e0bab65..74794714 100644 --- a/conf/distros/fedora-16.yaml +++ b/conf/distros/fedora-16.yaml @@ -39,6 +39,7 @@ commands: stop: service rabbitmq-server stop components: db: + action_classes: {} packages: - name: mysql removable: true @@ -47,6 +48,7 @@ components: removable: true version: 5.5* general: + action_classes: {} packages: - name: curl removable: false @@ -133,6 +135,7 @@ components: removable: false version: 1.12* glance: + action_classes: {} packages: - name: MySQL-python removable: true @@ -162,6 +165,7 @@ components: removable: true version: 0.5* horizon: + action_classes: {} packages: - name: Django removable: true @@ -215,6 +219,7 @@ components: removable: true version: 0.5* keystone: + action_classes: {} packages: - name: MySQL-python removable: true @@ -271,6 +276,7 @@ components: removable: false version: 3.7* keystone-client: + action_classes: {} packages: - name: python-argparse removable: true @@ -279,13 +285,16 @@ components: removable: true version: 0.5* melange: + action_classes: {} packages: null n-api: + action_classes: {} packages: - name: python-dateutil removable: false version: 1.5* n-cpu: + action_classes: {} packages: - name: iscsi-initiator-utils removable: true @@ -309,11 +318,13 @@ components: removable: true version: 0.15* n-vnc: + action_classes: {} packages: - name: numpy removable: true version: 1.6* n-vol: + action_classes: {} packages: - name: iscsi-initiator-utils removable: true @@ -325,6 +336,7 @@ components: removable: true version: 1.0* nova: + action_classes: {} packages: - name: MySQL-python removable: true @@ -405,6 +417,7 @@ components: removable: false version: 3.7* nova-client: + action_classes: {} packages: - name: python-argparse removable: true @@ -413,6 +426,7 @@ components: removable: true version: 0.5* quantum: + action_classes: {} packages: - name: python-eventlet removable: true @@ -436,11 +450,13 @@ components: removable: true version: 1.12* quantum-client: + action_classes: {} packages: - name: python-gflags removable: true version: 1.5* quantum-openvswitch: + action_classes: {} packages: - name: MySQL-python removable: true @@ -449,6 +465,7 @@ components: removable: true version: 0.7* rabbit-mq: + action_classes: {} packages: - name: rabbitmq-server pre-install: @@ -466,6 +483,7 @@ components: removable: true version: 2.6* swift: + action_classes: {} packages: - name: memcached removable: true @@ -496,3 +514,4 @@ components: version: 3.7* packager_name: devstack.packaging.yum:YumPackager ... + diff --git a/conf/distros/rhel-6.yaml b/conf/distros/rhel-6.yaml index a048a12d..aea77d02 100644 --- a/conf/distros/rhel-6.yaml +++ b/conf/distros/rhel-6.yaml @@ -39,7 +39,11 @@ commands: stop: service rabbitmq-server stop components: db: - install: devstack.distros.rhel6:DBInstaller + action_classes: + install: devstack.distros.rhel6:DBInstaller + start: devstack.components.db:DBRuntime + stop: devstack.components.db:DBRuntime + uninstall: devstack.components.db:DBUninstaller packages: - name: mysql removable: true @@ -47,11 +51,12 @@ components: - name: mysql-server removable: true version: 5.1* - start: devstack.components.db:DBRuntime - stop: devstack.components.db:DBRuntime - uninstall: devstack.components.db:DBUninstaller general: - install: devstack.components.pkglist:Installer + action_classes: + install: devstack.components.pkglist:Installer + start: devstack.component:EmptyRuntime + stop: devstack.component:EmptyRuntime + uninstall: devstack.components.pkglist:Uninstaller packages: - name: coreutils removable: false @@ -157,11 +162,12 @@ components: version: 0.8.0 - name: nose version: 1.1.2 - start: devstack.component:EmptyRuntime - stop: devstack.component:EmptyRuntime - uninstall: devstack.components.pkglist:Uninstaller glance: - install: devstack.components.glance:GlanceInstaller + action_classes: + install: devstack.components.glance:GlanceInstaller + start: devstack.components.glance:GlanceRuntime + stop: devstack.components.glance:GlanceRuntime + uninstall: devstack.components.glance:GlanceUninstaller packages: - name: MySQL-python removable: true @@ -211,11 +217,12 @@ components: - name: pycrypto options: --upgrade version: '2.5' - start: devstack.components.glance:GlanceRuntime - stop: devstack.components.glance:GlanceRuntime - uninstall: devstack.components.glance:GlanceUninstaller horizon: - install: devstack.distros.rhel6:HorizonInstaller + action_classes: + install: devstack.distros.rhel6:HorizonInstaller + start: devstack.components.horizon:HorizonRuntime + stop: devstack.components.horizon:HorizonRuntime + uninstall: devstack.components.horizon:HorizonUninstaller packages: - name: httpd removable: true @@ -273,11 +280,12 @@ components: version: 1.7.9.3 - name: sqlalchemy-migrate version: 0.7.2 - start: devstack.components.horizon:HorizonRuntime - stop: devstack.components.horizon:HorizonRuntime - uninstall: devstack.components.horizon:HorizonUninstaller keystone: - install: devstack.components.keystone:KeystoneInstaller + action_classes: + install: devstack.components.keystone:KeystoneInstaller + start: devstack.components.keystone:KeystoneRuntime + stop: devstack.components.keystone:KeystoneRuntime + uninstall: devstack.components.keystone:KeystoneUninstaller packages: - name: MySQL-python removable: true @@ -350,11 +358,12 @@ components: version: '2.5' - name: sqlalchemy-migrate version: 0.7.2 - start: devstack.components.keystone:KeystoneRuntime - stop: devstack.components.keystone:KeystoneRuntime - uninstall: devstack.components.keystone:KeystoneUninstaller keystone-client: - install: devstack.components.keystone_client:KeyStoneClientInstaller + action_classes: + install: devstack.components.keystone_client:KeyStoneClientInstaller + start: devstack.components.keystone_client:KeyStoneClientRuntime + stop: devstack.components.keystone_client:KeyStoneClientRuntime + uninstall: devstack.components.keystone_client:KeyStoneClientUninstaller packages: - meta: epel: true @@ -366,24 +375,27 @@ components: name: python-prettytable removable: true version: 0.5* - start: devstack.components.keystone_client:KeyStoneClientRuntime - stop: devstack.components.keystone_client:KeyStoneClientRuntime - uninstall: devstack.components.keystone_client:KeyStoneClientUninstaller melange: - install: devstack.components.melange:MelangeInstaller - start: devstack.components.melange:MelangeRuntime - stop: devstack.components.melange:MelangeRuntime - uninstall: devstack.components.melange:MelangeUninstaller + action_classes: + install: devstack.components.melange:MelangeInstaller + start: devstack.components.melange:MelangeRuntime + stop: devstack.components.melange:MelangeRuntime + uninstall: devstack.components.melange:MelangeUninstaller no-vnc: - install: devstack.components.novnc:NoVNCInstaller + action_classes: + install: devstack.components.novnc:NoVNCInstaller + start: devstack.components.novnc:NoVNCRuntime + stop: devstack.components.novnc:NoVNCRuntime + uninstall: devstack.components.novnc:NoVNCUninstaller pips: - name: numpy version: '1.5' - start: devstack.components.novnc:NoVNCRuntime - stop: devstack.components.novnc:NoVNCRuntime - uninstall: devstack.components.novnc:NoVNCUninstaller nova: - install: devstack.distros.rhel6:NovaInstaller + action_classes: + install: devstack.distros.rhel6:NovaInstaller + start: devstack.components.nova:NovaRuntime + stop: devstack.components.nova:NovaRuntime + uninstall: devstack.components.nova:NovaUninstaller packages: - name: MySQL-python removable: true @@ -498,8 +510,6 @@ components: version: '2.5' - name: sqlalchemy-migrate version: 0.7.2 - start: devstack.components.nova:NovaRuntime - stop: devstack.components.nova:NovaRuntime subsystems: api: packages: @@ -549,9 +559,12 @@ components: - name: scsi-target-utils removable: true version: 1.0* - uninstall: devstack.components.nova:NovaUninstaller nova-client: - install: devstack.components.nova_client:NovaClientInstaller + action_classes: + install: devstack.components.nova_client:NovaClientInstaller + start: devstack.components.nova_client:NovaClientRuntime + stop: devstack.components.nova_client:NovaClientRuntime + uninstall: devstack.components.nova_client:NovaClientUninstaller packages: - meta: epel: true @@ -563,11 +576,12 @@ components: name: python-prettytable removable: true version: 0.5* - start: devstack.components.nova_client:NovaClientRuntime - stop: devstack.components.nova_client:NovaClientRuntime - uninstall: devstack.components.nova_client:NovaClientUninstaller quantum: - install: devstack.components.quantum:QuantumInstaller + action_classes: + install: devstack.components.quantum:QuantumInstaller + start: devstack.components.quantum:QuantumRuntime + stop: devstack.components.quantum:QuantumRuntime + uninstall: devstack.components.quantum:QuantumUninstaller packages: - name: libxml2-python removable: false @@ -600,22 +614,24 @@ components: version: 0.7.5 - name: sqlalchemy-migrate version: 0.7.2 - start: devstack.components.quantum:QuantumRuntime - stop: devstack.components.quantum:QuantumRuntime - uninstall: devstack.components.quantum:QuantumUninstaller quantum-client: - install: devstack.components.quantum_client:QuantumClientInstaller + action_classes: + install: devstack.components.quantum_client:QuantumClientInstaller + start: devstack.components.quantum_client:QuantumClientRuntime + stop: devstack.components.quantum_client:QuantumClientRuntime + uninstall: devstack.components.quantum_client:QuantumClientUninstaller packages: - meta: epel: true name: python-gflags removable: true version: 1.4* - start: devstack.components.quantum_client:QuantumClientRuntime - stop: devstack.components.quantum_client:QuantumClientRuntime - uninstall: devstack.components.quantum_client:QuantumClientUninstaller rabbit-mq: - install: devstack.components.rabbit:RabbitInstaller + action_classes: + install: devstack.components.rabbit:RabbitInstaller + start: devstack.components.rabbit:RabbitRuntime + stop: devstack.components.rabbit:RabbitRuntime + uninstall: devstack.components.rabbit:RabbitUninstaller packages: - meta: epel: true @@ -635,14 +651,12 @@ components: run_as_root: true removable: true version: 2.6* - start: devstack.components.rabbit:RabbitRuntime - stop: devstack.components.rabbit:RabbitRuntime - uninstall: devstack.components.rabbit:RabbitUninstaller swift: - install: devstack.components.swift:SwiftInstaller - start: devstack.components.swift:SwiftRuntime - stop: devstack.components.swift:SwiftRuntime - uninstall: devstack.components.swift:SwiftUninstaller + action_classes: + install: devstack.components.swift:SwiftInstaller + start: devstack.components.swift:SwiftRuntime + stop: devstack.components.swift:SwiftRuntime + uninstall: devstack.components.swift:SwiftUninstaller packager_name: devstack.distros.rhel6:YumPackager ... diff --git a/conf/distros/ubuntu-oneiric.yaml b/conf/distros/ubuntu-oneiric.yaml index cccecfef..19f4e902 100644 --- a/conf/distros/ubuntu-oneiric.yaml +++ b/conf/distros/ubuntu-oneiric.yaml @@ -43,7 +43,11 @@ commands: components: db: - install: devstack.distros.oneiric:DBInstaller + action_classes: + install: devstack.distros.oneiric:DBInstaller + start: devstack.components.db:DBRuntime + stop: devstack.components.db:DBRuntime + uninstall: devstack.components.db:DBUninstaller packages: - name: mysql-client-5.1 removable: true @@ -66,11 +70,12 @@ components: - mysql-server-5.1 mysql-server/start_on_boot boolean %BOOT_START% removable: true version: 5.1* - start: devstack.components.db:DBRuntime - stop: devstack.components.db:DBRuntime - uninstall: devstack.components.db:DBUninstaller general: - install: devstack.components.pkglist:Installer + action_classes: + install: devstack.components.pkglist:Installer + start: devstack.component:EmptyRuntime + stop: devstack.component:EmptyRuntime + uninstall: devstack.components.pkglist:Uninstaller packages: - name: curl removable: false @@ -150,11 +155,12 @@ components: pips: - name: mock version: 0.8.0 - start: devstack.component:EmptyRuntime - stop: devstack.component:EmptyRuntime - uninstall: devstack.components.pkglist:Uninstaller glance: - install: devstack.components.glance:GlanceInstaller + action_classes: + install: devstack.components.glance:GlanceInstaller + start: devstack.components.glance:GlanceRuntime + stop: devstack.components.glance:GlanceRuntime + uninstall: devstack.components.glance:GlanceUninstaller packages: - name: python-eventlet removable: true @@ -192,11 +198,12 @@ components: pips: - name: iso8601 version: 0.1.4 - start: devstack.components.glance:GlanceRuntime - stop: devstack.components.glance:GlanceRuntime - uninstall: devstack.components.glance:GlanceUninstaller horizon: - install: devstack.components.horizon:HorizonInstaller + action_classes: + install: devstack.components.horizon:HorizonInstaller + start: devstack.components.horizon:HorizonRuntime + stop: devstack.components.horizon:HorizonRuntime + uninstall: devstack.components.horizon:HorizonUninstaller packages: - name: apache2 removable: true @@ -265,11 +272,12 @@ components: version: 2.3 - name: python-cloudfiles version: 1.7.9.3 - start: devstack.components.horizon:HorizonRuntime - stop: devstack.components.horizon:HorizonRuntime - uninstall: devstack.components.horizon:HorizonUninstaller keystone: - install: devstack.components.keystone:KeystoneInstaller + action_classes: + install: devstack.components.keystone:KeystoneInstaller + start: devstack.components.keystone:KeystoneRuntime + stop: devstack.components.keystone:KeystoneRuntime + uninstall: devstack.components.keystone:KeystoneUninstaller packages: - name: libldap2-dev removable: true @@ -328,11 +336,12 @@ components: pips: - name: passlib version: 1.5.3 - start: devstack.components.keystone:KeystoneRuntime - stop: devstack.components.keystone:KeystoneRuntime - uninstall: devstack.components.keystone:KeystoneUninstaller keystone-client: - install: devstack.components.keystone_client:KeyStoneClientInstaller + action_classes: + install: devstack.components.keystone_client:KeyStoneClientInstaller + start: devstack.components.keystone_client:KeyStoneClientRuntime + stop: devstack.components.keystone_client:KeyStoneClientRuntime + uninstall: devstack.components.keystone_client:KeyStoneClientUninstaller packages: - name: python-argparse removable: true @@ -340,11 +349,12 @@ components: - name: python-prettytable removable: true version: 0.5* - start: devstack.components.keystone_client:KeyStoneClientRuntime - stop: devstack.components.keystone_client:KeyStoneClientRuntime - uninstall: devstack.components.keystone_client:KeyStoneClientUninstaller melange: - install: devstack.components.melange:MelangeInstaller + action_classes: + install: devstack.components.melange:MelangeInstaller + start: devstack.components.melange:MelangeRuntime + stop: devstack.components.melange:MelangeRuntime + uninstall: devstack.components.melange:MelangeUninstaller packages: - name: python-eventlet removable: true @@ -370,20 +380,22 @@ components: - name: python-webob removable: true version: 1.0* - start: devstack.components.melange:MelangeRuntime - stop: devstack.components.melange:MelangeRuntime - uninstall: devstack.components.melange:MelangeUninstaller no-vnc: - install: devstack.components.novnc:NoVNCInstaller + action_classes: + install: devstack.components.novnc:NoVNCInstaller + start: devstack.components.novnc:NoVNCRuntime + stop: devstack.components.novnc:NoVNCRuntime + uninstall: devstack.components.novnc:NoVNCUninstaller packages: - name: python-numpy removable: true version: 1:1.5* - start: devstack.components.novnc:NoVNCRuntime - stop: devstack.components.novnc:NoVNCRuntime - uninstall: devstack.components.novnc:NoVNCUninstaller nova: - install: devstack.components.nova:NovaInstaller + action_classes: + install: devstack.components.nova:NovaInstaller + start: devstack.components.nova:NovaRuntime + stop: devstack.components.nova:NovaRuntime + uninstall: devstack.components.nova:NovaUninstaller packages: - name: dnsmasq-base removable: true @@ -484,8 +496,6 @@ components: pips: - name: iso8601 version: 0.1.4 - start: devstack.components.nova:NovaRuntime - stop: devstack.components.nova:NovaRuntime subsystems: api: packages: @@ -529,9 +539,12 @@ components: - name: tgt removable: true version: 1:1* - uninstall: devstack.components.nova:NovaUninstaller nova-client: - install: devstack.components.nova_client:NovaClientInstaller + action_classes: + install: devstack.components.nova_client:NovaClientInstaller + start: devstack.components.nova_client:NovaClientRuntime + stop: devstack.components.nova_client:NovaClientRuntime + uninstall: devstack.components.nova_client:NovaClientUninstaller packages: - name: python-argparse removable: true @@ -539,11 +552,12 @@ components: - name: python-prettytable removable: true version: 0.5* - start: devstack.components.nova_client:NovaClientRuntime - stop: devstack.components.nova_client:NovaClientRuntime - uninstall: devstack.components.nova_client:NovaClientUninstaller quantum: - install: devstack.components.quantum:QuantumInstaller + action_classes: + install: devstack.components.quantum:QuantumInstaller + start: devstack.components.quantum:QuantumRuntime + stop: devstack.components.quantum:QuantumRuntime + uninstall: devstack.components.quantum:QuantumUninstaller packages: - name: python-eventlet removable: true @@ -566,8 +580,6 @@ components: - name: python-routes removable: true version: 1.12* - start: devstack.components.quantum:QuantumRuntime - stop: devstack.components.quantum:QuantumRuntime subsystems: openvswitch: packages: @@ -583,27 +595,32 @@ components: - name: python-sqlalchemy removable: true version: 0.6* - uninstall: devstack.components.quantum:QuantumUninstaller quantum-client: - install: devstack.components.quantum_client:QuantumClientInstaller + action_classes: + install: devstack.components.quantum_client:QuantumClientInstaller + start: devstack.components.quantum_client:QuantumClientRuntime + stop: devstack.components.quantum_client:QuantumClientRuntime + uninstall: devstack.components.quantum_client:QuantumClientUninstaller packages: - name: python-gflags removable: true version: 1.5* - start: devstack.components.quantum_client:QuantumClientRuntime - stop: devstack.components.quantum_client:QuantumClientRuntime - uninstall: devstack.components.quantum_client:QuantumClientUninstaller rabbit-mq: - install: devstack.components.rabbit:RabbitInstaller + action_classes: + install: devstack.components.rabbit:RabbitInstaller + start: devstack.components.rabbit:RabbitRuntime + stop: devstack.components.rabbit:RabbitRuntime + uninstall: devstack.components.rabbit:RabbitUninstaller packages: - name: rabbitmq-server removable: true version: 2.5* - start: devstack.components.rabbit:RabbitRuntime - stop: devstack.components.rabbit:RabbitRuntime - uninstall: devstack.components.rabbit:RabbitUninstaller swift: - install: devstack.components.swift:SwiftInstaller + action_classes: + install: devstack.components.swift:SwiftInstaller + start: devstack.components.swift:SwiftRuntime + stop: devstack.components.swift:SwiftRuntime + uninstall: devstack.components.swift:SwiftUninstaller packages: - name: memcached removable: true @@ -635,8 +652,5 @@ components: - name: xfsprogs removable: true version: 3.1* - start: devstack.components.swift:SwiftRuntime - stop: devstack.components.swift:SwiftRuntime - uninstall: devstack.components.swift:SwiftUninstaller ... diff --git a/devstack/distro.py b/devstack/distro.py index a1b77814..97e0c1b0 100644 --- a/devstack/distro.py +++ b/devstack/distro.py @@ -22,7 +22,6 @@ import shlex import yaml -from devstack.progs import actions from devstack import decorators from devstack import importer from devstack import log as logging @@ -138,15 +137,11 @@ class Distro(object): try: # Use a copy instead of the original component_info = dict(self._components[name]) - entry_point = component_info[action] + entry_point = component_info['action_classes'][action] cls = importer.import_entry_point(entry_point) - # Knock all action class info (and any other keys) - # FIXME: This module shouldn't need to have knowledge - # of all available actions. - key_deletions = [action] + actions.get_action_names() - for k in key_deletions: - if k in component_info: - del component_info[k] + # Remove action class info + if 'action_classes' in component_info: + del component_info['action_classes'] return (cls, component_info) except KeyError: raise RuntimeError('No class configured to %s %s on %s' %