diff options
36 files changed, 439 insertions, 258 deletions
@@ -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 - <native-yum-libraries>` 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 |