summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Bielawa <tbielawa@redhat.com>2011-12-20 01:44:19 (GMT)
committerTim Bielawa <tbielawa@redhat.com>2011-12-20 01:44:19 (GMT)
commit47eee966d7d093c472f3e5373b0da16fbb74b272 (patch)
tree0f9e3fdac653ce6e81b8dbc525912a33fc1f82d3
parent9b6e2bafa0bdefa9521d837bdc6849b28b5947b4 (diff)
downloadTaboot-47eee966d7d093c472f3e5373b0da16fbb74b272.zip
Taboot-47eee966d7d093c472f3e5373b0da16fbb74b272.tar.gz
Taboot-47eee966d7d093c472f3e5373b0da16fbb74b272.tar.xz
Add logging mechanism (FINALLY) and update docs.
-rw-r--r--docs/man/man1/taboot.111
-rw-r--r--docs/man/man1/taboot.1.asciidoc10
-rw-r--r--taboot/Colors.py6
-rw-r--r--taboot/cli.py35
-rwxr-xr-xtaboot/log.py126
-rw-r--r--taboot/runner.py2
-rw-r--r--taboot/scripts.py17
-rw-r--r--taboot/util.py20
8 files changed, 196 insertions, 31 deletions
diff --git a/docs/man/man1/taboot.1 b/docs/man/man1/taboot.1
index aae398a..b8d82ca 100644
--- a/docs/man/man1/taboot.1
+++ b/docs/man/man1/taboot.1
@@ -2,12 +2,12 @@
.\" Title: taboot
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
-.\" Date: 12/08/2011
+.\" Date: 12/19/2011
.\" Manual: System administration commands
.\" Source: Taboot 0.4.x
.\" Language: English
.\"
-.TH "TABOOT" "1" "12/08/2011" "Taboot 0\&.4\&.x" "System administration commands"
+.TH "TABOOT" "1" "12/19/2011" "Taboot 0\&.4\&.x" "System administration commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -31,7 +31,7 @@
taboot \- run a taboot release script
.SH "SYNOPSIS"
.sp
-taboot [\-h] [\-V] [\-n] [\-p] [\-s] [\-L [LOGFILE]] [\-C CONCURRENCY] [\-E] [FILE [FILE \&...]]
+taboot [\-h] [\-V] [\-v] [\-n] [\-p] [\-s] [\-L [LOGFILE]] [\-C CONCURRENCY] [\-E] [FILE [FILE \&...]]
.SH "DESCRIPTION"
.sp
Taboot is a tool written for scripting and automating the task of performing software releases in a large\-scale infrastructure\&. Release scripts are written using YAML syntax\&.
@@ -57,6 +57,11 @@ Show this help message and exit\&.
Show program\(cqs version number and exit\&.
.RE
.PP
+\fB\-v\fR, \fB\-\-verbose\fR
+.RS 4
+Increase verbosity\&. Give up to twice\&.
+.RE
+.PP
\fB\-n\fR, \fB\-\-checkonly\fR
.RS 4
Don\(cqt execute the release, just check script syntax\&.
diff --git a/docs/man/man1/taboot.1.asciidoc b/docs/man/man1/taboot.1.asciidoc
index f2e4d4d..c8dcfb9 100644
--- a/docs/man/man1/taboot.1.asciidoc
+++ b/docs/man/man1/taboot.1.asciidoc
@@ -13,8 +13,8 @@ taboot - run a taboot release script
SYNOPSIS
--------
-taboot [-h] [-V] [-n] [-p] [-s] [-L [LOGFILE]] [-C CONCURRENCY] [-E]
- [FILE [FILE ...]]
+taboot [-h] [-V] [-v] [-n] [-p] [-s] [-L [LOGFILE]]
+ [-C CONCURRENCY] [-E] [FILE [FILE ...]]
@@ -51,6 +51,12 @@ Show program's version number and exit.
+*-v*, *--verbose*::
+
+Increase verbosity. Give up to twice.
+
+
+
*-n*, *--checkonly*::
Don't execute the release, just check script syntax.
diff --git a/taboot/Colors.py b/taboot/Colors.py
index 2179213..5a36dbf 100644
--- a/taboot/Colors.py
+++ b/taboot/Colors.py
@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from taboot.log import log_warn
+
"""
Colorizing functions and objects.
"""
@@ -60,7 +62,7 @@ class Colors(object):
@rtype: str
"""
if not self.does_color_exist(color):
- print "Color %s doesn't exist, using default." % color
+ log_warn("Color %s doesn't exist, using default.", color)
color = 'normal'
end_str = ""
if normalize:
@@ -107,7 +109,7 @@ class HTMLColors(Colors):
Returns a color formatted string.
"""
if not self.does_color_exist(color):
- print "Color %s doesn't exist, using default." % color
+ log_warn("Color %s doesn't exist, using default.", color)
color = 'normal'
end_str = ""
if normalize:
diff --git a/taboot/cli.py b/taboot/cli.py
index b100a11..c38e719 100644
--- a/taboot/cli.py
+++ b/taboot/cli.py
@@ -15,16 +15,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import sys
-import yaml
-import taboot.runner
import argparse
-import re
import datetime
import os
+import re
+import sys
import taboot
-from taboot.scripts import Scripts
+import taboot.runner
+import yaml
from taboot import __version__
+from taboot import log
+from taboot.log import *
+from taboot.scripts import Scripts
from util import log_update
@@ -58,6 +60,9 @@ Taboot is released under the terms of the GPLv3+ license""")
parser.add_argument('-V', '--version', action='version',
version='Taboot v%s' % __version__)
+ parser.add_argument('-v', '--verbose', action='count',
+ default=1,
+ help='Increase verbosity. Give up to twice.')
parser.add_argument('-n', '--checkonly', action='store_true',
default=False,
help='Don\'t execute the release, just check \
@@ -87,6 +92,9 @@ Taboot is released under the terms of the GPLv3+ license""")
if FILE is \'-\' or not given.')
args = parser.parse_args()
+ log_debug("Setting verbosity to %s", args.verbose)
+ log.LOG_LEVEL_CURRENT = args.verbose
+
if args.logfile:
# Since we are snarfing the next positional argument after -L,
# we may accidentally snarf up an input yaml file. Hence the
@@ -114,30 +122,35 @@ Taboot is released under the terms of the GPLv3+ license""")
input_files = ['-']
scripts = Scripts(input_files, args, config)
+ log_debug("scripts object created with %s items", len(scripts.scripts))
valid = True
# Failed script validation WILL terminate this release
for script in filter(lambda s: not s.valid, scripts.scripts):
- print "\nError: could not parse %s" % script.fileName
+ valid = False
+ log_error("Could not parse %s", script.fileName)
if not script.unknown_tasks == set():
- print "The following were used but are not valid tasks:"
+ log_error("\nThe following were used but are not valid tasks:")
for task in script.unknown_tasks:
- print " - %s" % task
+ log_error(" - %s", task)
if not script.elements_missing == set():
- print "The following required elements were not found:"
+ log_error("The following required elements were not found:")
for element in script.elements_missing:
- print " - %s" % element
- valid = False
+ log_error(" - %s", element)
if valid and args.checkonly:
+ log_debug("Exiting from dry run. Script verification passed.")
sys.exit(0)
elif valid and args.printonly:
scripts.print_scripts()
+ log_debug("Exiting from print only run. Script verification passed.")
sys.exit(0)
elif not valid:
+ log_debug("Exiting due to invalid scripts.")
sys.exit(1)
else:
+ log_debug("Executing main run loop in scripts object")
scripts.run()
if __name__ == '__main__':
diff --git a/taboot/log.py b/taboot/log.py
new file mode 100755
index 0000000..788f418
--- /dev/null
+++ b/taboot/log.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Taboot - Client utility for performing deployments with Func.
+# Copyright © 2011, Red Hat, Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import util
+
+LOG_DEBUG = 3
+LOG_WARN = 2
+LOG_INFO = 1
+LOG_ERROR = 0
+LOG_LEVEL_CURRENT = 1
+
+
+"""
+Log levels adapted from the `Apache Commons` Logging User Guide:
+
+http://commons.apache.org/logging/guide.html#Message%20Priorities/Levels
+
+Each level includes all of the levels below it.
+
+* ``Debug`` - Detailed information on the flow through the system.
+
+* ``Warn`` - Use of deprecated APIs, poor use of API, 'almost' errors,
+ other runtime situations that are undesirable or unexpected, but not
+ necessarily "wrong".
+
+* ``Info`` - Interesting runtime events (startup/shutdown). Expect
+ these to be immediately visible on a console, so be conservative and
+ keep to a minimum.
+
+* ``Error`` - Severe errors that cause premature termination. This
+ would rarely be seen.
+
+
+Examples:
+
+some_thing = "something"
+something = "something"
+thing = "thing"
+
+log_debug("Something is broken!!!")
+
+log_debug("%s is broken!!!!", some_thing)
+
+log_debug("In %s there is a broken %s", something, thing)
+
+log_debug("In %s there is a broken %s", (something, thing))
+
+log_debug("In %s there is a broken %s", [something, thing])
+
+"""
+
+
+def log_wrap(origfunc):
+ """
+ DRY: Use magic instead of code to get a string for the correct log
+ level when calling ``print_log_msg``.
+ """
+ def orig_func_wraper(msg, *args):
+ log_level = origfunc.__name__.split("_")[1]
+
+ import log
+ if getattr(log, "LOG_%s" % log_level.upper()) <= log.LOG_LEVEL_CURRENT:
+ # flatten the positional params so we don't tuple() a
+ # tuple or an array and end up with weirdness.
+ a = util.flatten(args)
+
+ print_log_msg(log_level, msg % tuple(a))
+ return orig_func_wraper
+
+
+def print_log_msg(log_level, msg):
+ for l in msg.split("\n"):
+ print "%s: %s" % (log_level.upper(), l)
+
+
+@log_wrap
+def log_error(mgs, LOG_ERROR, *args):
+ pass
+
+
+@log_wrap
+def log_warn(msg, LOG_WARN, *args):
+ pass
+
+
+@log_wrap
+def log_info(msg, LOG_INFO, *args):
+ pass
+
+
+@log_wrap
+def log_debug(msg, *args):
+ pass
+
+
+def _log_test():
+ some_thing = "something"
+ something = "something"
+ thing = "thing"
+
+ log_debug("Something is broken!!!")
+ log_debug("%s is broken!!!!", some_thing)
+ log_debug("In %s there is a broken %s", something, thing)
+ log_debug("In %s there is a broken %s", (something, thing))
+ log_debug("In %s there is a broken %s", [something, thing])
+
+
+if __name__ == "__main__":
+ #log_debug("test wrapped message: %s/%s", "one param", "two param")
+ _log_test()
diff --git a/taboot/runner.py b/taboot/runner.py
index b90fc58..bc74dd1 100644
--- a/taboot/runner.py
+++ b/taboot/runner.py
@@ -16,7 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import threading
-from util import instantiator
+from taboot.util import instantiator
class Runner(object):
diff --git a/taboot/scripts.py b/taboot/scripts.py
index b8d1596..956050d 100644
--- a/taboot/scripts.py
+++ b/taboot/scripts.py
@@ -21,6 +21,7 @@ import sys
import os
import yaml
from tabootScript import TabootScript
+from taboot.log import *
from errors import TabootMalformedYAMLException
from subprocess import call
@@ -66,19 +67,18 @@ class Scripts(object):
if self.args.edit:
blob = self._edit_input_file(blob)
except IOError, e:
- print "Failed to read input file '%s'. \
-Are you sure it exists?" % infile
+ log_error("Failed to read input file '%s'. \
+Are you sure it exists?", infile)
sys.exit(1)
- # Print a helpful message when loading the YAML fails
- ds = self._load_all_from_yaml(blob)
+ ds = self._load_all_from_yaml(blob, infile)
# Take the read in document and store each of its logical
# YAML documents as a TabootScript
for doc in ds:
self._add_taboot_script(doc, infile)
- def _load_all_from_yaml(self, blob):
+ def _load_all_from_yaml(self, blob, infile):
try:
ds = [doc for doc in yaml.load_all(blob)]
except yaml.YAMLError, exc:
@@ -86,13 +86,12 @@ Are you sure it exists?" % infile
mark = exc.problem_mark
probline = blob.split("\n")[mark.line]
arrow = " " * mark.column + "^"
- msg = """
-Syntax Error while loading YAML script, %s.
+ msg = """Syntax Error while loading YAML script, %s.
The problem is on line %s, column %s.
%s
-%s""" % (infile, mark.line + 1, mark.column + 1, probline, arrow)
- print msg
+%s"""
+ log_error(msg, infile, mark.line + 1, mark.column + 1, probline, arrow)
sys.exit(1)
else:
# No problem markers means we have to throw a generic
diff --git a/taboot/util.py b/taboot/util.py
index 3997150..9862403 100644
--- a/taboot/util.py
+++ b/taboot/util.py
@@ -110,10 +110,10 @@ def instantiator(type_blob, relative_to="taboot.tasks", **kwargs):
return instance_type(**kwargs)
except TypeError, e:
import pprint
- print "Unable to instantiate %s with the following arguments:"\
- % instance_type
+ log_error("Unable to instantiate %s with the following arguments:",
+ instance_type)
pprint.pprint(kwargs)
- print "Full backtrace below\n"
+ log_error("Full backtrace below\n")
raise
@@ -144,3 +144,17 @@ def sync_blob_copy(tmpfile):
tmpfile.close() # The file is erased when close()'d
open(tmpname, 'w').write(blob)
return blob
+
+
+def flatten(x):
+ """
+ Flatten an arbitrary depth nested list.
+ """
+ # Lifted from: http://stackoverflow.com/a/406822/263969
+ result = []
+ for el in x:
+ if hasattr(el, "__iter__") and not isinstance(el, basestring):
+ result.extend(flatten(el))
+ else:
+ result.append(el)
+ return result