diff options
-rw-r--r-- | doc/development/option_parsing.txt | 10 | ||||
-rw-r--r-- | misc/bcfg2.spec | 2 | ||||
-rw-r--r-- | reports/reports.wsgi | 9 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/__init__.py | 2 | ||||
-rw-r--r-- | src/lib/Bcfg2/DBSettings.py | 178 | ||||
-rw-r--r-- | src/lib/Bcfg2/Options/Parser.py | 41 | ||||
-rw-r--r-- | src/lib/Bcfg2/Reporting/Storage/DjangoORM.py | 16 | ||||
-rw-r--r-- | src/lib/Bcfg2/Reporting/migrations/0002_convert_perms_to_mode.py | 3 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Admin.py | 24 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Core.py | 22 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Reports/updatefix.py | 3 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/models.py | 4 | ||||
-rw-r--r-- | src/lib/Bcfg2/settings.py | 185 | ||||
-rwxr-xr-x | src/sbin/bcfg2-reports | 20 | ||||
-rw-r--r-- | testsuite/common.py | 12 | ||||
-rwxr-xr-x | tools/upgrade/1.3/migrate_dbstats.py | 1 | ||||
-rwxr-xr-x | tools/upgrade/1.3/migrate_probe_groups_to_db.py | 27 |
17 files changed, 281 insertions, 278 deletions
diff --git a/doc/development/option_parsing.txt b/doc/development/option_parsing.txt index 52da8fced..091f43cdd 100644 --- a/doc/development/option_parsing.txt +++ b/doc/development/option_parsing.txt @@ -67,9 +67,19 @@ There is no required interface for an option component. They may * An ``options`` attribute that is a list of :class:`Bcfg2.Options.Options.Option` objects or option groups. +* A boolean ``parse_first`` attribute; if set to True, the options for + the component are parsed before all other options. This is useful + for, e.g., Django database settings, which must be parsed before + plugins that use Django can be loaded. * A function or static method, ``options_parsed_hook``, that is called when all options have been parsed. (This will be called again if :func:`Bcfg2.Options.Parser.Parser.reparse` is called.) +* A function or static method, ``component_parsed_hook``, that is + called when early option parsing for a given component has + completed. This is *only* called for components with + ``parse_first`` set to True. It is passed a single argument: a + :class:`argparse.Namespace` object containing the complete set of + early options. Options are collected through two primary mechanisms: diff --git a/misc/bcfg2.spec b/misc/bcfg2.spec index 21516c0ed..fc997ebc0 100644 --- a/misc/bcfg2.spec +++ b/misc/bcfg2.spec @@ -712,7 +712,7 @@ sed "s@http://www.w3.org/2001/xml.xsd@file://$(pwd)/schemas/xml.xsd@" \ %config(noreplace) %{_sysconfdir}/sysconfig/bcfg2-server %{_sbindir}/bcfg2-* %dir %{_localstatedir}/lib/%{name} -%{python_sitelib}/Bcfg2/settings.py* +%{python_sitelib}/Bcfg2/DBSettings.py* %{python_sitelib}/Bcfg2/Server %{python_sitelib}/Bcfg2/Reporting %{python_sitelib}/Bcfg2/manage.py* diff --git a/reports/reports.wsgi b/reports/reports.wsgi index 92401d763..c2a2be1ce 100644 --- a/reports/reports.wsgi +++ b/reports/reports.wsgi @@ -1,9 +1,10 @@ import os -import Bcfg2.settings -os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings' +import Bcfg2.Options +import Bcfg2.DBSettings + +Bcfg2.Options.get_parser().parse() + import django.core.handlers.wsgi def application(environ, start_response): - if 'BCFG2_CONFIG_FILE' in environ: - Bcfg2.settings.read_config(cfile=environ['BCFG2_CONFIG_FILE']) return django.core.handlers.wsgi.WSGIHandler()(environ, start_response) diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index bae81f480..2461c1316 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -305,7 +305,7 @@ class Client(object): socket.gaierror, socket.error): err = sys.exc_info()[1] - self.logger.error("Failed to declare version: %s" % err) + self.fatal_error("Failed to declare version: %s" % err) self.run_probes() diff --git a/src/lib/Bcfg2/DBSettings.py b/src/lib/Bcfg2/DBSettings.py new file mode 100644 index 000000000..24835a3e8 --- /dev/null +++ b/src/lib/Bcfg2/DBSettings.py @@ -0,0 +1,178 @@ +""" Django settings for the Bcfg2 server """ + +import os +import sys +import logging +import Bcfg2.Logger +import Bcfg2.Options + +try: + import django + import django.conf + HAS_DJANGO = True +except ImportError: + HAS_DJANGO = False + +# required for reporting +try: + import south # pylint: disable=W0611 + HAS_SOUTH = True +except ImportError: + HAS_SOUTH = False + +settings = dict( # pylint: disable=C0103 + TIME_ZONE=None, + TEMPLATE_DEBUG=False, + DEBUG=False, + ALLOWED_HOSTS=['*'], + MEDIA_URL='/site_media/', + MANAGERS=(('Root', 'root')), + ADMINS=(('Root', 'root')), + # Language code for this installation. All choices can be found + # here: + # http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes + # http://blogs.law.harvard.edu/tech/stories/storyReader$15 + LANGUAGE_CODE='en-us', + SITE_ID=1, + INSTALLED_APPS=('django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.admin', + 'Bcfg2.Server'), + MEDIA_ROOT='', + STATIC_URL='/media/', + # TODO - make this unique + SECRET_KEY='eb5+y%oy-qx*2+62vv=gtnnxg1yig_odu0se5$h0hh#pc*lmo7', + TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader'), + MIDDLEWARE_CLASSES=( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.middleware.doc.XViewMiddleware'), + ROOT_URLCONF='Bcfg2.Reporting.urls', + AUTHENTICATION_BACKENDS=('django.contrib.auth.backends.ModelBackend'), + LOGIN_URL='/login', + SESSION_EXPIRE_AT_BROWSER_CLOSE=True, + TEMPLATE_DIRS=( + '/usr/share/python-support/python-django/django/contrib/admin/' + 'templates/'), + TEMPLATE_CONTEXT_PROCESSORS=( + 'django.contrib.auth.context_processors.auth', + 'django.core.context_processors.debug', + 'django.core.context_processors.i18n', + 'django.core.context_processors.media', + 'django.core.context_processors.request')) + +if HAS_SOUTH: + settings['INSTALLED_APPS'] += ('south', 'Bcfg2.Reporting') +if 'BCFG2_LEGACY_MODELS' in os.environ: + settings['INSTALLED_APPS'] += ('Bcfg2.Server.Reports.reports',) + +if HAS_DJANGO and django.VERSION[0] == 1 and django.VERSION[1] < 3: + settings['CACHE_BACKEND'] = 'locmem:///' +else: + settings['CACHES'] = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + } + } + + +def finalize_django_config(opts=None, silent=False): + """ Perform final Django configuration """ + if opts is None: + opts = Bcfg2.Options.setup + settings['DATABASES'] = dict( + default=dict( + ENGINE="django.db.backends.%s" % opts.db_engine, + NAME=opts.db_name, + USER=opts.db_user, + PASSWORD=opts.db_password, + HOST=opts.db_host, + PORT=opts.db_port, + OPTIONS=opts.db_opts, + SCHEMA=opts.db_schema)) + + settings['TIME_ZONE'] = opts.timezone + + settings['TEMPLATE_DEBUG'] = settings['DEBUG'] = \ + opts.web_debug + if opts.web_debug: + print("Warning: Setting web_debug to True causes extraordinary " + "memory leaks. Only use this setting if you know what " + "you're doing.") + + if opts.web_prefix: + settings['MEDIA_URL'] = \ + opts.web_prefix.rstrip('/') + \ + settings['MEDIA_URL'] + + logger = logging.getLogger() + + logger.debug("Finalizing Django settings: %s" % settings) + try: + django.conf.settings.configure(**settings) + except RuntimeError: + if not silent: + logger.warning("Failed to finalize Django settings: %s" % + sys.exc_info()[1]) + + +class _OptionContainer(object): + """ Container for options loaded at import-time to configure + databases """ + parse_first = True + options = [ + Bcfg2.Options.Common.repository, + Bcfg2.Options.PathOption( + '-W', '--web-config', cf=('reporting', 'config'), + default="/etc/bcfg2-web.conf", + action=Bcfg2.Options.ConfigFileAction, + help='Web interface configuration file'), + Bcfg2.Options.Option( + cf=('database', 'engine'), default='sqlite3', + help='Database engine', dest='db_engine'), + Bcfg2.Options.Option( + cf=('database', 'name'), default='<repository>/etc/bcfg2.sqlite', + help="Database name", dest="db_name"), + Bcfg2.Options.Option( + cf=('database', 'user'), help='Database username', dest='db_user'), + Bcfg2.Options.Option( + cf=('database', 'password'), help='Database password', + dest='db_password'), + Bcfg2.Options.Option( + cf=('database', 'host'), help='Database host', dest='db_host'), + Bcfg2.Options.Option( + cf=('database', 'port'), help='Database port', dest='db_port'), + Bcfg2.Options.Option( + cf=('database', 'schema'), help='Database schema', + dest='db_schema'), + Bcfg2.Options.Option( + cf=('database', 'options'), help='Database options', + dest='db_opts', type=Bcfg2.Options.Types.comma_dict, + default=dict()), + Bcfg2.Options.Option( + cf=('reporting', 'timezone'), help='Django timezone'), + Bcfg2.Options.BooleanOption( + cf=('reporting', 'web_debug'), help='Django debug'), + Bcfg2.Options.Option( + cf=('reporting', 'web_prefix'), help='Web prefix')] + + @staticmethod + def component_parsed_hook(opts): + """ Finalize the Django config after this component's options + are parsed. """ + finalize_django_config(opts=opts) + + @staticmethod + def options_parsed_hook(): + """ Finalize the Django config after all options are parsed. + This is added in case the DBSettings component isn't added + early enough in option parsing to be parsed in the 'early' + phase. Chances are good that things will break if that + happens, but we do our best to be a good citizen. """ + finalize_django_config(silent=True) + +Bcfg2.Options.get_parser().add_component(_OptionContainer) diff --git a/src/lib/Bcfg2/Options/Parser.py b/src/lib/Bcfg2/Options/Parser.py index bede85a1f..9258d000b 100644 --- a/src/lib/Bcfg2/Options/Parser.py +++ b/src/lib/Bcfg2/Options/Parser.py @@ -74,6 +74,11 @@ class Parser(argparse.ArgumentParser): self.namespace = kwargs.pop('namespace', setup) add_base_options = kwargs.pop('add_base_options', True) + #: Flag to indicate that this is the pre-parsing 'early' run + #: for important options like database settings that must be + #: loaded before other components can be. + self._early = kwargs.pop('early', False) + if 'add_help' not in kwargs: kwargs['add_help'] = add_base_options argparse.ArgumentParser.__init__(self, **kwargs) @@ -185,8 +190,8 @@ class Parser(argparse.ArgumentParser): :param argv: The argument list to parse. By default, ``sys.argv[1:]`` is used. This is stored in :attr:`Bcfg2.Options.Parser.argv` for reuse by - :func:`Bcfg2.Options.Parser.reparse`. :type - argv: list + :func:`Bcfg2.Options.Parser.reparse`. + :type argv: list """ if argv is None: argv = sys.argv[1:] @@ -204,7 +209,26 @@ class Parser(argparse.ArgumentParser): self.error("Could not read %s" % bootstrap.config) self.add_config_file(self.configfile.dest, bootstrap.config) - # phase 2: re-parse command line, loading additional + # phase 2: re-parse command line for early options; currently, + # that's database options + if not self._early: + early_opts = argparse.Namespace() + early_parser = Parser(add_help=False, namespace=early_opts, + early=True) + # add the repo option so we can resolve <repository> + # macros + early_parser.add_options([repository]) + early_components = [] + for component in self.components: + if getattr(component, "parse_first", False): + early_components.append(component) + early_parser.add_component(component) + early_parser.parse(self.argv) + for component in early_components: + if hasattr(component, "component_parsed_hook"): + getattr(component, "component_parsed_hook")(early_opts) + + # phase 3: re-parse command line, loading additional # components, until all components have been loaded. On each # iteration, set defaults from config file/environment # variables @@ -216,7 +240,7 @@ class Parser(argparse.ArgumentParser): self._finalize() self._parse_config_options() - # phase 3: fix up <repository> macros + # phase 4: fix up <repository> macros repo = getattr(self.namespace, "repository", repository.default) for attr in dir(self.namespace): value = getattr(self.namespace, attr) @@ -224,10 +248,11 @@ class Parser(argparse.ArgumentParser): setattr(self.namespace, attr, value.replace("<repository>", repo, 1)) - # phase 4: call post-parsing hooks - for component in self.components: - if hasattr(component, "options_parsed_hook"): - getattr(component, "options_parsed_hook")() + # phase 5: call post-parsing hooks + if not self._early: + for component in self.components: + if hasattr(component, "options_parsed_hook"): + getattr(component, "options_parsed_hook")() return self.namespace diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py index 69da9c571..f6f46ee12 100644 --- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py +++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py @@ -2,16 +2,11 @@ The base for the original DjangoORM (DBStats) """ -import os -import traceback from lxml import etree from datetime import datetime from time import strptime - -os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings' -from Bcfg2 import settings - 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 @@ -377,9 +372,6 @@ class DjangoORM(StorageBase): def validate(self): """Validate backend storage. Should be called once when loaded""" - - settings.read_config() - # verify our database schema try: if Bcfg2.Options.setup.debug: @@ -392,9 +384,9 @@ class DjangoORM(StorageBase): management.call_command("migrate", verbosity=vrb, interactive=False) except: - self.logger.error("Failed to update database schema: %s" % - sys.exc_info()[1]) - raise StorageError + msg = "Failed to update database schema: %s" % sys.exc_info()[1] + self.logger.error(msg) + raise StorageError(msg) def GetExtra(self, client): """Fetch extra entries for a client""" diff --git a/src/lib/Bcfg2/Reporting/migrations/0002_convert_perms_to_mode.py b/src/lib/Bcfg2/Reporting/migrations/0002_convert_perms_to_mode.py index 668094cf5..37cdd146c 100644 --- a/src/lib/Bcfg2/Reporting/migrations/0002_convert_perms_to_mode.py +++ b/src/lib/Bcfg2/Reporting/migrations/0002_convert_perms_to_mode.py @@ -3,8 +3,7 @@ import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models - -from Bcfg2 import settings +from django.conf import settings class Migration(SchemaMigration): diff --git a/src/lib/Bcfg2/Server/Admin.py b/src/lib/Bcfg2/Server/Admin.py index c82a6d7fd..6a56657cf 100644 --- a/src/lib/Bcfg2/Server/Admin.py +++ b/src/lib/Bcfg2/Server/Admin.py @@ -15,6 +15,7 @@ import argparse import lxml.etree import Bcfg2.Logger import Bcfg2.Options +import Bcfg2.DBSettings import Bcfg2.Server.Core import Bcfg2.Client.Proxy from Bcfg2.Server.Plugin import PullSource, Generator, MetadataConsistencyError @@ -22,10 +23,9 @@ from Bcfg2.Utils import hostnames2ranges, Executor, safe_input import Bcfg2.Server.Plugins.Metadata try: - import Bcfg2.settings - os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings' from django.core.exceptions import ImproperlyConfigured from django.core import management + import django.conf import Bcfg2.Server.models HAS_DJANGO = True @@ -833,12 +833,11 @@ class _ReportsCmd(AdminCmd): # pylint: disable=W0223 self.reports_classes = () def setup(self): - # this has to be imported after options are parsed, - # because Django finalizes its settings as soon as it's - # loaded, which means that if we import this before - # Bcfg2.settings has been populated, Django gets a null - # configuration, and subsequent updates to Bcfg2.settings - # won't help. + # this has to be imported after options are parsed, because + # Django finalizes its settings as soon as it's loaded, which + # means that if we import this before Bcfg2.DBSettings has + # been populated, Django gets a null configuration, and + # subsequent updates to Bcfg2.DBSettings won't help. import Bcfg2.Reporting.models # pylint: disable=W0621 self.reports_entries = (Bcfg2.Reporting.models.Group, Bcfg2.Reporting.models.Bundle, @@ -884,7 +883,6 @@ if HAS_DJANGO: """ Sync the Django ORM with the configured database """ def run(self, setup): - management.setup_environ(Bcfg2.settings) Bcfg2.Server.models.load_models() try: management.call_command("syncdb", interactive=False, @@ -911,9 +909,9 @@ if HAS_REPORTS: # this has to be imported after options are parsed, # because Django finalizes its settings as soon as it's # loaded, which means that if we import this before - # Bcfg2.settings has been populated, Django gets a null - # configuration, and subsequent updates to Bcfg2.settings - # won't help. + # Bcfg2.DBSettings has been populated, Django gets a null + # configuration, and subsequent updates to + # Bcfg2.DBSettings won't help. from django.db.transaction import commit_on_success self.run = commit_on_success(self.run) @@ -1011,7 +1009,7 @@ if HAS_REPORTS: self.logger.debug("Filtering by maxdate: %s" % maxdate) ipurge = ipurge.filter(timestamp__lt=maxdate) - if Bcfg2.settings.DATABASES['default']['ENGINE'] == \ + if django.conf.settings.DATABASES['default']['ENGINE'] == \ 'django.db.backends.sqlite3': grp_limit = 100 else: diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py index e3a9f7fce..9174878b2 100644 --- a/src/lib/Bcfg2/Server/Core.py +++ b/src/lib/Bcfg2/Server/Core.py @@ -14,7 +14,7 @@ import lxml.etree import Bcfg2.Server import Bcfg2.Logger import Bcfg2.Options -import Bcfg2.settings +import Bcfg2.DBSettings import Bcfg2.Server.Statistics import Bcfg2.Server.FileMonitor from itertools import chain @@ -25,13 +25,19 @@ from Bcfg2.Server.Plugin.interfaces import * # pylint: disable=W0401,W0614 from Bcfg2.Server.Plugin import track_statistics try: + from django.core.exceptions import ImproperlyConfigured + from django.core import management + import django.conf + HAS_DJANGO = True +except ImportError: + HAS_DJANGO = False + +try: import psyco psyco.full() except ImportError: pass -os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings' - def exposed(func): """ Decorator that sets the ``exposed`` attribute of a function to @@ -198,10 +204,6 @@ class Core(object): #: RLock to be held on writes to the backend db self.db_write_lock = threading.RLock() - # generate Django ORM settings. this must be done _before_ we - # load plugins - Bcfg2.settings.read_config() - # mapping of group name => plugin name to record where groups # that are created by Connector plugins came from self._dynamic_groups = dict() @@ -232,9 +234,7 @@ class Core(object): #: Whether or not it's possible to use the Django database #: backend for plugins that have that capability self._database_available = False - if Bcfg2.settings.HAS_DJANGO: - from django.core.exceptions import ImproperlyConfigured - from django.core import management + if HAS_DJANGO: try: management.call_command("syncdb", interactive=False, verbosity=0) @@ -1343,7 +1343,7 @@ class NetworkCore(Core): self.ca = Bcfg2.Options.setup.ca if self._database_available: - db_settings = Bcfg2.settings.DATABASES['default'] + db_settings = django.conf.settings.DATABASES['default'] if (Bcfg2.Options.setup.daemon and Bcfg2.Options.setup.daemon_uid and db_settings['ENGINE'].endswith(".sqlite3") and diff --git a/src/lib/Bcfg2/Server/Reports/updatefix.py b/src/lib/Bcfg2/Server/Reports/updatefix.py index c3fbcd2e9..91c370994 100644 --- a/src/lib/Bcfg2/Server/Reports/updatefix.py +++ b/src/lib/Bcfg2/Server/Reports/updatefix.py @@ -1,5 +1,4 @@ -import Bcfg2.settings - +import Bcfg2.DBSettings from django.db import connection import django.core.management import sys diff --git a/src/lib/Bcfg2/Server/models.py b/src/lib/Bcfg2/Server/models.py index 51cc835dc..7150c245a 100644 --- a/src/lib/Bcfg2/Server/models.py +++ b/src/lib/Bcfg2/Server/models.py @@ -51,9 +51,9 @@ def load_models(plugins=None): """ load models from plugins specified in the config """ # this has to be imported after options are parsed, because Django # finalizes its settings as soon as it's loaded, which means that - # if we import this before Bcfg2.settings has been populated, + # if we import this before Bcfg2.DBSettings has been populated, # Django gets a null configuration, and subsequent updates to - # Bcfg2.settings won't help. + # Bcfg2.DBSettings won't help. from django.db import models global MODELS diff --git a/src/lib/Bcfg2/settings.py b/src/lib/Bcfg2/settings.py deleted file mode 100644 index 7ddf58aed..000000000 --- a/src/lib/Bcfg2/settings.py +++ /dev/null @@ -1,185 +0,0 @@ -""" Django settings for the Bcfg2 server """ - -import os -import Bcfg2.Options - -try: - import django - HAS_DJANGO = True -except ImportError: - HAS_DJANGO = False - -# required for reporting -try: - import south # pylint: disable=W0611 - HAS_SOUTH = True -except ImportError: - HAS_SOUTH = False - -DATABASES = dict(default=dict()) - -TIME_ZONE = None - -TEMPLATE_DEBUG = DEBUG = False - -ALLOWED_HOSTS = ['*'] - -MEDIA_URL = '/site_media/' - -MANAGERS = ADMINS = (('Root', 'root')) - -# Language code for this installation. All choices can be found here: -# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes -# http://blogs.law.harvard.edu/tech/stories/storyReader$15 -LANGUAGE_CODE = 'en-us' - -SITE_ID = 1 - -# TODO - sanitize this -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.admin', - 'Bcfg2.Server', -) -if HAS_SOUTH: - INSTALLED_APPS = INSTALLED_APPS + ( - 'south', - 'Bcfg2.Reporting', - ) -if 'BCFG2_LEGACY_MODELS' in os.environ: - INSTALLED_APPS += ('Bcfg2.Server.Reports.reports',) - -# Imported from Bcfg2.Server.Reports -MEDIA_ROOT = '' - -# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a -# trailing slash. -STATIC_URL = '/media/' - -#TODO - make this unique -# Make this unique, and don't share it with anybody. -SECRET_KEY = 'eb5+y%oy-qx*2+62vv=gtnnxg1yig_odu0se5$h0hh#pc*lmo7' - -if HAS_DJANGO and django.VERSION[0] == 1 and django.VERSION[1] < 3: - CACHE_BACKEND = 'locmem:///' -else: - CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - } - } - -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) - -#TODO - review these. auth and sessions aren't really used -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.middleware.doc.XViewMiddleware', -) - -# TODO - move this to a higher root and dynamically import -ROOT_URLCONF = 'Bcfg2.Reporting.urls' - -# TODO - this isn't usable -# Authentication Settings -AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend') - -LOGIN_URL = '/login' - -SESSION_EXPIRE_AT_BROWSER_CLOSE = True - -TEMPLATE_DIRS = ( - # App loaders should take care of this.. not sure why this is here - '/usr/share/python-support/python-django/django/contrib/admin/templates/', -) - -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.contrib.auth.context_processors.auth', - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.media', - 'django.core.context_processors.request' -) - - -def read_config(): - """ read the config file and set django settings based on it """ - global DEBUG, TEMPLATE_DEBUG, TIME_ZONE, MEDIA_URL # pylint: disable=W0603 - - DATABASES['default'] = \ - dict(ENGINE="django.db.backends.%s" % Bcfg2.Options.setup.db_engine, - NAME=Bcfg2.Options.setup.db_name, - USER=Bcfg2.Options.setup.db_user, - PASSWORD=Bcfg2.Options.setup.db_password, - HOST=Bcfg2.Options.setup.db_host, - PORT=Bcfg2.Options.setup.db_port, - OPTIONS=Bcfg2.Options.setup.db_opts, - SCHEMA=Bcfg2.Options.setup.db_schema) - - TIME_ZONE = Bcfg2.Options.setup.timezone - - TEMPLATE_DEBUG = DEBUG = Bcfg2.Options.setup.web_debug - if DEBUG: - print("Warning: Setting web_debug to True causes extraordinary memory " - "leaks. Only use this setting if you know what you're doing.") - - if Bcfg2.Options.setup.web_prefix: - MEDIA_URL = Bcfg2.Options.setup.web_prefix.rstrip('/') + MEDIA_URL - - -class _OptionContainer(object): - """ Container for options loaded at import-time to configure - databases """ - options = [ - Bcfg2.Options.Common.repository, - Bcfg2.Options.PathOption( - '-W', '--web-config', cf=('reporting', 'config'), - default="/etc/bcfg2-web.conf", - action=Bcfg2.Options.ConfigFileAction, - help='Web interface configuration file'), - Bcfg2.Options.Option( - cf=('database', 'engine'), default='sqlite3', - help='Database engine', dest='db_engine'), - Bcfg2.Options.Option( - cf=('database', 'name'), default='<repository>/etc/bcfg2.sqlite', - help="Database name", dest="db_name"), - Bcfg2.Options.Option( - cf=('database', 'user'), help='Database username', dest='db_user'), - Bcfg2.Options.Option( - cf=('database', 'password'), help='Database password', - dest='db_password'), - Bcfg2.Options.Option( - cf=('database', 'host'), help='Database host', dest='db_host'), - Bcfg2.Options.Option( - cf=('database', 'port'), help='Database port', dest='db_port'), - Bcfg2.Options.Option( - cf=('database', 'schema'), help='Database schema', - dest='db_schema'), - Bcfg2.Options.Option( - cf=('database', 'options'), help='Database options', - dest='db_opts', type=Bcfg2.Options.Types.comma_dict, - default=dict()), - Bcfg2.Options.Option( - cf=('reporting', 'timezone'), help='Django timezone'), - Bcfg2.Options.BooleanOption( - cf=('reporting', 'web_debug'), help='Django debug'), - Bcfg2.Options.Option( - cf=('reporting', 'web_prefix'), help='Web prefix')] - - @staticmethod - def options_parsed_hook(): - """ initialize settings from /etc/bcfg2-web.conf or - /etc/bcfg2.conf, or set up basic defaults. this lets - manage.py work in all cases """ - read_config() - - -Bcfg2.Options.get_parser().add_component(_OptionContainer) diff --git a/src/sbin/bcfg2-reports b/src/sbin/bcfg2-reports index bb45e0009..f38d99435 100755 --- a/src/sbin/bcfg2-reports +++ b/src/sbin/bcfg2-reports @@ -4,25 +4,9 @@ import os import sys import datetime -from optparse import OptionParser, OptionGroup, make_option +import Bcfg2.DBSettings from Bcfg2.Compat import ConfigParser - -try: - import Bcfg2.settings -except ConfigParser.NoSectionError: - print("Your bcfg2.conf is currently missing the [database] section which " - "is necessary for the reporting interface. Please see bcfg2.conf(5) " - "for more details.") - sys.exit(1) - -project_directory = os.path.dirname(Bcfg2.settings.__file__) -project_name = os.path.basename(project_directory) -sys.path.append(os.path.join(project_directory, '..')) -project_module = __import__(project_name, '', '', ['']) -sys.path.pop() -# Set DJANGO_SETTINGS_MODULE appropriately. -os.environ['DJANGO_SETTINGS_MODULE'] = '%s.settings' % project_name - +from optparse import OptionParser, OptionGroup, make_option from Bcfg2.Reporting.models import (Client, BaseEntry) def hosts_by_entry_type(clients, etype, entryspec): diff --git a/testsuite/common.py b/testsuite/common.py index 49035a177..c0dd8b518 100644 --- a/testsuite/common.py +++ b/testsuite/common.py @@ -39,11 +39,9 @@ def set_setup_default(option, value=None): setattr(Bcfg2.Options.setup, option, value) try: - from django.core.management import setup_environ + import django.conf has_django = True - os.environ['DJANGO_SETTINGS_MODULE'] = "Bcfg2.settings" - set_setup_default("db_engine", "sqlite3") set_setup_default("db_name", os.path.join(os.path.dirname(os.path.abspath(__file__)), @@ -58,8 +56,8 @@ try: set_setup_default("web_debug", False) set_setup_default("web_prefix") - import Bcfg2.settings - Bcfg2.settings.read_config() + import Bcfg2.DBSettings + Bcfg2.DBSettings.finalize_django_config() except ImportError: has_django = False @@ -163,12 +161,12 @@ class DBModelTestCase(Bcfg2TestCase): def test_syncdb(self): """ Create the test database and sync the schema """ if self.models: - setup_environ(Bcfg2.settings) import django.core.management django.core.management.call_command("syncdb", interactive=False, verbosity=0) self.assertTrue( - os.path.exists(Bcfg2.settings.DATABASES['default']['NAME'])) + os.path.exists( + django.conf.settings.DATABASES['default']['NAME'])) @skipUnless(has_django, "Django not found, skipping") def test_cleandb(self): diff --git a/tools/upgrade/1.3/migrate_dbstats.py b/tools/upgrade/1.3/migrate_dbstats.py index f52ccab08..ed514289a 100755 --- a/tools/upgrade/1.3/migrate_dbstats.py +++ b/tools/upgrade/1.3/migrate_dbstats.py @@ -2,7 +2,6 @@ import os os.environ['BCFG2_LEGACY_MODELS'] = '1' -os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings' import sys import logging diff --git a/tools/upgrade/1.3/migrate_probe_groups_to_db.py b/tools/upgrade/1.3/migrate_probe_groups_to_db.py index 73339e787..f9abbf982 100755 --- a/tools/upgrade/1.3/migrate_probe_groups_to_db.py +++ b/tools/upgrade/1.3/migrate_probe_groups_to_db.py @@ -4,16 +4,13 @@ and Probe plugins. Does not migrate individual probe return data. Assumes migration to BOTH Metadata and Probe to database backends. """ import os -os.environ['DJANGO_SETTINGS_MODULE'] = 'Bcfg2.settings' - -import lxml.etree import sys +import lxml.etree import Bcfg2.Options +import Bcfg2.DBSettings -from Bcfg2.Server.Plugins.Metadata import MetadataClientModel -from Bcfg2.Server.Plugins.Probes import ProbesGroupsModel -def migrate(xclient): +def migrate(xclient, MetadataClientModel, ProbesGroupsModel): """ Helper to do the migration given a <Client/> XML element """ client_name = xclient.get('name') try: @@ -32,9 +29,11 @@ def migrate(xclient): group_name = xgroup.get('name') cgroups.append(group_name) try: - group = ProbesGroupsModel.objects.get(hostname=client_name, group=group_name) + group = ProbesGroupsModel.objects.get(hostname=client_name, + group=group_name) except ProbesGroupsModel.DoesNotExist: - group = ProbesGroupsModel(hostname=client_name, group=group_name) + group = ProbesGroupsModel(hostname=client_name, + group=group_name) group.save() ProbesGroupsModel.objects.filter( @@ -46,6 +45,7 @@ def migrate(xclient): return False return True + def main(): """ Main """ opts = dict(repo=Bcfg2.Options.SERVER_REPOSITORY) @@ -59,10 +59,15 @@ def main(): except lxml.etree.XMLSyntaxError: err = sys.exc_info()[1] print("Could not parse %s, skipping: %s" % (probefile, err)) - + + # these must be loaded after option parsing is complete + from Bcfg2.Server.Plugins.Metadata import MetadataClientModel + from Bcfg2.Server.Plugins.Probes import ProbesGroupsModel + for xclient in xdata.findall('Client'): - print "Migrating Metadata and Probe groups for %s" % xclient.get('name') - migrate(xclient) + print("Migrating Metadata and Probe groups for %s" % + xclient.get('name')) + migrate(xclient, MetadataClientModel, ProbesGroupsModel) if __name__ == '__main__': sys.exit(main()) |