summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/development/index.txt1
-rw-r--r--doc/development/plugins.txt4
-rw-r--r--doc/development/unit-testing.txt25
-rw-r--r--doc/server/plugins/connectors/templatehelper.txt72
-rw-r--r--examples/TemplateHelper/include.py97
-rw-r--r--schemas/base.xsd1
-rw-r--r--schemas/clients.xsd1
-rw-r--r--schemas/decisions.xsd1
-rw-r--r--schemas/defaults.xsd1
-rw-r--r--schemas/genshi.xsd1
-rw-r--r--schemas/metadata.xsd1
-rw-r--r--schemas/pathentry.xsd1
-rw-r--r--schemas/pkglist.xsd1
-rw-r--r--schemas/pkgtype.xsd1
-rw-r--r--schemas/rules.xsd1
-rw-r--r--schemas/services.xsd1
-rw-r--r--schemas/servicetype.xsd1
-rw-r--r--schemas/types.xsd1
-rw-r--r--src/lib/Client/Frame.py1
-rw-r--r--src/lib/Client/Tools/APK.py1
-rw-r--r--src/lib/Client/Tools/APT.py1
-rw-r--r--src/lib/Client/Tools/Action.py1
-rw-r--r--src/lib/Client/Tools/Blast.py2
-rw-r--r--src/lib/Client/Tools/Chkconfig.py2
-rw-r--r--src/lib/Client/Tools/DebInit.py1
-rw-r--r--src/lib/Client/Tools/Encap.py2
-rw-r--r--src/lib/Client/Tools/FreeBSDPackage.py1
-rw-r--r--src/lib/Client/Tools/IPS.py1
-rw-r--r--src/lib/Client/Tools/MacPorts.py1
-rw-r--r--src/lib/Client/Tools/POSIX.py1
-rw-r--r--src/lib/Client/Tools/Portage.py1
-rw-r--r--src/lib/Client/Tools/RPMng.py8
-rw-r--r--src/lib/Client/Tools/RcUpdate.py1
-rw-r--r--src/lib/Client/Tools/SMF.py1
-rw-r--r--src/lib/Client/Tools/SYSV.py2
-rw-r--r--src/lib/Client/Tools/Upstart.py1
-rw-r--r--src/lib/Client/Tools/YUM24.py7
-rw-r--r--src/lib/Client/Tools/YUMng.py7
-rw-r--r--src/lib/Client/Tools/__init__.py5
-rw-r--r--src/lib/Client/Tools/launchd.py1
-rwxr-xr-xsrc/lib/Client/Tools/rpmtools.py1
-rw-r--r--src/lib/Client/XML.py1
-rw-r--r--src/lib/Client/__init__.py1
-rw-r--r--src/lib/Component.py2
-rw-r--r--src/lib/Logger.py1
-rw-r--r--src/lib/Options.py1
-rw-r--r--src/lib/Proxy.py3
-rw-r--r--src/lib/SSLServer.py2
-rw-r--r--src/lib/Server/Admin/__init__.py2
-rw-r--r--src/lib/Server/Core.py1
-rw-r--r--src/lib/Server/Hostbase/backends.py2
-rw-r--r--src/lib/Server/Hostbase/hostbase/views.py2
-rw-r--r--src/lib/Server/Hostbase/nisauth.py4
-rw-r--r--src/lib/Server/Lint/__init__.py2
-rw-r--r--src/lib/Server/Plugin.py11
-rw-r--r--src/lib/Server/Plugins/Account.py2
-rw-r--r--src/lib/Server/Plugins/BB.py1
-rw-r--r--src/lib/Server/Plugins/Base.py2
-rw-r--r--src/lib/Server/Plugins/Bundler.py2
-rw-r--r--src/lib/Server/Plugins/Bzr.py1
-rw-r--r--src/lib/Server/Plugins/Cfg.py2
-rw-r--r--src/lib/Server/Plugins/Cvs.py1
-rw-r--r--src/lib/Server/Plugins/DBStats.py1
-rw-r--r--src/lib/Server/Plugins/Darcs.py1
-rw-r--r--src/lib/Server/Plugins/Decisions.py1
-rw-r--r--src/lib/Server/Plugins/Defaults.py2
-rw-r--r--src/lib/Server/Plugins/Deps.py2
-rw-r--r--src/lib/Server/Plugins/Editor.py1
-rw-r--r--src/lib/Server/Plugins/FileProbes.py2
-rw-r--r--src/lib/Server/Plugins/Fossil.py1
-rw-r--r--src/lib/Server/Plugins/Git.py1
-rw-r--r--src/lib/Server/Plugins/Guppy.py1
-rw-r--r--src/lib/Server/Plugins/Hg.py1
-rw-r--r--src/lib/Server/Plugins/Hostbase.py2
-rw-r--r--src/lib/Server/Plugins/Ldap.py1
-rw-r--r--src/lib/Server/Plugins/Metadata.py613
-rw-r--r--src/lib/Server/Plugins/NagiosGen.py1
-rw-r--r--src/lib/Server/Plugins/Pkgmgr.py2
-rw-r--r--src/lib/Server/Plugins/Probes.py1
-rw-r--r--src/lib/Server/Plugins/Properties.py1
-rw-r--r--src/lib/Server/Plugins/Rules.py2
-rw-r--r--src/lib/Server/Plugins/SGenshi.py2
-rw-r--r--src/lib/Server/Plugins/SSHbase.py2
-rw-r--r--src/lib/Server/Plugins/SSLCA.py1
-rw-r--r--src/lib/Server/Plugins/Statistics.py2
-rw-r--r--src/lib/Server/Plugins/Svcmgr.py2
-rw-r--r--src/lib/Server/Plugins/Svn.py1
-rw-r--r--src/lib/Server/Plugins/Svn2.py1
-rw-r--r--src/lib/Server/Plugins/TCheetah.py2
-rw-r--r--src/lib/Server/Plugins/TGenshi.py2
-rw-r--r--src/lib/Server/Plugins/TemplateHelper.py83
-rw-r--r--src/lib/Server/Plugins/Trigger.py1
-rw-r--r--src/lib/Server/Plugins/__init__.py1
-rwxr-xr-xsrc/lib/Server/Reports/importscript.py1
-rw-r--r--src/lib/Server/Reports/nisauth.py2
-rw-r--r--src/lib/Server/__init__.py2
-rw-r--r--src/lib/__init__.py1
-rwxr-xr-xsrc/sbin/bcfg21
-rwxr-xr-xsrc/sbin/bcfg2-build-reports2
-rwxr-xr-xsrc/sbin/bcfg2-info8
-rwxr-xr-xsrc/sbin/bcfg2-lint1
-rwxr-xr-xsrc/sbin/bcfg2-ping-sweep2
-rwxr-xr-xsrc/sbin/bcfg2-reports1
-rwxr-xr-xsrc/sbin/bcfg2-server1
-rwxr-xr-xsrc/sbin/bcfg2-yum-helper2
-rw-r--r--testsuite/TestFrame.py104
-rw-r--r--testsuite/TestOptions.py103
-rw-r--r--testsuite/TestPlugin.py122
-rw-r--r--testsuite/Testlib/TestOptions.py124
-rw-r--r--testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py898
-rwxr-xr-xtools/bcfg2-export-config1
-rwxr-xr-xtools/create-debian-pkglist.py1
-rwxr-xr-xtools/encap-util-count.sh1
-rwxr-xr-xtools/encap-util-expand.sh1
-rwxr-xr-xtools/encap-util-place.sh1
-rwxr-xr-xtools/encap-util-xml.sh1
-rwxr-xr-xtools/hostbasepush.py2
-rwxr-xr-xtools/pkgmgr_gen.py1
118 files changed, 1602 insertions, 827 deletions
diff --git a/doc/development/index.txt b/doc/development/index.txt
index 352000dc8..2a54bfad8 100644
--- a/doc/development/index.txt
+++ b/doc/development/index.txt
@@ -39,3 +39,4 @@ git access. Mail the :ref:`help-mailinglist` for details.
testing
documentation
docstyleguide
+ unit-testing
diff --git a/doc/development/plugins.txt b/doc/development/plugins.txt
index 15b512365..b2b70f553 100644
--- a/doc/development/plugins.txt
+++ b/doc/development/plugins.txt
@@ -164,7 +164,6 @@ Example Connector
Bcfg2.Server.Plugin.Connector):
'''The Foo plugin is here to illustrate a barebones connector'''
name = 'Foo'
- version = '$Revision: $'
experimental = True
def __init__(self, core, datastore):
@@ -195,13 +194,10 @@ do so. We will call our new plugin `MyMetadata`.
.. code-block:: python
- __revision__ = '$Revision$'
-
import Bcfg2.Server.Plugins.Metadata
class MyMetadata(Bcfg2.Server.Plugins.Metadata.Metadata):
'''This class contains data for bcfg2 server metadata'''
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore, watch_clients=True):
diff --git a/doc/development/unit-testing.txt b/doc/development/unit-testing.txt
new file mode 100644
index 000000000..30217dcc5
--- /dev/null
+++ b/doc/development/unit-testing.txt
@@ -0,0 +1,25 @@
+.. -*- mode: rst -*-
+
+.. _development-unit-testing:
+
+==================
+Bcfg2 unit testing
+==================
+
+.. _Python Mock Module: http://python-mock.sourceforge.net/
+.. _Python Nose: http://readthedocs.org/docs/nose/en/latest/
+
+You will first need to install the `Python Mock Module`_ and `Python
+Nose`_ modules. You can then run the existing tests with the
+following.::
+
+ cd testsuite
+ nosetests
+
+You should see output something like the following::
+
+ ..................................................
+ ----------------------------------------------------------------------
+ Ran 50 tests in 0.121s
+
+ OK
diff --git a/doc/server/plugins/connectors/templatehelper.txt b/doc/server/plugins/connectors/templatehelper.txt
new file mode 100644
index 000000000..67219109f
--- /dev/null
+++ b/doc/server/plugins/connectors/templatehelper.txt
@@ -0,0 +1,72 @@
+.. -*- mode: rst -*-
+
+.. _server-plugins-connectors-templatehelper:
+
+==============
+TemplateHelper
+==============
+
+The TemplateHelper plugin is a connector plugin that adds Python
+classes and methods to client metadata instances for use in
+templates. This allows you to easily reuse code that is common
+amongst multiple templates and add convenience methods.
+
+Using TemplateHelper
+====================
+
+First, ``mkdir /var/lib/bcfg2/TemplateHelper`` and add
+**TemplateHelper** to your ``plugins`` line in ``/etc/bcfg2.conf``.
+Restart ``bcfg2-server``.
+
+Now, any ``.py`` file placed in ``/var/lib/bcfg2/TemplateHelper/``
+will be read and added to matching client metadata objects. See
+:ref:`Writing Helpers` below for more information on how to write
+TemplateHelper scripts.
+
+TemplateHelper supports group- and host-specific helpers, so you could
+create, e.g., ``foo.py.G80_test`` to create a helper that only applied
+to machines in the group ``test``.
+
+Writing Helpers
+===============
+
+A helper module is just a Python module with three special conditions:
+
+* The filename must end with ``.py`` (before any specificity
+ strings, e.g., ``.G80_foo`` or ``.H_blah.example.com``
+* The module must have an attribute, ``__export__``, that lists all of
+ the classes, functions, variables, or other symbols you wish to
+ export from the module.
+* ``data``, ``handle_event``, ``name``, and ``specific`` are reserved
+ names. You should not include symbols with a reserved name in
+ ``__export__``. Additionally, including symbols that start with an
+ underscore or double underscore is bad form, and may also produce
+ errors.
+
+See ``examples/TemplateHelper`` for examples of helper modules.
+
+Usage
+=====
+
+Specific helpers can be referred to in
+templates as ``metadata.TemplateHelper[<modulename>]``. That accesses
+a HelperModule object will has, as attributes, all symbols listed in
+``__export__``. For example, consider this helper module::
+
+ __export__ = ["hello"]
+
+ def hello(metadata):
+ return "Hello, %s!" % metadata.hostname
+
+To use this in a TGenshi template, we could do::
+
+ ${metadata.TemplateHelper['hello'].hello(metadata)}
+
+The template would produce::
+
+ Hello, foo.example.com!
+
+Note that the client metadata object is not passed to a helper module
+in any magical way; if you want to access the client metadata object
+in a helper function or class, you must pass the object to the
+function manually.
diff --git a/examples/TemplateHelper/include.py b/examples/TemplateHelper/include.py
new file mode 100644
index 000000000..5fba75558
--- /dev/null
+++ b/examples/TemplateHelper/include.py
@@ -0,0 +1,97 @@
+"""IncludeHelper makes it easier to include group- and host-specific files in a template.
+
+Synopsis:
+
+ {% python
+ import os
+ include = metadata.TemplateHelper['include'].IncludeHelper
+ custom = include(metadata, path).files(os.path.basename(name))
+ %}\
+ {% for file in custom %}\
+
+ ########## Start ${include.specificity(file)} ##########
+ {% include ${file} %}
+ ########## End ${include.specificity(file)} ##########
+ {% end %}\
+
+This would let you include files with the same base name; e.g. in a
+template for ''foo.conf'', the include files would be called
+''foo.conf.G_<group>.genshi_include''. If a template needs to include
+different files in different places, you can do that like so:
+
+ inc = metadata.TemplateHelper['include'].IncludeHelper(metadata, path)
+ custom_bar = inc.files("bar")
+ custom_baz = inc.files("baz")
+
+This would result in two different sets of custom files being used,
+one drawn from ''bar.conf.G_<group>.genshi_include'' and the other
+from ''baz.conf.G_<group>.genshi_include''.
+
+==== Methods ====
+
+
+=== files ===
+
+Usage:
+
+
+
+"""
+
+import os
+import re
+import Bcfg2.Options
+
+__export__ = ["IncludeHelper"]
+
+class IncludeHelper (object):
+ def __init__(self, metadata, path):
+ """ Constructor.
+
+ The template path can be found in the ''path'' variable that is set for all Genshi templates."""
+ self.metadata = metadata
+ self.path = path
+
+ def _get_basedir(self):
+ setup = Bcfg2.Options.OptionParser({'repo':
+ Bcfg2.Options.SERVER_REPOSITORY})
+ setup.parse('--')
+ return os.path.join(setup['repo'], os.path.dirname(self.path))
+
+ def files(self, fname):
+ """ Return a list of files to include for this host. Files
+ are found in the template directory based on the following
+ patterns:
+
+ * ''<prefix>.H_<hostname>.genshi_include'': Host-specific files
+ * ''<prefix>.G_<group>.genshi_include'': Group-specific files
+
+ Note that there is no numeric priority on the group-specific
+ files. All matching files are returned by
+ ''IncludeHelper.files()''. """
+ files = []
+ hostfile = os.path.join(self._get_basedir(),
+ "%s.H_%s.genshi_include" %
+ (fname, self.metadata.hostname))
+ if os.path.isfile(hostfile):
+ files.append(hostfile)
+
+ for group in self.metadata.groups:
+ filename = os.path.join(self._get_basedir(),
+ "%s.G_%s.genshi_include" % (fname, group))
+ if os.path.isfile(filename):
+ files.append(filename)
+
+ return sorted(files)
+
+ @staticmethod
+ def specificity(fname):
+ """ Get a string describing the specificity of the given file """
+ match = re.search(r'(G|H)_(.*)\.genshi_include', fname)
+ if match:
+ if match.group(1) == "G":
+ stype = "group"
+ else:
+ stype = "host"
+ return "%s-specific configs for %s" % (stype, match.group(2))
+ return "Unknown specificity"
diff --git a/schemas/base.xsd b/schemas/base.xsd
index 91b7ac8f5..cca665b38 100644
--- a/schemas/base.xsd
+++ b/schemas/base.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
base schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/clients.xsd b/schemas/clients.xsd
index 0a9ce5202..9e8d5a22a 100644
--- a/schemas/clients.xsd
+++ b/schemas/clients.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
client schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/decisions.xsd b/schemas/decisions.xsd
index a354ec8cb..30115b367 100644
--- a/schemas/decisions.xsd
+++ b/schemas/decisions.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
decision list schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/defaults.xsd b/schemas/defaults.xsd
index 27e749470..c7e2edc7e 100644
--- a/schemas/defaults.xsd
+++ b/schemas/defaults.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
string enumeration definitions for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/genshi.xsd b/schemas/genshi.xsd
index 088ec56be..35d81e2f1 100644
--- a/schemas/genshi.xsd
+++ b/schemas/genshi.xsd
@@ -8,7 +8,6 @@
<xs:documentation>
Genshi schema
Chris St. Pierre
- $Id$
</xs:documentation>
</xs:annotation>
diff --git a/schemas/metadata.xsd b/schemas/metadata.xsd
index 58f9e8029..f79039d25 100644
--- a/schemas/metadata.xsd
+++ b/schemas/metadata.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
metadata schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/pathentry.xsd b/schemas/pathentry.xsd
index 315b0082e..080758d0b 100644
--- a/schemas/pathentry.xsd
+++ b/schemas/pathentry.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
path entry schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/pkglist.xsd b/schemas/pkglist.xsd
index c16ed654e..c0d449f54 100644
--- a/schemas/pkglist.xsd
+++ b/schemas/pkglist.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
package list schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/pkgtype.xsd b/schemas/pkgtype.xsd
index 3655c646b..9ddc0682e 100644
--- a/schemas/pkgtype.xsd
+++ b/schemas/pkgtype.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
package list schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/rules.xsd b/schemas/rules.xsd
index 06c3b38ac..924792b18 100644
--- a/schemas/rules.xsd
+++ b/schemas/rules.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
string enumeration definitions for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/services.xsd b/schemas/services.xsd
index 828959e82..b91e851d2 100644
--- a/schemas/services.xsd
+++ b/schemas/services.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
services schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/servicetype.xsd b/schemas/servicetype.xsd
index 6b497383d..af5bc64a6 100644
--- a/schemas/servicetype.xsd
+++ b/schemas/servicetype.xsd
@@ -5,7 +5,6 @@
<xsd:documentation>
services schema for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/schemas/types.xsd b/schemas/types.xsd
index 689e693b7..ead377192 100644
--- a/schemas/types.xsd
+++ b/schemas/types.xsd
@@ -4,7 +4,6 @@
<xsd:documentation>
string enumeration definitions for bcfg2
Narayan Desai, Argonne National Laboratory
- $Id$
</xsd:documentation>
</xsd:annotation>
diff --git a/src/lib/Client/Frame.py b/src/lib/Client/Frame.py
index d17f70f1b..67222f245 100644
--- a/src/lib/Client/Frame.py
+++ b/src/lib/Client/Frame.py
@@ -2,7 +2,6 @@
Frame is the Client Framework that verifies and
installs entries, and generates statistics.
"""
-__revision__ = '$Revision$'
import logging
import sys
diff --git a/src/lib/Client/Tools/APK.py b/src/lib/Client/Tools/APK.py
index 6a6fd51b3..aaaf2472f 100644
--- a/src/lib/Client/Tools/APK.py
+++ b/src/lib/Client/Tools/APK.py
@@ -1,5 +1,4 @@
"""This provides Bcfg2 support for Alpine Linux APK packages."""
-__revision__ = '$Revision$'
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/APT.py b/src/lib/Client/Tools/APT.py
index db8cd56e7..6b839ffbc 100644
--- a/src/lib/Client/Tools/APT.py
+++ b/src/lib/Client/Tools/APT.py
@@ -1,5 +1,4 @@
"""This is the Bcfg2 support for apt-get."""
-__revision__ = '$Revision$'
# suppress apt API warnings
import warnings
diff --git a/src/lib/Client/Tools/Action.py b/src/lib/Client/Tools/Action.py
index c089cde1d..dc49347e9 100644
--- a/src/lib/Client/Tools/Action.py
+++ b/src/lib/Client/Tools/Action.py
@@ -1,5 +1,4 @@
"""Action driver"""
-__revision__ = '$Revision$'
import Bcfg2.Client.Tools
from Bcfg2.Client.Frame import matches_white_list, passes_black_list
diff --git a/src/lib/Client/Tools/Blast.py b/src/lib/Client/Tools/Blast.py
index 29cdfa116..5d5e74ab2 100644
--- a/src/lib/Client/Tools/Blast.py
+++ b/src/lib/Client/Tools/Blast.py
@@ -1,6 +1,4 @@
-# This is the bcfg2 support for blastwave packages (pkg-get)
"""This provides Bcfg2 support for Blastwave."""
-__revision__ = '$Revision$'
import tempfile
import Bcfg2.Client.Tools.SYSV
diff --git a/src/lib/Client/Tools/Chkconfig.py b/src/lib/Client/Tools/Chkconfig.py
index 17e8bf09b..12ea5f132 100644
--- a/src/lib/Client/Tools/Chkconfig.py
+++ b/src/lib/Client/Tools/Chkconfig.py
@@ -1,8 +1,6 @@
# This is the bcfg2 support for chkconfig
-# $Id$
"""This is chkconfig support."""
-__revision__ = '$Revision$'
import os
diff --git a/src/lib/Client/Tools/DebInit.py b/src/lib/Client/Tools/DebInit.py
index 022332602..ca6fc439e 100644
--- a/src/lib/Client/Tools/DebInit.py
+++ b/src/lib/Client/Tools/DebInit.py
@@ -1,5 +1,4 @@
"""Debian Init Support for Bcfg2"""
-__revision__ = '$Revision$'
import glob
import os
diff --git a/src/lib/Client/Tools/Encap.py b/src/lib/Client/Tools/Encap.py
index 92062a750..fa09c3ec7 100644
--- a/src/lib/Client/Tools/Encap.py
+++ b/src/lib/Client/Tools/Encap.py
@@ -1,7 +1,5 @@
"""Bcfg2 Support for Encap Packages"""
-__revision__ = '$Revision$'
-
import glob
import re
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/FreeBSDPackage.py b/src/lib/Client/Tools/FreeBSDPackage.py
index 04c05adaa..3e6f2b6bb 100644
--- a/src/lib/Client/Tools/FreeBSDPackage.py
+++ b/src/lib/Client/Tools/FreeBSDPackage.py
@@ -1,5 +1,4 @@
"""This is the Bcfg2 tool for the FreeBSD package system."""
-__revision__ = '$Rev$'
# TODO
# - actual package installation
diff --git a/src/lib/Client/Tools/IPS.py b/src/lib/Client/Tools/IPS.py
index 9afd23143..e30bbd2a4 100644
--- a/src/lib/Client/Tools/IPS.py
+++ b/src/lib/Client/Tools/IPS.py
@@ -1,5 +1,4 @@
"""This is the Bcfg2 support for OpenSolaris packages."""
-__revision__ = '$Revision$'
import pkg.client.image as image
import pkg.client.progress as progress
diff --git a/src/lib/Client/Tools/MacPorts.py b/src/lib/Client/Tools/MacPorts.py
index 23b536451..e8c911390 100644
--- a/src/lib/Client/Tools/MacPorts.py
+++ b/src/lib/Client/Tools/MacPorts.py
@@ -1,5 +1,4 @@
"""This provides Bcfg2 support for macports packages."""
-__revision__ = '$Revision$'
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/POSIX.py b/src/lib/Client/Tools/POSIX.py
index 3591c33ad..326c6e02d 100644
--- a/src/lib/Client/Tools/POSIX.py
+++ b/src/lib/Client/Tools/POSIX.py
@@ -1,5 +1,4 @@
"""All POSIX Type client support for Bcfg2."""
-__revision__ = '$Revision$'
import binascii
from datetime import datetime
diff --git a/src/lib/Client/Tools/Portage.py b/src/lib/Client/Tools/Portage.py
index 17163afa9..646995f4e 100644
--- a/src/lib/Client/Tools/Portage.py
+++ b/src/lib/Client/Tools/Portage.py
@@ -1,5 +1,4 @@
"""This is the Bcfg2 tool for the Gentoo Portage system."""
-__revision__ = '$Revision$'
import re
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/RPMng.py b/src/lib/Client/Tools/RPMng.py
index 5376118c2..b28bec030 100644
--- a/src/lib/Client/Tools/RPMng.py
+++ b/src/lib/Client/Tools/RPMng.py
@@ -1,7 +1,5 @@
"""Bcfg2 Support for RPMS"""
-__revision__ = '$Revision$'
-
import os.path
import rpm
import rpmtools
@@ -9,12 +7,6 @@ import Bcfg2.Client.Tools
# Compatibility import
from Bcfg2.Bcfg2Py3k import ConfigParser
-# Fix for python2.3
-try:
- set
-except NameError:
- from sets import Set as set
-
class RPMng(Bcfg2.Client.Tools.PkgTool):
"""Support for RPM packages."""
name = 'RPMng'
diff --git a/src/lib/Client/Tools/RcUpdate.py b/src/lib/Client/Tools/RcUpdate.py
index d832d98a8..1b9a29478 100644
--- a/src/lib/Client/Tools/RcUpdate.py
+++ b/src/lib/Client/Tools/RcUpdate.py
@@ -1,5 +1,4 @@
"""This is rc-update support."""
-__revision__ = '$Revision$'
import os
import Bcfg2.Client.Tools
diff --git a/src/lib/Client/Tools/SMF.py b/src/lib/Client/Tools/SMF.py
index 944408326..f824410ad 100644
--- a/src/lib/Client/Tools/SMF.py
+++ b/src/lib/Client/Tools/SMF.py
@@ -1,5 +1,4 @@
"""SMF support for Bcfg2"""
-__revision__ = '$Revision$'
import glob
import os
diff --git a/src/lib/Client/Tools/SYSV.py b/src/lib/Client/Tools/SYSV.py
index b5e1f1c59..eb4a13dfb 100644
--- a/src/lib/Client/Tools/SYSV.py
+++ b/src/lib/Client/Tools/SYSV.py
@@ -1,6 +1,4 @@
-# This is the bcfg2 support for solaris sysv packages
"""This provides bcfg2 support for Solaris SYSV packages."""
-__revision__ = '$Revision$'
import tempfile
diff --git a/src/lib/Client/Tools/Upstart.py b/src/lib/Client/Tools/Upstart.py
index 41a585c23..7afc8edd7 100644
--- a/src/lib/Client/Tools/Upstart.py
+++ b/src/lib/Client/Tools/Upstart.py
@@ -1,5 +1,4 @@
"""Upstart support for Bcfg2."""
-__revision__ = '$Revision$'
import glob
import re
diff --git a/src/lib/Client/Tools/YUM24.py b/src/lib/Client/Tools/YUM24.py
index 66768fb34..4e488b9da 100644
--- a/src/lib/Client/Tools/YUM24.py
+++ b/src/lib/Client/Tools/YUM24.py
@@ -1,5 +1,4 @@
"""This provides bcfg2 support for yum."""
-__revision__ = '$Revision: $'
import copy
import os.path
@@ -10,12 +9,6 @@ import Bcfg2.Client.Tools.RPMng
# Compatibility import
from Bcfg2.Bcfg2Py3k import ConfigParser
-# Fix for python2.3
-try:
- set
-except NameError:
- from sets import Set as set
-
YAD = True
CP = ConfigParser.ConfigParser()
try:
diff --git a/src/lib/Client/Tools/YUMng.py b/src/lib/Client/Tools/YUMng.py
index 9b999df92..154676764 100644
--- a/src/lib/Client/Tools/YUMng.py
+++ b/src/lib/Client/Tools/YUMng.py
@@ -1,5 +1,4 @@
"""This provides bcfg2 support for yum."""
-__revision__ = '$Revision$'
import copy
import os.path
@@ -16,12 +15,6 @@ import Bcfg2.Client.Tools
# Compatibility import
from Bcfg2.Bcfg2Py3k import ConfigParser
-# Fix for python2.3
-try:
- set
-except NameError:
- from sets import Set as set
-
def build_yname(pkgname, inst):
"""Build yum appropriate package name."""
diff --git a/src/lib/Client/Tools/__init__.py b/src/lib/Client/Tools/__init__.py
index e879d7dd6..c6cb6e239 100644
--- a/src/lib/Client/Tools/__init__.py
+++ b/src/lib/Client/Tools/__init__.py
@@ -1,8 +1,4 @@
"""This contains all Bcfg2 Tool modules"""
-# suppress popen2 warnings for python 2.3
-import warnings
-warnings.filterwarnings("ignore", "The popen2 module is deprecated.*",
- DeprecationWarning)
import os
import stat
import sys
@@ -10,7 +6,6 @@ from subprocess import Popen, PIPE
import time
import Bcfg2.Client.XML
-__revision__ = '$Revision$'
__all__ = [tool.split('.')[0] \
for tool in os.listdir(os.path.dirname(__file__)) \
diff --git a/src/lib/Client/Tools/launchd.py b/src/lib/Client/Tools/launchd.py
index 03dd97e71..700234cc8 100644
--- a/src/lib/Client/Tools/launchd.py
+++ b/src/lib/Client/Tools/launchd.py
@@ -1,5 +1,4 @@
"""launchd support for Bcfg2."""
-__revision__ = '$Revision$'
import os
import popen2
diff --git a/src/lib/Client/Tools/rpmtools.py b/src/lib/Client/Tools/rpmtools.py
index 3cd2b7014..7441b2c06 100755
--- a/src/lib/Client/Tools/rpmtools.py
+++ b/src/lib/Client/Tools/rpmtools.py
@@ -18,7 +18,6 @@
Run 'rpmtools' -h for the options.
"""
-__revision__ = '$Revision$'
import grp
import optparse
diff --git a/src/lib/Client/XML.py b/src/lib/Client/XML.py
index 42b1017ac..858479611 100644
--- a/src/lib/Client/XML.py
+++ b/src/lib/Client/XML.py
@@ -1,5 +1,4 @@
'''XML lib compatibility layer for the Bcfg2 client'''
-__revision__ = '$Revision$'
# library will use lxml, then builtin xml.etree, then ElementTree
diff --git a/src/lib/Client/__init__.py b/src/lib/Client/__init__.py
index ea60a4259..6ed37b257 100644
--- a/src/lib/Client/__init__.py
+++ b/src/lib/Client/__init__.py
@@ -1,4 +1,3 @@
"""This contains all Bcfg2 Client modules"""
-__revision__ = '$Revision$'
__all__ = ["Frame", "Tools", "XML"]
diff --git a/src/lib/Component.py b/src/lib/Component.py
index caea3eda9..e21b5d359 100644
--- a/src/lib/Component.py
+++ b/src/lib/Component.py
@@ -1,7 +1,5 @@
"""Cobalt component base."""
-__revision__ = '$Revision$'
-
__all__ = ["Component", "exposed", "automatic", "run_component"]
import inspect
diff --git a/src/lib/Logger.py b/src/lib/Logger.py
index e8f9ecd47..06aae615e 100644
--- a/src/lib/Logger.py
+++ b/src/lib/Logger.py
@@ -1,5 +1,4 @@
"""Bcfg2 logging support"""
-__revision__ = '$Revision$'
import copy
import fcntl
diff --git a/src/lib/Options.py b/src/lib/Options.py
index 2f535397f..527b42d00 100644
--- a/src/lib/Options.py
+++ b/src/lib/Options.py
@@ -1,5 +1,4 @@
"""Option parsing library for utilities."""
-__revision__ = '$Revision$'
import getopt
import os
diff --git a/src/lib/Proxy.py b/src/lib/Proxy.py
index 977a49228..b4a7c6b41 100644
--- a/src/lib/Proxy.py
+++ b/src/lib/Proxy.py
@@ -8,9 +8,6 @@ load_config -- read configuration files
"""
-__revision__ = '$Revision: $'
-
-
import logging
import re
import socket
diff --git a/src/lib/SSLServer.py b/src/lib/SSLServer.py
index 32ab9933b..418e259cc 100644
--- a/src/lib/SSLServer.py
+++ b/src/lib/SSLServer.py
@@ -1,7 +1,5 @@
"""Bcfg2 SSL server."""
-__revision__ = '$Revision$'
-
__all__ = [
"SSLServer", "XMLRPCRequestHandler", "XMLRPCServer",
]
diff --git a/src/lib/Server/Admin/__init__.py b/src/lib/Server/Admin/__init__.py
index a9f0b8cd6..5031392d0 100644
--- a/src/lib/Server/Admin/__init__.py
+++ b/src/lib/Server/Admin/__init__.py
@@ -1,5 +1,3 @@
-__revision__ = '$Revision$'
-
__all__ = [
'Backup',
'Bundle',
diff --git a/src/lib/Server/Core.py b/src/lib/Server/Core.py
index 38f854f66..cb95e3e9c 100644
--- a/src/lib/Server/Core.py
+++ b/src/lib/Server/Core.py
@@ -1,5 +1,4 @@
"""Bcfg2.Server.Core provides the runtime support for Bcfg2 modules."""
-__revision__ = '$Revision$'
import atexit
import logging
diff --git a/src/lib/Server/Hostbase/backends.py b/src/lib/Server/Hostbase/backends.py
index bf774f695..ecaf3c109 100644
--- a/src/lib/Server/Hostbase/backends.py
+++ b/src/lib/Server/Hostbase/backends.py
@@ -2,8 +2,6 @@ from django.contrib.auth.models import User
#from ldapauth import *
from nisauth import *
-__revision__ = '$Revision$'
-
## class LDAPBackend(object):
## def authenticate(self,username=None,password=None):
diff --git a/src/lib/Server/Hostbase/hostbase/views.py b/src/lib/Server/Hostbase/hostbase/views.py
index ff1d4710d..57ef5eff8 100644
--- a/src/lib/Server/Hostbase/hostbase/views.py
+++ b/src/lib/Server/Hostbase/hostbase/views.py
@@ -2,8 +2,6 @@
Contains all the views associated with the hostbase app
Also has does form validation
"""
-__revision__ = "$Revision: $"
-
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
diff --git a/src/lib/Server/Hostbase/nisauth.py b/src/lib/Server/Hostbase/nisauth.py
index 9c7da8c0a..ae4c6c021 100644
--- a/src/lib/Server/Hostbase/nisauth.py
+++ b/src/lib/Server/Hostbase/nisauth.py
@@ -1,10 +1,8 @@
+"""Checks with NIS to see if the current user is in the support group"""
import os
import crypt, nis
from Bcfg2.Server.Hostbase.settings import AUTHORIZED_GROUP
-"""Checks with NIS to see if the current user is in the support group"""
-
-__revision__ = "$Revision: $"
class NISAUTHError(Exception):
"""NISAUTHError is raised when somehting goes boom."""
diff --git a/src/lib/Server/Lint/__init__.py b/src/lib/Server/Lint/__init__.py
index a9d57c46c..326f5b1dc 100644
--- a/src/lib/Server/Lint/__init__.py
+++ b/src/lib/Server/Lint/__init__.py
@@ -1,5 +1,3 @@
-__revision__ = '$Revision$'
-
__all__ = ['Bundles',
'Comments',
'Duplicates',
diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py
index 692aa8a81..c48859a44 100644
--- a/src/lib/Server/Plugin.py
+++ b/src/lib/Server/Plugin.py
@@ -1,5 +1,4 @@
"""This module provides the baseclass for Bcfg2 Server Plugins."""
-__revision__ = '$Revision$'
import copy
import logging
@@ -85,7 +84,6 @@ class Plugin(Debuggable):
"""This is the base class for all Bcfg2 Server plugins.
Several attributes must be defined in the subclass:
name : the name of the plugin
- __version__ : a version string
__author__ : the author/contact for the plugin
Plugins can provide three basic types of functionality:
@@ -94,7 +92,6 @@ class Plugin(Debuggable):
- Data collection (overloading GetProbes/ReceiveData)
"""
name = 'Plugin'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = False
deprecated = False
@@ -114,7 +111,7 @@ class Plugin(Debuggable):
object.__init__(self)
self.Entries = {}
self.core = core
- self.data = "%s/%s" % (datastore, self.name)
+ self.data = os.path.join(datastore, self.name)
self.running = True
Debuggable.__init__(self, name=self.name)
@@ -938,7 +935,7 @@ class SpecificData(object):
logger.error("Failed to read file %s" % self.name)
-class EntrySet:
+class EntrySet(object):
"""Entry sets deal with the host- and group-specific entries."""
ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\\.genshi_include)$")
@@ -1014,7 +1011,8 @@ class EntrySet:
spec = self.specificity_from_filename(event.filename)
except SpecificityError:
if not self.ignore.match(event.filename):
- logger.error("Could not process filename %s; ignoring" % fpath)
+ logger.error("Could not process filename %s; ignoring" %
+ fpath)
return
self.entries[event.filename] = self.entry_type(fpath,
spec, self.encoding)
@@ -1094,7 +1092,6 @@ class EntrySet:
class GroupSpool(Plugin, Generator):
"""Unified interface for handling group-specific data (e.g. .G## files)."""
name = 'GroupSpool'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
filename_pattern = ""
es_child_cls = object
diff --git a/src/lib/Server/Plugins/Account.py b/src/lib/Server/Plugins/Account.py
index f67819b9d..f2703dccb 100644
--- a/src/lib/Server/Plugins/Account.py
+++ b/src/lib/Server/Plugins/Account.py
@@ -1,5 +1,4 @@
"""This handles authentication setup."""
-__revision__ = '$Revision$'
import Bcfg2.Server.Plugin
@@ -16,7 +15,6 @@ class Account(Bcfg2.Server.Plugin.Plugin,
"""
name = 'Account'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/BB.py b/src/lib/Server/Plugins/BB.py
index 137142b66..c015ec47c 100644
--- a/src/lib/Server/Plugins/BB.py
+++ b/src/lib/Server/Plugins/BB.py
@@ -62,7 +62,6 @@ class BB(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Connector):
"""The BB plugin maps users to machines and metadata to machines."""
name = 'BB'
- version = '$Revision$'
deprecated = True
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Base.py b/src/lib/Server/Plugins/Base.py
index e8d798ae4..389ca7a95 100644
--- a/src/lib/Server/Plugins/Base.py
+++ b/src/lib/Server/Plugins/Base.py
@@ -1,5 +1,4 @@
"""This module sets up a base list of configuration entries."""
-__revision__ = '$Revision$'
import copy
import lxml.etree
@@ -18,7 +17,6 @@ class Base(Bcfg2.Server.Plugin.Plugin,
needed for most actual systems.
"""
name = 'Base'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
__child__ = Bcfg2.Server.Plugin.StructFile
deprecated = True
diff --git a/src/lib/Server/Plugins/Bundler.py b/src/lib/Server/Plugins/Bundler.py
index a58257712..ccb99481e 100644
--- a/src/lib/Server/Plugins/Bundler.py
+++ b/src/lib/Server/Plugins/Bundler.py
@@ -1,5 +1,4 @@
"""This provides bundle clauses with translation functionality."""
-__revision__ = '$Revision$'
import copy
import lxml.etree
@@ -35,7 +34,6 @@ class Bundler(Bcfg2.Server.Plugin.Plugin,
bundle/translation scheme from Bcfg1.
"""
name = 'Bundler'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
patterns = re.compile('^(?P<name>.*)\.(xml|genshi)$')
diff --git a/src/lib/Server/Plugins/Bzr.py b/src/lib/Server/Plugins/Bzr.py
index a9a5eb814..a71021cb5 100644
--- a/src/lib/Server/Plugins/Bzr.py
+++ b/src/lib/Server/Plugins/Bzr.py
@@ -10,7 +10,6 @@ class Bzr(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Bzr is a version plugin for dealing with Bcfg2 repos."""
name = 'Bzr'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Cfg.py b/src/lib/Server/Plugins/Cfg.py
index ed08d454d..c3e807d7a 100644
--- a/src/lib/Server/Plugins/Cfg.py
+++ b/src/lib/Server/Plugins/Cfg.py
@@ -1,5 +1,4 @@
"""This module implements a config file repository."""
-__revision__ = '$Revision$'
import binascii
import logging
@@ -271,7 +270,6 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool,
Bcfg2.Server.Plugin.PullTarget):
"""This generator in the configuration file repository for Bcfg2."""
name = 'Cfg'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
es_cls = CfgEntrySet
es_child_cls = Bcfg2.Server.Plugin.SpecificData
diff --git a/src/lib/Server/Plugins/Cvs.py b/src/lib/Server/Plugins/Cvs.py
index ea898c023..6ce72acd2 100644
--- a/src/lib/Server/Plugins/Cvs.py
+++ b/src/lib/Server/Plugins/Cvs.py
@@ -10,7 +10,6 @@ class Cvs(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""CVS is a version plugin for dealing with Bcfg2 repository."""
name = 'Cvs'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/DBStats.py b/src/lib/Server/Plugins/DBStats.py
index 8761d282d..95395f74e 100644
--- a/src/lib/Server/Plugins/DBStats.py
+++ b/src/lib/Server/Plugins/DBStats.py
@@ -22,7 +22,6 @@ class DBStats(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.ThreadedStatistics,
Bcfg2.Server.Plugin.PullSource):
name = 'DBStats'
- __version__ = '$Id$'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
diff --git a/src/lib/Server/Plugins/Darcs.py b/src/lib/Server/Plugins/Darcs.py
index eb34a52c4..9fb9ff4f1 100644
--- a/src/lib/Server/Plugins/Darcs.py
+++ b/src/lib/Server/Plugins/Darcs.py
@@ -10,7 +10,6 @@ class Darcs(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Darcs is a version plugin for dealing with Bcfg2 repos."""
name = 'Darcs'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/Decisions.py b/src/lib/Server/Plugins/Decisions.py
index 556f75502..b432474f2 100644
--- a/src/lib/Server/Plugins/Decisions.py
+++ b/src/lib/Server/Plugins/Decisions.py
@@ -50,7 +50,6 @@ class Decisions(DecisionSet,
Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Decision):
name = 'Decisions'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Defaults.py b/src/lib/Server/Plugins/Defaults.py
index b208c1126..718192e2a 100644
--- a/src/lib/Server/Plugins/Defaults.py
+++ b/src/lib/Server/Plugins/Defaults.py
@@ -1,5 +1,4 @@
"""This generator provides rule-based entry mappings."""
-__revision__ = '$Revision$'
import re
import Bcfg2.Server.Plugin
@@ -9,7 +8,6 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
Bcfg2.Server.Plugin.StructureValidator):
"""Set default attributes on bound entries"""
name = 'Defaults'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
# Rules is a Generator that happens to implement all of the
diff --git a/src/lib/Server/Plugins/Deps.py b/src/lib/Server/Plugins/Deps.py
index 482d457af..9b848baae 100644
--- a/src/lib/Server/Plugins/Deps.py
+++ b/src/lib/Server/Plugins/Deps.py
@@ -1,5 +1,4 @@
"""This plugin provides automatic dependency handling."""
-__revision__ = '$Revision$'
import lxml.etree
@@ -45,7 +44,6 @@ class DepXMLSrc(Bcfg2.Server.Plugin.XMLSrc):
class Deps(Bcfg2.Server.Plugin.PrioDir,
Bcfg2.Server.Plugin.StructureValidator):
name = 'Deps'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
__child__ = DepXMLSrc
diff --git a/src/lib/Server/Plugins/Editor.py b/src/lib/Server/Plugins/Editor.py
index 76a03a325..c0d2cfbad 100644
--- a/src/lib/Server/Plugins/Editor.py
+++ b/src/lib/Server/Plugins/Editor.py
@@ -59,7 +59,6 @@ class EditEntrySet(Bcfg2.Server.Plugin.EntrySet):
class Editor(Bcfg2.Server.Plugin.GroupSpool,
Bcfg2.Server.Plugin.Probing):
name = 'Editor'
- __version__ = '$Id$'
__author__ = 'bcfg2-dev@mcs.anl.gov'
filename_pattern = 'edits'
es_child_cls = EditDirectives
diff --git a/src/lib/Server/Plugins/FileProbes.py b/src/lib/Server/Plugins/FileProbes.py
index c58040b65..a76d7cac4 100644
--- a/src/lib/Server/Plugins/FileProbes.py
+++ b/src/lib/Server/Plugins/FileProbes.py
@@ -3,7 +3,6 @@ added to the specification. On subsequent runs, the file will be
replaced on the client if it is missing; if it has changed on the
client, it can either be updated in the specification or replaced on
the client """
-__revision__ = '$Revision: 1465 $'
import os
import sys
@@ -55,7 +54,6 @@ class FileProbes(Bcfg2.Server.Plugin.Plugin,
name = 'FileProbes'
experimental = True
- __version__ = '$Id$'
__author__ = 'chris.a.st.pierre@gmail.com'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Fossil.py b/src/lib/Server/Plugins/Fossil.py
index 57d427673..1b1627688 100644
--- a/src/lib/Server/Plugins/Fossil.py
+++ b/src/lib/Server/Plugins/Fossil.py
@@ -10,7 +10,6 @@ class Fossil(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Fossil is a version plugin for dealing with Bcfg2 repos."""
name = 'Fossil'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Git.py b/src/lib/Server/Plugins/Git.py
index aaeac12ae..8f8ea87f1 100644
--- a/src/lib/Server/Plugins/Git.py
+++ b/src/lib/Server/Plugins/Git.py
@@ -13,7 +13,6 @@ class Git(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Git is a version plugin for dealing with Bcfg2 repos."""
name = 'Git'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Guppy.py b/src/lib/Server/Plugins/Guppy.py
index b217378d6..046aedc0b 100644
--- a/src/lib/Server/Plugins/Guppy.py
+++ b/src/lib/Server/Plugins/Guppy.py
@@ -32,7 +32,6 @@ import Bcfg2.Server.Plugin
class Guppy(Bcfg2.Server.Plugin.Plugin):
"""Guppy is a debugging plugin to help trace memory leaks"""
name = 'Guppy'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/Hg.py b/src/lib/Server/Plugins/Hg.py
index 70e33ef1f..0c3537613 100644
--- a/src/lib/Server/Plugins/Hg.py
+++ b/src/lib/Server/Plugins/Hg.py
@@ -10,7 +10,6 @@ class Hg(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Mercurial is a version plugin for dealing with Bcfg2 repository."""
name = 'Mercurial'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
experimental = True
diff --git a/src/lib/Server/Plugins/Hostbase.py b/src/lib/Server/Plugins/Hostbase.py
index 4180fd716..e9c1c1cff 100644
--- a/src/lib/Server/Plugins/Hostbase.py
+++ b/src/lib/Server/Plugins/Hostbase.py
@@ -2,7 +2,6 @@
This file provides the Hostbase plugin.
It manages dns/dhcp/nis host information
"""
-__revision__ = '$Revision$'
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.Server.Hostbase.settings'
@@ -23,7 +22,6 @@ class Hostbase(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Generator):
"""The Hostbase plugin handles host/network info."""
name = 'Hostbase'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
filepath = '/my/adm/hostbase/files/bind'
diff --git a/src/lib/Server/Plugins/Ldap.py b/src/lib/Server/Plugins/Ldap.py
index f1e2198d2..04417339b 100644
--- a/src/lib/Server/Plugins/Ldap.py
+++ b/src/lib/Server/Plugins/Ldap.py
@@ -63,7 +63,6 @@ class Ldap(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Connector):
The Ldap plugin allows adding data from an LDAP server to your metadata.
"""
name = "Ldap"
- version = "$Revision: $"
experimental = True
debug_flag = False
diff --git a/src/lib/Server/Plugins/Metadata.py b/src/lib/Server/Plugins/Metadata.py
index f837b1305..fa2cf0a78 100644
--- a/src/lib/Server/Plugins/Metadata.py
+++ b/src/lib/Server/Plugins/Metadata.py
@@ -2,8 +2,6 @@
This file stores persistent metadata for the Bcfg2 Configuration Repository.
"""
-__revision__ = '$Revision$'
-
import copy
import fcntl
import lxml.etree
@@ -67,16 +65,16 @@ class XMLMetadataConfig(object):
def add_monitor(self, fname):
"""Add a fam monitor for an included file"""
if self.should_monitor:
- self.metadata.core.fam.AddMonitor("%s/%s" % (self.basedir, fname),
+ self.metadata.core.fam.AddMonitor(os.path.join(self.basedir, fname),
self.metadata)
self.extras.append(fname)
def load_xml(self):
"""Load changes from XML"""
try:
- xdata = lxml.etree.parse("%s/%s" % (self.basedir, self.basefile))
+ xdata = lxml.etree.parse(os.path.join(self.basedir, self.basefile))
except lxml.etree.XMLSyntaxError:
- self.logger.error('Failed to parse %s' % (self.basefile))
+ self.logger.error('Failed to parse %s' % self.basefile)
return
self.basedata = copy.copy(xdata)
included = [ent.get('href') for ent in \
@@ -88,19 +86,20 @@ class XMLMetadataConfig(object):
try:
xdata.xinclude()
except lxml.etree.XIncludeError:
- self.logger.error("Failed to process XInclude for file %s" % self.basefile)
+ self.logger.error("Failed to process XInclude for file %s" %
+ self.basefile)
self.data = xdata
def write(self):
"""Write changes to xml back to disk."""
- self.write_xml("%s/%s" % (self.basedir, self.basefile),
+ self.write_xml(os.path.join(self.basedir, self.basefile),
self.basedata)
def write_xml(self, fname, xmltree):
"""Write changes to xml back to disk."""
tmpfile = "%s.new" % fname
try:
- datafile = open("%s" % tmpfile, 'w')
+ datafile = open(tmpfile, 'w')
except IOError:
e = sys.exc_info()[1]
self.logger.error("Failed to write %s: %s" % (tmpfile, e))
@@ -116,40 +115,42 @@ class XMLMetadataConfig(object):
datafile.write(newcontents)
except:
fcntl.lockf(fd, fcntl.LOCK_UN)
- self.logger.error("Metadata: Failed to write new xml data to %s" % tmpfile, exc_info=1)
- os.unlink("%s" % tmpfile)
+ self.logger.error("Metadata: Failed to write new xml data to %s" %
+ tmpfile, exc_info=1)
+ os.unlink(tmpfile)
raise MetadataRuntimeError
datafile.close()
# check if clients.xml is a symlink
- xmlfile = "%s" % fname
- if os.path.islink(xmlfile):
- xmlfile = os.readlink(xmlfile)
+ if os.path.islink(fname):
+ fname = os.readlink(fname)
try:
- os.rename("%s" % tmpfile, xmlfile)
+ os.rename(tmpfile, fname)
except:
self.logger.error("Metadata: Failed to rename %s" % tmpfile)
raise MetadataRuntimeError
def find_xml_for_xpath(self, xpath):
- """Find and load xml data containing the xpath query"""
+ """Find and load xml file containing the xpath query"""
if self.pseudo_monitor:
# Reload xml if we don't have a real monitor
self.load_xml()
cli = self.basedata.xpath(xpath)
if len(cli) > 0:
- return {'filename': "%s/%s" % (self.basedir, self.basefile),
+ return {'filename': os.path.join(self.basedir, self.basefile),
'xmltree': self.basedata,
'xquery': cli}
else:
"""Try to find the data in included files"""
for included in self.extras:
try:
- xdata = lxml.etree.parse("%s/%s" % (self.basedir, included))
+ xdata = lxml.etree.parse(os.path.join(self.basedir,
+ included))
cli = xdata.xpath(xpath)
if len(cli) > 0:
- return {'filename': "%s/%s" % (self.basedir, included),
+ return {'filename': os.path.join(self.basedir,
+ included),
'xmltree': xdata,
'xquery': cli}
except lxml.etree.XMLSyntaxError:
@@ -221,7 +222,6 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Metadata,
Bcfg2.Server.Plugin.Statistics):
"""This class contains data for bcfg2 server metadata."""
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
name = "Metadata"
sort_order = 500
@@ -232,8 +232,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Statistics.__init__(self)
if watch_clients:
try:
- core.fam.AddMonitor("%s/%s" % (self.data, "groups.xml"), self)
- core.fam.AddMonitor("%s/%s" % (self.data, "clients.xml"), self)
+ core.fam.AddMonitor(os.path.join(self.data, "groups.xml"), self)
+ core.fam.AddMonitor(os.path.join(self.data, "clients.xml"), self)
except:
print("Unable to add file monitor for groups.xml or clients.xml")
raise Bcfg2.Server.Plugin.PluginInitError
@@ -274,269 +274,241 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
@classmethod
def init_repo(cls, repo, groups, os_selection, clients):
- path = '%s/%s' % (repo, cls.name)
+ path = os.path.join(repo, cls.name)
os.makedirs(path)
- open("%s/Metadata/groups.xml" %
- repo, "w").write(groups % os_selection)
- open("%s/Metadata/clients.xml" %
- repo, "w").write(clients % socket.getfqdn())
+ open(os.path.join(repo, "Metadata", "groups.xml"),
+ "w").write(groups % os_selection)
+ open(os.path.join(repo, "Metadata", "clients.xml"),
+ "w").write(clients % socket.getfqdn())
def get_groups(self):
'''return groups xml tree'''
- groups_tree = lxml.etree.parse(self.data + "/groups.xml")
+ groups_tree = lxml.etree.parse(os.path.join(self.data, "groups.xml"))
root = groups_tree.getroot()
return root
+ def _search_xdata(self, tag, name, tree, alias=False):
+ for node in tree.findall("//%s" % tag):
+ if node.get("name") == name:
+ return node
+ elif alias:
+ for child in node:
+ if (child.tag == "Alias" and
+ child.attrib["name"] == name):
+ return node
+ return None
+
def search_group(self, group_name, tree):
"""Find a group."""
- for node in tree.findall("//Group"):
- if node.get("name") == group_name:
- return node
- for child in node:
- if child.tag == "Alias" and child.attrib["name"] == group_name:
- return node
- return None
+ return self._search_xdata("Group", group_name, tree)
- def add_group(self, group_name, attribs):
- """Add group to groups.xml."""
+ def search_bundle(self, bundle_name, tree):
+ """Find a bundle."""
+ return self._search_xdata("Bundle", bundle_name, tree)
- node = self.search_group(group_name, self.groups_xml.xdata)
+ def search_client(self, client_name, tree):
+ return self._search_xdata("Client", client_name, tree, alias=True)
+
+ def _add_xdata(self, config, tag, name, attribs=None, alias=False):
+ node = self._search_xdata(tag, name, config.xdata, alias=alias)
if node != None:
- self.logger.error("Group \"%s\" already exists" % (group_name))
+ self.logger.error("%s \"%s\" already exists" % (tag, name))
raise MetadataConsistencyError
+ element = lxml.etree.SubElement(config.base_xdata.getroot(),
+ tag, name=name)
+ if attribs:
+ for key, val in list(attribs.items()):
+ element.set(key, val)
+ config.write()
+
+ def add_group(self, group_name, attribs):
+ """Add group to groups.xml."""
+ return self._add_xdata(self.groups_xml, "Group", group_name,
+ attribs=attribs)
- element = lxml.etree.SubElement(self.groups_xml.base_xdata.getroot(),
- "Group", name=group_name)
- for key, val in list(attribs.items()):
- element.set(key, val)
- self.groups_xml.write()
+ def add_bundle(self, bundle_name):
+ """Add bundle to groups.xml."""
+ return self._add_xdata(self.groups_xml, "Bundle", bundle_name)
- def update_group(self, group_name, attribs):
- """Update a groups attributes."""
- node = self.search_group(group_name, self.groups_xml.xdata)
+ def add_client(self, client_name, attribs):
+ """Add client to clients.xml."""
+ return self._add_xdata(self.clients_xml, "Client", client_name,
+ attribs=attribs, alias=True)
+
+ def _update_xdata(self, config, tag, name, attribs, alias=False):
+ node = self._search_xdata(tag, name, config.xdata, alias=alias)
if node == None:
- self.logger.error("Group \"%s\" does not exist" % (group_name))
+ self.logger.error("%s \"%s\" does not exist" % (tag, name))
raise MetadataConsistencyError
- xdict = self.groups_xml.find_xml_for_xpath('.//Group[@name="%s"]' % (node.get('name')))
+ xdict = config.find_xml_for_xpath('.//%s[@name="%s"]' %
+ (tag, node.get('name')))
if not xdict:
- self.logger.error("Unexpected error finding group")
+ self.logger.error("Unexpected error finding %s \"%s\"" %
+ (tag, name))
raise MetadataConsistencyError
-
for key, val in list(attribs.items()):
xdict['xquery'][0].set(key, val)
- self.groups_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ config.write_xml(xdict['filename'], xdict['xmltree'])
- def remove_group(self, group_name):
- """Remove a group."""
- node = self.search_group(group_name, self.groups_xml.xdata)
+ def update_group(self, group_name, attribs):
+ """Update a groups attributes."""
+ return self._update_xdata(self.groups_xml, "Group", group_name, attribs)
+
+ def update_client(self, client_name, attribs):
+ """Update a clients attributes."""
+ return self._update_xdata(self.clients_xml, "Client", client_name,
+ attribs, alias=True)
+
+ def _remove_xdata(self, config, tag, name, alias=False):
+ node = self._search_xdata(tag, name, config.xdata)
if node == None:
- self.logger.error("Group \"%s\" does not exist" % (group_name))
+ self.logger.error("%s \"%s\" does not exist" % (tag, name))
raise MetadataConsistencyError
- xdict = self.groups_xml.find_xml_for_xpath('.//Group[@name="%s"]' % (node.get('name')))
+ xdict = config.find_xml_for_xpath('.//%s[@name="%s"]' %
+ (tag, node.get('name')))
if not xdict:
- self.logger.error("Unexpected error finding group")
+ self.logger.error("Unexpected error finding %s \"%s\"" %
+ (tag, name))
raise MetadataConsistencyError
xdict['xquery'][0].getparent().remove(xdict['xquery'][0])
self.groups_xml.write_xml(xdict['filename'], xdict['xmltree'])
- def add_bundle(self, bundle_name):
- """Add bundle to groups.xml."""
- tree = lxml.etree.parse(self.data + "/groups.xml")
- root = tree.getroot()
- element = lxml.etree.Element("Bundle", name=bundle_name)
- node = self.search_group(bundle_name, tree)
- if node != None:
- self.logger.error("Bundle \"%s\" already exists" % (bundle_name))
- raise MetadataConsistencyError
- root.append(element)
- group_tree = open(self.data + "/groups.xml", "w")
- fd = group_tree.fileno()
- while True:
- try:
- fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError:
- continue
- else:
- break
- tree.write(group_tree)
- fcntl.lockf(fd, fcntl.LOCK_UN)
- group_tree.close()
+ def remove_group(self, group_name):
+ """Remove a group."""
+ return self._remove_xdata(self.groups_xml, "Group", group_name)
def remove_bundle(self, bundle_name):
"""Remove a bundle."""
- tree = lxml.etree.parse(self.data + "/groups.xml")
- root = tree.getroot()
- node = self.search_group(bundle_name, tree)
- if node == None:
- self.logger.error("Bundle \"%s\" not found" % (bundle_name))
- raise MetadataConsistencyError
- root.remove(node)
- group_tree = open(self.data + "/groups.xml", "w")
- fd = group_tree.fileno()
- while True:
- try:
- fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
- except IOError:
- continue
- else:
- break
- tree.write(group_tree)
- fcntl.lockf(fd, fcntl.LOCK_UN)
- group_tree.close()
+ return self._remove_xdata(self.groups_xml, "Bundle", bundle_name)
- def search_client(self, client_name, tree):
- """Find a client."""
- for node in tree.findall("//Client"):
- if node.get("name") == client_name:
- return node
- for child in node:
- if child.tag == "Alias" and child.attrib["name"] == client_name:
- return node
- return None
-
- def add_client(self, client_name, attribs):
- """Add client to clients.xml."""
- node = self.search_client(client_name, self.clients_xml.xdata)
- if node != None:
- self.logger.error("Client \"%s\" already exists" % (client_name))
- raise MetadataConsistencyError
-
- element = lxml.etree.SubElement(self.clients_xml.base_xdata.getroot(),
- "Client", name=client_name)
- for key, val in list(attribs.items()):
- element.set(key, val)
- self.clients_xml.write()
-
- def update_client(self, client_name, attribs):
- """Update a clients attributes."""
- node = self.search_client(client_name, self.clients_xml.xdata)
- if node == None:
- self.logger.error("Client \"%s\" does not exist" % (client_name))
- raise MetadataConsistencyError
-
- xdict = self.clients_xml.find_xml_for_xpath('.//Client[@name="%s"]' % (node.get('name')))
- if not xdict:
- self.logger.error("Unexpected error finding client")
- raise MetadataConsistencyError
-
- node = xdict['xquery'][0]
- [node.set(key, value) for key, value in list(attribs.items())]
- self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ def _handle_clients_xml_event(self, event):
+ xdata = self.clients_xml.xdata
+ self.clients = {}
+ self.aliases = {}
+ self.raliases = {}
+ self.bad_clients = {}
+ self.secure = []
+ self.floating = []
+ self.addresses = {}
+ self.raddresses = {}
+ for client in xdata.findall('.//Client'):
+ clname = client.get('name').lower()
+ if 'address' in client.attrib:
+ caddr = client.get('address')
+ if caddr in self.addresses:
+ self.addresses[caddr].append(clname)
+ else:
+ self.addresses[caddr] = [clname]
+ if clname not in self.raddresses:
+ self.raddresses[clname] = set()
+ self.raddresses[clname].add(caddr)
+ if 'auth' in client.attrib:
+ self.auth[client.get('name')] = client.get('auth',
+ 'cert+password')
+ if 'uuid' in client.attrib:
+ self.uuid[client.get('uuid')] = clname
+ if client.get('secure', 'false') == 'true':
+ self.secure.append(clname)
+ if client.get('location', 'fixed') == 'floating':
+ self.floating.append(clname)
+ if 'password' in client.attrib:
+ self.passwords[clname] = client.get('password')
+
+ self.raliases[clname] = set()
+ for alias in client.findall('Alias'):
+ self.aliases.update({alias.get('name'): clname})
+ self.raliases[clname].add(alias.get('name'))
+ if 'address' not in alias.attrib:
+ continue
+ if alias.get('address') in self.addresses:
+ self.addresses[alias.get('address')].append(clname)
+ else:
+ self.addresses[alias.get('address')] = [clname]
+ if clname not in self.raddresses:
+ self.raddresses[clname] = set()
+ self.raddresses[clname].add(alias.get('address'))
+ self.clients.update({clname: client.get('profile')})
+ self.states['clients.xml'] = True
+
+ def _handle_groups_xml_event(self, event):
+ xdata = self.groups_xml.xdata
+ self.public = []
+ self.private = []
+ self.profiles = []
+ self.groups = {}
+ grouptmp = {}
+ self.categories = {}
+ groupseen = list()
+ for group in xdata.xpath('//Groups/Group'):
+ if group.get('name') not in groupseen:
+ groupseen.append(group.get('name'))
+ else:
+ self.logger.error("Metadata: Group %s defined multiply" %
+ group.get('name'))
+ grouptmp[group.get('name')] = \
+ ([item.get('name') for item in group.findall('./Bundle')],
+ [item.get('name') for item in group.findall('./Group')])
+ grouptmp[group.get('name')][1].append(group.get('name'))
+ if group.get('default', 'false') == 'true':
+ self.default = group.get('name')
+ if group.get('profile', 'false') == 'true':
+ self.profiles.append(group.get('name'))
+ if group.get('public', 'false') == 'true':
+ self.public.append(group.get('name'))
+ elif group.get('public', 'true') == 'false':
+ self.private.append(group.get('name'))
+ if 'category' in group.attrib:
+ self.categories[group.get('name')] = group.get('category')
+
+ for group in grouptmp:
+ # self.groups[group] => (bundles, groups, categories)
+ self.groups[group] = (set(), set(), {})
+ tocheck = [group]
+ group_cat = self.groups[group][2]
+ while tocheck:
+ now = tocheck.pop()
+ self.groups[group][1].add(now)
+ if now in grouptmp:
+ (bundles, groups) = grouptmp[now]
+ for ggg in groups:
+ if ggg in self.groups[group][1]:
+ continue
+ if (ggg not in self.categories or \
+ self.categories[ggg] not in self.groups[group][2]):
+ self.groups[group][1].add(ggg)
+ tocheck.append(ggg)
+ if ggg in self.categories:
+ group_cat[self.categories[ggg]] = ggg
+ elif ggg in self.categories:
+ self.logger.info("Group %s: %s cat-suppressed %s" % \
+ (group,
+ group_cat[self.categories[ggg]],
+ ggg))
+ [self.groups[group][0].add(bund) for bund in bundles]
+ self.states['groups.xml'] = True
def HandleEvent(self, event):
"""Handle update events for data files."""
if self.clients_xml.HandleEvent(event):
- xdata = self.clients_xml.xdata
- self.clients = {}
- self.aliases = {}
- self.raliases = {}
- self.bad_clients = {}
- self.secure = []
- self.floating = []
- self.addresses = {}
- self.raddresses = {}
- for client in xdata.findall('.//Client'):
- clname = client.get('name').lower()
- if 'address' in client.attrib:
- caddr = client.get('address')
- if caddr in self.addresses:
- self.addresses[caddr].append(clname)
- else:
- self.addresses[caddr] = [clname]
- if clname not in self.raddresses:
- self.raddresses[clname] = set()
- self.raddresses[clname].add(caddr)
- if 'auth' in client.attrib:
- self.auth[client.get('name')] = client.get('auth',
- 'cert+password')
- if 'uuid' in client.attrib:
- self.uuid[client.get('uuid')] = clname
- if client.get('secure', 'false') == 'true':
- self.secure.append(clname)
- if client.get('location', 'fixed') == 'floating':
- self.floating.append(clname)
- if 'password' in client.attrib:
- self.passwords[clname] = client.get('password')
- for alias in [alias for alias in client.findall('Alias')\
- if 'address' in alias.attrib]:
- if alias.get('address') in self.addresses:
- self.addresses[alias.get('address')].append(clname)
- else:
- self.addresses[alias.get('address')] = [clname]
- if clname not in self.raddresses:
- self.raddresses[clname] = set()
- self.raddresses[clname].add(alias.get('address'))
- self.clients.update({clname: client.get('profile')})
- [self.aliases.update({alias.get('name'): clname}) \
- for alias in client.findall('Alias')]
- self.raliases[clname] = set()
- [self.raliases[clname].add(alias.get('name')) for alias \
- in client.findall('Alias')]
- self.states['clients.xml'] = True
+ self._handle_clients_xml_event(event)
elif self.groups_xml.HandleEvent(event):
- xdata = self.groups_xml.xdata
- self.public = []
- self.private = []
- self.profiles = []
- self.groups = {}
- grouptmp = {}
- self.categories = {}
- groupseen = list()
- for group in xdata.xpath('//Groups/Group'):
- if group.get('name') not in groupseen:
- groupseen.append(group.get('name'))
- else:
- self.logger.error("Metadata: Group %s defined multiply" % (group.get('name')))
- grouptmp[group.get('name')] = tuple([[item.get('name') for item in group.findall(spec)]
- for spec in ['./Bundle', './Group']])
- grouptmp[group.get('name')][1].append(group.get('name'))
- if group.get('default', 'false') == 'true':
- self.default = group.get('name')
- if group.get('profile', 'false') == 'true':
- self.profiles.append(group.get('name'))
- if group.get('public', 'false') == 'true':
- self.public.append(group.get('name'))
- elif group.get('public', 'true') == 'false':
- self.private.append(group.get('name'))
- if 'category' in group.attrib:
- self.categories[group.get('name')] = group.get('category')
- for group in grouptmp:
- # self.groups[group] => (bundles, groups, categories)
- self.groups[group] = (set(), set(), {})
- tocheck = [group]
- group_cat = self.groups[group][2]
- while tocheck:
- now = tocheck.pop()
- self.groups[group][1].add(now)
- if now in grouptmp:
- (bundles, groups) = grouptmp[now]
- for ggg in [ggg for ggg in groups if ggg not in self.groups[group][1]]:
- if ggg not in self.categories or \
- self.categories[ggg] not in self.groups[group][2]:
- self.groups[group][1].add(ggg)
- tocheck.append(ggg)
- if ggg in self.categories:
- group_cat[self.categories[ggg]] = ggg
- elif ggg in self.categories:
- self.logger.info("Group %s: %s cat-suppressed %s" % \
- (group,
- group_cat[self.categories[ggg]],
- ggg))
- [self.groups[group][0].add(bund) for bund in bundles]
- self.states['groups.xml'] = True
+ self._handle_groups_xml_event(event)
+
if False not in list(self.states.values()):
# check that all client groups are real and complete
real = list(self.groups.keys())
for client in list(self.clients.keys()):
if self.clients[client] not in self.profiles:
- self.logger.error("Client %s set as nonexistent or incomplete group %s" \
- % (client, self.clients[client]))
- self.logger.error("Removing client mapping for %s" % (client))
+ self.logger.error("Client %s set as nonexistent or "
+ "incomplete group %s" %
+ (client, self.clients[client]))
+ self.logger.error("Removing client mapping for %s" % client)
self.bad_clients[client] = self.clients[client]
del self.clients[client]
for bclient in list(self.bad_clients.keys()):
if self.bad_clients[bclient] in self.profiles:
- self.logger.info("Restored profile mapping for client %s" % bclient)
+ self.logger.info("Restored profile mapping for client %s" %
+ bclient)
self.clients[bclient] = self.bad_clients[bclient]
del self.bad_clients[bclient]
@@ -546,42 +518,37 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
if False in list(self.states.values()):
raise MetadataRuntimeError
if profile not in self.public:
- self.logger.error("Failed to set client %s to private group %s" % (client, profile))
+ self.logger.error("Failed to set client %s to private group %s" %
+ (client, profile))
raise MetadataConsistencyError
if client in self.clients:
- self.logger.info("Changing %s group from %s to %s" % (client, self.clients[client], profile))
- xdict = self.clients_xml.find_xml_for_xpath('.//Client[@name="%s"]' % (client))
- if not xdict:
- self.logger.error("Metadata: Unable to update profile for client %s. Use of Xinclude?" % client)
- raise MetadataConsistencyError
- xdict['xquery'][0].set('profile', profile)
- self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ self.logger.info("Changing %s group from %s to %s" %
+ (client, self.clients[client], profile))
+ self.update_client(client, dict(profile=profile))
else:
- self.logger.info("Creating new client: %s, profile %s" % \
+ self.logger.info("Creating new client: %s, profile %s" %
(client, profile))
if addresspair in self.session_cache:
# we are working with a uuid'd client
- lxml.etree.SubElement(self.clients_xml.base_xdata.getroot(),
- 'Client',
- name=self.session_cache[addresspair][1],
- uuid=client, profile=profile,
- address=addresspair[0])
+ self.add_client(self.session_cache[addresspair][1],
+ dict(uuid=client, profile=profile,
+ address=addresspair[0]))
else:
- lxml.etree.SubElement(self.clients_xml.base_xdata.getroot(),
- 'Client', name=client,
- profile=profile)
+ self.add_client(client, dict(profile=profile))
self.clients[client] = profile
self.clients_xml.write()
def resolve_client(self, addresspair, cleanup_cache=False):
"""Lookup address locally or in DNS to get a hostname."""
if addresspair in self.session_cache:
- # client _was_ cached, so there can be some expired entries
- # we need to clean them up to avoid potentially infinite memory swell
+ # client _was_ cached, so there can be some expired
+ # entries. we need to clean them up to avoid potentially
+ # infinite memory swell
cache_ttl = 90
if cleanup_cache:
- # remove entries for this client's IP address with _any_ port numbers
- # - perhaps a priority queue could be faster?
+ # remove entries for this client's IP address with
+ # _any_ port numbers - perhaps a priority queue could
+ # be faster?
curtime = time.time()
for addrpair in self.session_cache.keys():
if addresspair[0] == addrpair[0]:
@@ -589,13 +556,18 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
if curtime - stamp > cache_ttl:
del self.session_cache[addrpair]
# return the cached data
- (stamp, uuid) = self.session_cache[addresspair]
- if time.time() - stamp < cache_ttl:
- return self.session_cache[addresspair][1]
+ try:
+ (stamp, uuid) = self.session_cache[addresspair]
+ if time.time() - stamp < cache_ttl:
+ return self.session_cache[addresspair][1]
+ except KeyError:
+ # we cleaned all cached data for this client in cleanup_cache
+ pass
address = addresspair[0]
if address in self.addresses:
if len(self.addresses[address]) != 1:
- self.logger.error("Address %s has multiple reverse assignments; a uuid must be used" % (address))
+ self.logger.error("Address %s has multiple reverse assignments; "
+ "a uuid must be used" % (address))
raise MetadataConsistencyError
return self.addresses[address][0]
try:
@@ -620,7 +592,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
(bundles, groups, categories) = self.groups[profile]
else:
if self.default == None:
- self.logger.error("Cannot set group for client %s; no default group set" % (client))
+ self.logger.error("Cannot set group for client %s; "
+ "no default group set" % client)
raise MetadataConsistencyError
self.set_profile(client, self.default, (None, None))
profile = self.default
@@ -635,7 +608,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
password = self.passwords[client]
else:
password = None
- uuids = [item for item, value in list(self.uuid.items()) if value == client]
+ uuids = [item for item, value in list(self.uuid.items())
+ if value == client]
if uuids:
uuid = uuids[0]
else:
@@ -649,7 +623,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
[newgroups.add(g) for g in ngroups if g not in newgroups]
newcategories.update(ncategories)
return ClientMetadata(client, profile, newgroups, newbundles, aliases,
- addresses, newcategories, uuid, password, self.query)
+ addresses, newcategories, uuid, password,
+ self.query)
def get_all_group_names(self):
all_groups = set()
@@ -673,22 +648,27 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
def merge_additional_groups(self, imd, groups):
for group in groups:
- if group in self.categories and \
- self.categories[group] in imd.categories:
+ if (group in self.categories and
+ self.categories[group] in imd.categories):
continue
- nb, ng, _ = self.groups.get(group, (list(), [group], dict()))
- for b in nb:
- if b not in imd.bundles:
- imd.bundles.add(b)
- for g in ng:
- if g not in imd.groups:
- if g in self.categories and \
- self.categories[g] in imd.categories:
+ newbundles, newgroups, _ = self.groups.get(group,
+ (list(),
+ [group],
+ dict()))
+ for newbundle in newbundles:
+ if newbundle not in imd.bundles:
+ imd.bundles.add(newbundle)
+ for newgroup in newgroups:
+ if newgroup not in imd.groups:
+ if (newgroup in self.categories and
+ self.categories[newgroup] in imd.categories):
continue
- if g in self.private:
- self.logger.error("Refusing to add dynamic membership in private group %s for client %s" % (g, imd.hostname))
+ if newgroup in self.private:
+ self.logger.error("Refusing to add dynamic membership "
+ "in private group %s for client %s" %
+ (newgroup, imd.hostname))
continue
- imd.groups.add(g)
+ imd.groups.add(newgroup)
def merge_additional_data(self, imd, source, data):
if not hasattr(imd, source):
@@ -703,18 +683,19 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
return True
if address in self.addresses:
if client in self.addresses[address]:
- self.debug_log("Client %s matches address %s" % (client, address))
+ self.debug_log("Client %s matches address %s" %
+ (client, address))
return True
else:
- self.logger.error("Got request for non-float client %s from %s" \
- % (client, address))
+ self.logger.error("Got request for non-float client %s from %s" %
+ (client, address))
return False
resolved = self.resolve_client(addresspair)
if resolved.lower() == client.lower():
return True
else:
- self.logger.error("Got request for %s from incorrect address %s" \
- % (client, address))
+ self.logger.error("Got request for %s from incorrect address %s" %
+ (client, address))
self.logger.error("Resolved to %s" % resolved)
return False
@@ -732,7 +713,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
try:
client = self.resolve_client(address)
except MetadataConsistencyError:
- self.logger.error("Client %s failed to resolve; metadata problem" % (address[0]))
+ self.logger.error("Client %s failed to resolve; metadata problem"
+ % address[0])
return False
else:
id_method = 'uuid'
@@ -764,10 +746,12 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
if client not in self.passwords:
if client in self.secure:
- self.logger.error("Client %s in secure mode but has no password" % (address[0]))
+ self.logger.error("Client %s in secure mode but has no password"
+ % address[0])
return False
if password != self.password:
- self.logger.error("Client %s used incorrect global password" % (address[0]))
+ self.logger.error("Client %s used incorrect global password" %
+ address[0])
return False
if client not in self.secure:
if client in self.passwords:
@@ -775,14 +759,14 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
else:
plist = [self.password]
if password not in plist:
- self.logger.error("Client %s failed to use either allowed password" % \
- (address[0]))
+ self.logger.error("Client %s failed to use either allowed "
+ "password" % address[0])
return False
else:
# client in secure mode and has a client password
if password != self.passwords[client]:
- self.logger.error("Client %s failed to use client password in secure mode" % \
- (address[0]))
+ self.logger.error("Client %s failed to use client password in "
+ "secure mode" % address[0])
return False
# populate the session cache
if user.decode('utf-8') != 'root':
@@ -793,13 +777,7 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
"""Hook into statistics interface to toggle clients in bootstrap mode."""
client = meta.hostname
if client in self.auth and self.auth[client] == 'bootstrap':
- self.logger.info("Asserting client %s auth mode to cert" % client)
- xdict = self.clients_xml.find_xml_for_xpath('.//Client[@name="%s"]' % (client))
- if not xdict:
- self.logger.error("Metadata: Unable to update profile for client %s. Use of Xinclude?" % client)
- raise MetadataConsistencyError
- xdict['xquery'][0].set('auth', 'cert')
- self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
+ self.update_client(client, dict(auth='cert'))
def viz(self, hosts, bundles, key, only_client, colors):
"""Admin mode viz support."""
@@ -815,14 +793,14 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
def include_group(group):
return not only_client or group in clientmeta.groups
- groups_tree = lxml.etree.parse(self.data + "/groups.xml")
+ groups_tree = lxml.etree.parse(os.path.join(self.data, "groups.xml"))
try:
groups_tree.xinclude()
except lxml.etree.XIncludeError:
self.logger.error("Failed to process XInclude for file %s" % dest)
groups = groups_tree.getroot()
categories = {'default': 'grey83'}
- viz_str = ""
+ viz_str = []
egroups = groups.findall("Group") + groups.findall('.//Groups/Group')
for group in egroups:
if not group.get('category') in categories:
@@ -842,10 +820,10 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
instances[profile] = [client]
for profile, clist in list(instances.items()):
clist.sort()
- viz_str += '''\t"%s-instances" [ label="%s", shape="record" ];\n''' \
- % (profile, '|'.join(clist))
- viz_str += '''\t"%s-instances" -> "group-%s";\n''' \
- % (profile, profile)
+ viz_str.append('"%s-instances" [ label="%s", shape="record" ];' %
+ (profile, '|'.join(clist)))
+ viz_str.append('"%s-instances" -> "group-%s";' %
+ (profile, profile))
if bundles:
bundles = []
[bundles.append(bund.get('name')) \
@@ -854,8 +832,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
and include_bundle(bund.get('name'))]
bundles.sort()
for bundle in bundles:
- viz_str += '''\t"bundle-%s" [ label="%s", shape="septagon"];\n''' \
- % (bundle, bundle)
+ viz_str.append('"bundle-%s" [ label="%s", shape="septagon"];' %
+ (bundle, bundle))
gseen = []
for group in egroups:
if group.get('profile', 'false') == 'true':
@@ -864,24 +842,25 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
style = "filled"
gseen.append(group.get('name'))
if include_group(group.get('name')):
- viz_str += '\t"group-%s" [label="%s", style="%s", fillcolor=%s];\n' % \
- (group.get('name'), group.get('name'), style, group.get('color'))
+ viz_str.append('"group-%s" [label="%s", style="%s", fillcolor=%s];' %
+ (group.get('name'), group.get('name'), style,
+ group.get('color')))
if bundles:
for bundle in group.findall('Bundle'):
- viz_str += '\t"group-%s" -> "bundle-%s";\n' % \
- (group.get('name'), bundle.get('name'))
- gfmt = '\t"group-%s" [label="%s", style="filled", fillcolor="grey83"];\n'
+ viz_str.append('"group-%s" -> "bundle-%s";' %
+ (group.get('name'), bundle.get('name')))
+ gfmt = '"group-%s" [label="%s", style="filled", fillcolor="grey83"];'
for group in egroups:
for parent in group.findall('Group'):
if parent.get('name') not in gseen and include_group(parent.get('name')):
- viz_str += gfmt % (parent.get('name'), parent.get('name'))
+ viz_str.append(gfmt % (parent.get('name'),
+ parent.get('name')))
gseen.append(parent.get("name"))
if include_group(group.get('name')):
- viz_str += '\t"group-%s" -> "group-%s" ;\n' % \
- (group.get('name'), parent.get('name'))
+ viz_str.append('"group-%s" -> "group-%s";' %
+ (group.get('name'), parent.get('name')))
if key:
for category in categories:
- viz_str += '''\t"''' + category + '''" [label="''' + category + \
- '''", shape="record", style="filled", fillcolor=''' + \
- categories[category] + '''];\n'''
- return viz_str
+ viz_str.append('"%s" [label="%s", shape="record", style="filled", fillcolor="%s"];' %
+ (category, category, categories[category]))
+ return "\n".join("\t" + s for s in viz_str)
diff --git a/src/lib/Server/Plugins/NagiosGen.py b/src/lib/Server/Plugins/NagiosGen.py
index 8a76c130d..d67dac6a6 100644
--- a/src/lib/Server/Plugins/NagiosGen.py
+++ b/src/lib/Server/Plugins/NagiosGen.py
@@ -27,7 +27,6 @@ class NagiosGen(Bcfg2.Server.Plugin.Plugin,
Nagios configuration file based on Bcfg2 data.
"""
name = 'NagiosGen'
- __version__ = '0.7'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Pkgmgr.py b/src/lib/Server/Plugins/Pkgmgr.py
index bc11bfdcf..e9254cdcc 100644
--- a/src/lib/Server/Plugins/Pkgmgr.py
+++ b/src/lib/Server/Plugins/Pkgmgr.py
@@ -1,5 +1,4 @@
'''This module implements a package management scheme for all images'''
-__revision__ = '$Revision$'
import logging
import re
@@ -135,7 +134,6 @@ class PkgSrc(Bcfg2.Server.Plugin.XMLSrc):
class Pkgmgr(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles package assignments."""
name = 'Pkgmgr'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
__child__ = PkgSrc
__element__ = 'Package'
diff --git a/src/lib/Server/Plugins/Probes.py b/src/lib/Server/Plugins/Probes.py
index ae1ed4c2b..2af3635e9 100644
--- a/src/lib/Server/Plugins/Probes.py
+++ b/src/lib/Server/Plugins/Probes.py
@@ -181,7 +181,6 @@ class Probes(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Connector):
"""A plugin to gather information from a client machine."""
name = 'Probes'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Server/Plugins/Properties.py
index 76945b3a0..680881858 100644
--- a/src/lib/Server/Plugins/Properties.py
+++ b/src/lib/Server/Plugins/Properties.py
@@ -60,7 +60,6 @@ class Properties(Bcfg2.Server.Plugin.Plugin,
files into client metadata instances.
"""
name = 'Properties'
- version = '$Revision$'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
diff --git a/src/lib/Server/Plugins/Rules.py b/src/lib/Server/Plugins/Rules.py
index c66276179..684f0c290 100644
--- a/src/lib/Server/Plugins/Rules.py
+++ b/src/lib/Server/Plugins/Rules.py
@@ -1,5 +1,4 @@
"""This generator provides rule-based entry mappings."""
-__revision__ = '$Revision$'
import re
import Bcfg2.Server.Plugin
@@ -10,7 +9,6 @@ class RulesConfig(Bcfg2.Server.Plugin.SimpleConfig):
class Rules(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles service assignments."""
name = 'Rules'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/SGenshi.py b/src/lib/Server/Plugins/SGenshi.py
index f6a98c141..0ba08125e 100644
--- a/src/lib/Server/Plugins/SGenshi.py
+++ b/src/lib/Server/Plugins/SGenshi.py
@@ -1,5 +1,4 @@
'''This module implements a templating generator based on Genshi'''
-__revision__ = '$Revision$'
import genshi.input
import genshi.template
@@ -84,7 +83,6 @@ class SGenshi(SGenshiEntrySet,
Bcfg2.Server.Plugin.Structure):
"""The SGenshi plugin provides templated structures."""
name = 'SGenshi'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
deprecated = True
diff --git a/src/lib/Server/Plugins/SSHbase.py b/src/lib/Server/Plugins/SSHbase.py
index eb91bea39..2e247caa7 100644
--- a/src/lib/Server/Plugins/SSHbase.py
+++ b/src/lib/Server/Plugins/SSHbase.py
@@ -1,5 +1,4 @@
'''This module manages ssh key files for bcfg2'''
-__revision__ = '$Revision$'
import binascii
import re
@@ -99,7 +98,6 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
"""
name = 'SSHbase'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
keypatterns = ["ssh_host_dsa_key",
diff --git a/src/lib/Server/Plugins/SSLCA.py b/src/lib/Server/Plugins/SSLCA.py
index 7b4a08ae1..0072dc62d 100644
--- a/src/lib/Server/Plugins/SSLCA.py
+++ b/src/lib/Server/Plugins/SSLCA.py
@@ -16,7 +16,6 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
management of ssl certificates and their keys.
"""
name = 'SSLCA'
- __version__ = '$Id:$'
__author__ = 'g.hagger@gmail.com'
__child__ = Bcfg2.Server.Plugin.FileBacked
key_specs = {}
diff --git a/src/lib/Server/Plugins/Statistics.py b/src/lib/Server/Plugins/Statistics.py
index 9dbbeec28..265ef95a8 100644
--- a/src/lib/Server/Plugins/Statistics.py
+++ b/src/lib/Server/Plugins/Statistics.py
@@ -1,5 +1,4 @@
'''This file manages the statistics collected by the BCFG2 Server'''
-__revision__ = '$Revision$'
import binascii
import copy
@@ -117,7 +116,6 @@ class Statistics(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.ThreadedStatistics,
Bcfg2.Server.Plugin.PullSource):
name = 'Statistics'
- __version__ = '$Id$'
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
diff --git a/src/lib/Server/Plugins/Svcmgr.py b/src/lib/Server/Plugins/Svcmgr.py
index 6d25c1a6d..f4232ad5c 100644
--- a/src/lib/Server/Plugins/Svcmgr.py
+++ b/src/lib/Server/Plugins/Svcmgr.py
@@ -1,5 +1,4 @@
"""This generator provides service mappings."""
-__revision__ = '$Revision$'
import Bcfg2.Server.Plugin
@@ -7,6 +6,5 @@ import Bcfg2.Server.Plugin
class Svcmgr(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles service assignments."""
name = 'Svcmgr'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
deprecated = True
diff --git a/src/lib/Server/Plugins/Svn.py b/src/lib/Server/Plugins/Svn.py
index 9fd6f1051..ae43388ea 100644
--- a/src/lib/Server/Plugins/Svn.py
+++ b/src/lib/Server/Plugins/Svn.py
@@ -12,7 +12,6 @@ class Svn(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Svn is a version plugin for dealing with Bcfg2 repos."""
name = 'Svn'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/Svn2.py b/src/lib/Server/Plugins/Svn2.py
index b8a8e6b7e..8d79348f8 100644
--- a/src/lib/Server/Plugins/Svn2.py
+++ b/src/lib/Server/Plugins/Svn2.py
@@ -9,7 +9,6 @@ class Svn2(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Version):
"""Svn is a version plugin for dealing with Bcfg2 repos."""
name = 'Svn2'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
conflicts = ['Svn']
diff --git a/src/lib/Server/Plugins/TCheetah.py b/src/lib/Server/Plugins/TCheetah.py
index 49be88881..8879fdef1 100644
--- a/src/lib/Server/Plugins/TCheetah.py
+++ b/src/lib/Server/Plugins/TCheetah.py
@@ -1,5 +1,4 @@
'''This module implements a templating generator based on Cheetah'''
-__revision__ = '$Revision$'
import binascii
import logging
@@ -76,7 +75,6 @@ class TemplateFile:
class TCheetah(Bcfg2.Server.Plugin.GroupSpool):
"""The TCheetah generator implements a templating mechanism for configuration files."""
name = 'TCheetah'
- __version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
filename_pattern = 'template'
es_child_cls = TemplateFile
diff --git a/src/lib/Server/Plugins/TGenshi.py b/src/lib/Server/Plugins/TGenshi.py
index bc5e00400..3ba0f4272 100644
--- a/src/lib/Server/Plugins/TGenshi.py
+++ b/src/lib/Server/Plugins/TGenshi.py
@@ -1,5 +1,4 @@
"""This module implements a templating generator based on Genshi."""
-__revision__ = '$Revision$'
import binascii
import logging
@@ -131,7 +130,6 @@ class TGenshi(Bcfg2.Server.Plugin.GroupSpool):
"""
name = 'TGenshi'
- __version__ = '$Id$'
__author__ = 'jeff@ocjtech.us'
filename_pattern = 'template\.(txt|newtxt|xml)'
es_child_cls = TemplateFile
diff --git a/src/lib/Server/Plugins/TemplateHelper.py b/src/lib/Server/Plugins/TemplateHelper.py
new file mode 100644
index 000000000..42eafed56
--- /dev/null
+++ b/src/lib/Server/Plugins/TemplateHelper.py
@@ -0,0 +1,83 @@
+import re
+import imp
+import sys
+import logging
+import Bcfg2.Server.Plugin
+
+logger = logging.getLogger(__name__)
+
+class HelperModule(Bcfg2.Server.Plugin.SpecificData):
+ _module_name_re = re.compile(r'([^/]+?)\.py')
+
+ def __init__(self, name, specific, encoding):
+ Bcfg2.Server.Plugin.SpecificData.__init__(self, name, specific,
+ encoding)
+ match = self._module_name_re.search(self.name)
+ if match:
+ self._module_name = match.group(1)
+ else:
+ self._module_name = name
+ self._attrs = []
+
+ def handle_event(self, event):
+ Bcfg2.Server.Plugin.SpecificData.handle_event(self, event)
+ try:
+ module = imp.load_source(self._module_name, self.name)
+ except:
+ err = sys.exc_info()[1]
+ logger.error("TemplateHelper: Failed to import %s: %s" %
+ (self.name, err))
+ return
+
+ if not hasattr(module, "__export__"):
+ logger.error("TemplateHelper: %s has no __export__ list" %
+ self.name)
+ return
+
+ for sym in module.__export__:
+ if sym not in self._attrs and hasattr(self, sym):
+ logger.warning("TemplateHelper: %s: %s is a reserved keyword, "
+ "skipping export" % (self.name, sym))
+ setattr(self, sym, getattr(module, sym))
+ # remove old exports
+ for sym in set(self._attrs) - set(module.__export__):
+ delattr(self, sym)
+
+ self._attrs = module.__export__
+
+
+class HelperSet(Bcfg2.Server.Plugin.EntrySet):
+ ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\.py[co])$")
+
+ def __init__(self, path, fam, encoding, plugin_name):
+ fpattern = '[0-9A-Za-z_\-]+\.py'
+ self.plugin_name = plugin_name
+ Bcfg2.Server.Plugin.EntrySet.__init__(self, fpattern, path,
+ HelperModule, encoding)
+ fam.AddMonitor(path, self)
+
+ def HandleEvent(self, event):
+ if (event.filename != self.path and
+ not self.ignore.match(event.filename)):
+ return self.handle_event(event)
+
+
+class TemplateHelper(Bcfg2.Server.Plugin.Plugin,
+ Bcfg2.Server.Plugin.Connector):
+ """ A plugin to provide helper classes and functions to templates """
+ name = 'TemplateHelper'
+ __author__ = 'chris.a.st.pierre@gmail.com'
+
+ def __init__(self, core, datastore):
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
+ Bcfg2.Server.Plugin.Connector.__init__(self)
+
+ try:
+ self.helpers = HelperSet(self.data, core.fam, core.encoding,
+ self.name)
+ except:
+ raise Bcfg2.Server.Plugin.PluginInitError
+
+ def get_additional_data(self, metadata):
+ return dict([(h._module_name, h)
+ for h in list(self.helpers.entries.values())])
diff --git a/src/lib/Server/Plugins/Trigger.py b/src/lib/Server/Plugins/Trigger.py
index eb3310a4e..b0d21545c 100644
--- a/src/lib/Server/Plugins/Trigger.py
+++ b/src/lib/Server/Plugins/Trigger.py
@@ -17,7 +17,6 @@ class Trigger(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Statistics):
"""Trigger is a plugin that calls external scripts (on the server)."""
name = 'Trigger'
- __version__ = '$Id'
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
diff --git a/src/lib/Server/Plugins/__init__.py b/src/lib/Server/Plugins/__init__.py
index c69c37452..a36a9b766 100644
--- a/src/lib/Server/Plugins/__init__.py
+++ b/src/lib/Server/Plugins/__init__.py
@@ -1,5 +1,4 @@
"""Imports for Bcfg2.Server.Plugins."""
-__revision__ = '$Revision$'
__all__ = [
'Account',
diff --git a/src/lib/Server/Reports/importscript.py b/src/lib/Server/Reports/importscript.py
index 7dfac6fae..cbdf019f5 100755
--- a/src/lib/Server/Reports/importscript.py
+++ b/src/lib/Server/Reports/importscript.py
@@ -3,7 +3,6 @@
Imports statistics.xml and clients.xml files in to database backend for
new statistics engine
"""
-__revision__ = '$Revision$'
import binascii
import os
diff --git a/src/lib/Server/Reports/nisauth.py b/src/lib/Server/Reports/nisauth.py
index 6fc346f1e..b3e37113b 100644
--- a/src/lib/Server/Reports/nisauth.py
+++ b/src/lib/Server/Reports/nisauth.py
@@ -4,8 +4,6 @@ from Bcfg2.Server.Reports.settings import AUTHORIZED_GROUP
"""Checks with NIS to see if the current user is in the support group"""
-__revision__ = "$Revision: $"
-
class NISAUTHError(Exception):
"""NISAUTHError is raised when somehting goes boom."""
diff --git a/src/lib/Server/__init__.py b/src/lib/Server/__init__.py
index 25f397565..96777b0bf 100644
--- a/src/lib/Server/__init__.py
+++ b/src/lib/Server/__init__.py
@@ -1,6 +1,4 @@
-# $Id$
"""This is the set of modules for Bcfg2.Server."""
-__revision__ = '$Revision$'
__all__ = ["Admin", "Core", "FileMonitor", "Plugin", "Plugins",
"Hostbase", "Reports", "Snapshots"]
diff --git a/src/lib/__init__.py b/src/lib/__init__.py
index d36c0a00a..357f66f6d 100644
--- a/src/lib/__init__.py
+++ b/src/lib/__init__.py
@@ -1,4 +1,3 @@
"""Base modules definition."""
-__revision__ = '$Revision$'
__all__ = ['Server', 'Client', 'Component', 'Logger', 'Options', 'Proxy', 'Statistics']
diff --git a/src/sbin/bcfg2 b/src/sbin/bcfg2
index 1d1cc8424..fb34e627b 100755
--- a/src/sbin/bcfg2
+++ b/src/sbin/bcfg2
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""Bcfg2 Client"""
-__revision__ = '$Revision$'
import fcntl
import logging
diff --git a/src/sbin/bcfg2-build-reports b/src/sbin/bcfg2-build-reports
index 7122fb300..7fa08110a 100755
--- a/src/sbin/bcfg2-build-reports
+++ b/src/sbin/bcfg2-build-reports
@@ -4,8 +4,6 @@
bcfg2-build-reports generates & distributes reports of statistic
information for Bcfg2."""
-__revision__ = '$Revision$'
-
import copy
import getopt
import re
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 808761d7e..b68434e11 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""This tool loads the Bcfg2 core into an interactive debugger."""
-__revision__ = '$Revision$'
from code import InteractiveConsole
import cmd
@@ -51,8 +50,7 @@ profile <command> <args> - Profile a single bcfg2-info command
quit - Exit the bcfg2-info command line
showentries <hostname> <type> - Show abstract configuration entries for a given host
showclient <client1> <client2> - Show metadata for given hosts
-update - Process pending file events
-version - Print version of this tool"""
+update - Process pending file events"""
BUILDDIR_USAGE = """Usage: builddir [-f] <hostname> <output dir>
@@ -192,10 +190,6 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
"""Process pending filesystem events."""
self.fam.handle_events_in_interval(0.1)
- def do_version(self, _):
- """Print out code version."""
- print(__revision__)
-
def do_build(self, args):
"""Build client configuration."""
alist = args.split()
diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint
index 2d371f4aa..ad6b6139c 100755
--- a/src/sbin/bcfg2-lint
+++ b/src/sbin/bcfg2-lint
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""This tool examines your Bcfg2 specifications for errors."""
-__revision__ = '$Revision$'
import sys
import inspect
diff --git a/src/sbin/bcfg2-ping-sweep b/src/sbin/bcfg2-ping-sweep
index 70f718690..be8994be3 100755
--- a/src/sbin/bcfg2-ping-sweep
+++ b/src/sbin/bcfg2-ping-sweep
@@ -3,8 +3,6 @@
"""Generates hostinfo.xml at a regular interval."""
-__revision__ = '$Revision$'
-
from os import dup2, execl, fork, uname, wait
import sys
import time
diff --git a/src/sbin/bcfg2-reports b/src/sbin/bcfg2-reports
index 6acdd27e3..1f101b9a7 100755
--- a/src/sbin/bcfg2-reports
+++ b/src/sbin/bcfg2-reports
@@ -1,6 +1,5 @@
#!/usr/bin/env python
"""Query reporting system for client status."""
-__revision__ = '$Revision$'
import os
import sys
diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server
index 546d5a249..89bc7c331 100755
--- a/src/sbin/bcfg2-server
+++ b/src/sbin/bcfg2-server
@@ -1,7 +1,6 @@
#!/usr/bin/env python
"""The XML-RPC Bcfg2 server."""
-__revision__ = '$Revision$'
import logging
import os.path
diff --git a/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper
index dc46bb81a..2da7c6336 100755
--- a/src/sbin/bcfg2-yum-helper
+++ b/src/sbin/bcfg2-yum-helper
@@ -5,8 +5,6 @@ the right way to get around that in long-running processes it to have
a short-lived helper. No, seriously -- check out the yum-updatesd
code. It's pure madness. """
-__revision__ = '$Revision$'
-
import os
import sys
import yum
diff --git a/testsuite/TestFrame.py b/testsuite/TestFrame.py
deleted file mode 100644
index a76c97eec..000000000
--- a/testsuite/TestFrame.py
+++ /dev/null
@@ -1,104 +0,0 @@
-import lxml.etree
-
-import Bcfg2.Client.Frame
-import Bcfg2.Client.Tools
-
-c1 = lxml.etree.XML("<Configuration><Bundle name='foo'><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/></Bundle></Configuration>")
-
-c2 = lxml.etree.XML("<Configuration><Bundle name='foo'><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/><Configfile name='/tmp/test12' owner='root' group='root' empty='true' perms='644'/></Bundle></Configuration>")
-
-
-class DriverInitFail(object):
- def __init__(self, *args):
- raise Bcfg2.Client.Tools.toolInstantiationError
-
-
-class DriverInventoryFail(object):
- __name__ = 'dif'
-
- def __init__(self, logger, setup, config):
- self.config = config
- self.handled = []
- self.modified = []
- self.extra = []
-
- def Inventory(self):
- raise Error
-
-
-class TestFrame(object):
- def test__init(self):
- setup = {}
- times = {}
- config = lxml.etree.Element('Configuration')
- frame = Bcfg2.Client.Frame.Frame(config, setup, times, [], False)
- assert frame.tools == []
-
- def test__init2(self):
- setup = {}
- times = {}
- frame2 = Bcfg2.Client.Frame.Frame(c1, setup, times, ['POSIX'], False)
- assert len(frame2.tools) == 1
-
- def test__init3(self):
- setup = {}
- times = {}
- frame3 = Bcfg2.Client.Frame.Frame(c2, setup, times, ['foo'], False)
- assert len(frame3.tools) == 0
-
- def test__init4(self):
- setup = {}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times, [DriverInitFail], False)
- assert len(frame.tools) == 0
-
- def test__Decide_Inventory(self):
- setup = {'remove': 'none',
- 'bundle': [],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], False)
- assert len(frame.tools) == 1
- frame.Inventory()
- assert len([x for x in list(frame.states.values()) if x]) == 0
- frame.Decide()
- assert len(frame.whitelist)
-
- def test__Decide_Bundle(self):
- setup = {'remove': 'none',
- 'bundle': ['bar'],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], False)
- assert len(frame.tools) == 1
- frame.Inventory()
- assert len([x for x in list(frame.states.values()) if x]) == 0
- frame.Decide()
- assert len(frame.whitelist) == 0
-
- def test__Decide_Dryrun(self):
- setup = {'remove': 'none',
- 'bundle': [],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], True)
- assert len(frame.tools) == 1
- frame.Inventory()
- assert len([x for x in list(frame.states.values()) if x]) == 0
- frame.Decide()
- assert len(frame.whitelist) == 0
-
- def test__GenerateStats(self):
- setup = {'remove': 'none',
- 'bundle': [],
- 'interactive': False}
- times = {}
- frame = Bcfg2.Client.Frame.Frame(c2, setup, times,
- [DriverInventoryFail], False)
- frame.Inventory()
- frame.Decide()
- stats = frame.GenerateStats()
- assert len(stats.findall('.//Bad')[0].getchildren()) != 0
diff --git a/testsuite/TestOptions.py b/testsuite/TestOptions.py
deleted file mode 100644
index 735455d45..000000000
--- a/testsuite/TestOptions.py
+++ /dev/null
@@ -1,103 +0,0 @@
-import os
-import sys
-
-import Bcfg2.Options
-
-
-class TestOption(object):
- def test__init(self):
- o = Bcfg2.Options.Option('foo', False, cmd='-F')
- try:
- p = Bcfg2.Options.Option('foo', False, cmd='--F')
- assert False
- except Bcfg2.Options.OptionFailure:
- pass
-
- def test_parse(self):
- o = Bcfg2.Options.Option('foo', 'test4', cmd='-F', env='TEST2',
- odesc='bar', cf=('communication', 'password'))
- o.parse([], ['-F', 'test'])
- assert o.value == 'test'
- o.parse([('-F', 'test2')], [])
- assert o.value == 'test2'
- os.environ['TEST2'] = 'test3'
- o.parse([], [])
- assert o.value == 'test3'
- del os.environ['TEST2']
- o.parse([], [])
- print(o.value)
- assert o.value == 'foobat'
- o.cf = ('communication', 'pwd')
- o.parse([], [])
- print(o.value)
- assert o.value == 'test4'
- o.cf = False
- o.parse([], [])
- assert o.value == 'test4'
-
- def test_cook(self):
- # check that default value isn't cooked
- o1 = Bcfg2.Options.Option('foo', 'test4', cook=Bcfg2.Options.bool_cook)
- o1.parse([], [])
- assert o1.value == 'test4'
- o2 = Bcfg2.Options.Option('foo', False, cmd='-F')
- o2.parse([('-F', '')], [])
- assert o2.value == True
-
-
-class TestOptionSet(object):
- def test_buildGetopt(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
- odesc='1'))]
- os = Bcfg2.Options.OptionSet(opts)
- res = os.buildGetopt()
- assert 'H:' in res and 'G' in res and len(res) == 3
-
- def test_buildLongGetopt(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='--H',
- odesc='1', long_arg=True))]
- os = Bcfg2.Options.OptionSet(opts)
- res = os.buildLongGetopt()
- print(res)
- assert 'H=' in res and len(res) == 1
-
- def test_parse(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
- odesc='1'))]
- os = Bcfg2.Options.OptionSet(opts)
- try:
- os.parse(['-G', '-H'])
- assert False
- except SystemExit:
- pass
- os2 = Bcfg2.Options.OptionSet(opts)
- try:
- os2.parse(['-h'])
- assert False
- except SystemExit:
- pass
- os3 = Bcfg2.Options.OptionSet(opts)
- os3.parse(['-G'])
- assert os3['foo'] == True
-
-
-class TestOptionParser(object):
- def test__init(self):
- opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-h')),
- ('bar', Bcfg2.Options.Option('foo', 'test2')),
- ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
- odesc='1'))]
- os1 = Bcfg2.Options.OptionParser(opts)
- assert Bcfg2.Options.Option.cfpath == '/etc/bcfg2.conf'
- sys.argv = ['foo', '-C', '/usr/local/etc/bcfg2.conf']
- os2 = Bcfg2.Options.OptionParser(opts)
- assert Bcfg2.Options.Option.cfpath == '/usr/local/etc/bcfg2.conf'
- sys.argv = []
- os3 = Bcfg2.Options.OptionParser(opts)
- assert Bcfg2.Options.Option.cfpath == '/etc/bcfg2.conf'
diff --git a/testsuite/TestPlugin.py b/testsuite/TestPlugin.py
deleted file mode 100644
index aa619249a..000000000
--- a/testsuite/TestPlugin.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import gamin
-import lxml.etree
-import os
-
-import Bcfg2.Server.Core
-from Bcfg2.Server.Plugin import EntrySet
-
-
-class es_testtype(object):
- def __init__(self, name, properties, specific):
- self.name = name
- self.properties = properties
- self.specific = specific
- self.handled = 0
- self.built = 0
-
- def handle_event(self, event):
- self.handled += 1
-
- def bind_entry(self, entry, metadata):
- entry.set('bound', '1')
- entry.set('name', self.name)
- self.built += 1
-
-
-class metadata(object):
- def __init__(self, hostname):
- self.hostname = hostname
- self.groups = ['base', 'debian']
-
-#FIXME add test_specific
-
-
-class test_entry_set(object):
- def __init__(self):
- self.dirname = '/tmp/estest-%d' % os.getpid()
- os.path.isdir(self.dirname) or os.mkdir(self.dirname)
- self.metadata = metadata('testhost')
- self.es = EntrySet('template', self.dirname, None, es_testtype)
- self.e = Bcfg2.Server.Core.GaminEvent(1, 'template',
- gamin.GAMExists)
-
- def test_init(self):
- es = self.es
- e = self.e
- e.action = 'exists'
- es.handle_event(e)
- es.handle_event(e)
- assert len(es.entries) == 1
- assert list(es.entries.values())[0].handled == 2
- e.action = 'changed'
- es.handle_event(e)
- assert list(es.entries.values())[0].handled == 3
-
- def test_info(self):
- """Test info and info.xml handling."""
- es = self.es
- e = self.e
- dirname = self.dirname
- metadata = self.metadata
-
- # test 'info' handling
- assert es.metadata['group'] == 'root'
- self.mk_info(dirname)
- e.filename = 'info'
- e.action = 'exists'
- es.handle_event(e)
- assert es.metadata['group'] == 'sys'
- e.action = 'deleted'
- es.handle_event(e)
- assert es.metadata['group'] == 'root'
-
- # test 'info.xml' handling
- assert es.infoxml == None
- self.mk_info_xml(dirname)
- e.filename = 'info.xml'
- e.action = 'exists'
- es.handle_event(e)
- assert es.infoxml
- e.action = 'deleted'
- es.handle_event(e)
- assert es.infoxml == None
-
- def test_file_building(self):
- """Test file building."""
- self.test_init()
- ent = lxml.etree.Element('foo')
- self.es.bind_entry(ent, self.metadata)
- print(list(self.es.entries.values())[0])
- assert list(self.es.entries.values())[0].built == 1
-
- def test_host_specific_file_building(self):
- """Add a host-specific template and build it."""
- self.e.filename = 'template.H_%s' % self.metadata.hostname
- self.e.action = 'exists'
- self.es.handle_event(self.e)
- assert len(self.es.entries) == 1
- ent = lxml.etree.Element('foo')
- self.es.bind_entry(ent, self.metadata)
- # FIXME need to test that it built the _right_ file here
-
- def test_deletion(self):
- """Test deletion of files."""
- self.test_init()
- self.e.filename = 'template'
- self.e.action = 'deleted'
- self.es.handle_event(self.e)
- assert len(self.es.entries) == 0
-
- # TODO - how to clean up the temp dir & files after tests done?
-
- def mk_info(self, dir):
- i = open("%s/info" % dir, 'w')
- i.write('owner: root\n')
- i.write('group: sys\n')
- i.write('perms: 0600\n')
- i.close
-
- def mk_info_xml(self, dir):
- i = open("%s/info.xml" % dir, 'w')
- i.write('<FileInfo><Info owner="root" group="other" perms="0600" /></FileInfo>\n')
- i.close
diff --git a/testsuite/Testlib/TestOptions.py b/testsuite/Testlib/TestOptions.py
new file mode 100644
index 000000000..f5833a54a
--- /dev/null
+++ b/testsuite/Testlib/TestOptions.py
@@ -0,0 +1,124 @@
+import os
+import sys
+import unittest
+from mock import Mock, patch
+import Bcfg2.Options
+
+class TestOption(unittest.TestCase):
+ def test__init(self):
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='f')
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='--f')
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='-foo')
+ self.assertRaises(Bcfg2.Options.OptionFailure,
+ Bcfg2.Options.Option,
+ 'foo', False, cmd='-foo', long_arg=True)
+
+ @patch('ConfigParser.ConfigParser')
+ @patch('__builtin__.open')
+ def test_getCFP(self, mock_open, mock_cp):
+ mock_cp.return_value = Mock()
+ o = Bcfg2.Options.Option('foo', False, cmd='-f')
+ self.assertFalse(o._Option__cfp)
+ o.getCFP()
+ mock_cp.assert_any_call()
+ mock_open.assert_any_call(Bcfg2.Options.DEFAULT_CONFIG_LOCATION)
+ self.assertTrue(mock_cp.return_value.readfp.called)
+
+ @patch('Bcfg2.Options.Option.cfp')
+ def test_parse(self, mock_cfp):
+ cf = ('communication', 'password')
+ o = Bcfg2.Options.Option('foo', 'test4', cmd='-F', env='TEST2',
+ odesc='bar', cf=cf)
+ o.parse([], ['-F', 'test'])
+ self.assertEqual(o.value, 'test')
+ o.parse([('-F', 'test2')], [])
+ self.assertEqual(o.value, 'test2')
+
+ os.environ['TEST2'] = 'test3'
+ o.parse([], [])
+ self.assertEqual(o.value, 'test3')
+ del os.environ['TEST2']
+
+ mock_cfp.get = Mock()
+ mock_cfp.get.return_value = 'test5'
+ o.parse([], [])
+ self.assertEqual(o.value, 'test5')
+ mock_cfp.get.assert_any_call(*cf)
+
+ o.cf = False
+ o.parse([], [])
+ assert o.value == 'test4'
+
+ def test_cook(self):
+ # check that default value isn't cooked
+ o1 = Bcfg2.Options.Option('foo', 'test4', cook=Bcfg2.Options.bool_cook)
+ o1.parse([], [])
+ assert o1.value == 'test4'
+ o2 = Bcfg2.Options.Option('foo', False, cmd='-F')
+ o2.parse([('-F', '')], [])
+ assert o2.value == True
+
+
+class TestOptionSet(unittest.TestCase):
+ def test_buildGetopt(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
+ oset = Bcfg2.Options.OptionSet(opts)
+ res = oset.buildGetopt()
+ self.assertIn('H:', res)
+ self.assertIn('G', res)
+ self.assertEqual(len(res), 3)
+
+ def test_buildLongGetopt(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='--H',
+ odesc='1', long_arg=True))]
+ oset = Bcfg2.Options.OptionSet(opts)
+ res = oset.buildLongGetopt()
+ self.assertIn('H=', res)
+ self.assertEqual(len(res), 1)
+
+ def test_parse(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-G')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
+ oset = Bcfg2.Options.OptionSet(opts)
+ self.assertRaises(SystemExit,
+ oset.parse,
+ ['-G', '-H'])
+ oset2 = Bcfg2.Options.OptionSet(opts)
+ self.assertRaises(SystemExit,
+ oset2.parse,
+ ['-h'])
+ oset3 = Bcfg2.Options.OptionSet(opts)
+ oset3.parse(['-G'])
+ self.assertTrue(oset3['foo'])
+
+
+class TestOptionParser(unittest.TestCase):
+ def test__init(self):
+ opts = [('foo', Bcfg2.Options.Option('foo', 'test1', cmd='-h')),
+ ('bar', Bcfg2.Options.Option('foo', 'test2')),
+ ('baz', Bcfg2.Options.Option('foo', 'test1', cmd='-H',
+ odesc='1'))]
+ oset1 = Bcfg2.Options.OptionParser(opts)
+ self.assertEqual(Bcfg2.Options.Option.cfpath,
+ Bcfg2.Options.DEFAULT_CONFIG_LOCATION)
+ sys.argv = ['foo', '-C', '/usr/local/etc/bcfg2.conf']
+ oset2 = Bcfg2.Options.OptionParser(opts)
+ self.assertEqual(Bcfg2.Options.Option.cfpath,
+ '/usr/local/etc/bcfg2.conf')
+ sys.argv = []
+ oset3 = Bcfg2.Options.OptionParser(opts)
+ self.assertEqual(Bcfg2.Options.Option.cfpath,
+ Bcfg2.Options.DEFAULT_CONFIG_LOCATION)
diff --git a/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py b/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py
new file mode 100644
index 000000000..4d0b96ee7
--- /dev/null
+++ b/testsuite/Testlib/TestServer/TestPlugins/TestMetadata.py
@@ -0,0 +1,898 @@
+import os
+import copy
+import time
+import socket
+import unittest
+import lxml.etree
+from mock import Mock, patch
+import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugins.Metadata import *
+
+XI_NAMESPACE = "http://www.w3.org/2001/XInclude"
+XI = "{%s}" % XI_NAMESPACE
+
+clients_test_tree = lxml.etree.XML('''
+<Clients>
+ <Client name="client1" address="1.2.3.1" auth="cert"
+ location="floating" password="password2" profile="group1"/>
+ <Client name="client2" address="1.2.3.2" secure="true" profile="group2"/>
+ <Client name="client3" address="1.2.3.3" uuid="uuid1" profile="group1"
+ password="password2">
+ <Alias name="alias1"/>
+ </Client>
+ <Client name="client4" profile="group1">
+ <Alias name="alias2" address="1.2.3.2"/>
+ <Alias name="alias3"/>
+ </Client>
+ <Client name="client5" profile="group1"/>
+ <Client name="client6" profile="group1" auth="bootstrap"/>
+ <Client name="client7" profile="group1" auth="cert" address="1.2.3.4"/>
+ <Client name="client8" profile="group1" auth="cert+password"
+ address="1.2.3.5"/>
+ <Client name="client9" profile="group2" secure="true" password="password3"/>
+</Clients>''').getroottree()
+
+groups_test_tree = lxml.etree.XML('''
+<Groups xmlns:xi="http://www.w3.org/2001/XInclude">
+ <Group name="group1" default="true" profile="true" public="true"
+ category="category1"/>
+ <Group name="group2" profile="true" public="true" category="category1">
+ <Bundle name="bundle1"/>
+ <Bundle name="bundle2"/>
+ <Group name="group1"/>
+ <Group name="group4"/>
+ </Group>
+ <Group name="group3" category="category2" public="false"/>
+ <Group name="group4" category="category1">
+ <Group name="group1"/>
+ <Group name="group6"/>
+ </Group>
+ <Group name="group5"/>
+ <Group name="group7">
+ <Bundle name="bundle3"/>
+ </Group>
+ <Group name="group8">
+ <Group name="group9"/>
+ </Group>
+</Groups>''').getroottree()
+
+datastore = "/"
+
+def get_metadata_object(core=None, watch_clients=False):
+ if core is None:
+ core = Mock()
+ metadata = Metadata(core, datastore, watch_clients=watch_clients)
+ #metadata.logger = Mock()
+ return metadata
+
+
+class TestXMLMetadataConfig(unittest.TestCase):
+ def get_config_object(self, basefile=None, core=None, watch_clients=False):
+ self.metadata = get_metadata_object(core=core,
+ watch_clients=watch_clients)
+ return XMLMetadataConfig(self.metadata, watch_clients, basefile)
+
+ def test_xdata(self):
+ config = self.get_config_object()
+ # we can't use assertRaises here because xdata is a property
+ try:
+ config.xdata
+ except MetadataRuntimeError:
+ pass
+ except:
+ assert False
+ config.data = "<test/>"
+ self.assertEqual(config.xdata, "<test/>")
+
+ def test_base_xdata(self):
+ config = self.get_config_object()
+ # we can't use assertRaises here because base_xdata is a property
+ try:
+ config.base_xdata
+ except MetadataRuntimeError:
+ pass
+ except:
+ assert False
+ config.basedata = "<test/>"
+ self.assertEqual(config.base_xdata, "<test/>")
+
+ def test_add_monitor(self):
+ core = Mock()
+ config = self.get_config_object(core=core)
+ config.extras = []
+ config.add_monitor("test.xml")
+ self.assertFalse(core.fam.AddMonitor.called)
+ self.assertEqual(config.extras, [])
+
+ config = self.get_config_object(core=core, watch_clients=True)
+ config.add_monitor("test.xml")
+ core.fam.AddMonitor.assert_called_with(os.path.join(self.metadata.data,
+ "test.xml"),
+ self.metadata)
+ self.assertItemsEqual(config.extras, ["test.xml"])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.add_monitor")
+ @patch("lxml.etree.parse")
+ def test_load_xml(self, mock_parse, mock_add_monitor):
+ config = self.get_config_object("clients.xml")
+ mock_parse.side_effect = lxml.etree.XMLSyntaxError(None, None, None,
+ None)
+ config.load_xml()
+ self.assertIsNone(config.data)
+ self.assertIsNone(config.basedata)
+
+ config.data = None
+ config.basedata = None
+ mock_parse.side_effect = None
+ mock_parse.return_value.findall = Mock(return_value=[])
+ config.load_xml()
+ self.assertIsNotNone(config.data)
+ self.assertIsNotNone(config.basedata)
+
+ config.data = None
+ config.basedata = None
+ mock_parse.return_value.findall = \
+ Mock(return_value=[lxml.etree.Element(XI + "include",
+ href="more.xml"),
+ lxml.etree.Element(XI + "include",
+ href="evenmore.xml")])
+ config.load_xml()
+ mock_add_monitor.assert_any_call("more.xml")
+ mock_add_monitor.assert_any_call("evenmore.xml")
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.write_xml")
+ def test_write(self, mock_write_xml):
+ config = self.get_config_object("clients.xml")
+ config.basedata = "<test/>"
+ config.write()
+ mock_write_xml.assert_called_with(os.path.join(self.metadata.data,
+ "clients.xml"),
+ "<test/>")
+
+ @patch('Bcfg2.Server.Plugins.Metadata.locked', Mock(return_value=False))
+ @patch('fcntl.lockf', Mock())
+ @patch('__builtin__.open')
+ @patch('os.unlink')
+ @patch('os.rename')
+ @patch('os.path.islink')
+ @patch('os.readlink')
+ def test_write_xml(self, mock_readlink, mock_islink, mock_rename,
+ mock_unlink, mock_open):
+ fname = "clients.xml"
+ config = self.get_config_object(fname)
+ fpath = os.path.join(self.metadata.data, fname)
+ tmpfile = "%s.new" % fpath
+ linkdest = os.path.join(self.metadata.data, "client-link.xml")
+
+ mock_islink.return_value = False
+
+ config.write_xml(fpath, clients_test_tree)
+ mock_open.assert_called_with(tmpfile, "w")
+ self.assertTrue(mock_open.return_value.write.called)
+ mock_islink.assert_called_with(fpath)
+ mock_rename.assert_called_with(tmpfile, fpath)
+
+ mock_islink.return_value = True
+ mock_readlink.return_value = linkdest
+ config.write_xml(fpath, clients_test_tree)
+ mock_rename.assert_called_with(tmpfile, linkdest)
+
+ mock_rename.side_effect = OSError
+ self.assertRaises(MetadataRuntimeError,
+ config.write_xml, fpath, clients_test_tree)
+
+ mock_open.return_value.write.side_effect = IOError
+ self.assertRaises(MetadataRuntimeError,
+ config.write_xml, fpath, clients_test_tree)
+ mock_unlink.assert_called_with(tmpfile)
+
+ mock_open.side_effect = IOError
+ self.assertRaises(MetadataRuntimeError,
+ config.write_xml, fpath, clients_test_tree)
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch('lxml.etree.parse')
+ def test_find_xml_for_xpath(self, mock_parse):
+ config = self.get_config_object("groups.xml")
+ config.basedata = groups_test_tree
+ xpath = "//Group[@name='group1']"
+ self.assertItemsEqual(config.find_xml_for_xpath(xpath),
+ dict(filename=os.path.join(self.metadata.data,
+ "groups.xml"),
+ xmltree=groups_test_tree,
+ xquery=groups_test_tree.xpath(xpath)))
+
+ self.assertEqual(config.find_xml_for_xpath("//boguselement"), dict())
+
+ config.extras = ["foo.xml", "bar.xml", "clients.xml"]
+
+ def parse_side_effect(fname):
+ if fname == os.path.join(self.metadata.data, "clients.xml"):
+ return clients_test_tree
+ else:
+ return lxml.etree.XML("<null/>").getroottree()
+
+ mock_parse.side_effect = parse_side_effect
+ xpath = "//Client[@secure='true']"
+ self.assertItemsEqual(config.find_xml_for_xpath(xpath),
+ dict(filename=os.path.join(self.metadata.data,
+ "clients.xml"),
+ xmltree=clients_test_tree,
+ xquery=clients_test_tree.xpath(xpath)))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml")
+ def test_HandleEvent(self, mock_load_xml):
+ config = self.get_config_object("groups.xml")
+ evt = Mock()
+ evt.filename = os.path.join(self.metadata.data, "groups.xml")
+ evt.code2str = Mock(return_value="changed")
+ self.assertTrue(config.HandleEvent(evt))
+ mock_load_xml.assert_called_with()
+
+
+class TestClientMetadata(unittest.TestCase):
+ def test_inGroup(self):
+ cm = ClientMetadata("client1", "group1", ["group1", "group2"],
+ ["bundle1"], [], [], [], None, None, None)
+ self.assertTrue(cm.inGroup("group1"))
+ self.assertFalse(cm.inGroup("group3"))
+
+
+class TestMetadata(unittest.TestCase):
+ def test__init_no_fam(self):
+ # test with watch_clients=False
+ core = Mock()
+ metadata = get_metadata_object(core=core)
+ self.check_metadata_object(metadata)
+ self.assertEqual(metadata.states, dict())
+
+ def test__init_with_fam(self):
+ # test with watch_clients=True
+ core = Mock()
+ core.fam = Mock()
+ metadata = get_metadata_object(core=core, watch_clients=True)
+ self.assertEqual(len(metadata.states), 2)
+ core.fam.AddMonitor.assert_any_call(os.path.join(metadata.data,
+ "groups.xml"),
+ metadata)
+ core.fam.AddMonitor.assert_any_call(os.path.join(metadata.data,
+ "clients.xml"),
+ metadata)
+
+ core.fam.reset_mock()
+ core.fam.AddMonitor = Mock(side_effect=IOError)
+ self.assertRaises(Bcfg2.Server.Plugin.PluginInitError,
+ get_metadata_object,
+ core=core, watch_clients=True)
+
+ def check_metadata_object(self, metadata):
+ self.assertIsInstance(metadata, Bcfg2.Server.Plugin.Plugin)
+ self.assertIsInstance(metadata, Bcfg2.Server.Plugin.Metadata)
+ self.assertIsInstance(metadata, Bcfg2.Server.Plugin.Statistics)
+ self.assertIsInstance(metadata.clients_xml, XMLMetadataConfig)
+ self.assertIsInstance(metadata.groups_xml, XMLMetadataConfig)
+ self.assertIsInstance(metadata.query, MetadataQuery)
+
+ @patch('os.makedirs', Mock())
+ @patch('__builtin__.open')
+ def test_init_repo(self, mock_open):
+ groups = "groups %s"
+ os_selection = "os"
+ clients = "clients %s"
+ Metadata.init_repo(datastore, groups, os_selection, clients)
+ mock_open.assert_any_call(os.path.join(datastore, "Metadata",
+ "groups.xml"), "w")
+ mock_open.assert_any_call(os.path.join(datastore, "Metadata",
+ "clients.xml"), "w")
+
+ @patch('lxml.etree.parse')
+ def test_get_groups(self, mock_parse):
+ metadata = get_metadata_object()
+ mock_parse.return_value = groups_test_tree
+ groups = metadata.get_groups()
+ mock_parse.assert_called_with(os.path.join(datastore, "Metadata",
+ "groups.xml"))
+ self.assertIsInstance(groups, lxml.etree._Element)
+
+ def test_search_xdata_name(self):
+ # test finding a node with the proper name
+ metadata = get_metadata_object()
+ tree = groups_test_tree
+ res = metadata._search_xdata("Group", "group1", tree)
+ self.assertIsInstance(res, lxml.etree._Element)
+ self.assertEqual(res.get("name"), "group1")
+
+ def test_search_xdata_alias(self):
+ # test finding a node with the wrong name but correct alias
+ metadata = get_metadata_object()
+ tree = clients_test_tree
+ res = metadata._search_xdata("Client", "alias3", tree, alias=True)
+ self.assertIsInstance(res, lxml.etree._Element)
+ self.assertNotEqual(res.get("name"), "alias3")
+
+ def test_search_xdata_not_found(self):
+ # test failure finding a node
+ metadata = get_metadata_object()
+ tree = clients_test_tree
+ res = metadata._search_xdata("Client", "bogus_client", tree, alias=True)
+ self.assertIsNone(res)
+
+ def search_xdata(self, tag, name, tree, alias=False):
+ metadata = get_metadata_object()
+ res = metadata._search_xdata(tag, name, tree, alias=alias)
+ self.assertIsInstance(res, lxml.etree._Element)
+ if not alias:
+ self.assertEqual(res.get("name"), name)
+
+ def test_search_group(self):
+ # test finding a group with the proper name
+ tree = groups_test_tree
+ self.search_xdata("Group", "group1", tree)
+
+ def test_search_bundle(self):
+ # test finding a bundle with the proper name
+ tree = groups_test_tree
+ self.search_xdata("Bundle", "bundle1", tree)
+
+ def test_search_client(self):
+ # test finding a client with the proper name
+ tree = clients_test_tree
+ self.search_xdata("Client", "client1", tree, alias=True)
+ self.search_xdata("Client", "alias1", tree, alias=True)
+
+ def test_add_group(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write = Mock()
+ metadata.groups_xml.data = lxml.etree.XML('<Groups/>').getroottree()
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.add_group("test1", dict())
+ metadata.groups_xml.write.assert_any_call()
+ grp = metadata.search_group("test1", metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test1'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.add_group("test2", dict(foo='bar'))
+ metadata.groups_xml.write.assert_any_call()
+ grp = metadata.search_group("test2", metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test2', foo='bar'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.groups_xml.write.reset_mock()
+ self.assertRaises(MetadataConsistencyError,
+ metadata.add_group,
+ "test1", dict())
+ self.assertFalse(metadata.groups_xml.write.called)
+
+ def test_update_group(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write_xml = Mock()
+ metadata.groups_xml.data = copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.update_group("group1", dict(foo="bar"))
+ grp = metadata.search_group("group1", metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertIn("foo", grp.attrib)
+ self.assertEqual(grp.get("foo"), "bar")
+ self.assertTrue(metadata.groups_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.update_group,
+ "bogus_group", dict())
+
+ def test_remove_group(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write_xml = Mock()
+ metadata.groups_xml.data = copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.remove_group("group5")
+ grp = metadata.search_group("group5", metadata.groups_xml.base_xdata)
+ self.assertIsNone(grp)
+ self.assertTrue(metadata.groups_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.remove_group,
+ "bogus_group")
+
+ def test_add_bundle(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write = Mock()
+ metadata.groups_xml.data = lxml.etree.XML('<Groups/>').getroottree()
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.add_bundle("bundle1")
+ metadata.groups_xml.write.assert_any_call()
+ bundle = metadata.search_bundle("bundle1",
+ metadata.groups_xml.base_xdata)
+ self.assertIsNotNone(bundle)
+ self.assertEqual(bundle.attrib, dict(name='bundle1'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.groups_xml.write.reset_mock()
+ self.assertRaises(MetadataConsistencyError,
+ metadata.add_bundle,
+ "bundle1")
+ self.assertFalse(metadata.groups_xml.write.called)
+
+ def test_remove_bundle(self):
+ metadata = get_metadata_object()
+ metadata.groups_xml.write_xml = Mock()
+ metadata.groups_xml.data = copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+
+ metadata.remove_bundle("bundle1")
+ grp = metadata.search_bundle("bundle1", metadata.groups_xml.base_xdata)
+ self.assertIsNone(grp)
+ self.assertTrue(metadata.groups_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.remove_bundle,
+ "bogus_bundle")
+
+ def test_add_client(self):
+ metadata = get_metadata_object()
+ metadata.clients_xml.write = Mock()
+ metadata.clients_xml.data = lxml.etree.XML('<Clients/>').getroottree()
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.add_client("test1", dict())
+ metadata.clients_xml.write.assert_any_call()
+ grp = metadata.search_client("test1", metadata.clients_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test1'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.add_client("test2", dict(foo='bar'))
+ metadata.clients_xml.write.assert_any_call()
+ grp = metadata.search_client("test2", metadata.clients_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertEqual(grp.attrib, dict(name='test2', foo='bar'))
+
+ # have to call this explicitly -- usually load_xml does this
+ # on FAM events
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.clients_xml.write.reset_mock()
+ self.assertRaises(MetadataConsistencyError,
+ metadata.add_client,
+ "test1", dict())
+ self.assertFalse(metadata.clients_xml.write.called)
+
+ def test_update_client(self):
+ metadata = get_metadata_object()
+ metadata.clients_xml.write_xml = Mock()
+ metadata.clients_xml.data = copy.deepcopy(clients_test_tree)
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+
+ metadata.update_client("client1", dict(foo="bar"))
+ grp = metadata.search_client("client1", metadata.clients_xml.base_xdata)
+ self.assertIsNotNone(grp)
+ self.assertIn("foo", grp.attrib)
+ self.assertEqual(grp.get("foo"), "bar")
+ self.assertTrue(metadata.clients_xml.write_xml.called)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.update_client,
+ "bogus_client", dict())
+
+ def load_clients_data(self, metadata=None, xdata=None):
+ if metadata is None:
+ metadata = get_metadata_object()
+ metadata.clients_xml.data = xdata or copy.deepcopy(clients_test_tree)
+ metadata.clients_xml.basedata = copy.copy(metadata.clients_xml.data)
+ evt = Mock()
+ evt.filename = os.path.join(datastore, "Metadata", "clients.xml")
+ evt.code2str = Mock(return_value="changed")
+ metadata.HandleEvent(evt)
+ return metadata
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml")
+ def test_clients_xml_event(self, mock_load_xml):
+ metadata = get_metadata_object()
+ metadata.profiles = ["group1", "group2"]
+ self.load_clients_data(metadata=metadata)
+ mock_load_xml.assert_any_call()
+ self.assertItemsEqual(metadata.clients,
+ dict([(c.get("name"), c.get("profile"))
+ for c in clients_test_tree.findall("//Client")]))
+ aliases = dict([(a.get("name"), a.getparent().get("name"))
+ for a in clients_test_tree.findall("//Alias")])
+ self.assertItemsEqual(metadata.aliases, aliases)
+
+ raliases = dict([(c.get("name"), set())
+ for c in clients_test_tree.findall("//Client")])
+ for alias in clients_test_tree.findall("//Alias"):
+ raliases[alias.getparent().get("name")].add(alias.get("name"))
+ self.assertItemsEqual(metadata.raliases, raliases)
+
+ self.assertEqual(metadata.bad_clients, dict())
+ self.assertEqual(metadata.secure,
+ [c.get("name")
+ for c in clients_test_tree.findall("//Client[@secure='true']")])
+ self.assertEqual(metadata.floating, ["client1"])
+
+ addresses = dict([(c.get("address"), [])
+ for c in clients_test_tree.findall("//*[@address]")])
+ raddresses = dict()
+ for client in clients_test_tree.findall("//Client[@address]"):
+ addresses[client.get("address")].append(client.get("name"))
+ try:
+ raddresses[client.get("name")].append(client.get("address"))
+ except KeyError:
+ raddresses[client.get("name")] = [client.get("address")]
+ for alias in clients_test_tree.findall("//Alias[@address]"):
+ addresses[alias.get("address")].append(alias.getparent().get("name"))
+ try:
+ raddresses[alias.getparent().get("name")].append(alias.get("address"))
+ except KeyError:
+ raddresses[alias.getparent().get("name")] = alias.get("address")
+
+ self.assertItemsEqual(metadata.addresses, addresses)
+ self.assertItemsEqual(metadata.raddresses, raddresses)
+ self.assertTrue(metadata.states['clients.xml'])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_clients_xml_event_bad_clients(self):
+ metadata = get_metadata_object()
+ metadata.profiles = ["group2"]
+ self.load_clients_data(metadata=metadata)
+ clients = dict()
+ badclients = dict()
+ for client in clients_test_tree.findall("//Client"):
+ if client.get("profile") in metadata.profiles:
+ clients[client.get("name")] = client.get("profile")
+ else:
+ badclients[client.get("name")] = client.get("profile")
+ self.assertItemsEqual(metadata.clients, clients)
+ self.assertItemsEqual(metadata.bad_clients, badclients)
+
+ def load_groups_data(self, metadata=None, xdata=None):
+ if metadata is None:
+ metadata = get_metadata_object()
+ metadata.groups_xml.data = xdata or copy.deepcopy(groups_test_tree)
+ metadata.groups_xml.basedata = copy.copy(metadata.groups_xml.data)
+ evt = Mock()
+ evt.filename = os.path.join(datastore, "Metadata", "groups.xml")
+ evt.code2str = Mock(return_value="changed")
+ metadata.HandleEvent(evt)
+ return metadata
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml")
+ def test_groups_xml_event(self, mock_load_xml):
+ dup_data = copy.deepcopy(groups_test_tree)
+ lxml.etree.SubElement(dup_data.getroot(),
+ "Group", name="group1")
+ metadata = self.load_groups_data(xdata=dup_data)
+ mock_load_xml.assert_any_call()
+ self.assertEqual(metadata.public, ["group1", "group2"])
+ self.assertEqual(metadata.private, ["group3"])
+ self.assertEqual(metadata.profiles, ["group1", "group2"])
+ self.assertItemsEqual(metadata.groups.keys(),
+ [g.get("name")
+ for g in groups_test_tree.findall("/Group")])
+ self.assertEqual(metadata.categories,
+ dict(group1="category1",
+ group2="category1",
+ group3="category2",
+ group4="category1"))
+ self.assertEqual(metadata.default, "group1")
+ self.assertTrue(metadata.states['groups.xml'])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.add_client")
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.update_client")
+ def test_set_profile(self, mock_update_client, mock_add_client):
+ metadata = get_metadata_object()
+ metadata.states['clients.xml'] = False
+ self.assertRaises(MetadataRuntimeError,
+ metadata.set_profile,
+ None, None, None)
+
+ self.load_groups_data(metadata=metadata)
+ self.load_clients_data(metadata=metadata)
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.set_profile,
+ "client1", "group5", None)
+
+ metadata.clients_xml.write = Mock()
+ metadata.set_profile("client1", "group2", None)
+ mock_update_client.assert_called_with("client1", dict(profile="group2"))
+ metadata.clients_xml.write.assert_any_call()
+ self.assertEqual(metadata.clients["client1"], "group2")
+
+ metadata.clients_xml.write.reset_mock()
+ metadata.set_profile("client_new", "group1", None)
+ mock_add_client.assert_called_with("client_new", dict(profile="group1"))
+ metadata.clients_xml.write.assert_any_call()
+ self.assertEqual(metadata.clients["client_new"], "group1")
+
+ metadata.session_cache[('1.2.3.6', None)] = (None, 'client_new2')
+ metadata.clients_xml.write.reset_mock()
+ metadata.set_profile("uuid_new", "group1", ('1.2.3.6', None))
+ mock_add_client.assert_called_with("client_new2",
+ dict(uuid='uuid_new',
+ profile="group1",
+ address='1.2.3.6'))
+ metadata.clients_xml.write.assert_any_call()
+ self.assertEqual(metadata.clients["uuid_new"], "group1")
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("socket.gethostbyaddr")
+ def test_resolve_client(self, mock_gethostbyaddr):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ metadata.session_cache[('1.2.3.3', None)] = (time.time(), 'client3')
+ self.assertEqual(metadata.resolve_client(('1.2.3.3', None)), 'client3')
+
+ self.assertRaises(MetadataConsistencyError,
+ metadata.resolve_client,
+ ('1.2.3.2', None))
+ self.assertEqual(metadata.resolve_client(('1.2.3.1', None)), 'client1')
+
+ metadata.session_cache[('1.2.3.3', None)] = (time.time() - 100,
+ 'client3')
+ self.assertEqual(metadata.resolve_client(('1.2.3.3', None)), 'client3')
+ self.assertEqual(metadata.resolve_client(('1.2.3.3', None),
+ cleanup_cache=True), 'client3')
+ self.assertEqual(metadata.session_cache, dict())
+
+ mock_gethostbyaddr.return_value = ('client6', [], ['1.2.3.6'])
+ self.assertEqual(metadata.resolve_client(('1.2.3.6', None)), 'client6')
+ mock_gethostbyaddr.assert_called_with('1.2.3.6')
+
+ mock_gethostbyaddr.reset_mock()
+ mock_gethostbyaddr.return_value = ('alias3', [], ['1.2.3.7'])
+ self.assertEqual(metadata.resolve_client(('1.2.3.7', None)), 'client4')
+ mock_gethostbyaddr.assert_called_with('1.2.3.7')
+
+ mock_gethostbyaddr.reset_mock()
+ mock_gethostbyaddr.return_value = None
+ mock_gethostbyaddr.side_effect = socket.herror
+ self.assertRaises(MetadataConsistencyError,
+ metadata.resolve_client,
+ ('1.2.3.8', None))
+ mock_gethostbyaddr.assert_called_with('1.2.3.8')
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.write_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.ClientMetadata")
+ def test_get_initial_metadata(self, mock_clientmetadata):
+ metadata = get_metadata_object()
+ metadata.states['clients.xml'] = False
+ self.assertRaises(MetadataRuntimeError,
+ metadata.get_initial_metadata, None)
+
+ self.load_groups_data(metadata=metadata)
+ self.load_clients_data(metadata=metadata)
+
+ metadata.get_initial_metadata("client1")
+ self.assertEqual(mock_clientmetadata.call_args[0][:9],
+ ("client1", "group1", set(["group1"]), set(), set(),
+ set(["1.2.3.1"]), dict(), None, 'password2'))
+
+ metadata.get_initial_metadata("client2")
+ self.assertEqual(mock_clientmetadata.call_args[0][:9],
+ ("client2", "group2", set(["group1", "group2"]),
+ set(["bundle1", "bundle2"]), set(),
+ set(["1.2.3.2"]), dict(category1="group1"),
+ None, None))
+
+ imd = metadata.get_initial_metadata("alias1")
+ self.assertEqual(mock_clientmetadata.call_args[0][:9],
+ ("client3", "group1", set(["group1"]), set(),
+ set(['alias1']), set(["1.2.3.3"]), dict(), 'uuid1',
+ 'password2'))
+
+ imd = metadata.get_initial_metadata("client_new")
+ self.assertEqual(mock_clientmetadata.call_args[0][:9],
+ ("client_new", "group1", set(["group1"]), set(),
+ set(), set(), dict(), None, None))
+
+ metadata.default = None
+ self.assertRaises(MetadataConsistencyError,
+ metadata.get_initial_metadata,
+ "client_new2")
+
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_get_all_group_names(self):
+ metadata = self.load_groups_data()
+ self.assertItemsEqual(metadata.get_all_group_names(),
+ set([g.get("name")
+ for g in groups_test_tree.findall("//Group")]))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_get_all_groups_in_category(self):
+ metadata = self.load_groups_data()
+ self.assertItemsEqual(metadata.get_all_groups_in_category("category1"),
+ set([g.get("name")
+ for g in groups_test_tree.findall("//Group[@category='category1']")]))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_get_client_names_by_profiles(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ self.assertItemsEqual(metadata.get_client_names_by_profiles("group2"),
+ [c.get("name")
+ for c in clients_test_tree.findall("//Client[@profile='group2']")])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_get_client_names_by_groups(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ # this is not the best test in the world, since we mock
+ # core.build_metadata to just build _initial_ metadata, which
+ # is not at all the same thing. it turns out that mocking
+ # this out without starting a Bcfg2 server is pretty
+ # non-trivial, so this works-ish
+ metadata.core.build_metadata = Mock()
+ metadata.core.build_metadata.side_effect = \
+ lambda c: metadata.get_initial_metadata(c)
+ self.assertItemsEqual(metadata.get_client_names_by_groups(["group2"]),
+ [c.get("name")
+ for c in clients_test_tree.findall("//Client[@profile='group2']")])
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_merge_additional_groups(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ imd = metadata.get_initial_metadata("client2")
+
+ # test adding a group excluded by categories
+ oldgroups = imd.groups
+ metadata.merge_additional_groups(imd, ["group4"])
+ self.assertEqual(imd.groups, oldgroups)
+
+ # test adding a private group
+ oldgroups = imd.groups
+ metadata.merge_additional_groups(imd, ["group3"])
+ self.assertEqual(imd.groups, oldgroups)
+
+ # test adding groups with bundles
+ oldgroups = imd.groups
+ oldbundles = imd.bundles
+ metadata.merge_additional_groups(imd, ["group7"])
+ self.assertEqual(imd.groups, oldgroups.union(["group7"]))
+ self.assertEqual(imd.bundles, oldbundles.union(["bundle3"]))
+
+ # test adding multiple groups
+ imd = metadata.get_initial_metadata("client2")
+ oldgroups = imd.groups
+ metadata.merge_additional_groups(imd, ["group6", "group8"])
+ self.assertItemsEqual(imd.groups,
+ oldgroups.union(["group6", "group8", "group9"]))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ def test_merge_additional_data(self):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ imd = metadata.get_initial_metadata("client1")
+
+ # we need to use a unique attribute name for this test. this
+ # is probably overkill, but it works
+ pattern = "connector%d"
+ for i in range(0, 100):
+ connector = pattern % i
+ if not hasattr(imd, connector):
+ break
+ self.assertFalse(hasattr(imd, connector),
+ "Could not find unique connector name to test "
+ "merge_additional_data()")
+
+ metadata.merge_additional_data(imd, connector, "test data")
+ self.assertEqual(getattr(imd, connector), "test data")
+ self.assertIn(connector, imd.connectors)
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.resolve_client")
+ def test_validate_client_address(self, mock_resolve_client):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ self.assertTrue(metadata.validate_client_address("client1",
+ (None, None)))
+ self.assertTrue(metadata.validate_client_address("client2",
+ ("1.2.3.2", None)))
+ self.assertFalse(metadata.validate_client_address("client2",
+ ("1.2.3.8", None)))
+ self.assertTrue(metadata.validate_client_address("client4",
+ ("1.2.3.2", None)))
+ # this is upper case to ensure that case is folded properly in
+ # validate_client_address()
+ mock_resolve_client.return_value = "CLIENT4"
+ self.assertTrue(metadata.validate_client_address("client4",
+ ("1.2.3.7", None)))
+ mock_resolve_client.assert_called_with(("1.2.3.7", None))
+
+ mock_resolve_client.reset_mock()
+ self.assertFalse(metadata.validate_client_address("client5",
+ ("1.2.3.5", None)))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.validate_client_address")
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.resolve_client")
+ def test_AuthenticateConnection(self, mock_resolve_client,
+ mock_validate_client_address):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ metadata.password = "password1"
+
+ cert = dict(subject=[[("commonName", "client1")]])
+ mock_validate_client_address.return_value = False
+ self.assertFalse(metadata.AuthenticateConnection(cert, "root", None,
+ "1.2.3.1"))
+ mock_validate_client_address.return_value = True
+ self.assertTrue(metadata.AuthenticateConnection(cert, "root", None,
+ "1.2.3.1"))
+ # floating cert-auth clients add themselves to the cache
+ self.assertIn("1.2.3.1", metadata.session_cache)
+ self.assertEqual(metadata.session_cache["1.2.3.1"][1], "client1")
+
+ cert = dict(subject=[[("commonName", "client7")]])
+ self.assertTrue(metadata.AuthenticateConnection(cert, "root", None,
+ "1.2.3.4"))
+ # non-floating cert-auth clients do not add themselves to the cache
+ self.assertNotIn("1.2.3.4", metadata.session_cache)
+
+ cert = dict(subject=[[("commonName", "client8")]])
+
+ mock_resolve_client.return_value = "client5"
+ self.assertTrue(metadata.AuthenticateConnection(None, "root",
+ "password1", "1.2.3.8"))
+
+ mock_resolve_client.side_effect = MetadataConsistencyError
+ self.assertFalse(metadata.AuthenticateConnection(None, "root",
+ "password1",
+ "1.2.3.8"))
+
+ # secure mode, no password
+ self.assertFalse(metadata.AuthenticateConnection(None, 'client2', None,
+ "1.2.3.2"))
+
+ self.assertTrue(metadata.AuthenticateConnection(None, 'uuid1',
+ "password1", "1.2.3.3"))
+ # non-root, non-cert clients populate session cache
+ self.assertIn("1.2.3.3", metadata.session_cache)
+ self.assertEqual(metadata.session_cache["1.2.3.3"][1], "client3")
+
+ # use alternate password
+ self.assertTrue(metadata.AuthenticateConnection(None, 'client3',
+ "password2", "1.2.3.3"))
+
+ # test secure mode
+ self.assertFalse(metadata.AuthenticateConnection(None, 'client9',
+ "password1",
+ "1.2.3.9"))
+ self.assertTrue(metadata.AuthenticateConnection(None, 'client9',
+ "password3", "1.2.3.9"))
+
+ self.assertFalse(metadata.AuthenticateConnection(None, "client5",
+ "password2",
+ "1.2.3.7"))
+
+ @patch("Bcfg2.Server.Plugins.Metadata.XMLMetadataConfig.load_xml", Mock())
+ @patch("Bcfg2.Server.Plugins.Metadata.Metadata.update_client")
+ def test_process_statistics(self, mock_update_client):
+ metadata = self.load_clients_data(metadata=self.load_groups_data())
+ md = Mock()
+ md.hostname = "client6"
+ metadata.process_statistics(md, None)
+ mock_update_client.assert_called_with(md.hostname,
+ dict(auth='cert'))
+
+ mock_update_client.reset_mock()
+ md.hostname = "client5"
+ metadata.process_statistics(md, None)
+ self.assertFalse(mock_update_client.called)
+
+ def test_viz(self):
+ pass
diff --git a/tools/bcfg2-export-config b/tools/bcfg2-export-config
index 4e7790a22..f7e939a3e 100755
--- a/tools/bcfg2-export-config
+++ b/tools/bcfg2-export-config
@@ -16,7 +16,6 @@ The metadata information get stored in an index file in the output directory.
The format of the index file is:
user,group,permission,/path/to/file
'''
-__revision__ = '$Revision: 221 $'
import logging, lxml.etree, Bcfg2.Logging, Bcfg2.Server.Core
import Bcfg2.Server.Plugins.Metadata, Bcfg2.Server.Plugin
diff --git a/tools/create-debian-pkglist.py b/tools/create-debian-pkglist.py
index 8e1210582..4b268c6ee 100755
--- a/tools/create-debian-pkglist.py
+++ b/tools/create-debian-pkglist.py
@@ -1,7 +1,6 @@
#!/usr/bin/env python
'''Build debian/ubuntu package indexes'''
-__revision__ = '$Id$'
# Original code from Bcfg2 sources
diff --git a/tools/encap-util-count.sh b/tools/encap-util-count.sh
index 848a8d648..460bb689b 100755
--- a/tools/encap-util-count.sh
+++ b/tools/encap-util-count.sh
@@ -1,5 +1,4 @@
#!/bin/sh
-# $Id$
# This shows a count of encap packages per directory
# Can be useful to make sure you have everything
diff --git a/tools/encap-util-expand.sh b/tools/encap-util-expand.sh
index 2bb3f7f62..5764bdc79 100755
--- a/tools/encap-util-expand.sh
+++ b/tools/encap-util-expand.sh
@@ -1,5 +1,4 @@
#!/bin/sh
-# $Id$
# This gets the encaps out of a makeself .run file
diff --git a/tools/encap-util-place.sh b/tools/encap-util-place.sh
index c3e96ebd8..dedf14448 100755
--- a/tools/encap-util-place.sh
+++ b/tools/encap-util-place.sh
@@ -1,5 +1,4 @@
#!/bin/bash
-# $Id$
# This puts encaps in the right directories, creating the
# directories if needed.
diff --git a/tools/encap-util-xml.sh b/tools/encap-util-xml.sh
index 0fdd75fe4..ae762637f 100755
--- a/tools/encap-util-xml.sh
+++ b/tools/encap-util-xml.sh
@@ -1,5 +1,4 @@
#!/bin/sh
-# $Id$
# This builds the XML Pkgmgr files for the encap directory
# structure created by the place script. It assumes the
diff --git a/tools/hostbasepush.py b/tools/hostbasepush.py
index 070711c82..02b7a582f 100755
--- a/tools/hostbasepush.py
+++ b/tools/hostbasepush.py
@@ -1,7 +1,5 @@
#!/usr/bin/python
-__revision__ = "$Revision: $"
-
import os
import Bcfg2.Client.Proxy
diff --git a/tools/pkgmgr_gen.py b/tools/pkgmgr_gen.py
index 03d36dfc0..7101ba17d 100755
--- a/tools/pkgmgr_gen.py
+++ b/tools/pkgmgr_gen.py
@@ -10,7 +10,6 @@
bcfg2 client drivers. The output can also contain the PackageList
and nested group headers.
"""
-__revision__ = '$Revision: $'
import collections
import datetime
import glob