From 89656fb253de10f30fa2b1330745ba4ced16160d Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 24 Apr 2012 17:23:23 -0700 Subject: [PATCH] Reduce phase messaging + rework colors + move action program to top level (since its only file in its folder) + cleanup logging, adjust phase functions. --- devstack/{progs => }/actions.py | 157 ++++++++++++++++++++------------ devstack/cfg.py | 8 +- devstack/colorizer.py | 59 ++++++++++++ devstack/colorlog.py | 47 ---------- devstack/components/db.py | 3 +- devstack/distro.py | 3 +- devstack/log.py | 58 ++++++++++-- devstack/progs/__init__.py | 15 --- devstack/runner.py | 10 +- devstack/runners/fork.py | 2 +- devstack/runners/screen.py | 2 +- devstack/runners/upstart.py | 2 +- devstack/shell.py | 7 -- devstack/utils.py | 66 ++++---------- stack | 58 ++++++++---- tools/upload-img.py | 4 +- 16 files changed, 285 insertions(+), 216 deletions(-) rename devstack/{progs => }/actions.py (74%) create mode 100644 devstack/colorizer.py delete mode 100644 devstack/colorlog.py delete mode 100644 devstack/progs/__init__.py diff --git a/devstack/progs/actions.py b/devstack/actions.py similarity index 74% rename from devstack/progs/actions.py rename to devstack/actions.py index 09b4126a..b9c974f7 100644 --- a/devstack/progs/actions.py +++ b/devstack/actions.py @@ -15,7 +15,9 @@ # under the License.. import abc +import collections +from devstack import colorizer from devstack import date from devstack import env_rc from devstack import exceptions as excp @@ -31,6 +33,9 @@ from devstack import utils LOG = logging.getLogger("devstack.progs.actions") +PhaseFunctors = collections.namedtuple('PhaseFunctors', ['start', 'run', 'end']) + + class ActionRunner(object): __meta__ = abc.ABCMeta NAME = None @@ -161,27 +166,32 @@ class ActionRunner(object): } writer.mark(details) - def _run_phase(self, start_msg, functor, end_msg, component_order, instances, phase_name): + def _run_phase(self, functors, component_order, instances, phase_name): """ Run a given 'functor' across all of the components, in order. """ + component_results = dict() for c in component_order: instance = instances[c] if self._skip_phase(instance, phase_name): LOG.debug("Skipping phase named %r for component %r", phase_name, c) else: - 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)) + if functors.start: + functors.start(c) + result = None + if functors.run: + result = functors.run(instance) + if functors.end: + functors.end(c, result) + component_results[c] = result self._mark_phase(instance, phase_name) except (excp.NoTraceException) as e: if self.force: LOG.debug("Skipping exception: %s" % (e)) else: raise + return component_results def _delete_phase_files(self, names): phase_dir = self.root_dir @@ -198,8 +208,8 @@ class ActionRunner(object): components_needing_prereq.append(c) preq_cls_name = preq_cls.NAME or "???" if components_needing_prereq: - LOG.info("Processing prerequisite action %r requested by (%s) components.", - preq_cls_name, ", ".join(components_needing_prereq)) + LOG.info("Processing prerequisite action %s requested by (%s) components.", + colorizer.quote(preq_cls_name), ", ".join(components_needing_prereq)) prereq_instance = preq_cls(self.distro, self.cfg, self.pw_gen, @@ -213,9 +223,9 @@ class ActionRunner(object): instances = self._construct_instances(persona) self._handle_prereq(persona, instances) component_order = self._order_components(persona.wanted_components) - LOG.info("Processing components for action %r", (self.NAME or "???")) + LOG.info("Processing components for action %s.", colorizer.quote(self.NAME or "???")) utils.log_iterable(component_order, - header="Activating in the following order:", + header="Activating in the following order", logger=LOG) self._verify_components(component_order, instances) self._warm_components(component_order, instances) @@ -242,45 +252,56 @@ class InstallRunner(ActionRunner): def _run(self, persona, component_order, instances): self._write_rc_file() - self._run_phase( - 'Downloading {name}', - lambda i: i.download(), - "Performed {result} downloads.", + results = self._run_phase( + PhaseFunctors( + start=lambda name: LOG.info('Downloading %s.', colorizer.quote(name)), + run=lambda i: i.download(), + end=lambda name, result: LOG.info("Performed %s downloads.", result), + ), component_order, instances, "Download" ) self._run_phase( - 'Configuring {name}', - lambda i: i.configure(), - "Configured {result} items.", + PhaseFunctors( + start=lambda name: LOG.info('Configuring %s.', colorizer.quote(name)), + run=lambda i: i.configure(), + end=lambda name, result: LOG.info("Configured %s items.", colorizer.quote(result)), + ), component_order, instances, "Configure" ) self._run_phase( - 'Pre-installing {name}', - lambda i: i.pre_install(), - None, + PhaseFunctors( + start=None, + run=lambda i: i.pre_install(), + end=None, + ), component_order, instances, "Pre-install" ) self._run_phase( - 'Installing {name}', - lambda i: i.install(), - "Finished install of {name} - check {result} for information on what was done.", + PhaseFunctors( + start=lambda name: LOG.info('Installing %s.', colorizer.quote(name)), + run=lambda i: i.install(), + end=(lambda name, result: LOG.info("Finished install of %s items - check %s for information on what was done", + colorizer.quote(name), colorizer.quote(result))), + ), component_order, instances, "Install" ) self._run_phase( - 'Post-installing {name}', - lambda i: i.post_install(), - None, + PhaseFunctors( + start=lambda name: LOG.info('Post-installing %s.', colorizer.quote(name)), + run=lambda i: i.post_install(), + end=None + ), component_order, instances, - "Post-install" + "Post-install", ) @@ -296,36 +317,44 @@ class StartRunner(ActionRunner): def _run(self, persona, component_order, instances): self._run_phase( - None, - lambda i: i.configure(), - None, + PhaseFunctors( + start=None, + run=lambda i: i.configure(), + end=None, + ), component_order, instances, - "Configure" + "Configure", ) self._run_phase( - 'Pre-starting {name}', - lambda i: i.pre_start(), - None, + PhaseFunctors( + start=None, + run=lambda i: i.pre_start(), + end=None, + ), component_order, instances, - "Pre-start" + "Pre-start", ) self._run_phase( - 'Starting {name}', - lambda i: i.start(), - "Started {result} applications", + PhaseFunctors( + start=lambda name: LOG.info('Starting %s.', name), + run=lambda i: i.start(), + end=lambda name, result: LOG.info("Start %s applications", colorizer.quote(result)), + ), component_order, instances, "Start" ) self._run_phase( - 'Post-starting {name}', - lambda i: i.post_start(), - None, + PhaseFunctors( + start=lambda name: LOG.info('Post-starting %s.', colorizer.quote(name)), + run=lambda i: i.post_start(), + end=None, + ), component_order, instances, - "Post-start" + "Post-start", ) @@ -343,9 +372,11 @@ class StopRunner(ActionRunner): def _run(self, persona, component_order, instances): self._run_phase( - 'Stopping {name}', - lambda i: i.stop(), - 'Stopped {result} items', + PhaseFunctors( + start=lambda name: LOG.info('Stopping %s.', colorizer.quote(name)), + run=lambda i: i.stop(), + end=lambda name, result: LOG.info("Stopped %s items", colorizer.quote(result)), + ), component_order, instances, "Stopped" @@ -370,36 +401,44 @@ class UninstallRunner(ActionRunner): def _run(self, persona, component_order, instances): self._run_phase( - 'Unconfiguring {name}', - lambda i: i.unconfigure(), - None, + PhaseFunctors( + start=lambda name: LOG.info('Unconfiguring %s.', colorizer.quote(name)), + run=lambda i: i.unconfigure(), + end=None, + ), component_order, instances, "Unconfigure" ) self._run_phase( - 'Pre-uninstalling {name}', - lambda i: i.pre_uninstall(), - None, + PhaseFunctors( + start=None, + run=lambda i: i.pre_uninstall(), + end=None, + ), component_order, instances, - "Pre-uninstall" + "Pre-uninstall", ) self._run_phase( - 'Uninstalling {name}', - lambda i: i.uninstall(), - None, + PhaseFunctors( + start=lambda name: LOG.info('Uninstalling %s.', colorizer.quote(name)), + run=lambda i: i.uninstall(), + end=None, + ), component_order, instances, "Uninstall" ) self._run_phase( - 'Post-uninstalling {name}', - lambda i: i.post_uninstall(), - None, + PhaseFunctors( + start=lambda name: LOG.info('Post-uninstalling %s.', colorizer.quote(name)), + run=lambda i: i.post_uninstall(), + end=None, + ), component_order, instances, - "Post-uninstall" + "Post-uninstall", ) self._delete_phase_files(set([self.NAME, InstallRunner.NAME])) diff --git a/devstack/cfg.py b/devstack/cfg.py index ecb8ae31..c2d8d974 100644 --- a/devstack/cfg.py +++ b/devstack/cfg.py @@ -237,14 +237,16 @@ class CliResolver(object): continue split_up = c.split("/") if len(split_up) != 3: - LOG.warn("Badly formatted cli option: %r", c) + LOG.warn("Incorrectly formatted cli option: %r", c) else: section = (split_up[0]).strip() if not section or section.lower() == 'default': section = 'DEFAULT' option = split_up[1].strip() - value = split_up[2] - parsed_args[cfg_helpers.make_id(section, option)] = value + if not option: + LOG.warn("Badly formatted cli option - no option name: %r", c) + else: + parsed_args[cfg_helpers.make_id(section, option)] = split_up[2] return cls(parsed_args) diff --git a/devstack/colorizer.py b/devstack/colorizer.py new file mode 100644 index 00000000..745cf0c9 --- /dev/null +++ b/devstack/colorizer.py @@ -0,0 +1,59 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved. +# +# 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 re +import sys + +import termcolor + + +COLORS = termcolor.COLORS.keys() + + +def is_terminal(): + return sys.stdout.isatty() + + +def quote(data, quote_color='green'): + if not is_terminal(): + return "'%s'" % (data) + else: + return color(data, quote_color) + + +def format(data, params): + text = str(data) + + def replacer(match): + param_name = match.group(1) + return color(params[param_name], color=match.group(2).strip()) + + return re.sub(r"\{([\w\d]+):(.*)\}", replacer, text) + + +def color(data, color, bold=False, underline=False, blink=False): + text = str(data) + text_attrs = list() + if bold: + text_attrs.append('bold') + if underline: + text_attrs.append('underline') + if blink: + text_attrs.append('blink') + if is_terminal() and color in COLORS: + return termcolor.colored(text, color, attrs=text_attrs) + else: + return text diff --git a/devstack/colorlog.py b/devstack/colorlog.py deleted file mode 100644 index 10010333..00000000 --- a/devstack/colorlog.py +++ /dev/null @@ -1,47 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved. -# -# 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 logging -from termcolor import colored - -COLOR_MAP = { - logging.DEBUG: 'blue', - logging.INFO: 'cyan', - logging.WARNING: 'yellow', - logging.ERROR: 'red', - logging.CRITICAL: 'red', - logging.AUDIT: 'green', -} - -COLOR_ATTRS = { - logging.CRITICAL: ['bold', 'blink'], -} - -UNKNOWN_COLOR = 'grey' - - -class TermFormatter(logging.Formatter): - def __init__(self, reg_fmt=None, date_format=None): - logging.Formatter.__init__(self, reg_fmt, date_format) - - def format(self, record): - lvl = record.levelno - color = COLOR_MAP.get(lvl, UNKNOWN_COLOR) - record.levelname = colored(record.levelname, color) - attrs = COLOR_ATTRS.get(lvl) - if attrs: - record.msg = colored(record.msg, attrs=attrs) - return logging.Formatter.format(self, record) diff --git a/devstack/components/db.py b/devstack/components/db.py index 8d9ed674..09c66e9b 100644 --- a/devstack/components/db.py +++ b/devstack/components/db.py @@ -32,7 +32,7 @@ RESET_BASE_PW = '' SQL_RESET_PW_LINKS = [ 'https://help.ubuntu.com/community/MysqlPasswordReset', 'http://dev.mysql.com/doc/refman/5.0/en/resetting-permissions.html', - ] +] # Used as a generic error message BASE_ERROR = 'Currently we do not know how to %r for database type %r' @@ -43,6 +43,7 @@ WARMUP_PWS = [('sql', PASSWORD_PROMPT)] class DBUninstaller(comp.PkgUninstallComponent): + def __init__(self, *args, **kargs): comp.PkgUninstallComponent.__init__(self, *args, **kargs) (runtime_cls, _) = self.distro.extract_component(self.component_name, 'running') diff --git a/devstack/distro.py b/devstack/distro.py index 16306f96..f411c217 100644 --- a/devstack/distro.py +++ b/devstack/distro.py @@ -22,6 +22,7 @@ import shlex import yaml +from devstack import colorizer from devstack import importer from devstack import log as logging from devstack import settings @@ -69,7 +70,7 @@ class Distro(object): LOG.debug('Looking for distro data for %r (%s)', plt, distname) for p in cls.load_all(): if p.supports_distro(plt): - LOG.info('Using distro %r for platform %r', p.name, plt) + LOG.info('Using distro %s for platform %s', colorizer.quote(p.name), colorizer.quote(plt)) return p else: raise RuntimeError( diff --git a/devstack/log.py b/devstack/log.py index b8082403..5ed640f9 100644 --- a/devstack/log.py +++ b/devstack/log.py @@ -18,11 +18,15 @@ # under the License. import logging +import sys from logging.handlers import SysLogHandler from logging.handlers import WatchedFileHandler -# Alist of things we want to replicate from logging levels +from devstack import colorizer + + +# A list of things we want to replicate from logging levels CRITICAL = logging.CRITICAL FATAL = logging.FATAL ERROR = logging.ERROR @@ -58,19 +62,61 @@ WatchedFileHandler = WatchedFileHandler SysLogHandler = SysLogHandler -class AuditAdapter(logging.LoggerAdapter): +class TermFormatter(logging.Formatter): + + COLOR_MAP = { + logging.DEBUG: 'blue', + logging.INFO: 'cyan', + logging.WARNING: 'yellow', + logging.ERROR: 'red', + logging.CRITICAL: 'red', + logging.AUDIT: 'green', + } + MSG_COLORS = { + logging.CRITICAL: 'red', + } + + def __init__(self, reg_fmt=None, date_format=None): + logging.Formatter.__init__(self, reg_fmt, date_format) + + def _format_msg(self, lvl, msg): + color_to_be = self.MSG_COLORS.get(lvl) + if color_to_be: + return colorizer.color(msg, color_to_be, bold=True) + else: + return msg + + def _format_lvl(self, lvl, lvl_name): + color_to_be = self.COLOR_MAP.get(lvl) + if color_to_be: + return colorizer.color(lvl_name, color_to_be) + else: + return lvl_name + + def format(self, record): + record.levelname = self._format_lvl(record.levelno, record.levelname) + record.msg = self._format_msg(record.levelno, record.msg) + return logging.Formatter.format(self, record) + + +class TermAdapter(logging.LoggerAdapter): + warn = logging.LoggerAdapter.warning def __init__(self, logger): logging.LoggerAdapter.__init__(self, logger, dict()) - self.logger = logger def audit(self, msg, *args, **kwargs): self.log(logging.AUDIT, msg, *args, **kwargs) - def process(self, msg, kwargs): - return msg, kwargs + +def setupLogging(log_level, format='%(levelname)s: @%(name)s : %(message)s'): + root_logger = getLogger().logger + console_logger = StreamHandler(sys.stdout) + console_logger.setFormatter(TermFormatter(format)) + root_logger.addHandler(console_logger) + root_logger.setLevel(log_level) def getLogger(name='devstack'): - return AuditAdapter(logging.getLogger(name)) + return TermAdapter(logging.getLogger(name)) diff --git a/devstack/progs/__init__.py b/devstack/progs/__init__.py deleted file mode 100644 index a7bfb005..00000000 --- a/devstack/progs/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012 Yahoo! Inc. All Rights Reserved. -# -# 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. diff --git a/devstack/runner.py b/devstack/runner.py index 50438219..69e54883 100644 --- a/devstack/runner.py +++ b/devstack/runner.py @@ -14,9 +14,12 @@ # License for the specific language governing permissions and limitations # under the License. +import abc + + +class Runner(object): + __meta__ = abc.ABCMeta -# This is the base class for the various runners -class RunnerBase(object): def __init__(self, config, component_name, trace_dir): self.cfg = config self.component_name = component_name @@ -34,7 +37,8 @@ class RunnerBase(object): def start(self, app_name, app_pth, app_dir, opts): # Returns a file name that contains what was started - return None + pass def stop(self, app_name): + # Stops the given app pass diff --git a/devstack/runners/fork.py b/devstack/runners/fork.py index a997541e..37884b07 100644 --- a/devstack/runners/fork.py +++ b/devstack/runners/fork.py @@ -46,7 +46,7 @@ NAME = "NAME" FORK_TEMPL = "%s.fork" -class ForkRunner(base.RunnerBase): +class ForkRunner(base.Runner): def __init__(self, cfg, component_name, trace_dir): base.RunnerBase.__init__(self, cfg, component_name, trace_dir) diff --git a/devstack/runners/screen.py b/devstack/runners/screen.py index a87b02c4..661730cf 100644 --- a/devstack/runners/screen.py +++ b/devstack/runners/screen.py @@ -61,7 +61,7 @@ SCREEN_SOCKET_PERM = 0700 SCREEN_RC = settings.RC_FN_TEMPL % ('screen') -class ScreenRunner(base.RunnerBase): +class ScreenRunner(base.Runner): def __init__(self, cfg, component_name, trace_dir): base.RunnerBase.__init__(self, cfg, component_name, trace_dir) self.socket_dir = sh.joinpths(tempfile.gettempdir(), SCREEN_SOCKET_DIR_NAME) diff --git a/devstack/runners/upstart.py b/devstack/runners/upstart.py index 8a2e92b0..c4838598 100644 --- a/devstack/runners/upstart.py +++ b/devstack/runners/upstart.py @@ -46,7 +46,7 @@ UPSTART_CONF_TMPL = 'upstart.conf' EMIT_BASE_CMD = ["/sbin/initctl", "emit"] -class UpstartRunner(base.RunnerBase): +class UpstartRunner(base.Runner): def __init__(self, cfg, component_name, trace_dir): base.RunnerBase.__init__(self, cfg, component_name, trace_dir) self.events = set() diff --git a/devstack/shell.py b/devstack/shell.py index e60d04ae..c11c3849 100644 --- a/devstack/shell.py +++ b/devstack/shell.py @@ -321,13 +321,6 @@ def explode_path(path): return _explode_form_path(path) -def in_terminal(check_both=False): - if check_both: - return sys.stdout.isatty() and sys.stderr.isatty() - else: - return sys.stdout.isatty() - - def remove_parents(child_path, paths): if not paths: return list() diff --git a/devstack/utils.py b/devstack/utils.py index 19f218e2..15a35825 100644 --- a/devstack/utils.py +++ b/devstack/utils.py @@ -28,9 +28,8 @@ import tempfile import distutils.version import netifaces import progressbar -import termcolor -from devstack import colorlog +from devstack import colorizer from devstack import date from devstack import exceptions as excp from devstack import log as logging @@ -72,15 +71,6 @@ COWS['unhappy'] = r''' ''' -def construct_log_level(verbosity_level, dry_run=False): - log_level = logging.INFO - if verbosity_level >= 3: - log_level = logging.DEBUG - elif verbosity_level == 2 or dry_run: - log_level = logging.AUDIT - return log_level - - def make_bool(val): if not val: return False @@ -140,18 +130,6 @@ def get_from_path(items, path, quiet=True): return get_from_path(get_method(first_token), remainder) -def configure_logging(log_level, cli_args): - root_logger = logging.getLogger().logger - console_logger = logging.StreamHandler(sys.stdout) - console_format = '%(levelname)s: @%(name)s : %(message)s' - if sh.in_terminal(): - console_logger.setFormatter(colorlog.TermFormatter(console_format)) - else: - console_logger.setFormatter(logging.Formatter(console_format)) - root_logger.addHandler(console_logger) - root_logger.setLevel(log_level) - - def load_template(component, template_name): templ_pth = sh.joinpths(settings.STACK_TEMPLATE_DIR, component, template_name) return (templ_pth, sh.load_file(templ_pth)) @@ -206,7 +184,7 @@ def mark_unexecute_file(fn, kvs, comment_start='#'): sh.chmod(fn, 0644) -def log_iterable(to_log, header=None, logger=None): +def log_iterable(to_log, header=None, logger=None, do_color=True): if not logger: logger = LOG if header: @@ -214,7 +192,9 @@ def log_iterable(to_log, header=None, logger=None): header += ":" logger.info(header) for c in to_log: - logger.info("|-- %s", color_text(c, 'blue')) + if do_color: + c = colorizer.color(c, 'blue') + logger.info("|-- %s", c) @contextlib.contextmanager @@ -354,6 +334,10 @@ def joinlinesep(*pieces): return os.linesep.join(pieces) +def get_class_names(objects): + return map((lambda i: i.__class__.__name__), objects) + + def param_replace_list(values, replacements, ignore_missing=False): new_values = list() if not values: @@ -486,27 +470,11 @@ def _welcome_slang(): return random.choice(potentials) -def color_text(text, color, bold=False, - underline=False, blink=False, - always_color=False): - text_attrs = list() - if bold: - text_attrs.append('bold') - if underline: - text_attrs.append('underline') - if blink: - text_attrs.append('blink') - if sh.in_terminal() or always_color: - return termcolor.colored(text, color, attrs=text_attrs) - else: - return text - - def _color_blob(text, text_color): def replacer(match): contents = match.group(1) - return color_text(contents, text_color) + return colorizer.color(contents, text_color) return MONTY_PYTHON_TEXT_RE.sub(replacer, text) @@ -704,12 +672,12 @@ def _goodbye_header(worked): def goodbye(worked): if worked: cow = COWS['happy'] - eye_fmt = color_text('o', 'green') - ear = color_text("^", 'green') + eye_fmt = colorizer.color('o', 'green') + ear = colorizer.color("^", 'green') else: cow = COWS['unhappy'] - eye_fmt = color_text("o", 'red') - ear = color_text("v", 'red') + eye_fmt = colorizer.color("o", 'red') + ear = colorizer.color("v", 'red') cow = cow.strip("\n\r") header = _goodbye_header(worked) msg = cow.format(eye=eye_fmt, ear=ear, @@ -721,9 +689,9 @@ def welcome(): lower = "| %s |" % (version.version_string()) welcome_header = _get_welcome_stack() max_line_len = len(max(welcome_header.splitlines(), key=len)) - footer = color_text(settings.PROG_NICE_NAME, 'green') + footer = colorizer.color(settings.PROG_NICE_NAME, 'green') footer += ": " - footer += color_text(lower, 'blue', True) + footer += colorizer.color(lower, 'blue', bold=True) uncolored_footer = (settings.PROG_NICE_NAME + ": " + lower) if max_line_len - len(uncolored_footer) > 0: # This format string will center the uncolored text which @@ -734,5 +702,5 @@ def welcome(): print(footer) real_max = max(max_line_len, len(uncolored_footer)) slang = center_text(_welcome_slang(), ' ', real_max) - print(color_text(slang, 'magenta', bold=True)) + print(colorizer.color(slang, 'magenta', bold=True)) return ("-", real_max) diff --git a/stack b/stack index b2b7738c..bc73c1d3 100755 --- a/stack +++ b/stack @@ -20,8 +20,10 @@ import sys import time import traceback as tb +from devstack import actions from devstack import cfg from devstack import cfg_helpers +from devstack import colorizer from devstack import date from devstack import distro from devstack import env @@ -34,8 +36,6 @@ from devstack import settings from devstack import shell as sh from devstack import utils -from devstack.progs import actions - LOG = logging.getLogger("devstack.stack") @@ -69,12 +69,12 @@ def load_rc_files(): loaded_am = 0 for fn in RC_FILES: try: - LOG.info("Attempting to load file %r which has your environment settings." % (fn)) + LOG.info("Attempting to load file %s which has your environment settings.", colorizer.quote(fn)) am_loaded = env_rc.RcReader().load(fn) - LOG.info("Loaded %s settings from file %r" % (am_loaded, fn)) + LOG.info("Loaded %s settings.", colorizer.quote(am_loaded)) loaded_am += 1 except IOError: - LOG.warn('Error reading file located at %r. Skipping loading it.' % (fn)) + LOG.warn('Error reading file located at %s. Skipping loading it.', colorizer.quote(fn)) return loaded_am @@ -149,12 +149,20 @@ def establish_config(args): env_resolver = cfg.EnvResolver(base_config) proxy_config.add_read_resolver(env_resolver) proxy_config.add_set_resolver(env_resolver) - utils.log_iterable(map((lambda i: i.__class__.__name__), proxy_config.read_resolvers), + utils.log_iterable(utils.get_class_names(proxy_config.read_resolvers), header="Config lookup will use the following resolvers:", logger=LOG) return proxy_config +def establish_passwords(config, args): + pw_gen = passwords.PasswordGenerator(config, args.get('prompt_for_passwords', True)) + utils.log_iterable(utils.get_class_names(pw_gen.lookups), + header="Password finding will use the following lookups:", + logger=LOG) + return pw_gen + + def run(args): """ Starts the execution of devstackpy after @@ -169,7 +177,7 @@ def run(args): action = args.pop("action", '').strip().lower() if action not in actions.get_action_names(): - print(utils.color_text("No valid action specified!", "red")) + print(colorizer.color_text("No valid action specified!", "red")) return False loaded_rcs = False @@ -185,7 +193,7 @@ def run(args): persona_fn = args.pop('persona_fn') if not persona_fn or not sh.isfile(persona_fn): - print(utils.color_text("No valid persona file name specified!", "red")) + print(colorizer.color_text("No valid persona file name specified!", "red")) return False persona_fn = sh.abspth(persona_fn) @@ -205,7 +213,7 @@ def run(args): dist = distro.Distro.get_current() persona_inst = load_verify_persona(persona_fn, dist) config = establish_config(args) - pw_gen = passwords.PasswordGenerator(config, args.get('prompt_for_passwords', True)) + pw_gen = establish_passwords(config, args) runner_factory = actions.get_runner_factory(action) runner = runner_factory(dist, @@ -214,22 +222,32 @@ def run(args): root_dir=root_dir, **args) - LOG.info("Starting action %r on %s for distro: %r" % (action, date.rcf8222date(), dist.name)) - LOG.info("Using persona: %r" % (persona_fn)) - LOG.info("In root directory: %r" % (root_dir)) + LOG.info("Starting action %s on %s for distro: %s", + colorizer.quote(action), colorizer.quote(date.rcf8222date()), colorizer.quote(dist.name)) + LOG.info("Using persona: %s", colorizer.quote(persona_fn)) + LOG.info("In root directory: %s", colorizer.quote(root_dir)) start_time = time.time() runner.run(persona_inst) end_time = time.time() - LOG.info("It took (%s) to complete action %r" % - (utils.format_secs_taken((end_time - start_time)), action)) - LOG.info("After action %r your settings which were created or read are:" % (action)) + LOG.info("It took (%s) to complete action %s.", + colorizer.quote(utils.format_secs_taken((end_time - start_time))), colorizer.quote(action)) + LOG.info("After action %s your settings which were created or read are:", colorizer.quote(action)) config.pprint(CFG_GROUPS, CFG_ORDERING) return True +def construct_log_level(verbosity_level, dry_run=False): + log_level = logging.INFO + if verbosity_level >= 3: + log_level = logging.DEBUG + elif verbosity_level == 2 or dry_run: + log_level = logging.AUDIT + return log_level + + def main(): """ Starts the execution of devstack py without @@ -245,8 +263,8 @@ def main(): prog_name = sys.argv[0] # Configure logging - log_level = utils.construct_log_level(args['verbosity'], args['dryrun']) - utils.configure_logging(log_level, args) + log_level = construct_log_level(args['verbosity'], args['dryrun']) + logging.setupLogging(log_level) LOG.debug("Command line options %s" % (args)) LOG.debug("Log level is: %s" % (log_level)) @@ -256,7 +274,7 @@ def main(): rest_args = sys.argv[1:] print("This program requires a user with sudo access.") msg = "Perhaps you should try %s %s" % \ - (utils.color_text("sudo %s" % (prog_name), "red", True), " ".join(rest_args)) + (colorizer.color("sudo %s" % (prog_name), "red", True), " ".join(rest_args)) print(msg) return 1 @@ -265,8 +283,8 @@ def main(): sh.user_mode(quiet=False) started_ok = run(args) if not started_ok: - me = utils.color_text(prog_name, "red", True) - me += " " + utils.color_text('--help', 'red') + me = colorizer.color(prog_name, "red", True) + me += " " + colorizer.color('--help', 'red') print("Perhaps you should try %s" % (me)) return 1 else: diff --git a/tools/upload-img.py b/tools/upload-img.py index 485ef996..b66920e5 100644 --- a/tools/upload-img.py +++ b/tools/upload-img.py @@ -31,8 +31,8 @@ if __name__ == "__main__": (options, args) = parser.parse_args() uris = options.uris or list() uri_sep = ",".join(uris) - utils.configure_logging(logging.DEBUG) - config = cfg.StackConfigParser() + logging.setupLogging(logging.DEBUG) + config = cfg.IgnoreMissingConfigParser() config.add_section('img') config.set('img', 'image_urls', uri_sep) pw_gen = passwords.PasswordGenerator(config)