summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.rst (renamed from README)2
-rw-r--r--doc/appendix/guides/import-existing-ssh-keys.txt7
-rw-r--r--doc/man/bcfg2.conf.txt2
-rw-r--r--doc/man/bcfg2.txt2
-rw-r--r--doc/server/plugins/generators/cfg.txt2
-rw-r--r--doc/server/plugins/generators/nagiosgen.txt5
-rw-r--r--doc/server/plugins/generators/packages.txt90
-rw-r--r--doc/server/plugins/generators/sshbase.txt31
-rw-r--r--man/bcfg2.12
-rw-r--r--man/bcfg2.conf.52
-rw-r--r--misc/bcfg2.spec2
-rw-r--r--schemas/pkgtype.xsd7
-rw-r--r--src/lib/Bcfg2/Client/Proxy.py3
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py29
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/Device.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/Pacman.py9
-rw-r--r--src/lib/Bcfg2/DBSettings.py3
-rw-r--r--src/lib/Bcfg2/Options/Parser.py2
-rw-r--r--src/lib/Bcfg2/Reporting/Compat.py14
-rwxr-xr-xsrc/lib/Bcfg2/Reporting/Reports.py2
-rw-r--r--src/lib/Bcfg2/Reporting/Storage/DjangoORM.py37
-rw-r--r--src/lib/Bcfg2/Reporting/urls.py56
-rw-r--r--src/lib/Bcfg2/Reporting/views.py2
-rw-r--r--src/lib/Bcfg2/Server/Admin.py6
-rw-r--r--src/lib/Bcfg2/Server/Info.py4
-rw-r--r--src/lib/Bcfg2/Server/Lint/RequiredAttrs.py166
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAKeyCreator.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py19
-rw-r--r--src/lib/Bcfg2/Server/Plugins/NagiosGen.py8
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Apt.py1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Pac.py145
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Probes.py18
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestMetadata.py3
-rwxr-xr-xtestsuite/install.sh5
-rw-r--r--testsuite/requirements.txt2
36 files changed, 439 insertions, 258 deletions
diff --git a/README b/README.rst
index 9c99d31c1..095378d2c 100644
--- a/README
+++ b/README.rst
@@ -1,5 +1,5 @@
Bcfg2 - A Configuration Management System
------------------------------------------
+=========================================
Bcfg2 (bee-config two) helps system administrators produce a
consistent, reproducible, and verifiable description of their
diff --git a/doc/appendix/guides/import-existing-ssh-keys.txt b/doc/appendix/guides/import-existing-ssh-keys.txt
index 4e2282044..225844448 100644
--- a/doc/appendix/guides/import-existing-ssh-keys.txt
+++ b/doc/appendix/guides/import-existing-ssh-keys.txt
@@ -40,9 +40,12 @@ files explicity:
<!-- requires a version of openssh that can generate ecdsa keys -->
<Path name="/etc/ssh/ssh_host_ecdsa_key"/>
<Path name="/etc/ssh/ssh_host_ecdsa_key.pub"/>
+ <!-- requires a version of openssh that can generate ed25519 keys -->
+ <Path name="/etc/ssh/ssh_host_ed25519_key"/>
+ <Path name="/etc/ssh/ssh_host_ed25519_key.pub"/>
<Path name='/etc/ssh/ssh_host_dsa_key'/>
- <Path name='/etc/ssh/ssh_host_rsa_key'/>
<Path name='/etc/ssh/ssh_host_dsa_key.pub'/>
+ <Path name='/etc/ssh/ssh_host_rsa_key'/>
<Path name='/etc/ssh/ssh_host_rsa_key.pub'/>
<Path name='/etc/ssh/ssh_host_key'/>
<Path name='/etc/ssh/ssh_host_key.pub'/>
@@ -97,7 +100,7 @@ Now, we pull the ssh host key data for the client out of the uploaded
stats and insert it as host-specific copies of these files in
``/var/lib/bcfg2/SSHBase``.::
- for key in ssh_host_ecdsa_key ssh_host_rsa_key ssh_host_dsa_key ssh_host_key; do
+ for key in ssh_host_ed25519_key ssh_host_ecdsa_key ssh_host_rsa_key ssh_host_dsa_key ssh_host_key; do
sudo bcfg2-admin pull <clientname> Path /etc/ssh/$key
sudo bcfg2-admin pull <clientname> Path /etc/ssh/$key.pub
done
diff --git a/doc/man/bcfg2.conf.txt b/doc/man/bcfg2.conf.txt
index 6c801ff1e..f6fb32cf5 100644
--- a/doc/man/bcfg2.conf.txt
+++ b/doc/man/bcfg2.conf.txt
@@ -624,7 +624,7 @@ configuration file.
chaincert
Specifies the location of your ssl chaining certificate. This is
- used when pre-existing certifcate hostfiles are found, so that
+ used when pre-existing certificate hostfiles are found, so that
they can be validated and only regenerated if they no longer
meet the specification. If you’re using a self signing CA this
would be the CA cert that you generated.
diff --git a/doc/man/bcfg2.txt b/doc/man/bcfg2.txt
index 6df4f9b4f..3810b27d4 100644
--- a/doc/man/bcfg2.txt
+++ b/doc/man/bcfg2.txt
@@ -55,7 +55,7 @@ Options
-b bundles Run only the specified colon-delimited set of
bundles.
-c cachefile Cache a copy of the configuration in cachefile.
---ca-cert=cacert Specifiy the path to the SSL CA certificate.
+--ca-cert=cacert Specify the path to the SSL CA certificate.
-d Enable debugging output.
-e When in verbose mode, display extra entry
information.
diff --git a/doc/server/plugins/generators/cfg.txt b/doc/server/plugins/generators/cfg.txt
index 026c33ba2..1b2fec834 100644
--- a/doc/server/plugins/generators/cfg.txt
+++ b/doc/server/plugins/generators/cfg.txt
@@ -655,7 +655,7 @@ paths.
`sslkey.xml`_ for details on how to change the key type and size.)
#. Similarly, create `sslcert.xml`_ in
- ``Cfg/etc/pki/tls/certs/localhost.cfg/``, containing the following:
+ ``Cfg/etc/pki/tls/certs/localhost.crt/``, containing the following:
.. code-block:: xml
diff --git a/doc/server/plugins/generators/nagiosgen.txt b/doc/server/plugins/generators/nagiosgen.txt
index 746adf44c..47bba8cc2 100644
--- a/doc/server/plugins/generators/nagiosgen.txt
+++ b/doc/server/plugins/generators/nagiosgen.txt
@@ -194,7 +194,4 @@ moderation.
``NagiosGen/config.xml`` replaces the files
``Properties/NagiosGen.xml`` and ``NagiosGen/parents.xml`` in older
versions of Bcfg2; your old configs can be migrated using the
-``nagiosgen-convert.py`` tool. The plugin does contain a
-backwards-compatibility layer for those older config files, but
-``NagiosGen/config.xml`` must exist (even if empty) for the plugin to
-function.
+``nagiosgen-convert.py`` tool.
diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt
index eea6c6659..5e14d3be5 100644
--- a/doc/server/plugins/generators/packages.txt
+++ b/doc/server/plugins/generators/packages.txt
@@ -483,6 +483,59 @@ See :ref:`configuration` for more details on these options.
.. _native-yum-libraries:
+Package Groups
+==============
+
+Some packaging systems provide package groups. To include a package
+group, use the :xml:attribute:`PackageStructure:group` attribute of
+the :xml:element:`Package` tag.
+
+pac
+---
+
+.. versionadded:: 1.4.0
+
+Pacman `groups <https://www.archlinux.org/groups/>`_ are supported:
+
+.. code-block:: xml
+
+ <Package group="base"/>
+
+yum
+---
+
+Yum package groups are supported by both the native Yum libraries and
+Bcfg2's internal dependency resolver. You can use either the short
+group ID or the long group name:
+
+.. code-block:: xml
+
+ <Package group="SNMP Support"/>
+ <Package group="system-management-snmp"/>
+
+By default, only those packages considered the "default" packages in a
+group will be installed. You can change this behavior using the
+:xml:attribute:`PackageStructure:type` attribute:
+
+.. code-block:: xml
+
+ <Package group="development" type="optional"/>
+ <Package group="Administration Tools" type="mandatory"/>
+
+Valid values of "type" are:
+
+* ``mandatory``: Only install mandatory packages in the group.
+* ``default``: Install default packages from the group (the default).
+* ``optional`` or ``all``: Install all packages in the group,
+ including mandatory, default, and optional packages.
+
+See :xml:type:`PackageStructure` for details.
+
+You can view the packages in a group by category with the ``yum
+groupinfo`` command. More information about the different levels can
+be found at
+http://fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups#Installation
+
Using Native Yum Libraries
==========================
@@ -546,43 +599,6 @@ generally be overridden:
* ``reposdir`` is set to ``/dev/null`` to prevent the server's Yum
configuration from being read; do not change this.
-Package Groups
---------------
-
-Yum package groups are supported by both the native Yum libraries and
-Bcfg2's internal dependency resolver. To include a package group, use
-the :xml:attribute:`PackageStructure:group` attribute of the
-:xml:element:`Package` tag. You can use either the short group ID or
-the long group name:
-
-.. code-block:: xml
-
- <Package group="SNMP Support"/>
- <Package group="system-management-snmp"/>
-
-By default, only those packages considered the "default" packages in a
-group will be installed. You can change this behavior using the
-:xml:attribute:`PackageStructure:type` attribute:
-
-.. code-block:: xml
-
- <Package group="development" type="optional"/>
- <Package group="Administration Tools" type="mandatory"/>
-
-Valid values of "type" are:
-
-* ``mandatory``: Only install mandatory packages in the group.
-* ``default``: Install default packages from the group (the default).
-* ``optional`` or ``all``: Install all packages in the group,
- including mandatory, default, and optional packages.
-
-See :xml:type:`PackageStructure` for details.
-
-You can view the packages in a group by category with the ``yum
-groupinfo`` command. More information about the different levels can
-be found at
-http://fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups#Installation
-
Abstract Package Tags
---------------------
diff --git a/doc/server/plugins/generators/sshbase.txt b/doc/server/plugins/generators/sshbase.txt
index 540cc1e06..26c1a8121 100644
--- a/doc/server/plugins/generators/sshbase.txt
+++ b/doc/server/plugins/generators/sshbase.txt
@@ -14,8 +14,8 @@ record for the current system.
It has two functions:
-* Generating new ssh keys -- When a client requests a ecdsa, dsa, rsa,
- or v1 key, and there is no existing key in the repository, one is
+* Generating new ssh keys -- When a client requests a key (v1, rsa,
+ ecdsa, etc.), and there is no existing key in the repository, one is
generated.
* Maintaining the ``ssh_known_hosts`` file -- all current known public
@@ -73,6 +73,7 @@ SSHbase currently supports the following key formats:
* RSA2 (``ssh_host_rsa_key``, ``ssh_host_rsa_key.pub``)
* DSA (``ssh_host_dsa_key``, ``ssh_host_dsa_key.pub``)
* ECDSA (``ssh_host_ecdsa_key``, ``ssh_host_ecdsa_key.pub``)
+* Ed25519 (``ssh_host_ed25519_key``, ``ssh_host_ed25519_key.pub``)
Group-specific keys
===================
@@ -143,19 +144,19 @@ control the permissions and other metadata for the keys and
Default permissions are as follows:
-+----------------------------------+-------+-------+------+-----------+----------+----------+
-| File | owner | group | mode | sensitive | paranoid | encoding |
-+==================================+=======+=======+======+===========+==========+==========+
-| ssh_known_hosts | root | root | 0644 | false | false | None |
-+----------------------------------+-------+-------+------+-----------+----------+----------+
-| ssh_host_key | root | root | 0600 | false | false | base64 |
-+----------------------------------+-------+-------+------+-----------+----------+----------+
-| ssh_host_key.pub | root | root | 0644 | false | false | base64 |
-+----------------------------------+-------+-------+------+-----------+----------+----------+
-| ssh_host_[rsa|dsa|ecdsa]_key | root | root | 0600 | false | false | None |
-+----------------------------------+-------+-------+------+-----------+----------+----------+
-| ssh_host_[rsa|dsa|ecdsa]_key.pub | root | root | 0644 | false | false | None |
-+----------------------------------+-------+-------+------+-----------+----------+----------+
++------------------------------------------+-------+-------+------+-----------+----------+----------+
+| File | owner | group | mode | sensitive | paranoid | encoding |
++==========================================+=======+=======+======+===========+==========+==========+
+| ssh_known_hosts | root | root | 0644 | false | false | None |
++------------------------------------------+-------+-------+------+-----------+----------+----------+
+| ssh_host_key | root | root | 0600 | false | false | base64 |
++------------------------------------------+-------+-------+------+-----------+----------+----------+
+| ssh_host_key.pub | root | root | 0644 | false | false | base64 |
++------------------------------------------+-------+-------+------+-----------+----------+----------+
+| ssh_host_[rsa|dsa|ecdsa|ed25519]_key | root | root | 0600 | false | false | None |
++------------------------------------------+-------+-------+------+-----------+----------+----------+
+| ssh_host_[rsa|dsa|ecdsa|ed25519]_key.pub | root | root | 0644 | false | false | None |
++------------------------------------------+-------+-------+------+-----------+----------+----------+
Note that the ``sensitive`` attribute is false, even for private keys,
in order to permit :ref:`pulling with bcfg2-admin
diff --git a/man/bcfg2.1 b/man/bcfg2.1
index 5b9449fda..73985abc0 100644
--- a/man/bcfg2.1
+++ b/man/bcfg2.1
@@ -109,7 +109,7 @@ bundles.
Cache a copy of the configuration in cachefile.
.TP
.BI \-\-ca\-cert\fB= cacert
-Specifiy the path to the SSL CA certificate.
+Specify the path to the SSL CA certificate.
.TP
.B \-d
Enable debugging output.
diff --git a/man/bcfg2.conf.5 b/man/bcfg2.conf.5
index 43a28bad0..87a710760 100644
--- a/man/bcfg2.conf.5
+++ b/man/bcfg2.conf.5
@@ -661,7 +661,7 @@ private key is stored unencrypted.
.TP
.B chaincert
Specifies the location of your ssl chaining certificate. This is
-used when pre\-existing certifcate hostfiles are found, so that
+used when pre\-existing certificate hostfiles are found, so that
they can be validated and only regenerated if they no longer
meet the specification. If you’re using a self signing CA this
would be the CA cert that you generated.
diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec
index 91260c317..443e0d27a 100644
--- a/misc/bcfg2.spec
+++ b/misc/bcfg2.spec
@@ -667,7 +667,7 @@ sed "s@http://www.w3.org/2001/xml.xsd@file://$(pwd)/schemas/xml.xsd@" \
# Required for EL5 and OpenSUSE
%defattr(-,root,root,-)
%endif
-%doc COPYRIGHT LICENSE README
+%doc COPYRIGHT LICENSE README.rst
%{_mandir}/man1/bcfg2.1*
%{_mandir}/man5/bcfg2.conf.5*
%ghost %attr(600,root,root) %config(noreplace,missingok) %{_sysconfdir}/bcfg2.cert
diff --git a/schemas/pkgtype.xsd b/schemas/pkgtype.xsd
index 7ad7606b2..993114a46 100644
--- a/schemas/pkgtype.xsd
+++ b/schemas/pkgtype.xsd
@@ -31,10 +31,9 @@
<xsd:annotation>
<xsd:documentation>
Install the named package group. Package groups are only
- supported for Yum :xml:element:`Source` repositories, and
- only if the :ref:`yum libraries
- &lt;native-yum-libraries&gt;` are in use. Either ``group``
- or :xml:attribute:`PackageStructure:name` must be specified.
+ supported for Pac and Yum :xml:element:`Source`
+ repositories. Either ``group`` or
+ :xml:attribute:`PackageStructure:name` must be specified.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
diff --git a/src/lib/Bcfg2/Client/Proxy.py b/src/lib/Bcfg2/Client/Proxy.py
index f1caa383a..f383911a3 100644
--- a/src/lib/Bcfg2/Client/Proxy.py
+++ b/src/lib/Bcfg2/Client/Proxy.py
@@ -1,3 +1,4 @@
+import os.path
import re
import sys
import time
@@ -202,6 +203,8 @@ class SSLHTTPConnection(httplib.HTTPConnection):
raise Exception("unknown protocol %s" % self.protocol)
if self.ca:
other_side_required = ssl.CERT_REQUIRED
+ if not os.path.isfile(self.ca):
+ self.logger.error("CA specified but none found at %s" % self.ca)
else:
other_side_required = ssl.CERT_NONE
self.logger.warning("No ca is specified. Cannot authenticate the "
diff --git a/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py b/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py
index fc4e16904..f4f1ee4bf 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py
@@ -10,10 +10,10 @@ from Bcfg2.Client.Tools.POSIX.File import POSIXFile
class AugeasCommand(object):
""" Base class for all Augeas command objects """
- def __init__(self, command, augeas_obj, logger):
+ def __init__(self, entry, command, augeas_obj, logger):
self._augeas = augeas_obj
self.command = command
- self.entry = self.command.getparent()
+ self.entry = entry
self.logger = logger
def get_path(self, attr="path"):
@@ -115,8 +115,8 @@ class Remove(AugeasCommand):
class Move(AugeasCommand):
""" Augeas ``move`` command """
- def __init__(self, command, augeas_obj, logger):
- AugeasCommand.__init__(self, command, augeas_obj, logger)
+ def __init__(self, entry, command, augeas_obj, logger):
+ AugeasCommand.__init__(self, entry, command, augeas_obj, logger)
self.source = self.get_path("source")
self.dest = self.get_path("destination")
@@ -131,8 +131,8 @@ class Move(AugeasCommand):
class Set(AugeasCommand):
""" Augeas ``set`` command """
- def __init__(self, command, augeas_obj, logger):
- AugeasCommand.__init__(self, command, augeas_obj, logger)
+ def __init__(self, entry, command, augeas_obj, logger):
+ AugeasCommand.__init__(self, entry, command, augeas_obj, logger)
self.value = self.command.get("value")
def verify(self):
@@ -146,15 +146,15 @@ class Set(AugeasCommand):
class Clear(Set):
""" Augeas ``clear`` command """
- def __init__(self, command, augeas_obj, logger):
- Set.__init__(self, command, augeas_obj, logger)
+ def __init__(self, entry, command, augeas_obj, logger):
+ Set.__init__(self, entry, command, augeas_obj, logger)
self.value = None
class SetMulti(AugeasCommand):
""" Augeas ``setm`` command """
- def __init__(self, command, augeas_obj, logger):
- AugeasCommand.__init__(self, command, augeas_obj, logger)
+ def __init__(self, entry, command, augeas_obj, logger):
+ AugeasCommand.__init__(self, entry, command, augeas_obj, logger)
self.sub = self.command.get("sub")
self.value = self.command.get("value")
self.base = self.get_path("base")
@@ -170,8 +170,8 @@ class SetMulti(AugeasCommand):
class Insert(AugeasCommand):
""" Augeas ``ins`` command """
- def __init__(self, command, augeas_obj, logger):
- AugeasCommand.__init__(self, command, augeas_obj, logger)
+ def __init__(self, entry, command, augeas_obj, logger):
+ AugeasCommand.__init__(self, entry, command, augeas_obj, logger)
self.label = self.command.get("label")
self.where = self.command.get("where", "before")
self.before = self.where == "before"
@@ -230,11 +230,12 @@ class POSIXAugeas(POSIXTool):
objects representing the commands.
"""
rv = []
- for cmd in entry.iterchildren():
+ for cmd in entry:
if cmd.tag == "Initial":
continue
if cmd.tag in globals():
- rv.append(globals()[cmd.tag](cmd, self.get_augeas(entry),
+ rv.append(globals()[cmd.tag](entry, cmd,
+ self.get_augeas(entry),
self.logger))
else:
err = "Augeas: Unknown command %s in %s" % (cmd.tag,
diff --git a/src/lib/Bcfg2/Client/Tools/POSIX/Device.py b/src/lib/Bcfg2/Client/Tools/POSIX/Device.py
index 6237ccce2..e90ecd384 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/Device.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/Device.py
@@ -1,4 +1,4 @@
-""" Handle <Path type='nonexistent' ...> entries """
+""" Handle <Path type='device' ...> entries """
import os
import sys
@@ -6,7 +6,7 @@ from Bcfg2.Client.Tools.POSIX.base import POSIXTool, device_map
class POSIXDevice(POSIXTool):
- """ Handle <Path type='nonexistent' ...> entries """
+ """ Handle <Path type='device' ...> entries """
__req__ = ['name', 'dev_type', 'mode', 'owner', 'group']
def fully_specified(self, entry):
diff --git a/src/lib/Bcfg2/Client/Tools/Pacman.py b/src/lib/Bcfg2/Client/Tools/Pacman.py
index ee4ef35af..fba946bfb 100644
--- a/src/lib/Bcfg2/Client/Tools/Pacman.py
+++ b/src/lib/Bcfg2/Client/Tools/Pacman.py
@@ -24,8 +24,8 @@ class Pacman(Bcfg2.Client.Tools.PkgTool):
def VerifyPackage(self, entry, _):
'''Verify Package status for entry'''
- self.logger.info("VerifyPackage: %s : %s" % (entry.get('name'),
- entry.get('version')))
+ self.logger.debug("VerifyPackage: %s : %s" % (entry.get('name'),
+ entry.get('version')))
if 'version' not in entry.attrib:
self.logger.info("Cannot verify unversioned package %s" %
@@ -42,11 +42,10 @@ class Pacman(Bcfg2.Client.Tools.PkgTool):
return True
else:
entry.set('current_version', self.installed[entry.get('name')])
- self.logger.info("attribname: %s" % (entry.attrib['name']))
- self.logger.info("attribname: %s" % (entry.attrib['name']))
+ self.logger.debug("attribname: %s" % (entry.attrib['name']))
return False
entry.set('current_exists', 'false')
- self.logger.info("attribname: %s" % (entry.attrib['name']))
+ self.logger.debug("attribname: %s" % (entry.attrib['name']))
return False
def Remove(self, packages):
diff --git a/src/lib/Bcfg2/DBSettings.py b/src/lib/Bcfg2/DBSettings.py
index 6409f8b37..254dfa4b8 100644
--- a/src/lib/Bcfg2/DBSettings.py
+++ b/src/lib/Bcfg2/DBSettings.py
@@ -143,6 +143,8 @@ def finalize_django_config(opts=None, silent=False):
setattr(module, name, value)
try:
django.conf.settings.configure(**settings)
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ django.setup() # pylint: disable=E1101
except RuntimeError:
if not silent:
logger.warning("Failed to finalize Django settings: %s" %
@@ -204,7 +206,6 @@ def migrate_databases(**kwargs):
for database in settings['DATABASES']:
logger.debug("Migrating database %s" % (database))
if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
- django.setup() # pylint: disable=E1101
if initial_django_migration(database):
logger.warning(
"No applied django migrations found for database %s. "
diff --git a/src/lib/Bcfg2/Options/Parser.py b/src/lib/Bcfg2/Options/Parser.py
index dd5087f33..ced61c591 100644
--- a/src/lib/Bcfg2/Options/Parser.py
+++ b/src/lib/Bcfg2/Options/Parser.py
@@ -17,7 +17,7 @@ __all__ = ["setup", "OptionParserException", "Parser", "get_parser",
#: circular imports.
repository = PathOption( # pylint: disable=C0103
'-Q', '--repository', cf=('server', 'repository'),
- default='var/lib/bcfg2', help="Server repository path")
+ default='/var/lib/bcfg2', help="Server repository path")
#: A module-level :class:`argparse.Namespace` object that stores all
diff --git a/src/lib/Bcfg2/Reporting/Compat.py b/src/lib/Bcfg2/Reporting/Compat.py
index 9113fdb91..9754314a7 100644
--- a/src/lib/Bcfg2/Reporting/Compat.py
+++ b/src/lib/Bcfg2/Reporting/Compat.py
@@ -13,4 +13,16 @@ try:
from django.conf.urls.defaults import url, patterns
except ImportError:
# Django > 1.6
- from django.conf.urls import url, patterns
+ from django.conf.urls import url
+
+ try:
+ from django.conf.urls import patterns
+ except:
+ # Django > 1.10
+ def patterns(_prefix, urls):
+ url_list = list()
+ for u in urls:
+ if isinstance(url_tuple, (list, tuple)):
+ u = url(*u)
+ url_list.append(u)
+ return url_list
diff --git a/src/lib/Bcfg2/Reporting/Reports.py b/src/lib/Bcfg2/Reporting/Reports.py
index 7ba0265ae..e60b2e82e 100755
--- a/src/lib/Bcfg2/Reporting/Reports.py
+++ b/src/lib/Bcfg2/Reporting/Reports.py
@@ -327,8 +327,6 @@ class CLI(Bcfg2.Options.CommandRegistry):
components=[self])
parser.add_options(self.subcommand_options)
parser.parse()
- if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
- django.setup() # pylint: disable=E1101
def run(self):
""" Run bcfg2-reports """
diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
index ac0cde783..e7b23e572 100644
--- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
+++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
@@ -2,26 +2,38 @@
The base for the original DjangoORM (DBStats)
"""
-from lxml import etree
-from datetime import datetime
+import difflib
import traceback
+from datetime import datetime
from time import strptime
-import Bcfg2.Options
-import Bcfg2.DBSettings
-from Bcfg2.Compat import md5
-from Bcfg2.Reporting.Storage.base import StorageBase, StorageError
-from Bcfg2.Server.Plugin.exceptions import PluginExecutionError
+from lxml import etree
+import sys
+
import django
-from django.core import management
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db.models import FieldDoesNotExist
from django.core.cache import cache
-#Used by GetCurrentEntry
-import difflib
-from Bcfg2.Compat import b64decode
-from Bcfg2.Reporting.models import *
+import Bcfg2.Options
+import Bcfg2.DBSettings
+from Bcfg2.Compat import b64decode, md5
from Bcfg2.Reporting.Compat import transaction
+from Bcfg2.Reporting.Storage.base import StorageBase, StorageError
+from Bcfg2.Server.Plugin.exceptions import PluginExecutionError
+
+
+def load_django_models():
+ """ Load models for Django after option parsing has completed """
+ # pylint: disable=W0602
+ global Interaction, PackageEntry, FilePerms, PathEntry, LinkEntry, \
+ Group, Client, Bundle, TYPE_EXTRA, TYPE_BAD, TYPE_MODIFIED, \
+ FailureEntry, Performance, BaseEntry
+ # pylint: enable=W0602
+
+ from Bcfg2.Reporting.models import \
+ Interaction, PackageEntry, FilePerms, PathEntry, LinkEntry, \
+ Group, Client, Bundle, TYPE_EXTRA, TYPE_BAD, TYPE_MODIFIED, \
+ FailureEntry, Performance, BaseEntry
def get_all_field_names(model):
@@ -42,6 +54,7 @@ class DjangoORM(StorageBase):
type=Bcfg2.Options.Types.size,
help='Reporting file size limit',
default=1024 * 1024)]
+ options_parsed_hook = staticmethod(load_django_models)
def _import_default(self, entry, state, entrytype=None, defaults=None,
mapping=None, boolean=None, xforms=None):
diff --git a/src/lib/Bcfg2/Reporting/urls.py b/src/lib/Bcfg2/Reporting/urls.py
index 3a40cb932..1ff955cad 100644
--- a/src/lib/Bcfg2/Reporting/urls.py
+++ b/src/lib/Bcfg2/Reporting/urls.py
@@ -2,6 +2,7 @@ from Bcfg2.Reporting.Compat import url, patterns # django compat imports
from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import HttpResponsePermanentRedirect
from Bcfg2.Reporting.utils import filteredUrls, paginatedUrls, timeviewUrls
+from Bcfg2.Reporting import views
handler500 = 'Bcfg2.Reporting.views.server_error'
@@ -12,52 +13,39 @@ def newRoot(request):
grid_view = '/grid'
return HttpResponsePermanentRedirect(grid_view)
-urlpatterns = patterns('Bcfg2.Reporting',
+urlpatterns = patterns('',
(r'^$', newRoot),
- url(r'^manage/?$', 'views.client_manage', name='reports_client_manage'),
- url(r'^client/(?P<hostname>[^/]+)/(?P<pk>\d+)/?$', 'views.client_detail', name='reports_client_detail_pk'),
- url(r'^client/(?P<hostname>[^/]+)/?$', 'views.client_detail', name='reports_client_detail'),
- url(r'^element/(?P<entry_type>\w+)/(?P<pk>\d+)/(?P<interaction>\d+)?/?$', 'views.config_item', name='reports_item'),
- url(r'^element/(?P<entry_type>\w+)/(?P<pk>\d+)/?$', 'views.config_item', name='reports_item'),
- url(r'^entry/(?P<entry_type>\w+)/(?P<pk>\w+)/?$', 'views.entry_status', name='reports_entry'),
+ url(r'^manage/?$', views.client_manage, name='reports_client_manage'),
+ url(r'^client/(?P<hostname>[^/]+)/(?P<pk>\d+)/?$', views.client_detail, name='reports_client_detail_pk'),
+ url(r'^client/(?P<hostname>[^/]+)/?$', views.client_detail, name='reports_client_detail'),
+ url(r'^element/(?P<entry_type>\w+)/(?P<pk>\d+)/(?P<interaction>\d+)?/?$', views.config_item, name='reports_item'),
+ url(r'^element/(?P<entry_type>\w+)/(?P<pk>\d+)/?$', views.config_item, name='reports_item'),
+ url(r'^entry/(?P<entry_type>\w+)/(?P<pk>\w+)/?$', views.entry_status, name='reports_entry'),
)
-urlpatterns += patterns('Bcfg2.Reporting',
+urlpatterns += patterns('',
*timeviewUrls(
- (r'^summary/?$', 'views.display_summary', None, 'reports_summary'),
- (r'^timing/?$', 'views.display_timing', None, 'reports_timing'),
- (r'^common/group/(?P<group>[^/]+)/(?P<threshold>\d+)/?$', 'views.common_problems', None, 'reports_common_problems'),
- (r'^common/group/(?P<group>[^/]+)+/?$', 'views.common_problems', None, 'reports_common_problems'),
- (r'^common/(?P<threshold>\d+)/?$', 'views.common_problems', None, 'reports_common_problems'),
- (r'^common/?$', 'views.common_problems', None, 'reports_common_problems'),
+ (r'^summary/?$', views.display_summary, None, 'reports_summary'),
+ (r'^timing/?$', views.display_timing, None, 'reports_timing'),
+ (r'^common/group/(?P<group>[^/]+)/(?P<threshold>\d+)/?$', views.common_problems, None, 'reports_common_problems'),
+ (r'^common/group/(?P<group>[^/]+)+/?$', views.common_problems, None, 'reports_common_problems'),
+ (r'^common/(?P<threshold>\d+)/?$', views.common_problems, None, 'reports_common_problems'),
+ (r'^common/?$', views.common_problems, None, 'reports_common_problems'),
))
-urlpatterns += patterns('Bcfg2.Reporting',
+urlpatterns += patterns('',
*filteredUrls(*timeviewUrls(
- (r'^grid/?$', 'views.client_index', None, 'reports_grid_view'),
+ (r'^grid/?$', views.client_index, None, 'reports_grid_view'),
(r'^detailed/?$',
- 'views.client_detailed_list', None, 'reports_detailed_list'),
- (r'^elements/(?P<item_state>\w+)/?$', 'views.config_item_list', None, 'reports_item_list'),
+ views.client_detailed_list, None, 'reports_detailed_list'),
+ (r'^elements/(?P<item_state>\w+)/?$', views.config_item_list, None, 'reports_item_list'),
)))
-urlpatterns += patterns('Bcfg2.Reporting',
+urlpatterns += patterns('',
*paginatedUrls( *filteredUrls(
(r'^history/?$',
- 'views.render_history_view', None, 'reports_history'),
+ views.render_history_view, None, 'reports_history'),
(r'^history/(?P<hostname>[^/|]+)/?$',
- 'views.render_history_view', None, 'reports_client_history'),
+ views.render_history_view, None, 'reports_client_history'),
)))
-
- # Uncomment this for admin:
- #(r'^admin/', include('django.contrib.admin.urls')),
-
-
-## Uncomment this section if using authentication
-#urlpatterns += patterns('',
-# (r'^login/$', 'django.contrib.auth.views.login',
-# {'template_name': 'auth/login.html'}),
-# (r'^logout/$', 'django.contrib.auth.views.logout',
-# {'template_name': 'auth/logout.html'})
-# )
-
diff --git a/src/lib/Bcfg2/Reporting/views.py b/src/lib/Bcfg2/Reporting/views.py
index 0b8ed65cc..7d60e724a 100644
--- a/src/lib/Bcfg2/Reporting/views.py
+++ b/src/lib/Bcfg2/Reporting/views.py
@@ -186,7 +186,7 @@ def config_item_list(request, item_state, timestamp=None, **kwargs):
lists = []
for etype in ENTRY_TYPES:
ldata = etype.objects.filter(state=state, interaction__in=current_clients)\
- .annotate(num_entries=Count('id')).select_related('linkentry', 'target_perms', 'current_perms')
+ .annotate(num_entries=Count('id')).select_related()
if len(ldata) > 0:
# Property doesn't render properly..
lists.append((etype.ENTRY_TYPE, ldata))
diff --git a/src/lib/Bcfg2/Server/Admin.py b/src/lib/Bcfg2/Server/Admin.py
index a387d53c6..77bca88eb 100644
--- a/src/lib/Bcfg2/Server/Admin.py
+++ b/src/lib/Bcfg2/Server/Admin.py
@@ -666,7 +666,7 @@ bcfg2 = %s
def create_key(self):
"""Creates a bcfg2.key at the directory specifed by keypath."""
cmd = Executor(timeout=120)
- subject = "/C=%s/ST=%s/L=%s/CN=%s'" % (
+ subject = "/C=%s/ST=%s/L=%s/CN=%s" % (
self.data['country'], self.data['state'], self.data['location'],
self.data['shostname'])
key = cmd.run(["openssl", "req", "-batch", "-x509", "-nodes",
@@ -1228,10 +1228,6 @@ class CLI(Bcfg2.Options.CommandRegistry):
components=[self])
parser.add_options(self.subcommand_options)
parser.parse()
- if HAS_DJANGO and django.VERSION[0] == 1 and django.VERSION[1] >= 7:
- # this has been introduced in django 1.7, so pylint fails with
- # older django releases
- django.setup() # pylint: disable=E1101
def run(self):
""" Run bcfg2-admin """
diff --git a/src/lib/Bcfg2/Server/Info.py b/src/lib/Bcfg2/Server/Info.py
index 418d984a1..bf23dc4cb 100644
--- a/src/lib/Bcfg2/Server/Info.py
+++ b/src/lib/Bcfg2/Server/Info.py
@@ -143,9 +143,7 @@ class Debug(InfoCmd):
if setup.cmd_list:
console = InteractiveConsole(locals())
for command in setup.cmd_list.readlines():
- command = command.strip()
- if command:
- console.push(command)
+ console.push(command.rstrip())
if not setup.non_interactive:
print("Dropping to interpreter; press ^D to resume")
self.interpreters[setup.interpreter](self.core.get_locals())
diff --git a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
index ebf4c4954..56b4e7477 100644
--- a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
+++ b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py
@@ -47,70 +47,118 @@ def is_device_mode(val):
return re.match(r'^\d+$', val)
+def is_vcs_type(val):
+ """ Return True if val is a supported vcs type handled by the
+ current client tool """
+ return (val != 'Path' and
+ hasattr(Bcfg2.Client.Tools.VCS.VCS, 'Install%s' % val))
+
+
class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin):
""" Verify attributes for configuration entries that cannot be
verified with an XML schema alone. """
def __init__(self, *args, **kwargs):
Bcfg2.Server.Lint.ServerPlugin.__init__(self, *args, **kwargs)
- self.required_attrs = dict(
- Path=dict(
- device=dict(name=is_filename,
- owner=is_username,
- group=is_username,
- dev_type=lambda v: v in device_map),
- directory=dict(name=is_filename, owner=is_username,
- group=is_username, mode=is_octal_mode),
- file=dict(name=is_filename, owner=is_username,
- group=is_username, mode=is_octal_mode,
- __text__=None),
- hardlink=dict(name=is_filename, to=is_filename),
- symlink=dict(name=is_filename),
- ignore=dict(name=is_filename),
- nonexistent=dict(name=is_filename),
- permissions=dict(name=is_filename, owner=is_username,
- group=is_username, mode=is_octal_mode),
- vcs=dict(vcstype=lambda v: (v != 'Path' and
- hasattr(Bcfg2.Client.Tools.VCS.VCS,
- "Install%s" % v)),
- revision=None, sourceurl=None)),
- Service={"__any__": dict(name=None),
- "smf": dict(name=None, FMRI=None)},
- Action={None: dict(name=None,
- timing=lambda v: v in ['pre', 'post', 'both'],
- when=lambda v: v in ['modified', 'always'],
- status=lambda v: v in ['ignore', 'check'],
- command=None)},
- ACL=dict(
- default=dict(scope=lambda v: v in ['user', 'group'],
- perms=lambda v: re.match(r'^([0-7]|[rwx\-]{0,3}',
- v)),
- access=dict(scope=lambda v: v in ['user', 'group'],
- perms=lambda v: re.match(r'^([0-7]|[rwx\-]{0,3}',
- v)),
- mask=dict(perms=lambda v: re.match(r'^([0-7]|[rwx\-]{0,3}',
- v))),
- Package={"__any__": dict(name=None)},
- SEBoolean={None: dict(name=None,
- value=lambda v: v in ['on', 'off'])},
- SEModule={None: dict(name=None, __text__=None)},
- SEPort={
- None: dict(name=lambda v: re.match(r'^\d+(-\d+)?/(tcp|udp)',
- v),
- selinuxtype=is_selinux_type)},
- SEFcontext={None: dict(name=None, selinuxtype=is_selinux_type)},
- SENode={None: dict(name=lambda v: "/" in v,
- selinuxtype=is_selinux_type,
- proto=lambda v: v in ['ipv6', 'ipv4'])},
- SELogin={None: dict(name=is_username,
- selinuxuser=is_selinux_user)},
- SEUser={None: dict(name=is_selinux_user,
- roles=lambda v: all(is_selinux_user(u)
- for u in " ".split(v)),
- prefix=None)},
- SEInterface={None: dict(name=None, selinuxtype=is_selinux_type)},
- SEPermissive={None: dict(name=is_selinux_type)},
- POSIXGroup={None: dict(name=is_username)},
- POSIXUser={None: dict(name=is_username)})
+ self.required_attrs = {
+ 'Path': {
+ '__any__': {'name': is_filename},
+ 'augeas': {'owner': is_username, 'group': is_username,
+ 'mode': is_octal_mode},
+ 'device': {'owner': is_username, 'group': is_username,
+ 'mode': is_octal_mode,
+ 'dev_type': lambda v: v in device_map},
+ 'directory': {'owner': is_username, 'group': is_username,
+ 'mode': is_octal_mode},
+ 'file': {'owner': is_username, 'group': is_username,
+ 'mode': is_octal_mode, '__text__': None},
+ 'hardlink': {'owner': is_username, 'group': is_username,
+ 'mode': is_octal_mode, 'to': is_filename},
+ 'symlink': {},
+ 'ignore': {},
+ 'nonexistent': {},
+ 'permissions': {'owner': is_username, 'group': is_username,
+ 'mode': is_octal_mode},
+ 'vcs': {'vcstype': is_vcs_type, 'revision': None,
+ 'sourceurl': None},
+ },
+ 'Service': {
+ '__any__': {'name': None},
+ 'smf': {'name': None, 'FMRI': None}
+ },
+ 'Action': {
+ None: {
+ 'name': None,
+ 'timing': lambda v: v in ['pre', 'post', 'both'],
+ 'when': lambda v: v in ['modified', 'always'],
+ 'status': lambda v: v in ['ignore', 'check'],
+ 'command': None,
+ },
+ },
+ 'ACL': {
+ 'default': {
+ 'scope': lambda v: v in ['user', 'group'],
+ 'perms': lambda v: re.match(r'^([0-7]|[rwx\-]{0,3}', v),
+ },
+ 'access': {
+ 'scope': lambda v: v in ['user', 'group'],
+ 'perms': lambda v: re.match(r'^([0-7]|[rwx\-]{0,3}', v),
+ },
+ 'mask': {
+ 'perms': lambda v: re.match(r'^([0-7]|[rwx\-]{0,3}', v),
+ },
+ },
+ 'Package': {
+ '__any__': {'name': None},
+ },
+ 'SEBoolean': {
+ None: {
+ 'name': None,
+ 'value': lambda v: v in ['on', 'off'],
+ },
+ },
+ 'SEModule': {
+ None: {'name': None, '__text__': None},
+ },
+ 'SEPort': {
+ None: {
+ 'name': lambda v: re.match(r'^\d+(-\d+)?/(tcp|udp)', v),
+ 'selinuxtype': is_selinux_type,
+ },
+ },
+ 'SEFcontext': {
+ None: {'name': None, 'selinuxtype': is_selinux_type},
+ },
+ 'SENode': {
+ None: {
+ 'name': lambda v: "/" in v,
+ 'selinuxtype': is_selinux_type,
+ 'proto': lambda v: v in ['ipv6', 'ipv4']
+ },
+ },
+ 'SELogin': {
+ None: {'name': is_username, 'selinuxuser': is_selinux_user},
+ },
+ 'SEUser': {
+ None: {
+ 'name': is_selinux_user,
+ 'roles': lambda v: all(is_selinux_user(u)
+ for u in " ".split(v)),
+ 'prefix': None,
+ },
+ },
+ 'SEInterface': {
+ None: {'name': None, 'selinuxtype': is_selinux_type},
+ },
+ 'SEPermissive': {
+ None: {'name': is_selinux_type},
+ },
+ 'POSIXGroup': {
+ None: {'name': is_username},
+ },
+ 'POSIXUser': {
+ None: {'name': is_username},
+ },
+ }
def Run(self):
self.check_packages()
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAKeyCreator.py
index a158302be..241bce34c 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAKeyCreator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAKeyCreator.py
@@ -19,8 +19,8 @@ class CfgSSLCAKeyCreator(XMLCfgCreator):
self.logger.info("Cfg: Generating new SSL key for %s" % self.name)
spec = self.XMLMatch(metadata)
key = spec.find("Key")
- if not key:
- key = dict()
+ if key is None:
+ key = {}
ktype = key.get('type', 'rsa')
bits = key.get('bits', '2048')
if ktype == 'rsa':
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index 40504e15e..b912d3725 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -21,30 +21,27 @@ from Bcfg2.Compat import MutableMapping, all, any, wraps
# pylint: enable=W0622
from Bcfg2.version import Bcfg2VersionInfo
+try:
+ from django.db import models
+ HAS_DJANGO = True
+except ImportError:
+ HAS_DJANGO = False
+
# pylint: disable=C0103
ClientVersions = None
MetadataClientModel = None
# pylint: enable=C0103
-HAS_DJANGO = False
def load_django_models():
""" Load models for Django after option parsing has completed """
# pylint: disable=W0602
- global MetadataClientModel, ClientVersions, HAS_DJANGO
+ global MetadataClientModel, ClientVersions
# pylint: enable=W0602
- try:
- import django
- from django.db import models
- HAS_DJANGO = True
- except ImportError:
- HAS_DJANGO = False
+ if not HAS_DJANGO:
return
- if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
- django.setup() # pylint: disable=E1101
-
class MetadataClientModel(models.Model, # pylint: disable=W0621
Bcfg2.Server.Plugin.PluginDatabaseModel):
""" django model for storing clients in the database """
diff --git a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
index d3c38ef19..067e2faad 100644
--- a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
+++ b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
@@ -92,17 +92,15 @@ class NagiosGen(Plugin, Generator):
for host in host_configs:
host_data.append(open(host, 'r').read())
- group_list = []
+ used_groups = set(['default'])
for line in "\n".join(host_data).splitlines():
# only include those groups which are actually used
if "hostgroup" in line:
- group_list += line.split()[1].split(',')
-
- group_list = list(set(group_list))
+ used_groups.update(line.split()[1].split(','))
for group in group_configs:
group_name = re.sub("(-group.cfg|.*/(?=[^/]+))", "", group)
- if group_name in group_list:
+ if group_name in used_groups:
groupfile = open(group, 'r')
group_data.append(groupfile.read())
groupfile.close()
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
index 5bcc482af..956cb9f51 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
@@ -86,6 +86,7 @@ class AptSource(Source):
bdeps = dict()
brecs = dict()
bprov = dict()
+ self.pkgnames = set()
self.essentialpkgs = set()
for fname in self.files:
if not self.rawurl:
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
index 0e15d2e15..6fc084cc4 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
@@ -1,10 +1,62 @@
""" Pacman backend for :mod:`Bcfg2.Server.Plugins.Packages` """
+import os
import tarfile
+from Bcfg2.Compat import cPickle
from Bcfg2.Server.Plugins.Packages.Collection import Collection
from Bcfg2.Server.Plugins.Packages.Source import Source
+def parse_db_file(pkgfile):
+ """ Parse a Pacman database file, returning a dictionary with
+ section headings for keys and lists of strings for values.
+ (Reference: ``sync_db_read`` in ``lib/libalpm/be_sync.c``)
+ """
+
+ pkg = {}
+ section = None
+
+ for line in pkgfile:
+ line = line.strip()
+
+ if section is not None:
+ if not line:
+ section = None
+ else:
+ pkg[section].append(line)
+ elif len(line) >= 2 and line[0] == line[-1] == '%':
+ section = line
+ pkg[section] = []
+
+ return pkg
+
+
+def parse_dep(dep):
+ """ Parse a Pacman dependency string, returning the package name,
+ version restriction (or ``None``), and description (or ``None``).
+ (Reference: ``alpm_dep_from_string`` in ``lib/libalpm/deps.c``)
+ """
+
+ rest_desc = dep.split(': ', 1)
+ if len(rest_desc) == 1:
+ rest, desc = rest_desc[0], None
+ else:
+ rest, desc = rest_desc
+
+ # Search for '=' last, since '<=' and '>=' are possible.
+ for symb in ['<', '>', '=']:
+ idx = rest.find(symb)
+ if idx >= 0:
+ name = rest[:idx]
+ version = rest[idx:]
+ break
+ else:
+ name = rest
+ version = None
+
+ return name, version, desc
+
+
class PacCollection(Collection):
""" Handle collections of Pacman sources. This is a no-op object
that simply inherits from
@@ -24,6 +76,10 @@ class PacCollection(Collection):
debug=debug)
__init__.__doc__ = Collection.__init__.__doc__.split(".. -----")[0]
+ @property
+ def __package_groups__(self):
+ return True
+
class PacSource(Source):
""" Handle Pacman sources """
@@ -31,6 +87,25 @@ class PacSource(Source):
#: PacSource sets the ``type`` on Package entries to "pacman"
ptype = 'pacman'
+ def __init__(self, basepath, xsource):
+ self.pacgroups = {}
+
+ Source.__init__(self, basepath, xsource)
+ __init__.__doc__ = Source.__init__.__doc__
+
+ def load_state(self):
+ data = open(self.cachefile, 'rb')
+ (self.pkgnames, self.deps, self.provides,
+ self.recommends, self.pacgroups) = cPickle.load(data)
+ load_state.__doc__ = Source.load_state.__doc__
+
+ def save_state(self):
+ cache = open(self.cachefile, 'wb')
+ cPickle.dump((self.pkgnames, self.deps, self.provides,
+ self.recommends, self.pacgroups), cache, 2)
+ cache.close()
+ save_state.__doc__ = Source.save_state.__doc__
+
@property
def urls(self):
""" A list of URLs to the base metadata file for each
@@ -45,14 +120,12 @@ class PacSource(Source):
else:
raise Exception("PacSource : RAWUrl not supported (yet)")
- def read_files(self):
- bdeps = dict()
- bprov = dict()
-
- depfnames = ['Depends', 'Pre-Depends']
- if self.recommended:
- depfnames.append('Recommends')
-
+ def read_files(self): # pylint: disable=R0912
+ bdeps = {}
+ brecs = {}
+ bprov = {}
+ self.pkgnames = set()
+ self.pacgroups = {}
for fname in self.files:
if not self.rawurl:
barch = [x for x in fname.split('@') if x in self.arches][0]
@@ -62,8 +135,9 @@ class PacSource(Source):
barch = self.arches[0]
if barch not in bdeps:
- bdeps[barch] = dict()
- bprov[barch] = dict()
+ bdeps[barch] = {}
+ brecs[barch] = {}
+ bprov[barch] = {}
try:
self.debug_log("Packages: try to read %s" % fname)
tar = tarfile.open(fname, "r")
@@ -71,11 +145,52 @@ class PacSource(Source):
self.logger.error("Packages: Failed to read file %s" % fname)
raise
+ packages = {}
for tarinfo in tar:
- if tarinfo.isdir():
- self.pkgnames.add(tarinfo.name.rsplit("-", 2)[0])
- self.debug_log("Packages: added %s" %
- tarinfo.name.rsplit("-", 2)[0])
+ if not tarinfo.isfile():
+ continue
+ prefix = os.path.dirname(tarinfo.name)
+ if prefix not in packages:
+ packages[prefix] = {}
+ pkg = parse_db_file(tar.extractfile(tarinfo))
+ packages[prefix].update(pkg)
+
+ for pkg in packages.values():
+ pkgname = pkg['%NAME%'][0]
+ self.pkgnames.add(pkgname)
+ bdeps[barch][pkgname] = []
+ brecs[barch][pkgname] = []
+
+ if '%DEPENDS%' in pkg:
+ for dep in pkg['%DEPENDS%']:
+ dname = parse_dep(dep)[0]
+ bdeps[barch][pkgname].append(dname)
+
+ if '%OPTDEPENDS%' in pkg:
+ for dep in pkg['%OPTDEPENDS%']:
+ dname = parse_dep(dep)[0]
+ brecs[barch][pkgname].append(dname)
+
+ if '%PROVIDES%' in pkg:
+ for dep in pkg['%PROVIDES%']:
+ dname = parse_dep(dep)[0]
+ if dname not in bprov[barch]:
+ bprov[barch][dname] = set()
+ bprov[barch][dname].add(pkgname)
+
+ if '%GROUPS%' in pkg:
+ for group in pkg['%GROUPS%']:
+ if group not in self.pacgroups:
+ self.pacgroups[group] = []
+ self.pacgroups[group].append(pkgname)
+
tar.close()
- self.process_files(bdeps, bprov)
+ self.process_files(bdeps, bprov, brecs)
read_files.__doc__ = Source.read_files.__doc__
+
+ def get_group(self, metadata, group, ptype=None):
+ try:
+ return self.pacgroups[group]
+ except KeyError:
+ return []
+ get_group.__doc__ = Source.get_group.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py
index 736cdcdd4..4938efb94 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py
@@ -56,6 +56,7 @@ class PkgngSource(Source):
def read_files(self):
bdeps = dict()
+ self.pkgnames = set()
for fname in self.files:
if not self.rawurl:
abi = [x
diff --git a/src/lib/Bcfg2/Server/Plugins/Probes.py b/src/lib/Bcfg2/Server/Plugins/Probes.py
index 33b0d4284..ae4ea4cb1 100644
--- a/src/lib/Bcfg2/Server/Plugins/Probes.py
+++ b/src/lib/Bcfg2/Server/Plugins/Probes.py
@@ -15,6 +15,12 @@ import Bcfg2.Server.FileMonitor
from Bcfg2.Logger import Debuggable
from Bcfg2.Server.Statistics import track_statistics
+try:
+ from django.db import models
+ HAS_DJANGO = True
+except ImportError:
+ HAS_DJANGO = False
+
HAS_DJANGO = False
# pylint: disable=C0103
ProbesDataModel = None
@@ -25,20 +31,12 @@ ProbesGroupsModel = None
def load_django_models():
""" Load models for Django after option parsing has completed """
# pylint: disable=W0602
- global ProbesDataModel, ProbesGroupsModel, HAS_DJANGO
+ global ProbesDataModel, ProbesGroupsModel
# pylint: enable=W0602
- try:
- import django
- from django.db import models
- HAS_DJANGO = True
- except ImportError:
- HAS_DJANGO = False
+ if not HAS_DJANGO:
return
- if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
- django.setup() # pylint: disable=E1101
-
class ProbesDataModel(models.Model, # pylint: disable=W0621,W0612
Bcfg2.Server.Plugin.PluginDatabaseModel):
""" The database model for storing probe data """
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestMetadata.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestMetadata.py
index 5d7ed50b7..8b9a631c1 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestMetadata.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestMetadata.py
@@ -302,8 +302,7 @@ class TestXMLMetadataConfig(TestXMLFileBacked):
self.assertIsNotNone(config.basedata)
reset()
- mock_parse.side_effect = lxml.etree.XMLSyntaxError(None, None, None,
- None)
+ mock_parse.side_effect = lxml.etree.XMLSyntaxError(None, 0, 0, 0)
config.load_xml()
mock_parse.assert_called_with(os.path.join(config.basedir,
"clients.xml"),
diff --git a/testsuite/install.sh b/testsuite/install.sh
index d8e5079be..4d8778ad7 100755
--- a/testsuite/install.sh
+++ b/testsuite/install.sh
@@ -17,8 +17,7 @@ if [[ "$WITH_OPTIONAL_DEPS" == "yes" ]]; then
sudo apt-get install -y yum libaugeas0 augeas-lenses libacl1-dev libssl-dev \
python-gamin python-selinux
- pip install PyYAML pyinotify boto pylibacl Jinja2 mercurial guppy cherrypy
- easy_install https://fedorahosted.org/released/python-augeas/python-augeas-0.4.1.tar.gz
+ pip install PyYAML pyinotify boto pylibacl Jinja2 mercurial guppy cherrypy python-augeas
if [[ ${PYVER:0:1} == "2" ]]; then
pip install cheetah m2crypto
@@ -26,7 +25,7 @@ if [[ "$WITH_OPTIONAL_DEPS" == "yes" ]]; then
if [[ $PYVER != "2.7" ]]; then
pip install 'django<1.7' 'South<0.8'
else
- pip install 'django<1.10'
+ pip install django
fi
fi
fi
diff --git a/testsuite/requirements.txt b/testsuite/requirements.txt
index d67c64db6..0d8c297aa 100644
--- a/testsuite/requirements.txt
+++ b/testsuite/requirements.txt
@@ -1,7 +1,7 @@
lxml
nose
mock
-sphinx
+sphinx<1.5
pylint<0.29
pep8
python-daemon<2.0.0