summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/development/plugins.txt4
-rw-r--r--doc/releases/1.4.0pre2.txt3
-rw-r--r--doc/server/plugins/generators/examples/jinja2/extends.txt5
-rw-r--r--doc/server/plugins/generators/examples/jinja2/include.txt5
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py2
-rw-r--r--src/lib/Bcfg2/Client/__init__.py10
-rw-r--r--src/lib/Bcfg2/Options/Parser.py11
-rw-r--r--src/lib/Bcfg2/Server/Admin.py4
-rw-r--r--src/lib/Bcfg2/Server/Core.py19
-rw-r--r--src/lib/Bcfg2/Server/Plugin/helpers.py36
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/NagiosGen.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py2
-rw-r--r--src/lib/Bcfg2/Server/SSLServer.py4
-rw-r--r--testsuite/Testsrc/Testlib/TestOptions/TestOptions.py7
-rw-r--r--testsuite/common.py4
16 files changed, 67 insertions, 60 deletions
diff --git a/doc/development/plugins.txt b/doc/development/plugins.txt
index 5993c4e29..d292c9dd7 100644
--- a/doc/development/plugins.txt
+++ b/doc/development/plugins.txt
@@ -171,12 +171,12 @@ perf``. This data can be invaluable for locating bottlenecks or other
performance issues.
The simplest way to track statistics is to use the
-:func:`Bcfg2.Server.Plugin.helpers.track_statistics` decorator to
+:func:`Bcfg2.Server.Statistics.track_statistics` decorator to
decorate functions that you would like to track execution times for:
.. code-block:: python
- from Bcfg2.Server.Plugin import track_statistics
+ from Bcfg2.Server.Statistics import track_statistics
@track_statistics()
def do_something(self, ...):
diff --git a/doc/releases/1.4.0pre2.txt b/doc/releases/1.4.0pre2.txt
index 7bbed5603..572748f73 100644
--- a/doc/releases/1.4.0pre2.txt
+++ b/doc/releases/1.4.0pre2.txt
@@ -17,6 +17,8 @@ and as such is not likely suitable for general production deployment.
That said, please help us test the release in non- and preproduction
environments.
+* NagiosGen: Add bundles to configuration
+
backwards-incompatible user-facing changes
------------------------------------------
@@ -35,3 +37,4 @@ Special thanks to the following contributors for this release
* Alexander Sulfrain
* Matt Kemp
+ * Jeremie Banier
diff --git a/doc/server/plugins/generators/examples/jinja2/extends.txt b/doc/server/plugins/generators/examples/jinja2/extends.txt
index a0eeb5c03..3c6dd06ab 100644
--- a/doc/server/plugins/generators/examples/jinja2/extends.txt
+++ b/doc/server/plugins/generators/examples/jinja2/extends.txt
@@ -1,8 +1,9 @@
.. -*- mode: rst -*-
+.. vim: ft=rst
-=========================
+===========================
Extending Jinja2 Templates
-=========================
+===========================
Jinja2 templates can use the {% extends %} directive to inherit file
fragments which might be common to many configuration files.
diff --git a/doc/server/plugins/generators/examples/jinja2/include.txt b/doc/server/plugins/generators/examples/jinja2/include.txt
index 49be7c277..466ea6b97 100644
--- a/doc/server/plugins/generators/examples/jinja2/include.txt
+++ b/doc/server/plugins/generators/examples/jinja2/include.txt
@@ -1,8 +1,9 @@
.. -*- mode: rst -*-
+.. vim: ft=rst
-=========================
+===========================
Including Jinja2 Templates
-=========================
+===========================
Jinja2 templates can use the {% include %} directive to include file
fragments which might be common to many configuration files.
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index ae7fa3aed..67cdd4d6d 100644
--- a/src/lib/Bcfg2/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -205,6 +205,8 @@ class Tool(object):
continue
try:
states[entry] = func(entry, mods)
+ except KeyboardInterrupt:
+ raise
except: # pylint: disable=W0702
self.logger.error("%s: Unexpected failure verifying %s"
% (self.name,
diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py
index d834576c9..359d7ac73 100644
--- a/src/lib/Bcfg2/Client/__init__.py
+++ b/src/lib/Bcfg2/Client/__init__.py
@@ -617,6 +617,8 @@ class Client(object):
for tool in self.tools:
try:
self.states.update(tool.Inventory())
+ except KeyboardInterrupt:
+ raise
except: # pylint: disable=W0702
self.logger.error("%s.Inventory() call failed:" % tool.name,
exc_info=1)
@@ -734,6 +736,8 @@ class Client(object):
continue
try:
self.states.update(tool.Install(handled))
+ except KeyboardInterrupt:
+ raise
except: # pylint: disable=W0702
self.logger.error("%s.Install() call failed:" % tool.name,
exc_info=1)
@@ -754,6 +758,8 @@ class Client(object):
for tool, bundle in tbm:
try:
self.states.update(tool.Inventory(structures=[bundle]))
+ except KeyboardInterrupt:
+ raise
except: # pylint: disable=W0702
self.logger.error("%s.Inventory() call failed:" %
tool.name,
@@ -785,6 +791,8 @@ class Client(object):
for tool in self.tools:
try:
self.states.update(tool.BundleNotUpdated(bundle))
+ except KeyboardInterrupt:
+ raise
except: # pylint: disable=W0702
self.logger.error('%s.BundleNotUpdated(%s:%s) call failed:'
% (tool.name, bundle.tag,
@@ -794,6 +802,8 @@ class Client(object):
for tool in self.tools:
try:
self.states.update(tool.BundleNotUpdated(indep))
+ except KeyboardInterrupt:
+ raise
except: # pylint: disable=W0702
self.logger.error("%s.BundleNotUpdated(%s:%s) call failed:"
% (tool.name, indep.tag,
diff --git a/src/lib/Bcfg2/Options/Parser.py b/src/lib/Bcfg2/Options/Parser.py
index c846e8093..d146e3aa2 100644
--- a/src/lib/Bcfg2/Options/Parser.py
+++ b/src/lib/Bcfg2/Options/Parser.py
@@ -6,7 +6,7 @@ import sys
from Bcfg2.version import __version__
from Bcfg2.Compat import ConfigParser
-from Bcfg2.Options import Option, PathOption, BooleanOption, _debug
+from Bcfg2.Options import Option, PathOption, _debug
__all__ = ["setup", "OptionParserException", "Parser", "get_parser",
"new_parser"]
@@ -43,9 +43,16 @@ class Parser(argparse.ArgumentParser):
help="Path to configuration file",
default="/etc/bcfg2.conf")
+ #: Verbose version string that is printed if executed with --version
+ _version_string = "%s %s on Python %s" % (
+ os.path.basename(sys.argv[0]),
+ __version__,
+ ".".join(str(v) for v in sys.version_info[0:3]))
+
#: Builtin options that apply to all commands
options = [configfile,
- BooleanOption('--version', help="Print the version and exit"),
+ Option('--version', help="Print the version and exit",
+ action="version", version=_version_string),
Option('-E', '--encoding', metavar='<encoding>',
default='UTF-8', help="Encoding of config files",
cf=('components', 'encoding'))]
diff --git a/src/lib/Bcfg2/Server/Admin.py b/src/lib/Bcfg2/Server/Admin.py
index ef7741880..c294e6be5 100644
--- a/src/lib/Bcfg2/Server/Admin.py
+++ b/src/lib/Bcfg2/Server/Admin.py
@@ -1198,7 +1198,9 @@ class CLI(Bcfg2.Options.CommandRegistry):
def run(self):
""" Run bcfg2-admin """
try:
- self.commands[Bcfg2.Options.setup.subcommand].setup()
+ cmd = self.commands[Bcfg2.Options.setup.subcommand]
+ if hasattr(cmd, 'setup'):
+ cmd.setup()
return self.runcommand()
finally:
self.shutdown()
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index bc305e47a..03ab40343 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -22,7 +22,7 @@ from Bcfg2.Server.Cache import Cache
from Bcfg2.Compat import xmlrpclib, wraps # pylint: disable=W0622
from Bcfg2.Server.Plugin.exceptions import * # pylint: disable=W0401,W0614
from Bcfg2.Server.Plugin.interfaces import * # pylint: disable=W0401,W0614
-from Bcfg2.Server.Plugin import track_statistics
+from Bcfg2.Server.Statistics import track_statistics
try:
from django.core.exceptions import ImproperlyConfigured
@@ -495,7 +495,7 @@ class Core(object):
(self.__class__.__name__, hook),
time.time() - start)
- @Bcfg2.Server.Statistics.track_statistics()
+ @track_statistics()
def validate_structures(self, metadata, data):
""" Checks the data structures by calling the
:func:`Bcfg2.Server.Plugin.interfaces.StructureValidator.validate_structures`
@@ -522,7 +522,7 @@ class Core(object):
self.logger.error("Plugin %s: unexpected structure validation "
"failure" % plugin.name, exc_info=1)
- @Bcfg2.Server.Statistics.track_statistics()
+ @track_statistics()
def validate_goals(self, metadata, data):
""" Checks that the config matches the goals enforced by
:class:`Bcfg2.Server.Plugin.interfaces.GoalValidator` plugins
@@ -548,7 +548,7 @@ class Core(object):
self.logger.error("Plugin %s: unexpected goal validation "
"failure" % plugin.name, exc_info=1)
- @Bcfg2.Server.Statistics.track_statistics()
+ @track_statistics()
def GetStructures(self, metadata):
""" Get all structures (i.e., bundles) for the given client
@@ -560,14 +560,15 @@ class Core(object):
structures = list(
chain(*[struct.BuildStructures(metadata)
for struct in self.plugins_by_type(Structure)]))
- sbundles = [b.get('name') for b in structures if b.tag == 'Bundle']
+ sbundles = [b.get('name') for b in structures
+ if b.tag == 'Bundle' or b.tag == 'Independent']
missing = [b for b in metadata.bundles if b not in sbundles]
if missing:
self.logger.error("Client %s configuration missing bundles: %s" %
(metadata.hostname, ':'.join(missing)))
return structures
- @Bcfg2.Server.Statistics.track_statistics()
+ @track_statistics()
def BindStructures(self, structures, metadata, config):
""" Given a list of structures (i.e. bundles), bind all the
entries in them and add the structures to the config.
@@ -588,7 +589,7 @@ class Core(object):
except:
self.logger.error("error in BindStructure", exc_info=1)
- @Bcfg2.Server.Statistics.track_statistics()
+ @track_statistics()
def BindStructure(self, structure, metadata):
""" Bind all elements in a single structure (i.e., bundle).
@@ -821,7 +822,7 @@ class Core(object):
% plugin.name, exc_info=1)
return result
- @Bcfg2.Server.Statistics.track_statistics()
+ @track_statistics()
def check_acls(self, address, rmi):
""" Check client IP address and metadata object against all
:class:`Bcfg2.Server.Plugin.interfaces.ClientACLs` plugins.
@@ -876,7 +877,7 @@ class Core(object):
"%s" % (client, rmi, sys.exc_info()[1]))
return False # failsafe
- @Bcfg2.Server.Statistics.track_statistics()
+ @track_statistics()
def build_metadata(self, client_name):
""" Build initial client metadata for a client
diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py
index 559612d1e..2aab231c6 100644
--- a/src/lib/Bcfg2/Server/Plugin/helpers.py
+++ b/src/lib/Bcfg2/Server/Plugin/helpers.py
@@ -3,7 +3,6 @@
import os
import re
import sys
-import time
import copy
import glob
import logging
@@ -35,41 +34,6 @@ except ImportError:
LOGGER = logging.getLogger(__name__)
-class track_statistics(object): # pylint: disable=C0103
- """ Decorator that tracks execution time for the given
- :class:`Plugin` method with :mod:`Bcfg2.Statistics` for reporting
- via ``bcfg2-admin perf`` """
-
- def __init__(self, name=None):
- """
- :param name: The name under which statistics for this function
- will be tracked. By default, the name will be
- the name of the function concatenated with the
- name of the class the function is a member of.
- :type name: string
- """
- # if this is None, it will be set later during __call_
- self.name = name
-
- def __call__(self, func):
- if self.name is None:
- self.name = func.__name__
-
- @wraps(func)
- def inner(obj, *args, **kwargs):
- """ The decorated function """
- name = "%s:%s" % (obj.__class__.__name__, self.name)
-
- start = time.time()
- try:
- return func(obj, *args, **kwargs)
- finally:
- Bcfg2.Server.Statistics.stats.add_value(name,
- time.time() - start)
-
- return inner
-
-
def removecomment(stream):
""" A Genshi filter that removes comments from the stream. This
function is a generator.
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index 1d15656af..26f39e50d 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -502,6 +502,8 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
"""This class contains data for bcfg2 server metadata."""
__author__ = 'bcfg-dev@mcs.anl.gov'
sort_order = 500
+ __rmi__ = Bcfg2.Server.Plugin.DatabaseBacked.__rmi__ + ['list_clients',
+ 'remove_client']
options = Bcfg2.Server.Plugin.DatabaseBacked.options + [
Bcfg2.Options.Common.password,
diff --git a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
index 045e46350..d3c38ef19 100644
--- a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
+++ b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
@@ -45,7 +45,11 @@ class NagiosGen(Plugin, Generator):
raise PluginExecutionError("Failed to find IP address for %s" %
metadata.hostname)
host_groups = [grp for grp in metadata.groups
- if os.path.isfile('%s/%s-group.cfg' % (self.data, grp))]
+ if os.path.isfile('%s/%s-group.cfg' %
+ (self.data, grp))] + \
+ [bundle for bundle in metadata.bundles
+ if os.path.isfile('%s/%s-bundle.cfg' %
+ (self.data, bundle))]
host_config = ['define host {',
self.line_fmt % ('host_name', metadata.hostname),
self.line_fmt % ('alias', metadata.hostname),
@@ -81,7 +85,8 @@ class NagiosGen(Plugin, Generator):
def createserverconfig(self, entry, _):
"""Build monolithic server configuration file."""
host_configs = glob.glob(os.path.join(self.data, '*-host.cfg'))
- group_configs = glob.glob(os.path.join(self.data, '*-group.cfg'))
+ group_configs = glob.glob(os.path.join(self.data, '*-group.cfg')) + \
+ glob.glob(os.path.join(self.data, '*-bundle.cfg'))
host_data = []
group_data = []
for host in host_configs:
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index f26ded4c5..b6e9f13eb 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -1231,7 +1231,7 @@ class YumSource(Source):
self.provides[arch][prov] = list()
self.provides[arch][prov].append(pkgname)
- @Bcfg2.Server.Plugin.track_statistics()
+ @track_statistics()
def parse_group(self, data):
""" parse comps.xml.gz data """
for group in data.getchildren():
diff --git a/src/lib/Bcfg2/Server/SSLServer.py b/src/lib/Bcfg2/Server/SSLServer.py
index 6ad5b5635..1f8febd0e 100644
--- a/src/lib/Bcfg2/Server/SSLServer.py
+++ b/src/lib/Bcfg2/Server/SSLServer.py
@@ -43,8 +43,10 @@ class XMLRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
params = (address, ) + params
response = self.instance._dispatch(method, params, self.funcs)
# py3k compatibility
- if type(response) not in [bool, str, list, dict]:
+ if type(response) not in [bool, str, list, dict, set, type(None)]:
response = (response.decode('utf-8'), )
+ elif type(response) == set:
+ response = (list(response), )
else:
response = (response, )
raw_response = xmlrpclib.dumps(response, methodresponse=True,
diff --git a/testsuite/Testsrc/Testlib/TestOptions/TestOptions.py b/testsuite/Testsrc/Testlib/TestOptions/TestOptions.py
index a2dc8ffe2..034a4580a 100644
--- a/testsuite/Testsrc/Testlib/TestOptions/TestOptions.py
+++ b/testsuite/Testsrc/Testlib/TestOptions/TestOptions.py
@@ -185,6 +185,13 @@ class TestBasicOptions(OptionTestCase):
options = self._test_options(env={"TEST_PATH_OPTION": "/foo"})
self.assertEqual(options.test_path_option, "/foo")
+ def test_version(self):
+ """print version and exit on --version"""
+ self.assertRaises(
+ SystemExit,
+ self._test_options,
+ options=['--version'])
+
def test_set_boolean_in_cli(self):
"""set boolean options in CLI options."""
# passing the option yields the reverse of the default, no
diff --git a/testsuite/common.py b/testsuite/common.py
index a86e9c5d9..4b29e5f52 100644
--- a/testsuite/common.py
+++ b/testsuite/common.py
@@ -16,9 +16,9 @@ import lxml.etree
import Bcfg2.Options
from mock import patch, MagicMock, _patch, DEFAULT
try:
- from unittest import skip, skipIf, skipUnless, TestCase
-except ImportError:
from unittest2 import skip, skipIf, skipUnless, TestCase
+except ImportError:
+ from unittest import skip, skipIf, skipUnless, TestCase
#: The XInclude namespace name
XI_NAMESPACE = "http://www.w3.org/2001/XInclude"