summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Bcfg2')
-rw-r--r--src/lib/Bcfg2/Client/Proxy.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/APT.py56
-rw-r--r--src/lib/Bcfg2/Client/Tools/BundleDeps.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/Chkconfig.py8
-rw-r--r--src/lib/Bcfg2/Client/Tools/DebInit.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/Dummy.py16
-rw-r--r--src/lib/Bcfg2/Client/Tools/FreeBSDInit.py1
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py38
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/Device.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/File.py20
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/__init__.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/base.py69
-rw-r--r--src/lib/Bcfg2/Client/Tools/Pacman.py11
-rw-r--r--src/lib/Bcfg2/Client/Tools/Pkgng.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/RPM.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/RcUpdate.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/SYSV.py4
-rw-r--r--src/lib/Bcfg2/Client/Tools/Systemd.py22
-rw-r--r--src/lib/Bcfg2/Client/XML.py2
-rw-r--r--src/lib/Bcfg2/Client/__init__.py19
-rw-r--r--src/lib/Bcfg2/Compat.py6
-rw-r--r--src/lib/Bcfg2/DBSettings.py152
-rw-r--r--src/lib/Bcfg2/Logger.py9
-rw-r--r--src/lib/Bcfg2/Options/Options.py2
-rw-r--r--src/lib/Bcfg2/Options/Parser.py11
-rw-r--r--src/lib/Bcfg2/Options/Subcommands.py38
-rw-r--r--src/lib/Bcfg2/Options/Types.py27
-rw-r--r--src/lib/Bcfg2/Reporting/Collector.py2
-rw-r--r--src/lib/Bcfg2/Reporting/Compat.py14
-rwxr-xr-xsrc/lib/Bcfg2/Reporting/Reports.py198
-rw-r--r--src/lib/Bcfg2/Reporting/Storage/DjangoORM.py74
-rw-r--r--src/lib/Bcfg2/Reporting/migrations/0001_initial.py744
-rw-r--r--src/lib/Bcfg2/Reporting/migrations/0002_convert_perms_to_mode.py181
-rw-r--r--src/lib/Bcfg2/Reporting/migrations/0003_expand_hash_key.py222
-rw-r--r--src/lib/Bcfg2/Reporting/migrations/0004_profile_can_be_null.py164
-rw-r--r--src/lib/Bcfg2/Reporting/migrations/0005_add_selinux_entry_support.py705
-rw-r--r--src/lib/Bcfg2/Reporting/migrations/0006_add_user_group_entry_support.py396
-rw-r--r--src/lib/Bcfg2/Reporting/migrations/0007_add_flag_fields_interaction.py26
-rw-r--r--src/lib/Bcfg2/Reporting/models.py21
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/0001_initial.py465
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/0002_convert_perms_to_mode.py170
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/0003_expand_hash_key.py180
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/0004_profile_can_be_null.py156
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/0005_add_selinux_entry_support.py485
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/0006_add_user_group_entry_support.py340
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/0007_add_flag_fields_interaction.py298
-rw-r--r--src/lib/Bcfg2/Reporting/south_migrations/__init__.py0
-rw-r--r--src/lib/Bcfg2/Reporting/templates/base-timeview.html2
-rw-r--r--src/lib/Bcfg2/Reporting/templates/base.html8
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/detail.html19
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html13
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/index.html5
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/manage.html7
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/common.html4
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html11
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/item.html8
-rw-r--r--src/lib/Bcfg2/Reporting/templates/config_items/listing.html7
-rw-r--r--src/lib/Bcfg2/Reporting/templates/displays/summary.html7
-rw-r--r--src/lib/Bcfg2/Reporting/templates/displays/timing.html7
-rw-r--r--src/lib/Bcfg2/Reporting/templates/widgets/interaction_list.inc6
-rw-r--r--src/lib/Bcfg2/Reporting/templatetags/bcfg2_compat.py14
-rw-r--r--src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py11
-rw-r--r--src/lib/Bcfg2/Reporting/urls.py56
-rw-r--r--src/lib/Bcfg2/Reporting/views.py74
-rw-r--r--src/lib/Bcfg2/Server/Admin.py48
-rw-r--r--src/lib/Bcfg2/Server/BuiltinCore.py21
-rw-r--r--src/lib/Bcfg2/Server/Cache.py17
-rw-r--r--src/lib/Bcfg2/Server/CherrypyCore.py16
-rw-r--r--src/lib/Bcfg2/Server/Core.py53
-rwxr-xr-xsrc/lib/Bcfg2/Server/Encryption.py2
-rw-r--r--src/lib/Bcfg2/Server/FileMonitor/Inotify.py1
-rw-r--r--src/lib/Bcfg2/Server/Info.py97
-rw-r--r--src/lib/Bcfg2/Server/Lint/Bundler.py4
-rw-r--r--src/lib/Bcfg2/Server/Lint/MergeFiles.py3
-rw-r--r--src/lib/Bcfg2/Server/Lint/RequiredAttrs.py166
-rw-r--r--src/lib/Bcfg2/Server/Lint/TemplateHelper.py9
-rw-r--r--src/lib/Bcfg2/Server/Lint/Validate.py3
-rw-r--r--src/lib/Bcfg2/Server/Lint/__init__.py1
-rw-r--r--src/lib/Bcfg2/Server/MultiprocessingCore.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugin/__init__.py1
-rw-r--r--src/lib/Bcfg2/Server/Plugin/helpers.py100
-rw-r--r--src/lib/Bcfg2/Server/Plugins/AWSTags.py5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Bundler.py8
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py39
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgSSLCAKeyCreator.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Defaults.py12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/GroupLogic.py13
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Ldap.py413
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py24
-rw-r--r--src/lib/Bcfg2/Server/Plugins/NagiosGen.py8
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Ohai.py7
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Apt.py22
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Collection.py27
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Dummy.py35
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Layman.py142
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Pac.py145
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Portage.py333
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Source.py134
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py157
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/YumHelper.py47
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/__init__.py75
-rw-r--r--src/lib/Bcfg2/Server/Plugins/PkgVars.py65
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Probes.py31
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Properties.py41
-rw-r--r--src/lib/Bcfg2/Server/Plugins/PuppetENC.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Reporting.py15
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Rules.py27
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSHbase.py35
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TemplateHelper.py26
-rw-r--r--src/lib/Bcfg2/Server/Reports/reports/models.py2
-rw-r--r--src/lib/Bcfg2/Server/Reports/updatefix.py8
-rw-r--r--src/lib/Bcfg2/Server/migrations/0001_initial.py45
-rw-r--r--src/lib/Bcfg2/Server/migrations/__init__.py0
-rw-r--r--src/lib/Bcfg2/Server/models.py29
-rw-r--r--src/lib/Bcfg2/Server/south_migrations/0001_initial.py70
-rw-r--r--src/lib/Bcfg2/Server/south_migrations/__init__.py0
-rw-r--r--src/lib/Bcfg2/Utils.py27
-rwxr-xr-xsrc/lib/Bcfg2/manage.py33
-rw-r--r--src/lib/Bcfg2/version.py2
122 files changed, 5526 insertions, 2773 deletions
diff --git a/src/lib/Bcfg2/Client/Proxy.py b/src/lib/Bcfg2/Client/Proxy.py
index 679b4c52b..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 "
@@ -316,6 +319,7 @@ class ComponentProxy(xmlrpclib.ServerProxy):
help='The time in seconds to wait between retries'),
Bcfg2.Options.Option(
'--ssl-cns', cf=('communication', 'serverCommonNames'),
+ dest="ssl_cns",
type=Bcfg2.Options.Types.colon_list,
help='List of server commonNames')]
diff --git a/src/lib/Bcfg2/Client/Tools/APT.py b/src/lib/Bcfg2/Client/Tools/APT.py
index 5a86e8cd4..77610d9bc 100644
--- a/src/lib/Bcfg2/Client/Tools/APT.py
+++ b/src/lib/Bcfg2/Client/Tools/APT.py
@@ -35,6 +35,7 @@ class APT(Bcfg2.Client.Tools.Tool):
self.debsums = '%s/bin/debsums' % Bcfg2.Options.setup.apt_install_path
self.aptget = '%s/bin/apt-get' % Bcfg2.Options.setup.apt_install_path
self.dpkg = '%s/bin/dpkg' % Bcfg2.Options.setup.apt_install_path
+ self.aptmark = '%s/bin/apt-mark' % Bcfg2.Options.setup.apt_install_path
self.__execs__ = [self.debsums, self.aptget, self.dpkg]
path_entries = os.environ['PATH'].split(':')
@@ -42,10 +43,11 @@ class APT(Bcfg2.Client.Tools.Tool):
if reqdir not in path_entries:
os.environ['PATH'] = os.environ['PATH'] + ':' + reqdir
self.pkgcmd = '%s ' % self.aptget + \
- '-o DPkg::Options::=--force-confold ' + \
- '-o DPkg::Options::=--force-confmiss ' + \
- '--reinstall ' + \
- '--force-yes '
+ '-o DPkg::Options::=--force-confold ' + \
+ '-o DPkg::Options::=--force-confmiss ' + \
+ '--reinstall ' + \
+ '--no-install-recommends ' + \
+ '--force-yes '
if not Bcfg2.Options.setup.debug:
self.pkgcmd += '-q=2 '
self.pkgcmd += '-y install %s'
@@ -68,8 +70,8 @@ class APT(Bcfg2.Client.Tools.Tool):
Bcfg2.Options.setup.apt_etc_path))]
self.nonexistent = [entry.get('name') for struct in config
for entry in struct
- if entry.tag == 'Path' and
- entry.get('type') == 'nonexistent']
+ if (entry.tag == 'Path' and
+ entry.get('type') == 'nonexistent')]
os.environ["DEBIAN_FRONTEND"] = 'noninteractive'
self.actions = {}
if Bcfg2.Options.setup.kevlar and not Bcfg2.Options.setup.dry_run:
@@ -87,6 +89,23 @@ class APT(Bcfg2.Client.Tools.Tool):
except apt.cache.FetchFailedException:
err = sys.exc_info()[1]
self.logger.info("Failed to update APT cache: %s" % err)
+ # mark dependencies as being automatically installed and vice versa
+ mark = []
+ unmark = []
+ try:
+ installed_pkgs = [p.name for p in self.pkg_cache if p.is_installed]
+ except AttributeError:
+ installed_pkgs = [p.name for p in self.pkg_cache if p.isInstalled]
+ for pkg in self.getSupportedEntries():
+ if pkg.get('name') in installed_pkgs:
+ if pkg.get('origin') == 'Packages':
+ mark.append(pkg.get('name'))
+ else:
+ unmark.append(pkg.get('name'))
+ if mark:
+ self.cmd.run("%s markauto %s" % (self.aptmark, (" ".join(mark))))
+ if unmark:
+ self.cmd.run("%s unmarkauto %s" % (self.aptmark, (" ".join(unmark))))
self.pkg_cache = apt.cache.Cache()
def FindExtra(self):
@@ -158,21 +177,23 @@ class APT(Bcfg2.Client.Tools.Tool):
(entry.attrib['name']))
return False
pkgname = entry.get('name')
- if pkgname not in self.pkg_cache or \
- not self.pkg_cache[pkgname].is_installed:
+ if (pkgname not in self.pkg_cache or
+ not self.pkg_cache[pkgname].is_installed):
self.logger.info("Package %s not installed" % (entry.get('name')))
entry.set('current_exists', 'false')
return False
pkg = self.pkg_cache[pkgname]
installed_version = pkg.installed.version
- if entry.get('version') == 'auto':
+ if entry.get('version').startswith('auto'):
if pkg.is_upgradable:
desired_version = pkg.candidate.version
else:
desired_version = installed_version
- elif entry.get('version') == 'any':
+ entry.set('version', "auto: %s" % desired_version)
+ elif entry.get('version').startswith('any'):
desired_version = installed_version
+ entry.set('version', "any: %s" % desired_version)
else:
desired_version = entry.get('version')
if desired_version != installed_version:
@@ -183,9 +204,9 @@ class APT(Bcfg2.Client.Tools.Tool):
return False
else:
# version matches
- if not Bcfg2.Options.setup.quick \
- and entry.get('verify', 'true') == 'true' \
- and checksums:
+ if (not Bcfg2.Options.setup.quick and
+ entry.get('verify', 'true') == 'true' and
+ checksums):
pkgsums = self.VerifyDebsums(entry, modlist)
return pkgsums
return True
@@ -215,7 +236,7 @@ class APT(Bcfg2.Client.Tools.Tool):
self.logger.error("APT has no information about package %s"
% pkgname)
continue
- if pkg.get('version') in ['auto', 'any']:
+ if any([pkg.get('version').startswith(v) for v in ['auto', 'any']]):
try:
ipkgs.append("%s=%s" % (
pkgname,
@@ -236,16 +257,21 @@ class APT(Bcfg2.Client.Tools.Tool):
self.logger.error("Cannot find correct versions of packages:")
self.logger.error(bad_pkgs)
if not ipkgs:
- return
+ return dict()
if not self.cmd.run(self.pkgcmd % (" ".join(ipkgs))):
self.logger.error("APT command failed")
self.pkg_cache = apt.cache.Cache()
self.extra = self.FindExtra()
+ mark = []
states = dict()
for package in packages:
states[package] = self.VerifyPackage(package, [], checksums=False)
if states[package]:
self.modified.append(package)
+ if package.get('origin') == 'Packages':
+ mark.append(package.get('name'))
+ if mark:
+ self.cmd.run("%s markauto %s" % (self.aptmark, (" ".join(mark))))
return states
def VerifyPath(self, entry, _): # pylint: disable=W0613
diff --git a/src/lib/Bcfg2/Client/Tools/BundleDeps.py b/src/lib/Bcfg2/Client/Tools/BundleDeps.py
index aaa090633..c1af3f7f1 100644
--- a/src/lib/Bcfg2/Client/Tools/BundleDeps.py
+++ b/src/lib/Bcfg2/Client/Tools/BundleDeps.py
@@ -28,7 +28,7 @@ class BundleDeps(Bcfg2.Client.Tools.Tool):
bundle_name = entry.get('name')
for bundle in self.config.findall('./Bundle/Bundle'):
- if bundle.get('name') == bundle_name and \
- bundle not in self.modified:
+ if (bundle.get('name') == bundle_name and
+ bundle not in self.modified):
self.modified.append(bundle)
return dict()
diff --git a/src/lib/Bcfg2/Client/Tools/Chkconfig.py b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
index fab142a7c..b1abb376a 100644
--- a/src/lib/Bcfg2/Client/Tools/Chkconfig.py
+++ b/src/lib/Bcfg2/Client/Tools/Chkconfig.py
@@ -88,17 +88,17 @@ class Chkconfig(Bcfg2.Client.Tools.SvcTool):
if bootstatus is not None:
if bootstatus == 'on':
# make sure service is enabled on boot
- bootcmd = '/sbin/chkconfig %s %s' % \
- (entry.get('name'), bootstatus)
+ bootcmd = ('/sbin/chkconfig %s %s' %
+ (entry.get('name'), bootstatus))
elif bootstatus == 'off':
# make sure service is disabled on boot
bootcmd = '/sbin/chkconfig %s %s' % (entry.get('name'),
bootstatus)
bootcmdrv = self.cmd.run(bootcmd).success
- if Bcfg2.Options.setup.servicemode == 'disabled':
+ if Bcfg2.Options.setup.service_mode == 'disabled':
# 'disabled' means we don't attempt to modify running svcs
return bootcmdrv
- buildmode = Bcfg2.Options.setup.servicemode == 'build'
+ buildmode = Bcfg2.Options.setup.service_mode == 'build'
if ((entry.get('status') == 'on' and not buildmode) and
entry.get('current_status') == 'off'):
svccmdrv = self.start_service(entry)
diff --git a/src/lib/Bcfg2/Client/Tools/DebInit.py b/src/lib/Bcfg2/Client/Tools/DebInit.py
index 53e5e7ec6..35768f0fe 100644
--- a/src/lib/Bcfg2/Client/Tools/DebInit.py
+++ b/src/lib/Bcfg2/Client/Tools/DebInit.py
@@ -142,8 +142,8 @@ class DebInit(Bcfg2.Client.Tools.SvcTool):
# 'disabled' means we don't attempt to modify running svcs
return bootcmdrv and seqcmdrv
buildmode = Bcfg2.Options.setup.service_mode == 'build'
- if (entry.get('status') == 'on' and not buildmode) and \
- entry.get('current_status') == 'off':
+ if ((entry.get('status') == 'on' and not buildmode) and
+ entry.get('current_status') == 'off'):
svccmdrv = self.start_service(entry)
elif (entry.get('status') == 'off' or buildmode) and \
entry.get('current_status') == 'on':
diff --git a/src/lib/Bcfg2/Client/Tools/Dummy.py b/src/lib/Bcfg2/Client/Tools/Dummy.py
new file mode 100644
index 000000000..9a96eb904
--- /dev/null
+++ b/src/lib/Bcfg2/Client/Tools/Dummy.py
@@ -0,0 +1,16 @@
+"""This is the Bcfg2 tool for the Dummy package system."""
+
+import re
+import Bcfg2.Client.Tools
+
+
+class Dummy(Bcfg2.Client.Tools.PkgTool):
+ __handles__ = [('Package', 'dummy')]
+ __req__ = {'Package': []}
+ pkgtype = 'dummy'
+
+ def RefreshPackages(self):
+ pass
+
+ def VerifyPackage(self, _entry, _):
+ return True
diff --git a/src/lib/Bcfg2/Client/Tools/FreeBSDInit.py b/src/lib/Bcfg2/Client/Tools/FreeBSDInit.py
index 24bc4cf36..7c25e6804 100644
--- a/src/lib/Bcfg2/Client/Tools/FreeBSDInit.py
+++ b/src/lib/Bcfg2/Client/Tools/FreeBSDInit.py
@@ -42,7 +42,6 @@ class FreeBSDInit(Bcfg2.Client.Tools.SvcTool):
self.logger.debug('Stopping service %s' % service.get('name'))
return self.cmd.run(self.get_svc_command(service, 'onestop'))
-
def VerifyService(self, entry, _):
"""Verify Service status for entry."""
entry.set('target_status', entry.get('status')) # for reporting
diff --git a/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py b/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py
index fc4e16904..bcd695058 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/Augeas.py
@@ -5,15 +5,16 @@ import Bcfg2.Client.XML
from augeas import Augeas
from Bcfg2.Client.Tools.POSIX.base import POSIXTool
from Bcfg2.Client.Tools.POSIX.File import POSIXFile
+from Bcfg2.Compat import all # pylint: disable=W0622
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 +116,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 +132,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 +147,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 +171,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 +231,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,
@@ -248,8 +250,8 @@ class POSIXAugeas(POSIXTool):
for cmd in self.get_commands(entry):
try:
if not cmd.verify():
- err = "Augeas: Command has not been applied to %s: %s" % \
- (entry.get("name"), cmd)
+ err = ("Augeas: Command has not been applied to %s: %s" %
+ (entry.get("name"), cmd))
self.logger.debug(err)
entry.set('qtext', "\n".join([entry.get('qtext', ''),
err]))
@@ -258,8 +260,8 @@ class POSIXAugeas(POSIXTool):
else:
cmd.command.set("verified", "true")
except: # pylint: disable=W0702
- err = "Augeas: Unexpected error verifying %s: %s: %s" % \
- (entry.get("name"), cmd, sys.exc_info()[1])
+ err = ("Augeas: Unexpected error verifying %s: %s: %s" %
+ (entry.get("name"), cmd, sys.exc_info()[1]))
self.logger.error(err)
entry.set('qtext', "\n".join([entry.get('qtext', ''), err]))
rv = False
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/POSIX/File.py b/src/lib/Bcfg2/Client/Tools/POSIX/File.py
index fc445e07c..1f1772d46 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/File.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/File.py
@@ -8,6 +8,7 @@ import tempfile
import Bcfg2.Options
from Bcfg2.Client.Tools.POSIX.base import POSIXTool
from Bcfg2.Compat import unicode, b64encode, b64decode # pylint: disable=W0622
+import Bcfg2.Utils
class POSIXFile(POSIXTool):
@@ -17,21 +18,6 @@ class POSIXFile(POSIXTool):
def fully_specified(self, entry):
return entry.text is not None or entry.get('empty', 'false') == 'true'
- def _is_string(self, strng, encoding):
- """ Returns true if the string contains no ASCII control
- characters and can be decoded from the specified encoding. """
- for char in strng:
- if ord(char) < 9 or ord(char) > 13 and ord(char) < 32:
- return False
- if not hasattr(strng, "decode"):
- # py3k
- return True
- try:
- strng.decode(encoding)
- return True
- except: # pylint: disable=W0702
- return False
-
def _get_data(self, entry):
""" Get a tuple of (<file data>, <is binary>) for the given entry """
is_binary = entry.get('encoding', 'ascii') == 'base64'
@@ -181,8 +167,8 @@ class POSIXFile(POSIXTool):
(entry.get("name"), sys.exc_info()[1]))
return False
if not is_binary:
- is_binary |= not self._is_string(content,
- Bcfg2.Options.setup.encoding)
+ is_binary |= not Bcfg2.Utils.is_string(
+ content, Bcfg2.Options.setup.encoding)
if is_binary:
# don't compute diffs if the file is binary
prompt.append('Binary file, no printable diff')
diff --git a/src/lib/Bcfg2/Client/Tools/POSIX/__init__.py b/src/lib/Bcfg2/Client/Tools/POSIX/__init__.py
index c27c7559d..41bff751d 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/__init__.py
@@ -14,7 +14,7 @@ from Bcfg2.Client.Tools.POSIX.base import POSIXTool
class POSIX(Bcfg2.Client.Tools.Tool):
"""POSIX File support code."""
- options = Bcfg2.Client.Tools.Tool.options + [
+ options = Bcfg2.Client.Tools.Tool.options + POSIXTool.options + [
Bcfg2.Options.PathOption(
cf=('paranoid', 'path'), default='/var/cache/bcfg2',
dest='paranoid_path',
diff --git a/src/lib/Bcfg2/Client/Tools/POSIX/base.py b/src/lib/Bcfg2/Client/Tools/POSIX/base.py
index 8895eaae1..ffa527cd6 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/base.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/base.py
@@ -6,9 +6,11 @@ import pwd
import grp
import stat
import copy
+import errno
import shutil
import Bcfg2.Client.Tools
import Bcfg2.Client.XML
+import Bcfg2.Options
from Bcfg2.Compat import oct_mode
try:
@@ -37,6 +39,22 @@ device_map = dict(block=stat.S_IFBLK, # pylint: disable=C0103
class POSIXTool(Bcfg2.Client.Tools.Tool):
""" Base class for tools that handle POSIX (Path) entries """
+
+ options = [
+ Bcfg2.Options.Option(
+ cf=('POSIX', 'secontext_ignore'),
+ default=['anon_inodefs_t', 'bdev_t', 'binfmt_misc_fs_t',
+ 'capifs_t', 'configfs_t', 'cpusetfs_t', 'ecryptfs_t',
+ 'eventpollfs_t', 'futexfs_t', 'hugetlbfs_t', 'ibmasmfs_t',
+ 'inotifyfs_t', 'mvfs_t', 'nfsd_fs_t', 'oprofilefs_t',
+ 'ramfs_t', 'romfs_t', 'rpc_pipefs_t', 'spufs_t',
+ 'squash_t', 'vmblock_t', 'vxfs_t', 'xenfs_t', 'autofs_t',
+ 'cifs_t', 'dosfs_t', 'fusefs_t', 'iso9660_t',
+ 'removable_t', 'nfs_t'],
+ help='secontext types to ignore labeling errors',
+ type=Bcfg2.Options.Types.colon_list)
+ ]
+
def fully_specified(self, entry): # pylint: disable=W0613
""" return True if the entry is fully specified """
# checking is done by __req__
@@ -112,14 +130,14 @@ class POSIXTool(Bcfg2.Client.Tools.Tool):
% (path,
self._norm_entry_uid(entry),
self._norm_entry_gid(entry)))
- os.chown(path, self._norm_entry_uid(entry),
- self._norm_entry_gid(entry))
+ os.lchown(path, self._norm_entry_uid(entry),
+ self._norm_entry_gid(entry))
except (OSError, KeyError):
self.logger.error('POSIX: Failed to change ownership of %s'
% path)
rv = False
if sys.exc_info()[0] == KeyError:
- os.chown(path, 0, 0)
+ os.lchown(path, 0, 0)
else:
self.logger.debug("POSIX: Run as non-root, not setting ownership")
@@ -272,7 +290,7 @@ class POSIXTool(Bcfg2.Client.Tools.Tool):
rv &= self._apply_acl(defacl, path, posix1e.ACL_TYPE_DEFAULT)
return rv
- def _set_secontext(self, entry, path=None):
+ def _set_secontext(self, entry, path=None): # pylint: disable=R0911
""" set the SELinux context of the file on disk according to the
config"""
if not HAS_SELINUX:
@@ -284,25 +302,28 @@ class POSIXTool(Bcfg2.Client.Tools.Tool):
if not context:
# no context listed
return True
-
- if context == '__default__':
- try:
+ secontext = selinux.lgetfilecon(path)[1].split(":")[2]
+ if secontext in Bcfg2.Options.setup.secontext_ignore:
+ return True
+ try:
+ if context == '__default__':
selinux.restorecon(path)
- rv = True
- except OSError:
- err = sys.exc_info()[1]
- self.logger.error("POSIX: Failed to restore SELinux context "
- "for %s: %s" % (path, err))
- rv = False
- else:
- try:
- rv = selinux.lsetfilecon(path, context) == 0
- except OSError:
- err = sys.exc_info()[1]
- self.logger.error("POSIX: Failed to restore SELinux context "
- "for %s: %s" % (path, err))
- rv = False
- return rv
+ return True
+ else:
+ return selinux.lsetfilecon(path, context) == 0
+ except OSError:
+ err = sys.exc_info()[1]
+ if err.errno == errno.EOPNOTSUPP:
+ # Operation not supported
+ if context != '__default__':
+ self.logger.debug("POSIX: Failed to set SELinux context "
+ "for %s: %s" % (path, err))
+ return False
+ return True
+ err = sys.exc_info()[1]
+ self.logger.error("POSIX: Failed to set or restore SELinux "
+ "context for %s: %s" % (path, err))
+ return False
def _norm_gid(self, gid):
""" This takes a group name or gid and returns the
@@ -541,8 +562,8 @@ class POSIXTool(Bcfg2.Client.Tools.Tool):
except OSError:
errors.append("%s has no default SELinux context" %
entry.get("name"))
- else:
- wanted_secontext = entry.get("secontext")
+ elif entry.get("secontext"):
+ wanted_secontext = entry.get("secontext").split(":")[2]
if (wanted_secontext and
attrib['current_secontext'] != wanted_secontext):
errors.append("SELinux context for path %s is incorrect. "
diff --git a/src/lib/Bcfg2/Client/Tools/Pacman.py b/src/lib/Bcfg2/Client/Tools/Pacman.py
index b82b905e7..fba946bfb 100644
--- a/src/lib/Bcfg2/Client/Tools/Pacman.py
+++ b/src/lib/Bcfg2/Client/Tools/Pacman.py
@@ -5,7 +5,7 @@ import Bcfg2.Client.Tools
class Pacman(Bcfg2.Client.Tools.PkgTool):
- '''Archlinux package support'''
+ '''Arch Linux package support'''
name = 'Pacman'
__execs__ = ["/usr/bin/pacman"]
__handles__ = [('Package', 'pacman')]
@@ -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/Client/Tools/Pkgng.py b/src/lib/Bcfg2/Client/Tools/Pkgng.py
index eef86a131..025bc59be 100644
--- a/src/lib/Bcfg2/Client/Tools/Pkgng.py
+++ b/src/lib/Bcfg2/Client/Tools/Pkgng.py
@@ -205,7 +205,7 @@ class Pkgng(Bcfg2.Client.Tools.Tool):
self.logger.error("Cannot find correct versions of packages:")
self.logger.error(bad_pkgs)
if not ipkgs:
- return
+ return dict()
if not self.cmd.run(self.pkgcmd % (" ".join(ipkgs))):
self.logger.error("pkg command failed")
self._load_pkg_cache()
diff --git a/src/lib/Bcfg2/Client/Tools/RPM.py b/src/lib/Bcfg2/Client/Tools/RPM.py
index 464b7e389..6b379918a 100644
--- a/src/lib/Bcfg2/Client/Tools/RPM.py
+++ b/src/lib/Bcfg2/Client/Tools/RPM.py
@@ -1185,7 +1185,7 @@ class RPM(Bcfg2.Client.Tools.PkgTool):
self.logger.debug('%s: pkg_verify = %s' %
(self.name, Bcfg2.Options.setup.rpm_pkg_verify))
self.logger.debug('%s: install_missing = %s' %
- (self.name, Bcfg2.Options.setup.install_missing))
+ (self.name, Bcfg2.Options.setup.rpm_install_missing))
self.logger.debug('%s: fix_version = %s' %
(self.name, Bcfg2.Options.setup.rpm_fix_version))
self.logger.debug('%s: reinstall_broken = %s' %
diff --git a/src/lib/Bcfg2/Client/Tools/RcUpdate.py b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
index a482dbc00..21257f64b 100644
--- a/src/lib/Bcfg2/Client/Tools/RcUpdate.py
+++ b/src/lib/Bcfg2/Client/Tools/RcUpdate.py
@@ -102,8 +102,8 @@ class RcUpdate(Bcfg2.Client.Tools.SvcTool):
# 'disabled' means we don't attempt to modify running svcs
return bootcmdrv
buildmode = Bcfg2.Options.setup.service_mode == 'build'
- if (entry.get('status') == 'on' and not buildmode) and \
- entry.get('current_status') == 'off':
+ if ((entry.get('status') == 'on' and not buildmode) and
+ entry.get('current_status') == 'off'):
svccmdrv = self.start_service(entry)
elif (entry.get('status') == 'off' or buildmode) and \
entry.get('current_status') == 'on':
diff --git a/src/lib/Bcfg2/Client/Tools/SYSV.py b/src/lib/Bcfg2/Client/Tools/SYSV.py
index 332638de4..4eea0273f 100644
--- a/src/lib/Bcfg2/Client/Tools/SYSV.py
+++ b/src/lib/Bcfg2/Client/Tools/SYSV.py
@@ -119,8 +119,8 @@ class SYSV(Bcfg2.Client.Tools.PkgTool):
self.logger.debug("Package %s not installed" %
entry.get("name"))
else:
- if Bcfg2.Options.setup.quick or \
- entry.attrib.get('verify', 'true') == 'false':
+ if (Bcfg2.Options.setup.quick or
+ entry.attrib.get('verify', 'true') == 'false'):
return True
rv = self.cmd.run("/usr/sbin/pkgchk -n %s" % entry.get('name'))
if rv.success:
diff --git a/src/lib/Bcfg2/Client/Tools/Systemd.py b/src/lib/Bcfg2/Client/Tools/Systemd.py
index f7e5b1b0b..8919e777b 100644
--- a/src/lib/Bcfg2/Client/Tools/Systemd.py
+++ b/src/lib/Bcfg2/Client/Tools/Systemd.py
@@ -2,6 +2,8 @@
"""This is systemd support."""
+import glob
+import os
import Bcfg2.Client.Tools
import Bcfg2.Client.XML
@@ -72,22 +74,34 @@ class Systemd(Bcfg2.Client.Tools.SvcTool):
# Return failure immediately and do not start/stop the service.
return False
- # Start or stop the service, depending on the current servicemode
+ # Start or stop the service, depending on the current service_mode
cmd = None
- if Bcfg2.Options.setup.servicemode == 'disabled':
+ if Bcfg2.Options.setup.service_mode == 'disabled':
# 'disabled' means we don't attempt to modify running svcs
pass
- elif Bcfg2.Options.setup.servicemode == 'build':
+ elif Bcfg2.Options.setup.service_mode == 'build':
# 'build' means we attempt to stop all services started
if entry.get('current_status') == 'on':
cmd = self.get_svc_command(entry, 'stop')
else:
if entry.get('status') == 'on':
cmd = self.get_svc_command(entry, 'start')
- else:
+ elif entry.get('status') == 'off':
cmd = self.get_svc_command(entry, 'stop')
if cmd:
return self.cmd.run(cmd).success
else:
return True
+
+ def FindExtra(self):
+ """Find Extra Systemd Service entries."""
+ specified = [self.get_svc_name(entry)
+ for entry in self.getSupportedEntries()]
+ extra = set()
+ for fname in glob.glob("/etc/systemd/system/*.wants/*"):
+ name = os.path.basename(fname)
+ if name not in specified:
+ extra.add(name)
+ return [Bcfg2.Client.XML.Element('Service', name=name, type='systemd')
+ for name in list(extra)]
diff --git a/src/lib/Bcfg2/Client/XML.py b/src/lib/Bcfg2/Client/XML.py
index 4ba06abae..93e4facdb 100644
--- a/src/lib/Bcfg2/Client/XML.py
+++ b/src/lib/Bcfg2/Client/XML.py
@@ -2,7 +2,7 @@
# library will use lxml, then builtin xml.etree, then ElementTree
-# pylint: disable=E0611,W0611,W0613,C0103
+# pylint: disable=E0611,E1101,W0611,W0613,C0103
try:
from lxml.etree import Element, SubElement, tostring, XMLParser
diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py
index 674162572..157cc7f65 100644
--- a/src/lib/Bcfg2/Client/__init__.py
+++ b/src/lib/Bcfg2/Client/__init__.py
@@ -10,6 +10,7 @@ import fnmatch
import logging
import argparse
import tempfile
+import copy
import Bcfg2.Logger
import Bcfg2.Options
from Bcfg2.Client import XML
@@ -67,7 +68,7 @@ def prompt(msg):
ans = safe_input(msg)
return ans in ['y', 'Y']
except UnicodeEncodeError:
- ans = input(msg.encode('utf-8'))
+ ans = safe_input(msg.encode('utf-8'))
return ans in ['y', 'Y']
except (EOFError, KeyboardInterrupt):
# handle ^C
@@ -920,8 +921,8 @@ class Client(object):
"""Generate XML summary of execution statistics."""
states = {}
for (item, val) in list(self.states.items()):
- if not Bcfg2.Options.setup.only_important or \
- item.get('important', 'false').lower() == 'true':
+ if (not Bcfg2.Options.setup.only_important or
+ item.get('important', 'false').lower() == 'true'):
states[item] = val
feedback = XML.Element("upload-statistics")
@@ -929,6 +930,11 @@ class Client(object):
'Statistics', total=str(len(states)),
version='2.0',
revision=self.config.get('revision', '-1'))
+ flags = XML.SubElement(stats, "Flags")
+ XML.SubElement(flags, "Flag", name="dry_run",
+ value=str(Bcfg2.Options.setup.dry_run))
+ XML.SubElement(flags, "Flag", name="only_important",
+ value=str(Bcfg2.Options.setup.only_important))
good_entries = [key for key, val in list(states.items()) if val]
good = len(good_entries)
stats.set('good', str(good))
@@ -945,9 +951,10 @@ class Client(object):
if not states[entry]], "Bad")]:
container = XML.SubElement(stats, ename)
for item in data:
- item.set('qtext', '')
- container.append(item)
- item.text = None
+ new_item = copy.deepcopy(item)
+ new_item.set('qtext', '')
+ container.append(new_item)
+ new_item.text = None
timeinfo = XML.Element("OpStamps")
feedback.append(stats)
diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py
index b8a75a0c5..1c2420ccf 100644
--- a/src/lib/Bcfg2/Compat.py
+++ b/src/lib/Bcfg2/Compat.py
@@ -286,3 +286,9 @@ except NameError:
def cmp(a, b):
""" Py3k implementation of cmp() """
return (a > b) - (a < b)
+
+# ast was introduced in python 2.6
+try:
+ from ast import literal_eval
+except ImportError:
+ literal_eval = eval
diff --git a/src/lib/Bcfg2/DBSettings.py b/src/lib/Bcfg2/DBSettings.py
index 5a83c25c3..5d99d4ee2 100644
--- a/src/lib/Bcfg2/DBSettings.py
+++ b/src/lib/Bcfg2/DBSettings.py
@@ -21,7 +21,8 @@ try:
except ImportError:
HAS_SOUTH = False
-settings = dict( # pylint: disable=C0103
+# pylint: disable=C0103
+settings = dict(
TIME_ZONE=None,
TEMPLATE_DEBUG=False,
DEBUG=False,
@@ -50,37 +51,66 @@ settings = dict( # pylint: disable=C0103
MIDDLEWARE_CLASSES=(
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.middleware.doc.XViewMiddleware'),
+ 'django.contrib.auth.middleware.AuthenticationMiddleware'),
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=(
+ DATABASE_ROUTERS=['Bcfg2.DBSettings.PerApplicationRouter'],
+ TEST_RUNNER='django.test.simple.DjangoTestSuiteRunner',
+ CACHES={
+ 'default': {
+ 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
+ }
+ })
+
+if HAS_DJANGO and django.VERSION[0] == 1 and django.VERSION[1] >= 6:
+ settings['MIDDLEWARE_CLASSES'] += \
+ ('django.contrib.admindocs.middleware.XViewMiddleware',)
+elif HAS_SOUTH:
+ settings['MIDDLEWARE_CLASSES'] += \
+ ('django.middleware.doc.XViewMiddleware',)
+
+if HAS_DJANGO and django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ settings['INSTALLED_APPS'] += ('Bcfg2.Reporting',)
+elif HAS_SOUTH:
+ settings['INSTALLED_APPS'] += ('south', 'Bcfg2.Reporting')
+ settings['SOUTH_MIGRATION_MODULES'] = {
+ 'Reporting': 'Bcfg2.Reporting.south_migrations',
+ 'Server': 'Bcfg2.Server.south_migrations',
+ }
+if HAS_DJANGO and django.VERSION[0] == 1 and django.VERSION[1] >= 8:
+ settings['TEMPLATES'] = [{
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [
+ '/usr/share/python-support/python-django/' +
+ 'django/contrib/admin/templates/'
+ ],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.contrib.auth.context_processors.auth',
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.i18n',
+ 'django.template.context_processors.media',
+ 'django.template.context_processors.request',
+ ],
+ },
+ }]
+else:
+ settings['TEMPLATE_DIRS'] = ('/usr/share/python-support/python-django/' +
+ 'django/contrib/admin/templates/')
+ settings['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'),
- DATABASE_ROUTERS=['Bcfg2.DBSettings.PerApplicationRouter'])
+ '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 """
@@ -97,8 +127,8 @@ def finalize_django_config(opts=None, silent=False):
OPTIONS=opts.db_opts,
SCHEMA=opts.db_schema))
- if hasattr(opts, "reporting_db_engine") and \
- opts.reporting_db_engine is not None:
+ if (hasattr(opts, "reporting_db_engine") and
+ opts.reporting_db_engine is not None):
settings['DATABASES']['Reporting'] = dict(
ENGINE="django.db.backends.%s" % opts.reporting_db_engine,
NAME=opts.reporting_db_name,
@@ -123,6 +153,9 @@ def finalize_django_config(opts=None, silent=False):
opts.web_prefix.rstrip('/') + \
settings['MEDIA_URL']
+ if opts.django_settings:
+ settings.update(opts.django_settings)
+
logger = logging.getLogger()
logger.debug("Finalizing Django settings: %s" % settings)
@@ -131,6 +164,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" %
@@ -139,6 +174,10 @@ def finalize_django_config(opts=None, silent=False):
def sync_databases(**kwargs):
""" Synchronize all databases that we know about. """
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ # Nothing needed here, it's all handled with migrate
+ return
+
logger = logging.getLogger()
for database in settings['DATABASES']:
logger.debug("Syncing database %s" % (database))
@@ -146,11 +185,55 @@ def sync_databases(**kwargs):
**kwargs)
+def upgrade_to_django_migrations(database, logger):
+ """
+ Get the migration state from south and move django migrations to
+ the same state by fake applying the same migration.
+
+ Note: We cannot use south directly here, because this functions
+ runs on django-1.7 or higher, that is not supported by south.
+ """
+
+ last_migration = None
+ try:
+ # get latest south migration
+ cursor = django.db.connections[database].cursor()
+ cursor.cursor.execute('SELECT migration FROM south_migrationhistory')
+ applied_migrations = [name for (name,) in cursor.fetchall()]
+ last_migration = sorted(applied_migrations).pop()
+ # django.db.DatabaseError is not working here, because we are
+ # using the low level api to interact directly with the database
+ except: # pylint: disable=W0702
+ logger.debug("No south migration detected for database: %s." %
+ database)
+
+ if last_migration is not None:
+ # fake-apply matching django migrations
+ django.core.management.call_command(
+ "migrate", 'Reporting', last_migration,
+ database=database, fake=True)
+
+
+def initial_django_migration(database):
+ """ Check if we ever executed an initial django migration. """
+ from django.db.migrations import loader # pylint: disable=E0611
+ loader = loader.MigrationLoader(django.db.connections[database])
+ return len(loader.applied_migrations) == 0
+
+
def migrate_databases(**kwargs):
""" Do South migrations on all databases that we know about. """
logger = logging.getLogger()
for database in settings['DATABASES']:
logger.debug("Migrating database %s" % (database))
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ if initial_django_migration(database):
+ logger.warning(
+ "No applied django migrations found for database %s. "
+ "Trying to get the state from south migration in case "
+ "you just upgraded your django version." % database)
+ upgrade_to_django_migrations(database, logger)
+
django.core.management.call_command("migrate", database=database,
**kwargs)
@@ -193,7 +276,14 @@ class PerApplicationRouter(object):
def allow_syncdb(self, *_):
""" Called when Django wants to determine which models to sync to a
given database. Take the cowards way out and sync all models to all
- databases to allow for easy migrations. """
+ databases to allow for easy migrations. This method is replaced with
+ allow_migrate in django 1.7 and higher. """
+ return True
+
+ def allow_migrate(self, *_args, **_kwargs):
+ """ Called when Django wants to determine which migrations should
+ be run on a given database. Take the cowards way out and run all
+ migrations to all databases to allow for easy migrations. """
return True
@@ -229,7 +319,7 @@ class _OptionContainer(object):
dest='db_schema', default='public'),
Bcfg2.Options.Option(
cf=('database', 'options'), help='Database options',
- dest='db_opts', type=Bcfg2.Options.Types.comma_dict,
+ dest='db_opts', type=Bcfg2.Options.Types.literal_dict,
default=dict()),
# reporting database options
Bcfg2.Options.Option(
@@ -258,20 +348,25 @@ class _OptionContainer(object):
Bcfg2.Options.Option(
cf=('database', 'reporting_options'),
help='Reporting database options', dest='reporting_db_opts',
- type=Bcfg2.Options.Types.comma_dict, default=dict()),
+ type=Bcfg2.Options.Types.literal_dict, default=dict()),
# Django options
Bcfg2.Options.Option(
cf=('reporting', 'time_zone'), help='Django timezone'),
Bcfg2.Options.BooleanOption(
cf=('reporting', 'web_debug'), help='Django debug'),
Bcfg2.Options.Option(
- cf=('reporting', 'web_prefix'), help='Web prefix')]
+ cf=('reporting', 'web_prefix'), help='Web prefix'),
+ Bcfg2.Options.Option(
+ cf=('reporting', 'django_settings'),
+ help='Additional django settings',
+ type=Bcfg2.Options.Types.literal_dict, default=dict())]
@staticmethod
def component_parsed_hook(opts):
""" Finalize the Django config after this component's options
are parsed. """
- finalize_django_config(opts=opts)
+ if HAS_DJANGO:
+ finalize_django_config(opts=opts)
@staticmethod
def options_parsed_hook():
@@ -280,6 +375,7 @@ class _OptionContainer(object):
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)
+ if HAS_DJANGO:
+ finalize_django_config(silent=True)
Bcfg2.Options.get_parser().add_component(_OptionContainer)
diff --git a/src/lib/Bcfg2/Logger.py b/src/lib/Bcfg2/Logger.py
index 11eaeebd1..a26971df4 100644
--- a/src/lib/Bcfg2/Logger.py
+++ b/src/lib/Bcfg2/Logger.py
@@ -41,6 +41,8 @@ class TermiosFormatter(logging.Formatter):
returns = []
line_len = self.width
if isinstance(record.msg, str):
+ if len(record.args) != 0:
+ record.msg = record.msg % record.args
for line in record.msg.split('\n'):
if len(line) <= line_len:
returns.append(line)
@@ -131,10 +133,11 @@ class FragmentingSysLogHandler(logging.handlers.SysLogHandler):
logging.WARNING),
self.format(reconn)))
self.socket.send(msg)
+
+ # If we still fail then drop it. Running
+ # bcfg2-server as non-root can trigger permission
+ # denied exceptions.
except: # pylint: disable=W0702
- # If we still fail then drop it. Running
- # bcfg2-server as non-root can trigger permission
- # denied exceptions.
pass
diff --git a/src/lib/Bcfg2/Options/Options.py b/src/lib/Bcfg2/Options/Options.py
index 752e01b4e..c85cfd87c 100644
--- a/src/lib/Bcfg2/Options/Options.py
+++ b/src/lib/Bcfg2/Options/Options.py
@@ -363,7 +363,7 @@ class RepositoryMacroOption(Option):
def transform_value(self, value):
"""transform the value after macro expansion.
- this can be overridden to further transform the value set by
+ this can be overridden to further transform the value set by
the user *after* macros are expanded, but before the user's
``type`` function is applied. principally exists for
PathOption to canonicalize the path.
diff --git a/src/lib/Bcfg2/Options/Parser.py b/src/lib/Bcfg2/Options/Parser.py
index d146e3aa2..51e41850c 100644
--- a/src/lib/Bcfg2/Options/Parser.py
+++ b/src/lib/Bcfg2/Options/Parser.py
@@ -12,12 +12,14 @@ __all__ = ["setup", "OptionParserException", "Parser", "get_parser",
"new_parser"]
+# pylint: disable=C0103
#: The repository option. This is specified here (and imported into
#: :module:`Bcfg2.Options.Common`) rather than vice-versa due to
#: circular imports.
-repository = PathOption( # pylint: disable=C0103
+repository = PathOption(
'-Q', '--repository', cf=('server', 'repository'),
- default='var/lib/bcfg2', help="Server repository path")
+ default='/var/lib/bcfg2', help="Server repository path")
+# pylint: enable=C0103
#: A module-level :class:`argparse.Namespace` object that stores all
@@ -141,6 +143,9 @@ class Parser(argparse.ArgumentParser):
self.option_list.extend(option.list_options())
option.add_to_parser(self)
+ for opt in option.list_options():
+ opt.default_from_config(self._cfp)
+ self._defaults_set.append(opt)
def add_component(self, component):
""" Add a component (and all of its options) to the
@@ -299,7 +304,7 @@ class Parser(argparse.ArgumentParser):
# check whether the specified bcfg2.conf exists
if not self.unit_test and not os.path.exists(bootstrap.config):
- self.error("Could not read %s" % bootstrap.config)
+ sys.stderr.write("Could not read %s\n" % bootstrap.config)
self.add_config_file(self.configfile.dest, bootstrap.config,
reparse=False)
diff --git a/src/lib/Bcfg2/Options/Subcommands.py b/src/lib/Bcfg2/Options/Subcommands.py
index 8972bde00..2ba81e18d 100644
--- a/src/lib/Bcfg2/Options/Subcommands.py
+++ b/src/lib/Bcfg2/Options/Subcommands.py
@@ -48,6 +48,14 @@ class Subcommand(object):
#: one, ``bcfg2-admin`` does not.)
interactive = True
+ #: Whether or not to expose this command as command line parameter
+ #: or only in an interactive :class:`cmd.Cmd` shell.
+ only_interactive = False
+
+ #: Additional aliases for the command. The contents of the list gets
+ #: added to the default command name (the lowercased class name)
+ aliases = []
+
_ws_re = re.compile(r'\s+', flags=re.MULTILINE)
def __init__(self):
@@ -82,6 +90,7 @@ class Subcommand(object):
"""
if args is not None:
self.parser.namespace = copy.copy(master_setup)
+ self.parser.parsed = False
alist = shlex.split(args)
try:
setup = self.parser.parse(alist)
@@ -141,7 +150,9 @@ class Help(Subcommand):
self._registry = registry
def run(self, setup):
- commands = self._registry.commands
+ commands = dict((name, cmd)
+ for (name, cmd) in self._registry.commands.items()
+ if not cmd.only_interactive)
if setup.command:
try:
commands[setup.command].parser.print_help()
@@ -207,15 +218,22 @@ class CommandRegistry(object):
else:
cmd_obj = cls_or_obj
cmdcls = cmd_obj.__class__
- name = cmdcls.__name__.lower()
- self.commands[name] = cmd_obj
- # py2.5 can't mix *magic and non-magical keyword args, thus
- # the **dict(...)
- self.subcommand_options.append(
- Subparser(*cmdcls.options, **dict(name=name, help=cmdcls.__doc__)))
- if issubclass(self.__class__, cmd.Cmd) and cmdcls.interactive:
- setattr(self, "do_%s" % name, cmd_obj)
- setattr(self, "help_%s" % name, cmd_obj.parser.print_help)
+ names = [cmdcls.__name__.lower()]
+ if cmdcls.aliases:
+ names.extend(cmdcls.aliases)
+
+ for name in names:
+ self.commands[name] = cmd_obj
+
+ if not cmdcls.only_interactive:
+ # py2.5 can't mix *magic and non-magical keyword args, thus
+ # the **dict(...)
+ self.subcommand_options.append(
+ Subparser(*cmdcls.options, **dict(name=name,
+ help=cmdcls.__doc__)))
+ if issubclass(self.__class__, cmd.Cmd) and cmdcls.interactive:
+ setattr(self, "do_%s" % name, cmd_obj)
+ setattr(self, "help_%s" % name, cmd_obj.parser.print_help)
return cmd_obj
def register_commands(self, candidates, parent=Subcommand):
diff --git a/src/lib/Bcfg2/Options/Types.py b/src/lib/Bcfg2/Options/Types.py
index ac099e135..ad2e04f10 100644
--- a/src/lib/Bcfg2/Options/Types.py
+++ b/src/lib/Bcfg2/Options/Types.py
@@ -5,6 +5,7 @@ import os
import re
import pwd
import grp
+from Bcfg2.Compat import literal_eval
_COMMA_SPLIT_RE = re.compile(r'\s*,\s*')
@@ -32,28 +33,10 @@ def colon_list(value):
return value.split(':')
-def comma_dict(value):
- """ Split an option string on commas, optionally surrounded by
- whitespace, and split the resulting items again on equals signs,
- returning a dict """
- result = dict()
- if value:
- items = comma_list(value)
- for item in items:
- if '=' in item:
- key, value = item.split(r'=', 1)
- if value in ["true", "yes", "on"]:
- result[key] = True
- elif value in ["false", "no", "off"]:
- result[key] = False
- else:
- try:
- result[key] = int(value)
- except ValueError:
- result[key] = value
- else:
- result[item] = True
- return result
+def literal_dict(value):
+ """ literally evaluate the option in order to allow for arbitrarily nested
+ dictionaries """
+ return literal_eval(value)
def anchored_regex_list(value):
diff --git a/src/lib/Bcfg2/Reporting/Collector.py b/src/lib/Bcfg2/Reporting/Collector.py
index 153809a35..f05a25732 100644
--- a/src/lib/Bcfg2/Reporting/Collector.py
+++ b/src/lib/Bcfg2/Reporting/Collector.py
@@ -116,7 +116,7 @@ class ReportingCollector(object):
self.storage.__class__.__name__)
self.storage.validate()
except:
- self.logger.error("Storage backed %s failed to validate: %s" %
+ self.logger.error("Storage backend %s failed to validate: %s" %
(self.storage.__class__.__name__,
sys.exc_info()[1]))
diff --git a/src/lib/Bcfg2/Reporting/Compat.py b/src/lib/Bcfg2/Reporting/Compat.py
index 9113fdb91..a87a8e480 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(u, (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 3b9c83433..e60b2e82e 100755
--- a/src/lib/Bcfg2/Reporting/Reports.py
+++ b/src/lib/Bcfg2/Reporting/Reports.py
@@ -4,60 +4,46 @@
import sys
import argparse
import datetime
+import django
import Bcfg2.DBSettings
+from django.core.exceptions import ObjectDoesNotExist
-def hosts_by_entry_type(clients, etype, entryspec):
- result = []
- for entry in entryspec:
- for client in clients:
- items = getattr(client.current_interaction, etype)()
- for item in items:
- if (item.entry_type == entry[0] and
- item.name == entry[1]):
- result.append(client)
- return result
+def print_entries(interaction, etype):
+ items = getattr(interaction, etype)()
+ for item in items:
+ print("%-70s %s" % (item.entry_type + ":" + item.name, etype))
-def print_fields(fields, client, fmt, extra=None):
- """ Prints the fields specified in fields of client, max_name
- specifies the column width of the name column. """
- fdata = []
- if extra is None:
- extra = dict()
- for field in fields:
- if field == 'time':
- fdata.append(str(client.current_interaction.timestamp))
- elif field == 'state':
- if client.current_interaction.isclean():
- fdata.append("clean")
- else:
- fdata.append("dirty")
- elif field == 'total':
- fdata.append(client.current_interaction.total_count)
- elif field == 'good':
- fdata.append(client.current_interaction.good_count)
- elif field == 'modified':
- fdata.append(client.current_interaction.modified_count)
- elif field == 'extra':
- fdata.append(client.current_interaction.extra_count)
- elif field == 'bad':
- fdata.append((client.current_interaction.bad_count))
- elif field == 'stale':
- fdata.append(client.current_interaction.isstale())
- else:
- try:
- fdata.append(getattr(client, field))
- except AttributeError:
- fdata.append(extra.get(field, "N/A"))
+class _FlagsFilterMixin(object):
+ """ Mixin that allows to filter the interactions based on the
+ only_important and/or the dry_run flag """
- print(fmt % tuple(fdata))
+ options = [
+ Bcfg2.Options.BooleanOption(
+ "-n", "--no-dry-run",
+ help="Do not consider interactions created with the --dry-run "
+ "flag"),
+ Bcfg2.Options.BooleanOption(
+ "-i", "--no-only-important",
+ help="Do not consider interactions created with the "
+ "--only-important flag")]
+ def get_interaction(self, client, setup):
+ if not setup.no_dry_run and not setup.no_only_important:
+ return client.current_interaction
-def print_entries(interaction, etype):
- items = getattr(interaction, etype)()
- for item in items:
- print("%-70s %s" % (item.entry_type + ":" + item.name, etype))
+ filter = {}
+ if setup.no_dry_run:
+ filter['dry_run'] = False
+ if setup.no_only_important:
+ filter['only_important'] = False
+
+ from Bcfg2.Reporting.models import Interaction
+ try:
+ return Interaction.objects.filter(client=client, **filter).latest()
+ except ObjectDoesNotExist:
+ return None
class _SingleHostCmd(Bcfg2.Options.Subcommand): # pylint: disable=W0223
@@ -74,10 +60,10 @@ class _SingleHostCmd(Bcfg2.Options.Subcommand): # pylint: disable=W0223
raise SystemExit(2)
-class Show(_SingleHostCmd):
+class Show(_SingleHostCmd, _FlagsFilterMixin):
""" Show bad, extra, modified, or all entries from a given host """
- options = _SingleHostCmd.options + [
+ options = _SingleHostCmd.options + _FlagsFilterMixin.options + [
Bcfg2.Options.BooleanOption(
"-b", "--bad", help="Show bad entries from HOST"),
Bcfg2.Options.BooleanOption(
@@ -88,22 +74,32 @@ class Show(_SingleHostCmd):
def run(self, setup):
client = self.get_client(setup)
show_all = not setup.bad and not setup.extra and not setup.modified
- if setup.bad or show_all:
- print_entries(client.current_interaction, "bad")
- if setup.modified or show_all:
- print_entries(client.current_interaction, "modified")
- if setup.extra or show_all:
- print_entries(client.current_interaction, "extra")
+ interaction = self.get_interaction(client, setup)
+ if interaction is None:
+ print("No interactions found for host: %s" % client.name)
+ else:
+ if setup.bad or show_all:
+ print_entries(interaction, "bad")
+ if setup.modified or show_all:
+ print_entries(interaction, "modified")
+ if setup.extra or show_all:
+ print_entries(interaction, "extra")
-class Total(_SingleHostCmd):
+class Total(_SingleHostCmd, _FlagsFilterMixin):
""" Show total number of managed and good entries from HOST """
+ options = _SingleHostCmd.options + _FlagsFilterMixin.options
+
def run(self, setup):
client = self.get_client(setup)
- managed = client.current_interaction.total_count
- good = client.current_interaction.good_count
- print("Total managed entries: %d (good: %d)" % (managed, good))
+ interaction = self.get_interaction(client, setup)
+ if interaction is None:
+ print("No interactions found for host: %s" % client.name)
+ else:
+ managed = interaction.total_count
+ good = interaction.good_count
+ print("Total managed entries: %d (good: %d)" % (managed, good))
class Expire(_SingleHostCmd):
@@ -120,9 +116,9 @@ class Expire(_SingleHostCmd):
client.save()
-class _ClientSelectCmd(Bcfg2.Options.Subcommand):
+class _ClientSelectCmd(Bcfg2.Options.Subcommand, _FlagsFilterMixin):
""" Base class for subcommands that display lists of clients """
- options = [
+ options = _FlagsFilterMixin.options + [
Bcfg2.Options.Option("--fields", metavar="FIELD,FIELD,...",
help="Only display the listed fields",
type=Bcfg2.Options.Types.comma_list,
@@ -132,7 +128,42 @@ class _ClientSelectCmd(Bcfg2.Options.Subcommand):
from Bcfg2.Reporting.models import Client
return Client.objects.exclude(current_interaction__isnull=True)
- def display(self, result, fields, extra=None):
+ def _print_fields(self, setup, fields, client, fmt, extra=None):
+ """ Prints the fields specified in fields of client, max_name
+ specifies the column width of the name column. """
+ fdata = []
+ if extra is None:
+ extra = dict()
+ interaction = self.get_interaction(client, setup)
+ for field in fields:
+ if field == 'time':
+ fdata.append(str(interaction.timestamp))
+ elif field == 'state':
+ if interaction.isclean():
+ fdata.append("clean")
+ else:
+ fdata.append("dirty")
+ elif field == 'total':
+ fdata.append(interaction.total_count)
+ elif field == 'good':
+ fdata.append(interaction.good_count)
+ elif field == 'modified':
+ fdata.append(interaction.modified_count)
+ elif field == 'extra':
+ fdata.append(interaction.extra_count)
+ elif field == 'bad':
+ fdata.append(interaction.bad_count)
+ elif field == 'stale':
+ fdata.append(interaction.isstale())
+ else:
+ try:
+ fdata.append(getattr(client, field))
+ except AttributeError:
+ fdata.append(extra.get(field, "N/A"))
+
+ print(fmt % tuple(fdata))
+
+ def display(self, setup, result, fields, extra=None):
if 'name' not in fields:
fields.insert(0, "name")
if not result:
@@ -153,8 +184,8 @@ class _ClientSelectCmd(Bcfg2.Options.Subcommand):
print(fmt % tuple(f.title() for f in fields))
for client in result:
if not client.expiration:
- print_fields(fields, client, fmt,
- extra=extra.get(client, None))
+ self._print_fields(setup, fields, client, fmt,
+ extra=extra.get(client, None))
class Clients(_ClientSelectCmd):
@@ -172,14 +203,17 @@ class Clients(_ClientSelectCmd):
result = []
show_all = not setup.stale and not setup.clean and not setup.dirty
for client in self.get_clients():
- interaction = client.current_interaction
+ interaction = self.get_interaction(client, setup)
+ if interaction is None:
+ continue
+
if (show_all or
(setup.stale and interaction.isstale()) or
(setup.clean and interaction.isclean()) or
(setup.dirty and not interaction.isclean())):
result.append(client)
- self.display(result, setup.fields)
+ self.display(setup, result, setup.fields)
class Entries(_ClientSelectCmd):
@@ -201,6 +235,21 @@ class Entries(_ClientSelectCmd):
Bcfg2.Options.PositionalArgument(
"entries", metavar="TYPE:NAME", nargs="*")]
+ def _hosts_by_entry_type(self, setup, clients, etype, entryspec):
+ result = []
+ for entry in entryspec:
+ for client in clients:
+ interaction = self.get_interaction(client, setup)
+ if interaction is None:
+ continue
+
+ items = getattr(interaction, etype)()
+ for item in items:
+ if (item.entry_type == entry[0] and
+ item.name == entry[1]):
+ result.append(client)
+ return result
+
def run(self, setup):
result = []
if setup.file:
@@ -216,13 +265,15 @@ class Entries(_ClientSelectCmd):
clients = self.get_clients()
if setup.badentry:
- result = hosts_by_entry_type(clients, "bad", entries)
+ result = self._hosts_by_entry_type(setup, clients, "bad", entries)
elif setup.modifiedentry:
- result = hosts_by_entry_type(clients, "modified", entries)
+ result = self._hosts_by_entry_type(setup, clients, "modified",
+ entries)
elif setup.extraentry:
- result = hosts_by_entry_type(clients, "extra", entries)
+ result = self._hosts_by_entry_type(setup, clients, "extra",
+ entries)
- self.display(result, setup.fields)
+ self.display(setup, result, setup.fields)
class Entry(_ClientSelectCmd):
@@ -250,16 +301,19 @@ class Entry(_ClientSelectCmd):
# TODO: batch fetch this. sqlite could break
extra = dict()
for client in self.get_clients():
+ interaction = self.get_interaction(client, setup)
+ if interaction is None:
+ continue
+
ents = entry_cls.objects.filter(
- name=ename,
- interaction=client.current_interaction)
+ name=ename, interaction=interaction)
if len(ents) == 0:
continue
extra[client] = {"entry state": ents[0].get_state_display(),
"reason": ents[0]}
result.append(client)
- self.display(result, fields, extra=extra)
+ self.display(setup, result, fields, extra=extra)
class CLI(Bcfg2.Options.CommandRegistry):
diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
index efd9e594c..af1b0c341 100644
--- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
+++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
@@ -2,26 +2,54 @@
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 django.core import management
+from lxml import etree
+import sys
+
+import django
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db.models import FieldDoesNotExist
from django.core.cache import cache
-from django import db
-#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, ServiceEntry, ActionEntry, \
+ POSIXGroupEntry, POSIXUserEntry, SEBooleanEntry, SEFcontextEntry, \
+ SEInterfaceEntry, SELoginEntry, SEModuleEntry, SENodeEntry, \
+ SEPermissiveEntry, SEPortEntry, SEUserEntry
+ # 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, ServiceEntry, ActionEntry, \
+ POSIXGroupEntry, POSIXUserEntry, SEBooleanEntry, SEFcontextEntry, \
+ SEInterfaceEntry, SELoginEntry, SEModuleEntry, SENodeEntry, \
+ SEPermissiveEntry, SEPortEntry, SEUserEntry
+
+
+def get_all_field_names(model):
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 8:
+ return [field.name
+ for field in model._meta.get_fields()
+ if field.auto_created == False and
+ not (field.is_relation and field.related_model is None)]
+ else:
+ return model._meta.get_all_field_names()
class DjangoORM(StorageBase):
@@ -32,6 +60,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):
@@ -80,7 +109,7 @@ class DjangoORM(StorageBase):
for attr in boolean + ["current_exists"]:
xforms[attr] = boolean_xform
act_dict = dict(state=state)
- for fieldname in entrytype._meta.get_all_field_names():
+ for fieldname in get_all_field_names(entrytype):
if fieldname in ['id', 'hash_key', 'state']:
continue
try:
@@ -284,6 +313,14 @@ class DjangoORM(StorageBase):
Group.objects.get_or_create(name=metadata['profile'])
else:
profile = None
+
+ flags = {'dry_run': False, 'only_important': False}
+ for flag in stats.findall('./Flags/Flag'):
+ value = flag.get('value', default='false').lower() == 'true'
+ name = flag.get('name')
+ if name in flags:
+ flags[name] = value
+
inter = Interaction(client=client,
timestamp=timestamp,
state=stats.get('state', default="unknown"),
@@ -292,7 +329,8 @@ class DjangoORM(StorageBase):
good_count=stats.get('good', default="0"),
total_count=stats.get('total', default="0"),
server=server,
- profile=profile)
+ profile=profile,
+ **flags)
inter.save()
self.logger.debug("Interaction for %s at %s with INSERTED in to db" %
(client.id, timestamp))
@@ -374,8 +412,12 @@ class DjangoORM(StorageBase):
finally:
self.logger.debug("%s: Closing database connection" %
self.__class__.__name__)
- db.close_connection()
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ for connection in django.db.connections.all():
+ connection.close()
+ else:
+ django.db.close_connection()
def validate(self):
"""Validate backend storage. Should be called once when loaded"""
diff --git a/src/lib/Bcfg2/Reporting/migrations/0001_initial.py b/src/lib/Bcfg2/Reporting/migrations/0001_initial.py
index 609290edb..6a52a8b2d 100644
--- a/src/lib/Bcfg2/Reporting/migrations/0001_initial.py
+++ b/src/lib/Bcfg2/Reporting/migrations/0001_initial.py
@@ -1,465 +1,281 @@
# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding model 'Client'
- db.create_table('Reporting_client', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('creation', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_interaction', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='parent_client', null=True, to=orm['Reporting.Interaction'])),
- ('expiration', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
- ))
- db.send_create_signal('Reporting', ['Client'])
-
- # Adding model 'Interaction'
- db.create_table('Reporting_interaction', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('client', self.gf('django.db.models.fields.related.ForeignKey')(related_name='interactions', to=orm['Reporting.Client'])),
- ('timestamp', self.gf('django.db.models.fields.DateTimeField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.CharField')(max_length=32)),
- ('repo_rev_code', self.gf('django.db.models.fields.CharField')(max_length=64)),
- ('server', self.gf('django.db.models.fields.CharField')(max_length=256)),
- ('good_count', self.gf('django.db.models.fields.IntegerField')()),
- ('total_count', self.gf('django.db.models.fields.IntegerField')()),
- ('bad_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
- ('modified_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
- ('extra_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
- ('profile', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['Reporting.Group'])),
- ))
- db.send_create_signal('Reporting', ['Interaction'])
-
- # Adding unique constraint on 'Interaction', fields ['client', 'timestamp']
- db.create_unique('Reporting_interaction', ['client_id', 'timestamp'])
-
- # Adding M2M table for field actions on 'Interaction'
- db.create_table('Reporting_interaction_actions', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('actionentry', models.ForeignKey(orm['Reporting.actionentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_actions', ['interaction_id', 'actionentry_id'])
-
- # Adding M2M table for field packages on 'Interaction'
- db.create_table('Reporting_interaction_packages', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('packageentry', models.ForeignKey(orm['Reporting.packageentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_packages', ['interaction_id', 'packageentry_id'])
-
- # Adding M2M table for field paths on 'Interaction'
- db.create_table('Reporting_interaction_paths', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('pathentry', models.ForeignKey(orm['Reporting.pathentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_paths', ['interaction_id', 'pathentry_id'])
-
- # Adding M2M table for field services on 'Interaction'
- db.create_table('Reporting_interaction_services', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('serviceentry', models.ForeignKey(orm['Reporting.serviceentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_services', ['interaction_id', 'serviceentry_id'])
-
- # Adding M2M table for field failures on 'Interaction'
- db.create_table('Reporting_interaction_failures', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('failureentry', models.ForeignKey(orm['Reporting.failureentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_failures', ['interaction_id', 'failureentry_id'])
-
- # Adding M2M table for field groups on 'Interaction'
- db.create_table('Reporting_interaction_groups', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('group', models.ForeignKey(orm['Reporting.group'], null=False))
- ))
- db.create_unique('Reporting_interaction_groups', ['interaction_id', 'group_id'])
-
- # Adding M2M table for field bundles on 'Interaction'
- db.create_table('Reporting_interaction_bundles', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('bundle', models.ForeignKey(orm['Reporting.bundle'], null=False))
- ))
- db.create_unique('Reporting_interaction_bundles', ['interaction_id', 'bundle_id'])
-
- # Adding model 'Performance'
- db.create_table('Reporting_performance', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('interaction', self.gf('django.db.models.fields.related.ForeignKey')(related_name='performance_items', to=orm['Reporting.Interaction'])),
- ('metric', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('value', self.gf('django.db.models.fields.DecimalField')(max_digits=32, decimal_places=16)),
- ))
- db.send_create_signal('Reporting', ['Performance'])
-
- # Adding model 'Group'
- db.create_table('Reporting_group', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
- ('profile', self.gf('django.db.models.fields.BooleanField')(default=False)),
- ('public', self.gf('django.db.models.fields.BooleanField')(default=False)),
- ('category', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)),
- ('comment', self.gf('django.db.models.fields.TextField')(blank=True)),
- ))
- db.send_create_signal('Reporting', ['Group'])
-
- # Adding M2M table for field groups on 'Group'
- db.create_table('Reporting_group_groups', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('from_group', models.ForeignKey(orm['Reporting.group'], null=False)),
- ('to_group', models.ForeignKey(orm['Reporting.group'], null=False))
- ))
- db.create_unique('Reporting_group_groups', ['from_group_id', 'to_group_id'])
-
- # Adding M2M table for field bundles on 'Group'
- db.create_table('Reporting_group_bundles', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('group', models.ForeignKey(orm['Reporting.group'], null=False)),
- ('bundle', models.ForeignKey(orm['Reporting.bundle'], null=False))
- ))
- db.create_unique('Reporting_group_bundles', ['group_id', 'bundle_id'])
-
- # Adding model 'Bundle'
- db.create_table('Reporting_bundle', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
- ))
- db.send_create_signal('Reporting', ['Bundle'])
-
- # Adding model 'FilePerms'
- db.create_table('Reporting_fileperms', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('owner', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('group', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('perms', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ))
- db.send_create_signal('Reporting', ['FilePerms'])
-
- # Adding unique constraint on 'FilePerms', fields ['owner', 'group', 'perms']
- db.create_unique('Reporting_fileperms', ['owner', 'group', 'perms'])
-
- # Adding model 'FileAcl'
- db.create_table('Reporting_fileacl', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ))
- db.send_create_signal('Reporting', ['FileAcl'])
-
- # Adding model 'FailureEntry'
- db.create_table('Reporting_failureentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
- ('entry_type', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('message', self.gf('django.db.models.fields.TextField')()),
- ))
- db.send_create_signal('Reporting', ['FailureEntry'])
-
- # Adding model 'ActionEntry'
- db.create_table('Reporting_actionentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('status', self.gf('django.db.models.fields.CharField')(default='check', max_length=128)),
- ('output', self.gf('django.db.models.fields.IntegerField')(default=0)),
- ))
- db.send_create_signal('Reporting', ['ActionEntry'])
-
- # Adding model 'PackageEntry'
- db.create_table('Reporting_packageentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('target_version', self.gf('django.db.models.fields.CharField')(default='', max_length=1024)),
- ('current_version', self.gf('django.db.models.fields.CharField')(max_length=1024)),
- ('verification_details', self.gf('django.db.models.fields.TextField')(default='')),
- ))
- db.send_create_signal('Reporting', ['PackageEntry'])
-
- # Adding model 'PathEntry'
- db.create_table('Reporting_pathentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('path_type', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('target_perms', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['Reporting.FilePerms'])),
- ('current_perms', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['Reporting.FilePerms'])),
- ('detail_type', self.gf('django.db.models.fields.IntegerField')(default=0)),
- ('details', self.gf('django.db.models.fields.TextField')(default='')),
- ))
- db.send_create_signal('Reporting', ['PathEntry'])
-
- # Adding M2M table for field acls on 'PathEntry'
- db.create_table('Reporting_pathentry_acls', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('pathentry', models.ForeignKey(orm['Reporting.pathentry'], null=False)),
- ('fileacl', models.ForeignKey(orm['Reporting.fileacl'], null=False))
- ))
- db.create_unique('Reporting_pathentry_acls', ['pathentry_id', 'fileacl_id'])
-
- # Adding model 'LinkEntry'
- db.create_table('Reporting_linkentry', (
- ('pathentry_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['Reporting.PathEntry'], unique=True, primary_key=True)),
- ('target_path', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)),
- ('current_path', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)),
- ))
- db.send_create_signal('Reporting', ['LinkEntry'])
-
- # Adding model 'DeviceEntry'
- db.create_table('Reporting_deviceentry', (
- ('pathentry_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['Reporting.PathEntry'], unique=True, primary_key=True)),
- ('device_type', self.gf('django.db.models.fields.CharField')(max_length=16)),
- ('target_major', self.gf('django.db.models.fields.IntegerField')()),
- ('target_minor', self.gf('django.db.models.fields.IntegerField')()),
- ('current_major', self.gf('django.db.models.fields.IntegerField')()),
- ('current_minor', self.gf('django.db.models.fields.IntegerField')()),
- ))
- db.send_create_signal('Reporting', ['DeviceEntry'])
-
- # Adding model 'ServiceEntry'
- db.create_table('Reporting_serviceentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('target_status', self.gf('django.db.models.fields.CharField')(default='', max_length=128)),
- ('current_status', self.gf('django.db.models.fields.CharField')(default='', max_length=128)),
- ))
- db.send_create_signal('Reporting', ['ServiceEntry'])
-
-
- def backwards(self, orm):
- # Removing unique constraint on 'FilePerms', fields ['owner', 'group', 'perms']
- db.delete_unique('Reporting_fileperms', ['owner', 'group', 'perms'])
-
- # Removing unique constraint on 'Interaction', fields ['client', 'timestamp']
- db.delete_unique('Reporting_interaction', ['client_id', 'timestamp'])
-
- # Deleting model 'Client'
- db.delete_table('Reporting_client')
-
- # Deleting model 'Interaction'
- db.delete_table('Reporting_interaction')
-
- # Removing M2M table for field actions on 'Interaction'
- db.delete_table('Reporting_interaction_actions')
-
- # Removing M2M table for field packages on 'Interaction'
- db.delete_table('Reporting_interaction_packages')
-
- # Removing M2M table for field paths on 'Interaction'
- db.delete_table('Reporting_interaction_paths')
-
- # Removing M2M table for field services on 'Interaction'
- db.delete_table('Reporting_interaction_services')
-
- # Removing M2M table for field failures on 'Interaction'
- db.delete_table('Reporting_interaction_failures')
-
- # Removing M2M table for field groups on 'Interaction'
- db.delete_table('Reporting_interaction_groups')
-
- # Removing M2M table for field bundles on 'Interaction'
- db.delete_table('Reporting_interaction_bundles')
-
- # Deleting model 'Performance'
- db.delete_table('Reporting_performance')
-
- # Deleting model 'Group'
- db.delete_table('Reporting_group')
-
- # Removing M2M table for field groups on 'Group'
- db.delete_table('Reporting_group_groups')
-
- # Removing M2M table for field bundles on 'Group'
- db.delete_table('Reporting_group_bundles')
-
- # Deleting model 'Bundle'
- db.delete_table('Reporting_bundle')
-
- # Deleting model 'FilePerms'
- db.delete_table('Reporting_fileperms')
-
- # Deleting model 'FileAcl'
- db.delete_table('Reporting_fileacl')
-
- # Deleting model 'FailureEntry'
- db.delete_table('Reporting_failureentry')
-
- # Deleting model 'ActionEntry'
- db.delete_table('Reporting_actionentry')
-
- # Deleting model 'PackageEntry'
- db.delete_table('Reporting_packageentry')
-
- # Deleting model 'PathEntry'
- db.delete_table('Reporting_pathentry')
-
- # Removing M2M table for field acls on 'PathEntry'
- db.delete_table('Reporting_pathentry_acls')
-
- # Deleting model 'LinkEntry'
- db.delete_table('Reporting_linkentry')
-
- # Deleting model 'DeviceEntry'
- db.delete_table('Reporting_deviceentry')
-
- # Deleting model 'ServiceEntry'
- db.delete_table('Reporting_serviceentry')
-
-
- models = {
- 'Reporting.actionentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
- },
- 'Reporting.bundle': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
- },
- 'Reporting.client': {
- 'Meta': {'object_name': 'Client'},
- 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
- 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.deviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_major': ('django.db.models.fields.IntegerField', [], {}),
- 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
- 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_major': ('django.db.models.fields.IntegerField', [], {}),
- 'target_minor': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.failureentry': {
- 'Meta': {'object_name': 'FailureEntry'},
- 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.TextField', [], {}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileacl': {
- 'Meta': {'object_name': 'FileAcl'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileperms': {
- 'Meta': {'unique_together': "(('owner', 'group', 'perms'),)", 'object_name': 'FilePerms'},
- 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'perms': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.group': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
- },
- 'Reporting.interaction': {
- 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
- 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
- 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
- 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
- 'good_count': ('django.db.models.fields.IntegerField', [], {}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
- 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
- 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.Group']"}),
- 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
- 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
- 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
- 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
- 'total_count': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.linkentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
- },
- 'Reporting.packageentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
- 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
- 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
- },
- 'Reporting.pathentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
- 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
- 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
- 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
- },
- 'Reporting.performance': {
- 'Meta': {'object_name': 'Performance'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
- 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
- },
- 'Reporting.serviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
- 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
- }
- }
-
- complete_apps = ['Reporting'] \ No newline at end of file
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ActionEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.IntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('status', models.CharField(default=b'check', max_length=128)),
+ ('output', models.IntegerField(default=0)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='Bundle',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(unique=True, max_length=255)),
+ ],
+ options={
+ 'ordering': ('name',),
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='Client',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('creation', models.DateTimeField(auto_now_add=True)),
+ ('name', models.CharField(max_length=128)),
+ ('expiration', models.DateTimeField(null=True, blank=True)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='FailureEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.IntegerField(editable=False, db_index=True)),
+ ('entry_type', models.CharField(max_length=128)),
+ ('message', models.TextField()),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='FileAcl',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='FilePerms',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('owner', models.CharField(max_length=128)),
+ ('group', models.CharField(max_length=128)),
+ ('perms', models.CharField(max_length=128)),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='Group',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(unique=True, max_length=255)),
+ ('profile', models.BooleanField(default=False)),
+ ('public', models.BooleanField(default=False)),
+ ('category', models.CharField(max_length=1024, blank=True)),
+ ('comment', models.TextField(blank=True)),
+ ('bundles', models.ManyToManyField(to='Reporting.Bundle')),
+ ('groups', models.ManyToManyField(to='Reporting.Group')),
+ ],
+ options={
+ 'ordering': ('name',),
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='Interaction',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('timestamp', models.DateTimeField(db_index=True)),
+ ('state', models.CharField(max_length=32)),
+ ('repo_rev_code', models.CharField(max_length=64)),
+ ('server', models.CharField(max_length=256)),
+ ('good_count', models.IntegerField()),
+ ('total_count', models.IntegerField()),
+ ('bad_count', models.IntegerField(default=0)),
+ ('modified_count', models.IntegerField(default=0)),
+ ('extra_count', models.IntegerField(default=0)),
+ ('actions', models.ManyToManyField(to='Reporting.ActionEntry')),
+ ('bundles', models.ManyToManyField(to='Reporting.Bundle')),
+ ('client', models.ForeignKey(related_name='interactions', to='Reporting.Client')),
+ ('failures', models.ManyToManyField(to='Reporting.FailureEntry')),
+ ('groups', models.ManyToManyField(to='Reporting.Group')),
+ ],
+ options={
+ 'ordering': ['-timestamp'],
+ 'get_latest_by': 'timestamp',
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='PackageEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.IntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('target_version', models.CharField(default=b'', max_length=1024)),
+ ('current_version', models.CharField(max_length=1024)),
+ ('verification_details', models.TextField(default=b'')),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='PathEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.IntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('path_type', models.CharField(max_length=128, choices=[(b'device', b'Device'), (b'directory', b'Directory'), (b'hardlink', b'Hard Link'), (b'nonexistent', b'Non Existent'), (b'permissions', b'Permissions'), (b'symlink', b'Symlink')])),
+ ('detail_type', models.IntegerField(default=0, choices=[(0, b'Unused'), (1, b'Diff'), (2, b'Binary'), (3, b'Sensitive'), (4, b'Size limit exceeded'), (5, b'VCS output'), (6, b'Pruned paths')])),
+ ('details', models.TextField(default=b'')),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='LinkEntry',
+ fields=[
+ ('pathentry_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='Reporting.PathEntry')),
+ ('target_path', models.CharField(max_length=1024, blank=True)),
+ ('current_path', models.CharField(max_length=1024, blank=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=('Reporting.pathentry',),
+ ),
+ migrations.CreateModel(
+ name='DeviceEntry',
+ fields=[
+ ('pathentry_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='Reporting.PathEntry')),
+ ('device_type', models.CharField(max_length=16, choices=[(b'block', b'Block'), (b'char', b'Char'), (b'fifo', b'Fifo')])),
+ ('target_major', models.IntegerField()),
+ ('target_minor', models.IntegerField()),
+ ('current_major', models.IntegerField()),
+ ('current_minor', models.IntegerField()),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=('Reporting.pathentry',),
+ ),
+ migrations.CreateModel(
+ name='Performance',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('metric', models.CharField(max_length=128)),
+ ('value', models.DecimalField(max_digits=32, decimal_places=16)),
+ ('interaction', models.ForeignKey(related_name='performance_items', to='Reporting.Interaction')),
+ ],
+ options={
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='ServiceEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.IntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('target_status', models.CharField(default=b'', max_length=128)),
+ ('current_status', models.CharField(default=b'', max_length=128)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.AddField(
+ model_name='pathentry',
+ name='acls',
+ field=models.ManyToManyField(to='Reporting.FileAcl'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='pathentry',
+ name='current_perms',
+ field=models.ForeignKey(related_name='+', to='Reporting.FilePerms'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='pathentry',
+ name='target_perms',
+ field=models.ForeignKey(related_name='+', to='Reporting.FilePerms'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='packages',
+ field=models.ManyToManyField(to='Reporting.PackageEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='paths',
+ field=models.ManyToManyField(to='Reporting.PathEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='profile',
+ field=models.ForeignKey(related_name='+', to='Reporting.Group'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='services',
+ field=models.ManyToManyField(to='Reporting.ServiceEntry'),
+ preserve_default=True,
+ ),
+ migrations.AlterUniqueTogether(
+ name='interaction',
+ unique_together=set([('client', 'timestamp')]),
+ ),
+ migrations.AlterUniqueTogether(
+ name='fileperms',
+ unique_together=set([('owner', 'group', 'perms')]),
+ ),
+ migrations.AddField(
+ model_name='client',
+ name='current_interaction',
+ field=models.ForeignKey(related_name='parent_client', blank=True, to='Reporting.Interaction', null=True),
+ preserve_default=True,
+ ),
+ ]
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 37cdd146c..48bfd6412 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
@@ -1,170 +1,23 @@
# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-from django.conf import settings
+from __future__ import unicode_literals
-class Migration(SchemaMigration):
+from django.db import models, migrations
- def forwards(self, orm):
- # Removing unique constraint on 'FilePerms', fields ['owner', 'perms', 'group']
- db.delete_unique('Reporting_fileperms', ['owner', 'perms', 'group'])
- # Renaming field 'FilePerms.perms' to 'FilePerms.mode'
- db.rename_column('Reporting_fileperms', 'perms', 'mode')
+class Migration(migrations.Migration):
- if not settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3':
- # Adding unique constraint on 'FilePerms', fields ['owner', 'group', 'mode']
- db.create_unique('Reporting_fileperms', ['owner', 'group', 'mode'])
+ dependencies = [
+ ('Reporting', '0001_initial'),
+ ]
-
- def backwards(self, orm):
- # Removing unique constraint on 'FilePerms', fields ['owner', 'group', 'mode']
- db.delete_unique('Reporting_fileperms', ['owner', 'group', 'mode'])
-
- # Renaming field 'FilePerms.mode' to 'FilePerms.perms'
- db.rename_column('Reporting_fileperms', 'mode', 'perms')
-
- if not settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3':
- # Adding unique constraint on 'FilePerms', fields ['owner', 'perms', 'group']
- db.create_unique('Reporting_fileperms', ['owner', 'perms', 'group'])
-
-
- models = {
- 'Reporting.actionentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
- },
- 'Reporting.bundle': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
- },
- 'Reporting.client': {
- 'Meta': {'object_name': 'Client'},
- 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
- 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.deviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_major': ('django.db.models.fields.IntegerField', [], {}),
- 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
- 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_major': ('django.db.models.fields.IntegerField', [], {}),
- 'target_minor': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.failureentry': {
- 'Meta': {'object_name': 'FailureEntry'},
- 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.TextField', [], {}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileacl': {
- 'Meta': {'object_name': 'FileAcl'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileperms': {
- 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
- 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.group': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
- },
- 'Reporting.interaction': {
- 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
- 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
- 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
- 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
- 'good_count': ('django.db.models.fields.IntegerField', [], {}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
- 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
- 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.Group']"}),
- 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
- 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
- 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
- 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
- 'total_count': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.linkentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
- },
- 'Reporting.packageentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
- 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
- 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
- },
- 'Reporting.pathentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
- 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
- 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
- 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
- },
- 'Reporting.performance': {
- 'Meta': {'object_name': 'Performance'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
- 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
- },
- 'Reporting.serviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
- 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
- }
- }
-
- complete_apps = ['Reporting']
+ operations = [
+ migrations.RenameField(
+ model_name='fileperms',
+ old_name='perms',
+ new_name='mode',
+ ),
+ migrations.AlterUniqueTogether(
+ name='fileperms',
+ unique_together=set([('owner', 'group', 'mode')]),
+ ),
+ ]
diff --git a/src/lib/Bcfg2/Reporting/migrations/0003_expand_hash_key.py b/src/lib/Bcfg2/Reporting/migrations/0003_expand_hash_key.py
index 2da1fa722..b9ae207dd 100644
--- a/src/lib/Bcfg2/Reporting/migrations/0003_expand_hash_key.py
+++ b/src/lib/Bcfg2/Reporting/migrations/0003_expand_hash_key.py
@@ -1,180 +1,44 @@
# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
-
- # Changing field 'FailureEntry.hash_key'
- db.alter_column('Reporting_failureentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
-
- # Changing field 'PackageEntry.hash_key'
- db.alter_column('Reporting_packageentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
-
- # Changing field 'ServiceEntry.hash_key'
- db.alter_column('Reporting_serviceentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
-
- # Changing field 'PathEntry.hash_key'
- db.alter_column('Reporting_pathentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
-
- # Changing field 'ActionEntry.hash_key'
- db.alter_column('Reporting_actionentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
-
- def backwards(self, orm):
-
- # Changing field 'FailureEntry.hash_key'
- db.alter_column('Reporting_failureentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
-
- # Changing field 'PackageEntry.hash_key'
- db.alter_column('Reporting_packageentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
-
- # Changing field 'ServiceEntry.hash_key'
- db.alter_column('Reporting_serviceentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
-
- # Changing field 'PathEntry.hash_key'
- db.alter_column('Reporting_pathentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
-
- # Changing field 'ActionEntry.hash_key'
- db.alter_column('Reporting_actionentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
-
- models = {
- 'Reporting.actionentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
- },
- 'Reporting.bundle': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
- },
- 'Reporting.client': {
- 'Meta': {'object_name': 'Client'},
- 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
- 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.deviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_major': ('django.db.models.fields.IntegerField', [], {}),
- 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
- 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_major': ('django.db.models.fields.IntegerField', [], {}),
- 'target_minor': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.failureentry': {
- 'Meta': {'object_name': 'FailureEntry'},
- 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.TextField', [], {}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileacl': {
- 'Meta': {'object_name': 'FileAcl'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileperms': {
- 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
- 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.group': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
- },
- 'Reporting.interaction': {
- 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
- 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
- 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
- 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
- 'good_count': ('django.db.models.fields.IntegerField', [], {}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
- 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
- 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.Group']"}),
- 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
- 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
- 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
- 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
- 'total_count': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.linkentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
- },
- 'Reporting.packageentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
- 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
- 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
- },
- 'Reporting.pathentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
- 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
- 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
- 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
- },
- 'Reporting.performance': {
- 'Meta': {'object_name': 'Performance'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
- 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
- },
- 'Reporting.serviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
- 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
- }
- }
-
- complete_apps = ['Reporting'] \ No newline at end of file
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('Reporting', '0002_convert_perms_to_mode'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='actionentry',
+ name='hash_key',
+ field=models.BigIntegerField(editable=False, db_index=True),
+ preserve_default=True,
+ ),
+ migrations.AlterField(
+ model_name='failureentry',
+ name='hash_key',
+ field=models.BigIntegerField(editable=False, db_index=True),
+ preserve_default=True,
+ ),
+ migrations.AlterField(
+ model_name='packageentry',
+ name='hash_key',
+ field=models.BigIntegerField(editable=False, db_index=True),
+ preserve_default=True,
+ ),
+ migrations.AlterField(
+ model_name='pathentry',
+ name='hash_key',
+ field=models.BigIntegerField(editable=False, db_index=True),
+ preserve_default=True,
+ ),
+ migrations.AlterField(
+ model_name='serviceentry',
+ name='hash_key',
+ field=models.BigIntegerField(editable=False, db_index=True),
+ preserve_default=True,
+ ),
+ ]
diff --git a/src/lib/Bcfg2/Reporting/migrations/0004_profile_can_be_null.py b/src/lib/Bcfg2/Reporting/migrations/0004_profile_can_be_null.py
index 26a053b67..d0b2de417 100644
--- a/src/lib/Bcfg2/Reporting/migrations/0004_profile_can_be_null.py
+++ b/src/lib/Bcfg2/Reporting/migrations/0004_profile_can_be_null.py
@@ -1,156 +1,20 @@
# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
+from __future__ import unicode_literals
+from django.db import models, migrations
-class Migration(SchemaMigration):
- def forwards(self, orm):
+class Migration(migrations.Migration):
- # Changing field 'Interaction.profile'
- db.alter_column('Reporting_interaction', 'profile_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['Reporting.Group']))
+ dependencies = [
+ ('Reporting', '0003_expand_hash_key'),
+ ]
- def backwards(self, orm):
-
- # User chose to not deal with backwards NULL issues for 'Interaction.profile'
- raise RuntimeError("Cannot reverse this migration. 'Interaction.profile' and its values cannot be restored.")
-
- models = {
- 'Reporting.actionentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
- },
- 'Reporting.bundle': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
- },
- 'Reporting.client': {
- 'Meta': {'object_name': 'Client'},
- 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
- 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.deviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_major': ('django.db.models.fields.IntegerField', [], {}),
- 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
- 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_major': ('django.db.models.fields.IntegerField', [], {}),
- 'target_minor': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.failureentry': {
- 'Meta': {'object_name': 'FailureEntry'},
- 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.TextField', [], {}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileacl': {
- 'Meta': {'object_name': 'FileAcl'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileperms': {
- 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
- 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.group': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
- },
- 'Reporting.interaction': {
- 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
- 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
- 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
- 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
- 'good_count': ('django.db.models.fields.IntegerField', [], {}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
- 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
- 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['Reporting.Group']"}),
- 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
- 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
- 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
- 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
- 'total_count': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.linkentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
- },
- 'Reporting.packageentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
- 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
- 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
- },
- 'Reporting.pathentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
- 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
- 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
- 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
- },
- 'Reporting.performance': {
- 'Meta': {'object_name': 'Performance'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
- 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
- },
- 'Reporting.serviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
- 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
- }
- }
-
- complete_apps = ['Reporting'] \ No newline at end of file
+ operations = [
+ migrations.AlterField(
+ model_name='interaction',
+ name='profile',
+ field=models.ForeignKey(related_name='+', to='Reporting.Group', null=True),
+ preserve_default=True,
+ ),
+ ]
diff --git a/src/lib/Bcfg2/Reporting/migrations/0005_add_selinux_entry_support.py b/src/lib/Bcfg2/Reporting/migrations/0005_add_selinux_entry_support.py
index d5f5d801a..0c6934b10 100644
--- a/src/lib/Bcfg2/Reporting/migrations/0005_add_selinux_entry_support.py
+++ b/src/lib/Bcfg2/Reporting/migrations/0005_add_selinux_entry_support.py
@@ -1,485 +1,222 @@
# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding model 'SELoginEntry'
- db.create_table('Reporting_seloginentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('selinuxuser', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_selinuxuser', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
- ))
- db.send_create_signal('Reporting', ['SELoginEntry'])
-
- # Adding model 'SEUserEntry'
- db.create_table('Reporting_seuserentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('roles', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_roles', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
- ('prefix', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_prefix', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
- ))
- db.send_create_signal('Reporting', ['SEUserEntry'])
-
- # Adding model 'SEBooleanEntry'
- db.create_table('Reporting_sebooleanentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('value', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ))
- db.send_create_signal('Reporting', ['SEBooleanEntry'])
-
- # Adding model 'SENodeEntry'
- db.create_table('Reporting_senodeentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
- ('proto', self.gf('django.db.models.fields.CharField')(max_length=4)),
- ))
- db.send_create_signal('Reporting', ['SENodeEntry'])
-
- # Adding model 'SEFcontextEntry'
- db.create_table('Reporting_sefcontextentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
- ('filetype', self.gf('django.db.models.fields.CharField')(max_length=16)),
- ))
- db.send_create_signal('Reporting', ['SEFcontextEntry'])
-
- # Adding model 'SEInterfaceEntry'
- db.create_table('Reporting_seinterfaceentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
- ))
- db.send_create_signal('Reporting', ['SEInterfaceEntry'])
-
- # Adding model 'SEPermissiveEntry'
- db.create_table('Reporting_sepermissiveentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ))
- db.send_create_signal('Reporting', ['SEPermissiveEntry'])
-
- # Adding model 'SEModuleEntry'
- db.create_table('Reporting_semoduleentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('disabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
- ('current_disabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
- ))
- db.send_create_signal('Reporting', ['SEModuleEntry'])
-
- # Adding model 'SEPortEntry'
- db.create_table('Reporting_seportentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
- ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
- ))
- db.send_create_signal('Reporting', ['SEPortEntry'])
-
- # Adding M2M table for field sebooleans on 'Interaction'
- db.create_table('Reporting_interaction_sebooleans', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('sebooleanentry', models.ForeignKey(orm['Reporting.sebooleanentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_sebooleans', ['interaction_id', 'sebooleanentry_id'])
-
- # Adding M2M table for field seports on 'Interaction'
- db.create_table('Reporting_interaction_seports', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('seportentry', models.ForeignKey(orm['Reporting.seportentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_seports', ['interaction_id', 'seportentry_id'])
-
- # Adding M2M table for field sefcontexts on 'Interaction'
- db.create_table('Reporting_interaction_sefcontexts', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('sefcontextentry', models.ForeignKey(orm['Reporting.sefcontextentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_sefcontexts', ['interaction_id', 'sefcontextentry_id'])
-
- # Adding M2M table for field senodes on 'Interaction'
- db.create_table('Reporting_interaction_senodes', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('senodeentry', models.ForeignKey(orm['Reporting.senodeentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_senodes', ['interaction_id', 'senodeentry_id'])
-
- # Adding M2M table for field selogins on 'Interaction'
- db.create_table('Reporting_interaction_selogins', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('seloginentry', models.ForeignKey(orm['Reporting.seloginentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_selogins', ['interaction_id', 'seloginentry_id'])
-
- # Adding M2M table for field seusers on 'Interaction'
- db.create_table('Reporting_interaction_seusers', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('seuserentry', models.ForeignKey(orm['Reporting.seuserentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_seusers', ['interaction_id', 'seuserentry_id'])
-
- # Adding M2M table for field seinterfaces on 'Interaction'
- db.create_table('Reporting_interaction_seinterfaces', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('seinterfaceentry', models.ForeignKey(orm['Reporting.seinterfaceentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_seinterfaces', ['interaction_id', 'seinterfaceentry_id'])
-
- # Adding M2M table for field sepermissives on 'Interaction'
- db.create_table('Reporting_interaction_sepermissives', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('sepermissiveentry', models.ForeignKey(orm['Reporting.sepermissiveentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_sepermissives', ['interaction_id', 'sepermissiveentry_id'])
-
- # Adding M2M table for field semodules on 'Interaction'
- db.create_table('Reporting_interaction_semodules', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('semoduleentry', models.ForeignKey(orm['Reporting.semoduleentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_semodules', ['interaction_id', 'semoduleentry_id'])
-
-
- def backwards(self, orm):
- # Deleting model 'SELoginEntry'
- db.delete_table('Reporting_seloginentry')
-
- # Deleting model 'SEUserEntry'
- db.delete_table('Reporting_seuserentry')
-
- # Deleting model 'SEBooleanEntry'
- db.delete_table('Reporting_sebooleanentry')
-
- # Deleting model 'SENodeEntry'
- db.delete_table('Reporting_senodeentry')
-
- # Deleting model 'SEFcontextEntry'
- db.delete_table('Reporting_sefcontextentry')
-
- # Deleting model 'SEInterfaceEntry'
- db.delete_table('Reporting_seinterfaceentry')
-
- # Deleting model 'SEPermissiveEntry'
- db.delete_table('Reporting_sepermissiveentry')
-
- # Deleting model 'SEModuleEntry'
- db.delete_table('Reporting_semoduleentry')
-
- # Deleting model 'SEPortEntry'
- db.delete_table('Reporting_seportentry')
-
- # Removing M2M table for field sebooleans on 'Interaction'
- db.delete_table('Reporting_interaction_sebooleans')
-
- # Removing M2M table for field seports on 'Interaction'
- db.delete_table('Reporting_interaction_seports')
-
- # Removing M2M table for field sefcontexts on 'Interaction'
- db.delete_table('Reporting_interaction_sefcontexts')
-
- # Removing M2M table for field senodes on 'Interaction'
- db.delete_table('Reporting_interaction_senodes')
-
- # Removing M2M table for field selogins on 'Interaction'
- db.delete_table('Reporting_interaction_selogins')
-
- # Removing M2M table for field seusers on 'Interaction'
- db.delete_table('Reporting_interaction_seusers')
-
- # Removing M2M table for field seinterfaces on 'Interaction'
- db.delete_table('Reporting_interaction_seinterfaces')
-
- # Removing M2M table for field sepermissives on 'Interaction'
- db.delete_table('Reporting_interaction_sepermissives')
-
- # Removing M2M table for field semodules on 'Interaction'
- db.delete_table('Reporting_interaction_semodules')
-
-
- models = {
- 'Reporting.actionentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
- },
- 'Reporting.bundle': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
- },
- 'Reporting.client': {
- 'Meta': {'object_name': 'Client'},
- 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
- 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.deviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_major': ('django.db.models.fields.IntegerField', [], {}),
- 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
- 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_major': ('django.db.models.fields.IntegerField', [], {}),
- 'target_minor': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.failureentry': {
- 'Meta': {'object_name': 'FailureEntry'},
- 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.TextField', [], {}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileacl': {
- 'Meta': {'object_name': 'FileAcl'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileperms': {
- 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
- 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.group': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
- },
- 'Reporting.interaction': {
- 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
- 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
- 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
- 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
- 'good_count': ('django.db.models.fields.IntegerField', [], {}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
- 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
- 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['Reporting.Group']"}),
- 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'sebooleans': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEBooleanEntry']", 'symmetrical': 'False'}),
- 'sefcontexts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEFcontextEntry']", 'symmetrical': 'False'}),
- 'seinterfaces': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEInterfaceEntry']", 'symmetrical': 'False'}),
- 'selogins': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SELoginEntry']", 'symmetrical': 'False'}),
- 'semodules': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEModuleEntry']", 'symmetrical': 'False'}),
- 'senodes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SENodeEntry']", 'symmetrical': 'False'}),
- 'sepermissives': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPermissiveEntry']", 'symmetrical': 'False'}),
- 'seports': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPortEntry']", 'symmetrical': 'False'}),
- 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
- 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
- 'seusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEUserEntry']", 'symmetrical': 'False'}),
- 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
- 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
- 'total_count': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.linkentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
- },
- 'Reporting.packageentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
- 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
- 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
- },
- 'Reporting.pathentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
- 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
- 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
- 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
- },
- 'Reporting.performance': {
- 'Meta': {'object_name': 'Performance'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
- 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
- },
- 'Reporting.sebooleanentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEBooleanEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'value': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
- },
- 'Reporting.sefcontextentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEFcontextEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'filetype': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.seinterfaceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEInterfaceEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.seloginentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SELoginEntry'},
- 'current_selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.semoduleentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEModuleEntry'},
- 'current_disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.senodeentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SENodeEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'proto': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.sepermissiveentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPermissiveEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.seportentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPortEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.serviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
- 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
- },
- 'Reporting.seuserentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEUserEntry'},
- 'current_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'current_roles': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'prefix': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'roles': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- }
- }
-
- complete_apps = ['Reporting'] \ No newline at end of file
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('Reporting', '0004_profile_can_be_null'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SEBooleanEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('value', models.BooleanField(default=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SEFcontextEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('selinuxtype', models.CharField(max_length=128)),
+ ('current_selinuxtype', models.CharField(max_length=128, null=True)),
+ ('filetype', models.CharField(max_length=16)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SEInterfaceEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('selinuxtype', models.CharField(max_length=128)),
+ ('current_selinuxtype', models.CharField(max_length=128, null=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SELoginEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('selinuxuser', models.CharField(max_length=128)),
+ ('current_selinuxuser', models.CharField(max_length=128, null=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SEModuleEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('disabled', models.BooleanField(default=False)),
+ ('current_disabled', models.BooleanField(default=False)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SENodeEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('selinuxtype', models.CharField(max_length=128)),
+ ('current_selinuxtype', models.CharField(max_length=128, null=True)),
+ ('proto', models.CharField(max_length=4)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SEPermissiveEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SEPortEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('selinuxtype', models.CharField(max_length=128)),
+ ('current_selinuxtype', models.CharField(max_length=128, null=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='SEUserEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('roles', models.CharField(max_length=128)),
+ ('current_roles', models.CharField(max_length=128, null=True)),
+ ('prefix', models.CharField(max_length=128)),
+ ('current_prefix', models.CharField(max_length=128, null=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='sebooleans',
+ field=models.ManyToManyField(to='Reporting.SEBooleanEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='sefcontexts',
+ field=models.ManyToManyField(to='Reporting.SEFcontextEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='seinterfaces',
+ field=models.ManyToManyField(to='Reporting.SEInterfaceEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='selogins',
+ field=models.ManyToManyField(to='Reporting.SELoginEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='semodules',
+ field=models.ManyToManyField(to='Reporting.SEModuleEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='senodes',
+ field=models.ManyToManyField(to='Reporting.SENodeEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='sepermissives',
+ field=models.ManyToManyField(to='Reporting.SEPermissiveEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='seports',
+ field=models.ManyToManyField(to='Reporting.SEPortEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='seusers',
+ field=models.ManyToManyField(to='Reporting.SEUserEntry'),
+ preserve_default=True,
+ ),
+ ]
diff --git a/src/lib/Bcfg2/Reporting/migrations/0006_add_user_group_entry_support.py b/src/lib/Bcfg2/Reporting/migrations/0006_add_user_group_entry_support.py
index d86e663d5..4853faaf3 100644
--- a/src/lib/Bcfg2/Reporting/migrations/0006_add_user_group_entry_support.py
+++ b/src/lib/Bcfg2/Reporting/migrations/0006_add_user_group_entry_support.py
@@ -1,340 +1,68 @@
# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
+from __future__ import unicode_literals
+from django.db import models, migrations
-class Migration(SchemaMigration):
- def forwards(self, orm):
- # Adding model 'POSIXGroupEntry'
- db.create_table('Reporting_posixgroupentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('gid', self.gf('django.db.models.fields.IntegerField')(null=True)),
- ('current_gid', self.gf('django.db.models.fields.IntegerField')(null=True)),
- ))
- db.send_create_signal('Reporting', ['POSIXGroupEntry'])
+class Migration(migrations.Migration):
- # Adding model 'POSIXUserEntry'
- db.create_table('Reporting_posixuserentry', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
- ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
- ('state', self.gf('django.db.models.fields.IntegerField')()),
- ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
- ('uid', self.gf('django.db.models.fields.IntegerField')(null=True)),
- ('current_uid', self.gf('django.db.models.fields.IntegerField')(null=True)),
- ('group', self.gf('django.db.models.fields.CharField')(max_length=64)),
- ('current_group', self.gf('django.db.models.fields.CharField')(max_length=64, null=True)),
- ('gecos', self.gf('django.db.models.fields.CharField')(max_length=1024)),
- ('current_gecos', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)),
- ('home', self.gf('django.db.models.fields.CharField')(max_length=1024)),
- ('current_home', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)),
- ('shell', self.gf('django.db.models.fields.CharField')(default='/bin/bash', max_length=1024)),
- ('current_shell', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)),
- ))
- db.send_create_signal('Reporting', ['POSIXUserEntry'])
+ dependencies = [
+ ('Reporting', '0005_add_selinux_entry_support'),
+ ]
- # Adding M2M table for field posixusers on 'Interaction'
- db.create_table('Reporting_interaction_posixusers', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('posixuserentry', models.ForeignKey(orm['Reporting.posixuserentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_posixusers', ['interaction_id', 'posixuserentry_id'])
-
- # Adding M2M table for field posixgroups on 'Interaction'
- db.create_table('Reporting_interaction_posixgroups', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
- ('posixgroupentry', models.ForeignKey(orm['Reporting.posixgroupentry'], null=False))
- ))
- db.create_unique('Reporting_interaction_posixgroups', ['interaction_id', 'posixgroupentry_id'])
-
-
- def backwards(self, orm):
- # Deleting model 'POSIXGroupEntry'
- db.delete_table('Reporting_posixgroupentry')
-
- # Deleting model 'POSIXUserEntry'
- db.delete_table('Reporting_posixuserentry')
-
- # Removing M2M table for field posixusers on 'Interaction'
- db.delete_table('Reporting_interaction_posixusers')
-
- # Removing M2M table for field posixgroups on 'Interaction'
- db.delete_table('Reporting_interaction_posixgroups')
-
-
- models = {
- 'Reporting.actionentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
- },
- 'Reporting.bundle': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
- },
- 'Reporting.client': {
- 'Meta': {'object_name': 'Client'},
- 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
- 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
- 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.deviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_major': ('django.db.models.fields.IntegerField', [], {}),
- 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
- 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_major': ('django.db.models.fields.IntegerField', [], {}),
- 'target_minor': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.failureentry': {
- 'Meta': {'object_name': 'FailureEntry'},
- 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'message': ('django.db.models.fields.TextField', [], {}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileacl': {
- 'Meta': {'object_name': 'FileAcl'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
- },
- 'Reporting.fileperms': {
- 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
- 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
- },
- 'Reporting.group': {
- 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
- },
- 'Reporting.interaction': {
- 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
- 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
- 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
- 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
- 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
- 'good_count': ('django.db.models.fields.IntegerField', [], {}),
- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
- 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
- 'posixgroups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.POSIXGroupEntry']", 'symmetrical': 'False'}),
- 'posixusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.POSIXUserEntry']", 'symmetrical': 'False'}),
- 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['Reporting.Group']"}),
- 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'sebooleans': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEBooleanEntry']", 'symmetrical': 'False'}),
- 'sefcontexts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEFcontextEntry']", 'symmetrical': 'False'}),
- 'seinterfaces': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEInterfaceEntry']", 'symmetrical': 'False'}),
- 'selogins': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SELoginEntry']", 'symmetrical': 'False'}),
- 'semodules': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEModuleEntry']", 'symmetrical': 'False'}),
- 'senodes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SENodeEntry']", 'symmetrical': 'False'}),
- 'sepermissives': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPermissiveEntry']", 'symmetrical': 'False'}),
- 'seports': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPortEntry']", 'symmetrical': 'False'}),
- 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
- 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
- 'seusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEUserEntry']", 'symmetrical': 'False'}),
- 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
- 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
- 'total_count': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.linkentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
- 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
- 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
- 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
- },
- 'Reporting.packageentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
- 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
- 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
- },
- 'Reporting.pathentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
- 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
- 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
- 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
- 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
- },
- 'Reporting.performance': {
- 'Meta': {'object_name': 'Performance'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
- 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
- },
- 'Reporting.posixgroupentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXGroupEntry'},
- 'current_gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.posixuserentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXUserEntry'},
- 'current_gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
- 'current_group': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
- 'current_home': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
- 'current_shell': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
- 'current_uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'group': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'home': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'shell': ('django.db.models.fields.CharField', [], {'default': "'/bin/bash'", 'max_length': '1024'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
- },
- 'Reporting.sebooleanentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEBooleanEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'value': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
- },
- 'Reporting.sefcontextentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEFcontextEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'filetype': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.seinterfaceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEInterfaceEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.seloginentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SELoginEntry'},
- 'current_selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.semoduleentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEModuleEntry'},
- 'current_disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.senodeentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SENodeEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'proto': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.sepermissiveentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPermissiveEntry'},
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.seportentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPortEntry'},
- 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- },
- 'Reporting.serviceentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
- 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'state': ('django.db.models.fields.IntegerField', [], {}),
- 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
- },
- 'Reporting.seuserentry': {
- 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEUserEntry'},
- 'current_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'current_roles': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
- 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
- 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
- 'prefix': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'roles': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
- 'state': ('django.db.models.fields.IntegerField', [], {})
- }
- }
-
- complete_apps = ['Reporting'] \ No newline at end of file
+ operations = [
+ migrations.CreateModel(
+ name='POSIXGroupEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('gid', models.IntegerField(null=True)),
+ ('current_gid', models.IntegerField(null=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.CreateModel(
+ name='POSIXUserEntry',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('name', models.CharField(max_length=128, db_index=True)),
+ ('hash_key', models.BigIntegerField(editable=False, db_index=True)),
+ ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])),
+ ('exists', models.BooleanField(default=True)),
+ ('uid', models.IntegerField(null=True)),
+ ('current_uid', models.IntegerField(null=True)),
+ ('group', models.CharField(max_length=64)),
+ ('current_group', models.CharField(max_length=64, null=True)),
+ ('gecos', models.CharField(max_length=1024)),
+ ('current_gecos', models.CharField(max_length=1024, null=True)),
+ ('home', models.CharField(max_length=1024)),
+ ('current_home', models.CharField(max_length=1024, null=True)),
+ ('shell', models.CharField(default=b'/bin/bash', max_length=1024)),
+ ('current_shell', models.CharField(max_length=1024, null=True)),
+ ],
+ options={
+ 'ordering': ('state', 'name'),
+ 'abstract': False,
+ },
+ bases=(models.Model,),
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='posixgroups',
+ field=models.ManyToManyField(to='Reporting.POSIXGroupEntry'),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='posixusers',
+ field=models.ManyToManyField(to='Reporting.POSIXUserEntry'),
+ preserve_default=True,
+ ),
+ ]
diff --git a/src/lib/Bcfg2/Reporting/migrations/0007_add_flag_fields_interaction.py b/src/lib/Bcfg2/Reporting/migrations/0007_add_flag_fields_interaction.py
new file mode 100644
index 000000000..1d1274288
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/migrations/0007_add_flag_fields_interaction.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('Reporting', '0006_add_user_group_entry_support'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='interaction',
+ name='dry_run',
+ field=models.BooleanField(default=False),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='interaction',
+ name='only_important',
+ field=models.BooleanField(default=False),
+ preserve_default=True,
+ ),
+ ]
diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py
index ae6f6731b..6ba7d3765 100644
--- a/src/lib/Bcfg2/Reporting/models.py
+++ b/src/lib/Bcfg2/Reporting/models.py
@@ -1,9 +1,10 @@
"""Django models for Bcfg2 reports."""
import sys
+import django
from django.core.exceptions import ImproperlyConfigured
try:
- from django.db import models, backend, connections
+ from django.db import models, connections
except ImproperlyConfigured:
e = sys.exc_info()[1]
print("Reports: unable to import django models: %s" % e)
@@ -61,11 +62,15 @@ def _quote(value):
"""
global _our_backend
if not _our_backend:
- try:
- _our_backend = backend.DatabaseOperations(
- connections[get_db_label('Reporting')])
- except TypeError:
- _our_backend = backend.DatabaseOperations()
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ _our_backend = connections[get_db_label('Reporting')].ops
+ else:
+ from django.db import backend
+ try:
+ _our_backend = backend.DatabaseOperations(
+ connections[get_db_label('Reporting')])
+ except TypeError:
+ _our_backend = backend.DatabaseOperations()
return _our_backend.quote_name(value)
@@ -144,6 +149,8 @@ class Interaction(models.Model):
bad_count = models.IntegerField(default=0)
modified_count = models.IntegerField(default=0)
extra_count = models.IntegerField(default=0)
+ dry_run = models.BooleanField(default=False)
+ only_important = models.BooleanField(default=False)
actions = models.ManyToManyField("ActionEntry")
packages = models.ManyToManyField("PackageEntry")
@@ -630,7 +637,7 @@ class POSIXGroupEntry(SuccessEntry):
class PackageEntry(SuccessEntry):
""" The new model for package information """
- # if this is an extra entry trget_version will be empty
+ # if this is an extra entry target_version will be empty
target_version = models.CharField(max_length=1024, default='')
current_version = models.CharField(max_length=1024)
verification_details = models.TextField(default="")
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0001_initial.py b/src/lib/Bcfg2/Reporting/south_migrations/0001_initial.py
new file mode 100644
index 000000000..609290edb
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/0001_initial.py
@@ -0,0 +1,465 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'Client'
+ db.create_table('Reporting_client', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('creation', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_interaction', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='parent_client', null=True, to=orm['Reporting.Interaction'])),
+ ('expiration', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('Reporting', ['Client'])
+
+ # Adding model 'Interaction'
+ db.create_table('Reporting_interaction', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('client', self.gf('django.db.models.fields.related.ForeignKey')(related_name='interactions', to=orm['Reporting.Client'])),
+ ('timestamp', self.gf('django.db.models.fields.DateTimeField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.CharField')(max_length=32)),
+ ('repo_rev_code', self.gf('django.db.models.fields.CharField')(max_length=64)),
+ ('server', self.gf('django.db.models.fields.CharField')(max_length=256)),
+ ('good_count', self.gf('django.db.models.fields.IntegerField')()),
+ ('total_count', self.gf('django.db.models.fields.IntegerField')()),
+ ('bad_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ('modified_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ('extra_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ('profile', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['Reporting.Group'])),
+ ))
+ db.send_create_signal('Reporting', ['Interaction'])
+
+ # Adding unique constraint on 'Interaction', fields ['client', 'timestamp']
+ db.create_unique('Reporting_interaction', ['client_id', 'timestamp'])
+
+ # Adding M2M table for field actions on 'Interaction'
+ db.create_table('Reporting_interaction_actions', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('actionentry', models.ForeignKey(orm['Reporting.actionentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_actions', ['interaction_id', 'actionentry_id'])
+
+ # Adding M2M table for field packages on 'Interaction'
+ db.create_table('Reporting_interaction_packages', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('packageentry', models.ForeignKey(orm['Reporting.packageentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_packages', ['interaction_id', 'packageentry_id'])
+
+ # Adding M2M table for field paths on 'Interaction'
+ db.create_table('Reporting_interaction_paths', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('pathentry', models.ForeignKey(orm['Reporting.pathentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_paths', ['interaction_id', 'pathentry_id'])
+
+ # Adding M2M table for field services on 'Interaction'
+ db.create_table('Reporting_interaction_services', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('serviceentry', models.ForeignKey(orm['Reporting.serviceentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_services', ['interaction_id', 'serviceentry_id'])
+
+ # Adding M2M table for field failures on 'Interaction'
+ db.create_table('Reporting_interaction_failures', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('failureentry', models.ForeignKey(orm['Reporting.failureentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_failures', ['interaction_id', 'failureentry_id'])
+
+ # Adding M2M table for field groups on 'Interaction'
+ db.create_table('Reporting_interaction_groups', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('group', models.ForeignKey(orm['Reporting.group'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_groups', ['interaction_id', 'group_id'])
+
+ # Adding M2M table for field bundles on 'Interaction'
+ db.create_table('Reporting_interaction_bundles', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('bundle', models.ForeignKey(orm['Reporting.bundle'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_bundles', ['interaction_id', 'bundle_id'])
+
+ # Adding model 'Performance'
+ db.create_table('Reporting_performance', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('interaction', self.gf('django.db.models.fields.related.ForeignKey')(related_name='performance_items', to=orm['Reporting.Interaction'])),
+ ('metric', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('value', self.gf('django.db.models.fields.DecimalField')(max_digits=32, decimal_places=16)),
+ ))
+ db.send_create_signal('Reporting', ['Performance'])
+
+ # Adding model 'Group'
+ db.create_table('Reporting_group', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+ ('profile', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('public', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('category', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)),
+ ('comment', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('Reporting', ['Group'])
+
+ # Adding M2M table for field groups on 'Group'
+ db.create_table('Reporting_group_groups', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('from_group', models.ForeignKey(orm['Reporting.group'], null=False)),
+ ('to_group', models.ForeignKey(orm['Reporting.group'], null=False))
+ ))
+ db.create_unique('Reporting_group_groups', ['from_group_id', 'to_group_id'])
+
+ # Adding M2M table for field bundles on 'Group'
+ db.create_table('Reporting_group_bundles', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('group', models.ForeignKey(orm['Reporting.group'], null=False)),
+ ('bundle', models.ForeignKey(orm['Reporting.bundle'], null=False))
+ ))
+ db.create_unique('Reporting_group_bundles', ['group_id', 'bundle_id'])
+
+ # Adding model 'Bundle'
+ db.create_table('Reporting_bundle', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
+ ))
+ db.send_create_signal('Reporting', ['Bundle'])
+
+ # Adding model 'FilePerms'
+ db.create_table('Reporting_fileperms', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('owner', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('group', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('perms', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ))
+ db.send_create_signal('Reporting', ['FilePerms'])
+
+ # Adding unique constraint on 'FilePerms', fields ['owner', 'group', 'perms']
+ db.create_unique('Reporting_fileperms', ['owner', 'group', 'perms'])
+
+ # Adding model 'FileAcl'
+ db.create_table('Reporting_fileacl', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ))
+ db.send_create_signal('Reporting', ['FileAcl'])
+
+ # Adding model 'FailureEntry'
+ db.create_table('Reporting_failureentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+ ('entry_type', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('message', self.gf('django.db.models.fields.TextField')()),
+ ))
+ db.send_create_signal('Reporting', ['FailureEntry'])
+
+ # Adding model 'ActionEntry'
+ db.create_table('Reporting_actionentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('status', self.gf('django.db.models.fields.CharField')(default='check', max_length=128)),
+ ('output', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('Reporting', ['ActionEntry'])
+
+ # Adding model 'PackageEntry'
+ db.create_table('Reporting_packageentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('target_version', self.gf('django.db.models.fields.CharField')(default='', max_length=1024)),
+ ('current_version', self.gf('django.db.models.fields.CharField')(max_length=1024)),
+ ('verification_details', self.gf('django.db.models.fields.TextField')(default='')),
+ ))
+ db.send_create_signal('Reporting', ['PackageEntry'])
+
+ # Adding model 'PathEntry'
+ db.create_table('Reporting_pathentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('path_type', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('target_perms', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['Reporting.FilePerms'])),
+ ('current_perms', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['Reporting.FilePerms'])),
+ ('detail_type', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ('details', self.gf('django.db.models.fields.TextField')(default='')),
+ ))
+ db.send_create_signal('Reporting', ['PathEntry'])
+
+ # Adding M2M table for field acls on 'PathEntry'
+ db.create_table('Reporting_pathentry_acls', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('pathentry', models.ForeignKey(orm['Reporting.pathentry'], null=False)),
+ ('fileacl', models.ForeignKey(orm['Reporting.fileacl'], null=False))
+ ))
+ db.create_unique('Reporting_pathentry_acls', ['pathentry_id', 'fileacl_id'])
+
+ # Adding model 'LinkEntry'
+ db.create_table('Reporting_linkentry', (
+ ('pathentry_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['Reporting.PathEntry'], unique=True, primary_key=True)),
+ ('target_path', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)),
+ ('current_path', self.gf('django.db.models.fields.CharField')(max_length=1024, blank=True)),
+ ))
+ db.send_create_signal('Reporting', ['LinkEntry'])
+
+ # Adding model 'DeviceEntry'
+ db.create_table('Reporting_deviceentry', (
+ ('pathentry_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['Reporting.PathEntry'], unique=True, primary_key=True)),
+ ('device_type', self.gf('django.db.models.fields.CharField')(max_length=16)),
+ ('target_major', self.gf('django.db.models.fields.IntegerField')()),
+ ('target_minor', self.gf('django.db.models.fields.IntegerField')()),
+ ('current_major', self.gf('django.db.models.fields.IntegerField')()),
+ ('current_minor', self.gf('django.db.models.fields.IntegerField')()),
+ ))
+ db.send_create_signal('Reporting', ['DeviceEntry'])
+
+ # Adding model 'ServiceEntry'
+ db.create_table('Reporting_serviceentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('target_status', self.gf('django.db.models.fields.CharField')(default='', max_length=128)),
+ ('current_status', self.gf('django.db.models.fields.CharField')(default='', max_length=128)),
+ ))
+ db.send_create_signal('Reporting', ['ServiceEntry'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'FilePerms', fields ['owner', 'group', 'perms']
+ db.delete_unique('Reporting_fileperms', ['owner', 'group', 'perms'])
+
+ # Removing unique constraint on 'Interaction', fields ['client', 'timestamp']
+ db.delete_unique('Reporting_interaction', ['client_id', 'timestamp'])
+
+ # Deleting model 'Client'
+ db.delete_table('Reporting_client')
+
+ # Deleting model 'Interaction'
+ db.delete_table('Reporting_interaction')
+
+ # Removing M2M table for field actions on 'Interaction'
+ db.delete_table('Reporting_interaction_actions')
+
+ # Removing M2M table for field packages on 'Interaction'
+ db.delete_table('Reporting_interaction_packages')
+
+ # Removing M2M table for field paths on 'Interaction'
+ db.delete_table('Reporting_interaction_paths')
+
+ # Removing M2M table for field services on 'Interaction'
+ db.delete_table('Reporting_interaction_services')
+
+ # Removing M2M table for field failures on 'Interaction'
+ db.delete_table('Reporting_interaction_failures')
+
+ # Removing M2M table for field groups on 'Interaction'
+ db.delete_table('Reporting_interaction_groups')
+
+ # Removing M2M table for field bundles on 'Interaction'
+ db.delete_table('Reporting_interaction_bundles')
+
+ # Deleting model 'Performance'
+ db.delete_table('Reporting_performance')
+
+ # Deleting model 'Group'
+ db.delete_table('Reporting_group')
+
+ # Removing M2M table for field groups on 'Group'
+ db.delete_table('Reporting_group_groups')
+
+ # Removing M2M table for field bundles on 'Group'
+ db.delete_table('Reporting_group_bundles')
+
+ # Deleting model 'Bundle'
+ db.delete_table('Reporting_bundle')
+
+ # Deleting model 'FilePerms'
+ db.delete_table('Reporting_fileperms')
+
+ # Deleting model 'FileAcl'
+ db.delete_table('Reporting_fileacl')
+
+ # Deleting model 'FailureEntry'
+ db.delete_table('Reporting_failureentry')
+
+ # Deleting model 'ActionEntry'
+ db.delete_table('Reporting_actionentry')
+
+ # Deleting model 'PackageEntry'
+ db.delete_table('Reporting_packageentry')
+
+ # Deleting model 'PathEntry'
+ db.delete_table('Reporting_pathentry')
+
+ # Removing M2M table for field acls on 'PathEntry'
+ db.delete_table('Reporting_pathentry_acls')
+
+ # Deleting model 'LinkEntry'
+ db.delete_table('Reporting_linkentry')
+
+ # Deleting model 'DeviceEntry'
+ db.delete_table('Reporting_deviceentry')
+
+ # Deleting model 'ServiceEntry'
+ db.delete_table('Reporting_serviceentry')
+
+
+ models = {
+ 'Reporting.actionentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
+ },
+ 'Reporting.bundle': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'Reporting.client': {
+ 'Meta': {'object_name': 'Client'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
+ 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.deviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_minor': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.failureentry': {
+ 'Meta': {'object_name': 'FailureEntry'},
+ 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileacl': {
+ 'Meta': {'object_name': 'FileAcl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileperms': {
+ 'Meta': {'unique_together': "(('owner', 'group', 'perms'),)", 'object_name': 'FilePerms'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'perms': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.group': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'Reporting.interaction': {
+ 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
+ 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
+ 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
+ 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
+ 'good_count': ('django.db.models.fields.IntegerField', [], {}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
+ 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
+ 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.Group']"}),
+ 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'total_count': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.linkentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
+ },
+ 'Reporting.packageentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
+ 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
+ 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
+ },
+ 'Reporting.pathentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
+ 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
+ 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
+ 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
+ },
+ 'Reporting.performance': {
+ 'Meta': {'object_name': 'Performance'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
+ 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
+ },
+ 'Reporting.serviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
+ 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
+ }
+ }
+
+ complete_apps = ['Reporting'] \ No newline at end of file
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0002_convert_perms_to_mode.py b/src/lib/Bcfg2/Reporting/south_migrations/0002_convert_perms_to_mode.py
new file mode 100644
index 000000000..37cdd146c
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/0002_convert_perms_to_mode.py
@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+from django.conf import settings
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Removing unique constraint on 'FilePerms', fields ['owner', 'perms', 'group']
+ db.delete_unique('Reporting_fileperms', ['owner', 'perms', 'group'])
+
+ # Renaming field 'FilePerms.perms' to 'FilePerms.mode'
+ db.rename_column('Reporting_fileperms', 'perms', 'mode')
+
+ if not settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3':
+ # Adding unique constraint on 'FilePerms', fields ['owner', 'group', 'mode']
+ db.create_unique('Reporting_fileperms', ['owner', 'group', 'mode'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'FilePerms', fields ['owner', 'group', 'mode']
+ db.delete_unique('Reporting_fileperms', ['owner', 'group', 'mode'])
+
+ # Renaming field 'FilePerms.mode' to 'FilePerms.perms'
+ db.rename_column('Reporting_fileperms', 'mode', 'perms')
+
+ if not settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3':
+ # Adding unique constraint on 'FilePerms', fields ['owner', 'perms', 'group']
+ db.create_unique('Reporting_fileperms', ['owner', 'perms', 'group'])
+
+
+ models = {
+ 'Reporting.actionentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
+ },
+ 'Reporting.bundle': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'Reporting.client': {
+ 'Meta': {'object_name': 'Client'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
+ 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.deviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_minor': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.failureentry': {
+ 'Meta': {'object_name': 'FailureEntry'},
+ 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileacl': {
+ 'Meta': {'object_name': 'FileAcl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileperms': {
+ 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.group': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'Reporting.interaction': {
+ 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
+ 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
+ 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
+ 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
+ 'good_count': ('django.db.models.fields.IntegerField', [], {}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
+ 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
+ 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.Group']"}),
+ 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'total_count': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.linkentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
+ },
+ 'Reporting.packageentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
+ 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
+ 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
+ },
+ 'Reporting.pathentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
+ 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
+ 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
+ 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
+ },
+ 'Reporting.performance': {
+ 'Meta': {'object_name': 'Performance'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
+ 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
+ },
+ 'Reporting.serviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
+ 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
+ }
+ }
+
+ complete_apps = ['Reporting']
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0003_expand_hash_key.py b/src/lib/Bcfg2/Reporting/south_migrations/0003_expand_hash_key.py
new file mode 100644
index 000000000..2da1fa722
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/0003_expand_hash_key.py
@@ -0,0 +1,180 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'FailureEntry.hash_key'
+ db.alter_column('Reporting_failureentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
+
+ # Changing field 'PackageEntry.hash_key'
+ db.alter_column('Reporting_packageentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
+
+ # Changing field 'ServiceEntry.hash_key'
+ db.alter_column('Reporting_serviceentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
+
+ # Changing field 'PathEntry.hash_key'
+ db.alter_column('Reporting_pathentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
+
+ # Changing field 'ActionEntry.hash_key'
+ db.alter_column('Reporting_actionentry', 'hash_key', self.gf('django.db.models.fields.BigIntegerField')())
+
+ def backwards(self, orm):
+
+ # Changing field 'FailureEntry.hash_key'
+ db.alter_column('Reporting_failureentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
+
+ # Changing field 'PackageEntry.hash_key'
+ db.alter_column('Reporting_packageentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
+
+ # Changing field 'ServiceEntry.hash_key'
+ db.alter_column('Reporting_serviceentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
+
+ # Changing field 'PathEntry.hash_key'
+ db.alter_column('Reporting_pathentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
+
+ # Changing field 'ActionEntry.hash_key'
+ db.alter_column('Reporting_actionentry', 'hash_key', self.gf('django.db.models.fields.IntegerField')())
+
+ models = {
+ 'Reporting.actionentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
+ },
+ 'Reporting.bundle': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'Reporting.client': {
+ 'Meta': {'object_name': 'Client'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
+ 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.deviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_minor': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.failureentry': {
+ 'Meta': {'object_name': 'FailureEntry'},
+ 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileacl': {
+ 'Meta': {'object_name': 'FileAcl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileperms': {
+ 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.group': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'Reporting.interaction': {
+ 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
+ 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
+ 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
+ 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
+ 'good_count': ('django.db.models.fields.IntegerField', [], {}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
+ 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
+ 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.Group']"}),
+ 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'total_count': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.linkentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
+ },
+ 'Reporting.packageentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
+ 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
+ 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
+ },
+ 'Reporting.pathentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
+ 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
+ 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
+ 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
+ },
+ 'Reporting.performance': {
+ 'Meta': {'object_name': 'Performance'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
+ 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
+ },
+ 'Reporting.serviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
+ 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
+ }
+ }
+
+ complete_apps = ['Reporting'] \ No newline at end of file
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0004_profile_can_be_null.py b/src/lib/Bcfg2/Reporting/south_migrations/0004_profile_can_be_null.py
new file mode 100644
index 000000000..26a053b67
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/0004_profile_can_be_null.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Changing field 'Interaction.profile'
+ db.alter_column('Reporting_interaction', 'profile_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['Reporting.Group']))
+
+ def backwards(self, orm):
+
+ # User chose to not deal with backwards NULL issues for 'Interaction.profile'
+ raise RuntimeError("Cannot reverse this migration. 'Interaction.profile' and its values cannot be restored.")
+
+ models = {
+ 'Reporting.actionentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
+ },
+ 'Reporting.bundle': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'Reporting.client': {
+ 'Meta': {'object_name': 'Client'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
+ 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.deviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_minor': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.failureentry': {
+ 'Meta': {'object_name': 'FailureEntry'},
+ 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileacl': {
+ 'Meta': {'object_name': 'FileAcl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileperms': {
+ 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.group': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'Reporting.interaction': {
+ 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
+ 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
+ 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
+ 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
+ 'good_count': ('django.db.models.fields.IntegerField', [], {}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
+ 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
+ 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['Reporting.Group']"}),
+ 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'total_count': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.linkentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
+ },
+ 'Reporting.packageentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
+ 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
+ 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
+ },
+ 'Reporting.pathentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
+ 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
+ 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
+ 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
+ },
+ 'Reporting.performance': {
+ 'Meta': {'object_name': 'Performance'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
+ 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
+ },
+ 'Reporting.serviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
+ 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
+ }
+ }
+
+ complete_apps = ['Reporting'] \ No newline at end of file
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0005_add_selinux_entry_support.py b/src/lib/Bcfg2/Reporting/south_migrations/0005_add_selinux_entry_support.py
new file mode 100644
index 000000000..d5f5d801a
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/0005_add_selinux_entry_support.py
@@ -0,0 +1,485 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'SELoginEntry'
+ db.create_table('Reporting_seloginentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('selinuxuser', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_selinuxuser', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
+ ))
+ db.send_create_signal('Reporting', ['SELoginEntry'])
+
+ # Adding model 'SEUserEntry'
+ db.create_table('Reporting_seuserentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('roles', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_roles', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
+ ('prefix', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_prefix', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
+ ))
+ db.send_create_signal('Reporting', ['SEUserEntry'])
+
+ # Adding model 'SEBooleanEntry'
+ db.create_table('Reporting_sebooleanentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('value', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ))
+ db.send_create_signal('Reporting', ['SEBooleanEntry'])
+
+ # Adding model 'SENodeEntry'
+ db.create_table('Reporting_senodeentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
+ ('proto', self.gf('django.db.models.fields.CharField')(max_length=4)),
+ ))
+ db.send_create_signal('Reporting', ['SENodeEntry'])
+
+ # Adding model 'SEFcontextEntry'
+ db.create_table('Reporting_sefcontextentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
+ ('filetype', self.gf('django.db.models.fields.CharField')(max_length=16)),
+ ))
+ db.send_create_signal('Reporting', ['SEFcontextEntry'])
+
+ # Adding model 'SEInterfaceEntry'
+ db.create_table('Reporting_seinterfaceentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
+ ))
+ db.send_create_signal('Reporting', ['SEInterfaceEntry'])
+
+ # Adding model 'SEPermissiveEntry'
+ db.create_table('Reporting_sepermissiveentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ))
+ db.send_create_signal('Reporting', ['SEPermissiveEntry'])
+
+ # Adding model 'SEModuleEntry'
+ db.create_table('Reporting_semoduleentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('disabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('current_disabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ))
+ db.send_create_signal('Reporting', ['SEModuleEntry'])
+
+ # Adding model 'SEPortEntry'
+ db.create_table('Reporting_seportentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128)),
+ ('current_selinuxtype', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)),
+ ))
+ db.send_create_signal('Reporting', ['SEPortEntry'])
+
+ # Adding M2M table for field sebooleans on 'Interaction'
+ db.create_table('Reporting_interaction_sebooleans', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('sebooleanentry', models.ForeignKey(orm['Reporting.sebooleanentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_sebooleans', ['interaction_id', 'sebooleanentry_id'])
+
+ # Adding M2M table for field seports on 'Interaction'
+ db.create_table('Reporting_interaction_seports', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('seportentry', models.ForeignKey(orm['Reporting.seportentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_seports', ['interaction_id', 'seportentry_id'])
+
+ # Adding M2M table for field sefcontexts on 'Interaction'
+ db.create_table('Reporting_interaction_sefcontexts', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('sefcontextentry', models.ForeignKey(orm['Reporting.sefcontextentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_sefcontexts', ['interaction_id', 'sefcontextentry_id'])
+
+ # Adding M2M table for field senodes on 'Interaction'
+ db.create_table('Reporting_interaction_senodes', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('senodeentry', models.ForeignKey(orm['Reporting.senodeentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_senodes', ['interaction_id', 'senodeentry_id'])
+
+ # Adding M2M table for field selogins on 'Interaction'
+ db.create_table('Reporting_interaction_selogins', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('seloginentry', models.ForeignKey(orm['Reporting.seloginentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_selogins', ['interaction_id', 'seloginentry_id'])
+
+ # Adding M2M table for field seusers on 'Interaction'
+ db.create_table('Reporting_interaction_seusers', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('seuserentry', models.ForeignKey(orm['Reporting.seuserentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_seusers', ['interaction_id', 'seuserentry_id'])
+
+ # Adding M2M table for field seinterfaces on 'Interaction'
+ db.create_table('Reporting_interaction_seinterfaces', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('seinterfaceentry', models.ForeignKey(orm['Reporting.seinterfaceentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_seinterfaces', ['interaction_id', 'seinterfaceentry_id'])
+
+ # Adding M2M table for field sepermissives on 'Interaction'
+ db.create_table('Reporting_interaction_sepermissives', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('sepermissiveentry', models.ForeignKey(orm['Reporting.sepermissiveentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_sepermissives', ['interaction_id', 'sepermissiveentry_id'])
+
+ # Adding M2M table for field semodules on 'Interaction'
+ db.create_table('Reporting_interaction_semodules', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('semoduleentry', models.ForeignKey(orm['Reporting.semoduleentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_semodules', ['interaction_id', 'semoduleentry_id'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'SELoginEntry'
+ db.delete_table('Reporting_seloginentry')
+
+ # Deleting model 'SEUserEntry'
+ db.delete_table('Reporting_seuserentry')
+
+ # Deleting model 'SEBooleanEntry'
+ db.delete_table('Reporting_sebooleanentry')
+
+ # Deleting model 'SENodeEntry'
+ db.delete_table('Reporting_senodeentry')
+
+ # Deleting model 'SEFcontextEntry'
+ db.delete_table('Reporting_sefcontextentry')
+
+ # Deleting model 'SEInterfaceEntry'
+ db.delete_table('Reporting_seinterfaceentry')
+
+ # Deleting model 'SEPermissiveEntry'
+ db.delete_table('Reporting_sepermissiveentry')
+
+ # Deleting model 'SEModuleEntry'
+ db.delete_table('Reporting_semoduleentry')
+
+ # Deleting model 'SEPortEntry'
+ db.delete_table('Reporting_seportentry')
+
+ # Removing M2M table for field sebooleans on 'Interaction'
+ db.delete_table('Reporting_interaction_sebooleans')
+
+ # Removing M2M table for field seports on 'Interaction'
+ db.delete_table('Reporting_interaction_seports')
+
+ # Removing M2M table for field sefcontexts on 'Interaction'
+ db.delete_table('Reporting_interaction_sefcontexts')
+
+ # Removing M2M table for field senodes on 'Interaction'
+ db.delete_table('Reporting_interaction_senodes')
+
+ # Removing M2M table for field selogins on 'Interaction'
+ db.delete_table('Reporting_interaction_selogins')
+
+ # Removing M2M table for field seusers on 'Interaction'
+ db.delete_table('Reporting_interaction_seusers')
+
+ # Removing M2M table for field seinterfaces on 'Interaction'
+ db.delete_table('Reporting_interaction_seinterfaces')
+
+ # Removing M2M table for field sepermissives on 'Interaction'
+ db.delete_table('Reporting_interaction_sepermissives')
+
+ # Removing M2M table for field semodules on 'Interaction'
+ db.delete_table('Reporting_interaction_semodules')
+
+
+ models = {
+ 'Reporting.actionentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
+ },
+ 'Reporting.bundle': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'Reporting.client': {
+ 'Meta': {'object_name': 'Client'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
+ 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.deviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_minor': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.failureentry': {
+ 'Meta': {'object_name': 'FailureEntry'},
+ 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileacl': {
+ 'Meta': {'object_name': 'FileAcl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileperms': {
+ 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.group': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'Reporting.interaction': {
+ 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
+ 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
+ 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
+ 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
+ 'good_count': ('django.db.models.fields.IntegerField', [], {}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
+ 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
+ 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['Reporting.Group']"}),
+ 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'sebooleans': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEBooleanEntry']", 'symmetrical': 'False'}),
+ 'sefcontexts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEFcontextEntry']", 'symmetrical': 'False'}),
+ 'seinterfaces': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEInterfaceEntry']", 'symmetrical': 'False'}),
+ 'selogins': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SELoginEntry']", 'symmetrical': 'False'}),
+ 'semodules': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEModuleEntry']", 'symmetrical': 'False'}),
+ 'senodes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SENodeEntry']", 'symmetrical': 'False'}),
+ 'sepermissives': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPermissiveEntry']", 'symmetrical': 'False'}),
+ 'seports': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPortEntry']", 'symmetrical': 'False'}),
+ 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
+ 'seusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEUserEntry']", 'symmetrical': 'False'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'total_count': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.linkentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
+ },
+ 'Reporting.packageentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
+ 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
+ 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
+ },
+ 'Reporting.pathentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
+ 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
+ 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
+ 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
+ },
+ 'Reporting.performance': {
+ 'Meta': {'object_name': 'Performance'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
+ 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
+ },
+ 'Reporting.sebooleanentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEBooleanEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'value': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'Reporting.sefcontextentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEFcontextEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'filetype': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seinterfaceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEInterfaceEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seloginentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SELoginEntry'},
+ 'current_selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.semoduleentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEModuleEntry'},
+ 'current_disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.senodeentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SENodeEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'proto': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.sepermissiveentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPermissiveEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seportentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPortEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.serviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
+ 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
+ },
+ 'Reporting.seuserentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEUserEntry'},
+ 'current_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'current_roles': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'prefix': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'roles': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ }
+ }
+
+ complete_apps = ['Reporting'] \ No newline at end of file
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0006_add_user_group_entry_support.py b/src/lib/Bcfg2/Reporting/south_migrations/0006_add_user_group_entry_support.py
new file mode 100644
index 000000000..d86e663d5
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/0006_add_user_group_entry_support.py
@@ -0,0 +1,340 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'POSIXGroupEntry'
+ db.create_table('Reporting_posixgroupentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('gid', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ('current_gid', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ))
+ db.send_create_signal('Reporting', ['POSIXGroupEntry'])
+
+ # Adding model 'POSIXUserEntry'
+ db.create_table('Reporting_posixuserentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)),
+ ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)),
+ ('state', self.gf('django.db.models.fields.IntegerField')()),
+ ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('uid', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ('current_uid', self.gf('django.db.models.fields.IntegerField')(null=True)),
+ ('group', self.gf('django.db.models.fields.CharField')(max_length=64)),
+ ('current_group', self.gf('django.db.models.fields.CharField')(max_length=64, null=True)),
+ ('gecos', self.gf('django.db.models.fields.CharField')(max_length=1024)),
+ ('current_gecos', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)),
+ ('home', self.gf('django.db.models.fields.CharField')(max_length=1024)),
+ ('current_home', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)),
+ ('shell', self.gf('django.db.models.fields.CharField')(default='/bin/bash', max_length=1024)),
+ ('current_shell', self.gf('django.db.models.fields.CharField')(max_length=1024, null=True)),
+ ))
+ db.send_create_signal('Reporting', ['POSIXUserEntry'])
+
+ # Adding M2M table for field posixusers on 'Interaction'
+ db.create_table('Reporting_interaction_posixusers', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('posixuserentry', models.ForeignKey(orm['Reporting.posixuserentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_posixusers', ['interaction_id', 'posixuserentry_id'])
+
+ # Adding M2M table for field posixgroups on 'Interaction'
+ db.create_table('Reporting_interaction_posixgroups', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('interaction', models.ForeignKey(orm['Reporting.interaction'], null=False)),
+ ('posixgroupentry', models.ForeignKey(orm['Reporting.posixgroupentry'], null=False))
+ ))
+ db.create_unique('Reporting_interaction_posixgroups', ['interaction_id', 'posixgroupentry_id'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'POSIXGroupEntry'
+ db.delete_table('Reporting_posixgroupentry')
+
+ # Deleting model 'POSIXUserEntry'
+ db.delete_table('Reporting_posixuserentry')
+
+ # Removing M2M table for field posixusers on 'Interaction'
+ db.delete_table('Reporting_interaction_posixusers')
+
+ # Removing M2M table for field posixgroups on 'Interaction'
+ db.delete_table('Reporting_interaction_posixgroups')
+
+
+ models = {
+ 'Reporting.actionentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
+ },
+ 'Reporting.bundle': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'Reporting.client': {
+ 'Meta': {'object_name': 'Client'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
+ 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.deviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_minor': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.failureentry': {
+ 'Meta': {'object_name': 'FailureEntry'},
+ 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileacl': {
+ 'Meta': {'object_name': 'FileAcl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileperms': {
+ 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.group': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'Reporting.interaction': {
+ 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
+ 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
+ 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
+ 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
+ 'good_count': ('django.db.models.fields.IntegerField', [], {}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
+ 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
+ 'posixgroups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.POSIXGroupEntry']", 'symmetrical': 'False'}),
+ 'posixusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.POSIXUserEntry']", 'symmetrical': 'False'}),
+ 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['Reporting.Group']"}),
+ 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'sebooleans': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEBooleanEntry']", 'symmetrical': 'False'}),
+ 'sefcontexts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEFcontextEntry']", 'symmetrical': 'False'}),
+ 'seinterfaces': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEInterfaceEntry']", 'symmetrical': 'False'}),
+ 'selogins': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SELoginEntry']", 'symmetrical': 'False'}),
+ 'semodules': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEModuleEntry']", 'symmetrical': 'False'}),
+ 'senodes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SENodeEntry']", 'symmetrical': 'False'}),
+ 'sepermissives': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPermissiveEntry']", 'symmetrical': 'False'}),
+ 'seports': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPortEntry']", 'symmetrical': 'False'}),
+ 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
+ 'seusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEUserEntry']", 'symmetrical': 'False'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'total_count': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.linkentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
+ },
+ 'Reporting.packageentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
+ 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
+ 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
+ },
+ 'Reporting.pathentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
+ 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
+ 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
+ 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
+ },
+ 'Reporting.performance': {
+ 'Meta': {'object_name': 'Performance'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
+ 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
+ },
+ 'Reporting.posixgroupentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXGroupEntry'},
+ 'current_gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.posixuserentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXUserEntry'},
+ 'current_gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
+ 'current_group': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+ 'current_home': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
+ 'current_shell': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
+ 'current_uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'home': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'shell': ('django.db.models.fields.CharField', [], {'default': "'/bin/bash'", 'max_length': '1024'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
+ },
+ 'Reporting.sebooleanentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEBooleanEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'value': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'Reporting.sefcontextentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEFcontextEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'filetype': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seinterfaceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEInterfaceEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seloginentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SELoginEntry'},
+ 'current_selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.semoduleentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEModuleEntry'},
+ 'current_disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.senodeentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SENodeEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'proto': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.sepermissiveentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPermissiveEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seportentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPortEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.serviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
+ 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
+ },
+ 'Reporting.seuserentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEUserEntry'},
+ 'current_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'current_roles': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'prefix': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'roles': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ }
+ }
+
+ complete_apps = ['Reporting'] \ No newline at end of file
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0007_add_flag_fields_interaction.py b/src/lib/Bcfg2/Reporting/south_migrations/0007_add_flag_fields_interaction.py
new file mode 100644
index 000000000..491ecb845
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/0007_add_flag_fields_interaction.py
@@ -0,0 +1,298 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding field 'Interaction.dry_run'
+ db.add_column('Reporting_interaction', 'dry_run',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+ # Adding field 'Interaction.only_important'
+ db.add_column('Reporting_interaction', 'only_important',
+ self.gf('django.db.models.fields.BooleanField')(default=False),
+ keep_default=False)
+
+
+ def backwards(self, orm):
+ # Deleting field 'Interaction.dry_run'
+ db.delete_column('Reporting_interaction', 'dry_run')
+
+ # Deleting field 'Interaction.only_important'
+ db.delete_column('Reporting_interaction', 'only_important')
+
+
+ models = {
+ 'Reporting.actionentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'})
+ },
+ 'Reporting.bundle': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'Reporting.client': {
+ 'Meta': {'object_name': 'Client'},
+ 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': "orm['Reporting.Interaction']"}),
+ 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.deviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'current_minor': ('django.db.models.fields.IntegerField', [], {}),
+ 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_major': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_minor': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.failureentry': {
+ 'Meta': {'object_name': 'FailureEntry'},
+ 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileacl': {
+ 'Meta': {'object_name': 'FileAcl'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'})
+ },
+ 'Reporting.fileperms': {
+ 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'Reporting.group': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Group'},
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ },
+ 'Reporting.interaction': {
+ 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'},
+ 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ActionEntry']", 'symmetrical': 'False'}),
+ 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Bundle']", 'symmetrical': 'False'}),
+ 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': "orm['Reporting.Client']"}),
+ 'dry_run': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FailureEntry']", 'symmetrical': 'False'}),
+ 'good_count': ('django.db.models.fields.IntegerField', [], {}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.Group']", 'symmetrical': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'only_important': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PackageEntry']", 'symmetrical': 'False'}),
+ 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.PathEntry']", 'symmetrical': 'False'}),
+ 'posixgroups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.POSIXGroupEntry']", 'symmetrical': 'False'}),
+ 'posixusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.POSIXUserEntry']", 'symmetrical': 'False'}),
+ 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['Reporting.Group']"}),
+ 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'sebooleans': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEBooleanEntry']", 'symmetrical': 'False'}),
+ 'sefcontexts': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEFcontextEntry']", 'symmetrical': 'False'}),
+ 'seinterfaces': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEInterfaceEntry']", 'symmetrical': 'False'}),
+ 'selogins': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SELoginEntry']", 'symmetrical': 'False'}),
+ 'semodules': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEModuleEntry']", 'symmetrical': 'False'}),
+ 'senodes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SENodeEntry']", 'symmetrical': 'False'}),
+ 'sepermissives': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPermissiveEntry']", 'symmetrical': 'False'}),
+ 'seports': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEPortEntry']", 'symmetrical': 'False'}),
+ 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}),
+ 'seusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.SEUserEntry']", 'symmetrical': 'False'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'total_count': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.linkentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': ['Reporting.PathEntry']},
+ 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
+ 'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}),
+ 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'})
+ },
+ 'Reporting.packageentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'},
+ 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
+ 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"})
+ },
+ 'Reporting.pathentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'},
+ 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['Reporting.FileAcl']", 'symmetrical': 'False'}),
+ 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"}),
+ 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'details': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['Reporting.FilePerms']"})
+ },
+ 'Reporting.performance': {
+ 'Meta': {'object_name': 'Performance'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': "orm['Reporting.Interaction']"}),
+ 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'})
+ },
+ 'Reporting.posixgroupentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXGroupEntry'},
+ 'current_gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.posixuserentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXUserEntry'},
+ 'current_gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
+ 'current_group': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
+ 'current_home': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
+ 'current_shell': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
+ 'current_uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'home': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'shell': ('django.db.models.fields.CharField', [], {'default': "'/bin/bash'", 'max_length': '1024'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'})
+ },
+ 'Reporting.sebooleanentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEBooleanEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'value': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'Reporting.sefcontextentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEFcontextEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'filetype': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seinterfaceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEInterfaceEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seloginentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SELoginEntry'},
+ 'current_selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.semoduleentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEModuleEntry'},
+ 'current_disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.senodeentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SENodeEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'proto': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.sepermissiveentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPermissiveEntry'},
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.seportentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPortEntry'},
+ 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'Reporting.serviceentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'},
+ 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {}),
+ 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'})
+ },
+ 'Reporting.seuserentry': {
+ 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEUserEntry'},
+ 'current_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'current_roles': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}),
+ 'prefix': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'roles': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'state': ('django.db.models.fields.IntegerField', [], {})
+ }
+ }
+
+ complete_apps = ['Reporting'] \ No newline at end of file
diff --git a/src/lib/Bcfg2/Reporting/south_migrations/__init__.py b/src/lib/Bcfg2/Reporting/south_migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/south_migrations/__init__.py
diff --git a/src/lib/Bcfg2/Reporting/templates/base-timeview.html b/src/lib/Bcfg2/Reporting/templates/base-timeview.html
index 9a5ef651c..28a9fa0f4 100644
--- a/src/lib/Bcfg2/Reporting/templates/base-timeview.html
+++ b/src/lib/Bcfg2/Reporting/templates/base-timeview.html
@@ -17,7 +17,7 @@ function bcfg2_check_date() {
}
document.write(getCalendarStyles());
</script>
-{% if not timestamp %}Rendered at {% now "Y-m-d H:i" %} | {% else %}View as of {{ timestamp|date:"Y-m-d H:i" }} | {% endif %}{% spaceless %}
+{% if not timestamp %}Rendered at {% now "SHORT_DATETIME_FORMAT" %} | {% else %}View as of {{ timestamp|date:"SHORT_DATETIME_FORMAT" }} | {% endif %}{% spaceless %}
<a id='cal_link' name='cal_link' href='#' onclick='showCalendar(); return false;'
>[change]</a>
<form method='post' action='{{ path }}' id='cal_form' name='cal_form'>
diff --git a/src/lib/Bcfg2/Reporting/templates/base.html b/src/lib/Bcfg2/Reporting/templates/base.html
index 8b197231c..2e9ec888a 100644
--- a/src/lib/Bcfg2/Reporting/templates/base.html
+++ b/src/lib/Bcfg2/Reporting/templates/base.html
@@ -1,9 +1,5 @@
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
-
+{% load url from bcfg2_compat %}
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
@@ -93,7 +89,7 @@ This is needed for Django versions less than 1.5
<div style='clear:both'></div>
</div><!-- document -->
<div id="footer">
- <span>Bcfg2 Version 1.4.0pre1</span>
+ <span>Bcfg2 Version 1.4.0pre2</span>
</div>
<div id="calendar_div" style='position:absolute; visibility:hidden; background-color:white; layer-background-color:white;'></div>
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/detail.html b/src/lib/Bcfg2/Reporting/templates/clients/detail.html
index e890589a7..5a638f2b1 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/detail.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/detail.html
@@ -1,9 +1,6 @@
{% extends "base.html" %}
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Client {{client.name}}{% endblock %}
@@ -39,7 +36,7 @@ span.history_links a {
<select id="quick" name="quick" onchange="javascript:pageJump('quick');">
<option value="" selected="selected">--- Time ---</option>
{% for i in client.interactions.all|slice:":25" %}
- <option value="{% url "reports_client_detail_pk" hostname=client.name pk=i.id %}">{{i.timestamp|date:"c"}}</option>
+ <option value="{% url "reports_client_detail_pk" hostname=client.name pk=i.id %}">{{i.timestamp|date:"DATETIME_FORMAT"}}</option>
{% endfor %}
</select></span>
</div>
@@ -76,7 +73,7 @@ span.history_links a {
</div>
<table id='groups_table' class='entry_list' style='display: none'>
{% endif %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td class='entry_list_type'>{{group}}</td>
</tr>
{% if forloop.last %}
@@ -90,11 +87,11 @@ span.history_links a {
<div class='entry_list'>
<div class='entry_list_head' onclick='javascript:toggleMe("bundles_table");'>
<h3>Bundle membership</h3>
- <div class='entry_expand_tab' id='plusminus_bundless_table'>[+]</div>
+ <div class='entry_expand_tab' id='plusminus_bundles_table'>[+]</div>
</div>
<table id='bundles_table' class='entry_list' style='display: none'>
{% endif %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td class='entry_list_type'>{{bundle}}</td>
</tr>
{% if forloop.last %}
@@ -112,7 +109,7 @@ span.history_links a {
</div>
<table id='{{entry_type}}_table' class='entry_list'>
{% for entry in entry_list %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td class='entry_list_type'>{{entry.entry_type}}</td>
<td><a href="{% url "reports_item" entry.class_name entry.pk interaction.pk %}">
{{entry.name}}</a></td>
@@ -127,11 +124,11 @@ span.history_links a {
<div class='entry_list'>
<div class='entry_list_head failed-lineitem' onclick='javascript:toggleMe("failures_table");'>
<h3>Failed Entries &#8212; {{ interaction.failures.all|length }}</h3>
- <div class='entry_expand_tab' id='plusminus_failuress_table'>[+]</div>
+ <div class='entry_expand_tab' id='plusminus_failures_table'>[+]</div>
</div>
<table id='failures_table' class='entry_list' style='display: none'>
{% for failure in interaction.failures.all %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td class='entry_list_type'>{{failure.entry_type}}</td>
<td><a href="{% url "reports_item" failure.class_name failure.pk interaction.pk %}">
{{failure.name}}</a></td>
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html b/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html
index 6a314bd88..65c53a76c 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/detailed-list.html
@@ -1,9 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Detailed Client Listing{% endblock %}
{% block pagebanner %}Clients - Detailed View{% endblock %}
@@ -24,15 +21,17 @@ This is needed for Django versions less than 1.5
<td class='right_column_wide'>{% sort_link 'server' 'Server' %}</td>
</tr>
{% for entry in entry_list %}
- <tr class='{% cycle listview,listview_alt %}'>
- <td class='left_column'><a href='{% url "Bcfg2.Reporting.views.client_detail" hostname=entry.client.name pk=entry.id %}'>{{ entry.client.name }}</a></td>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
+ <td class='left_column'><a href='{% url "reports_client_detail_pk" hostname=entry.client.name pk=entry.id %}'>{{ entry.client.name }}</a></td>
<td class='right_column' style='width:75px'><a href='{% add_url_filter state=entry.state %}'
class='{{entry|determine_client_state}}'>{{ entry.state }}</a></td>
<td class='right_column_narrow'>{{ entry.good_count }}</td>
<td class='right_column_narrow'>{{ entry.bad_count }}</td>
<td class='right_column_narrow'>{{ entry.modified_count }}</td>
<td class='right_column_narrow'>{{ entry.extra_count }}</td>
- <td class='right_column'><span {% if entry.isstale %}class='dirty-lineitem'{% endif %}>{{ entry.timestamp|date:"Y-m-d\&\n\b\s\p\;H:i"|safe }}</span></td>
+ <td class='right_column'><span {% if entry.isstale %}class='dirty-lineitem'{% endif %} style="white-space: nowrap;">
+ {{ entry.timestamp|date:"SHORT_DATETIME_FORMAT"|safe }}
+ </span></td>
<td class='right_column_wide'>
{% if entry.server %}
<a href='{% add_url_filter server=entry.server %}'>{{ entry.server }}</a>
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/index.html b/src/lib/Bcfg2/Reporting/templates/clients/index.html
index eba83670b..1eaa3ca9b 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/index.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/index.html
@@ -1,9 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block extra_header_info %}
{% endblock%}
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/manage.html b/src/lib/Bcfg2/Reporting/templates/clients/manage.html
index 03918aad7..3f080c52e 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/manage.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/manage.html
@@ -1,8 +1,5 @@
{% extends "base.html" %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block extra_header_info %}
{% endblock%}
@@ -24,7 +21,7 @@ This is needed for Django versions less than 1.5
<td class='right_column_narrow'>Manage</td>
</tr>
{% for client in clients %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td><span id="{{ client.name }}"> </span>
<span id="ttag-{{ client.name }}"> </span>
<span id="s-ttag-{{ client.name }}"> </span>
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/common.html b/src/lib/Bcfg2/Reporting/templates/config_items/common.html
index 91f37d7dc..8c2df8db1 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/common.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/common.html
@@ -1,6 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Common Problems{% endblock %}
@@ -28,7 +28,7 @@
<table id='table_{{ type_name }}' class='entry_list'>
<tr style='text-align: left'><th>Type</th><th>Name</th><th>Count</th><th>Reason</th></tr>
{% for item in type_list %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td>{{ item.ENTRY_TYPE }}</td>
<td><a href='{% url "reports_entry" item.class_name item.pk %}'>{{ item.name }}</a></td>
<td>{{ item.num_entries }}</td>
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html b/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html
index e3befb0eb..4d9c42391 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/entry_status.html
@@ -1,9 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Entry Status{% endblock %}
@@ -20,9 +17,11 @@ This is needed for Django versions less than 1.5
<tr style='text-align: left' ><th>Name</th><th>Timestamp</th><th>State</th><th>Reason</th></tr>
{% for item, inters in items %}
{% for inter in inters %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td><a href='{% url "reports_client_detail" hostname=inter.client.name %}'>{{inter.client.name}}</a></td>
- <td><a href='{% url "reports_client_detail_pk" hostname=inter.client.name pk=inter.pk %}'>{{inter.timestamp|date:"Y-m-d\&\n\b\s\p\;H:i"|safe}}</a></td>
+ <td><a href='{% url "reports_client_detail_pk" hostname=inter.client.name pk=inter.pk %}' style="white-space: nowrap;">
+ {{inter.timestamp|date:"SHORT_DATETIME_FORMAT"|safe}}
+ </a></td>
<td>{{ item.get_state_display }}</td>
<td style='white-space: nowrap'><a href='{% url "reports_item" entry_type=item.class_name pk=item.pk %}'>({{item.pk}}) {{item.short_list|join:","}}</a></td>
</tr>
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/item.html b/src/lib/Bcfg2/Reporting/templates/config_items/item.html
index c6e6df020..91c368bd7 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/item.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/item.html
@@ -1,11 +1,7 @@
{% extends "base.html" %}
{% load split %}
{% load syntax_coloring %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
-
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Element Details{% endblock %}
@@ -130,7 +126,7 @@ div.entry_list h3 {
<div class='entry_list'>
<div class='entry_list_head'>
- <h3>Occurences on {{ timestamp|date:"Y-m-d" }}</h3>
+ <h3>Occurences on {{ timestamp|date:"SHORT_DATE_FORMAT" }}</h3>
</div>
{% if associated_list %}
<table class="entry_list" cellpadding="3">
diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/listing.html b/src/lib/Bcfg2/Reporting/templates/config_items/listing.html
index 0e4812e85..b8fba61ce 100644
--- a/src/lib/Bcfg2/Reporting/templates/config_items/listing.html
+++ b/src/lib/Bcfg2/Reporting/templates/config_items/listing.html
@@ -1,9 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Element Listing{% endblock %}
@@ -24,7 +21,7 @@ This is needed for Django versions less than 1.5
<table id='table_{{ type_name }}' class='entry_list'>
<tr style='text-align: left' ><th>Name</th><th>Count</th><th>Reason</th></tr>
{% for entry in type_data %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td><a href='{% url "reports_entry" entry.class_name entry.pk %}'>{{entry.name}}</a></td>
<td>{{entry.num_entries}}</td>
<td><a href='{% url "reports_item" entry.class_name entry.pk %}'>{{entry.short_list|join:","}}</a></td>
diff --git a/src/lib/Bcfg2/Reporting/templates/displays/summary.html b/src/lib/Bcfg2/Reporting/templates/displays/summary.html
index ffafd52e0..a9082aeee 100644
--- a/src/lib/Bcfg2/Reporting/templates/displays/summary.html
+++ b/src/lib/Bcfg2/Reporting/templates/displays/summary.html
@@ -1,9 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Client Summary{% endblock %}
{% block pagebanner %}Clients - Summary{% endblock %}
@@ -33,7 +30,7 @@ hide_tables[{{ forloop.counter0 }}] = "table_{{ summary.name }}";
<table id='table_{{ summary.name }}' class='entry_list'>
{% for node in summary.nodes|sort_interactions_by_name %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td><a href='{% url "reports_client_detail_pk" hostname=node.client.name pk=node.id %}'>{{ node.client.name }}</a></td>
</tr>
{% endfor %}
diff --git a/src/lib/Bcfg2/Reporting/templates/displays/timing.html b/src/lib/Bcfg2/Reporting/templates/displays/timing.html
index 8ac5e49bb..69d56b62f 100644
--- a/src/lib/Bcfg2/Reporting/templates/displays/timing.html
+++ b/src/lib/Bcfg2/Reporting/templates/displays/timing.html
@@ -1,9 +1,6 @@
{% extends "base-timeview.html" %}
{% load bcfg2_tags %}
-{% comment %}
-This is needed for Django versions less than 1.5
-{% endcomment %}
-{% load url from future %}
+{% load url from bcfg2_compat %}
{% block title %}Bcfg2 - Performance Metrics{% endblock %}
{% block pagebanner %}Performance Metrics{% endblock %}
@@ -26,7 +23,7 @@ This is needed for Django versions less than 1.5
<td>Total</td>
</tr>
{% for metric in metrics|dictsort:"name" %}
- <tr class='{% cycle listview,listview_alt %}'>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
<td><a style='font-size: 100%'
href='{% url "reports_client_detail" hostname=metric.name %}'>{{ metric.name }}</a></td>
{% for mitem in metric|build_metric_list %}
diff --git a/src/lib/Bcfg2/Reporting/templates/widgets/interaction_list.inc b/src/lib/Bcfg2/Reporting/templates/widgets/interaction_list.inc
index 30ed2fd3e..1dbcbf3f1 100644
--- a/src/lib/Bcfg2/Reporting/templates/widgets/interaction_list.inc
+++ b/src/lib/Bcfg2/Reporting/templates/widgets/interaction_list.inc
@@ -14,8 +14,10 @@
<td class='right_column_wide'>Server</td>
</tr>
{% for entry in entry_list %}
- <tr class='{% cycle listview,listview_alt %}'>
- <td class='left_column'><a href='{% url reports_client_detail_pk hostname=entry.client.name, pk=entry.id %}'>{{ entry.timestamp|date:"Y-m-d\&\n\b\s\p\;H:i"|safe }}</a></td>
+ <tr class='{% cycle 'listview' 'listview_alt' %}'>
+ <td class='left_column'><a href='{% url "reports_client_detail_pk" hostname=entry.client.name pk=entry.id %}' class="white-space: nowrap;">
+ {{ entry.timestamp|date:"SHORT_DATETIME_FORMAT"|safe }}
+ </a></td>
{% if not client %}
<td class='right_column_wide'><a href='{% add_url_filter hostname=entry.client.name %}'>{{ entry.client.name }}</a></td>
{% endif %}
diff --git a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_compat.py b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_compat.py
new file mode 100644
index 000000000..da4c73745
--- /dev/null
+++ b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_compat.py
@@ -0,0 +1,14 @@
+from django.template import Library
+
+try:
+ from django.templatetags.future import url as django_url
+except ImportError:
+ # future is removed in django 1.9
+ from django.template.defaulttags import url as django_url
+
+register = Library()
+
+
+@register.tag
+def url(parser, token):
+ return django_url(parser, token)
diff --git a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
index 09aebc7fd..5c50c614d 100644
--- a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
+++ b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py
@@ -1,11 +1,11 @@
import sys
from copy import copy
+import django
from django import template
from django.conf import settings
from django.core.urlresolvers import resolve, reverse, \
Resolver404, NoReverseMatch
-from django.template.loader import get_template_from_string
from django.utils.encoding import smart_str
from django.utils.safestring import mark_safe
from datetime import datetime, timedelta
@@ -394,6 +394,13 @@ class SortLinkNode(template.Node):
self.sort_key = template.Variable(sort_key)
self.text = template.Variable(text)
+ def _render_template(self, context):
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 8:
+ return context.template.engine.from_string(self.__TMPL__)
+ else:
+ from django.template.loader import get_template_from_string
+ return get_template_from_string(self.__TMPL__).render(context)
+
def render(self, context):
try:
try:
@@ -419,7 +426,7 @@ class SortLinkNode(template.Node):
context.push()
context['key'] = sort_key
context['text'] = mark_safe(text)
- output = get_template_from_string(self.__TMPL__).render(context)
+ output = self._render_template(context)
context.pop()
return output
except:
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..3b9e2e53b 100644
--- a/src/lib/Bcfg2/Reporting/views.py
+++ b/src/lib/Bcfg2/Reporting/views.py
@@ -7,10 +7,10 @@ from datetime import datetime, timedelta
import sys
from time import strptime
-from django.template import Context, RequestContext
+from django.template import Context
from django.http import \
HttpResponse, HttpResponseRedirect, HttpResponseServerError, Http404
-from django.shortcuts import render_to_response, get_object_or_404
+from django.shortcuts import render, get_object_or_404
from django.core.urlresolvers import \
resolve, reverse, Resolver404, NoReverseMatch
from django.db import DatabaseError
@@ -166,11 +166,10 @@ def config_item(request, pk, entry_type, interaction=None):
template = 'config_items/item-failure.html'
else:
template = 'config_items/item.html'
- return render_to_response(template,
- {'item': item,
- 'associated_list': associated_list,
- 'timestamp': timestamp},
- context_instance=RequestContext(request))
+ return render(request, template,
+ {'item': item,
+ 'associated_list': associated_list,
+ 'timestamp': timestamp})
@timeview
@@ -186,16 +185,15 @@ 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))
- return render_to_response('config_items/listing.html',
- {'item_list': lists,
- 'item_state': item_state,
- 'timestamp': timestamp},
- context_instance=RequestContext(request))
+ return render(request, 'config_items/listing.html',
+ {'item_list': lists,
+ 'item_state': item_state,
+ 'timestamp': timestamp})
@timeview
@@ -219,12 +217,10 @@ def entry_status(request, entry_type, pk, timestamp=None, **kwargs):
items.append((it, it.interaction_set.filter(pk__in=current_clients).order_by('client__name').select_related('client')))
seen.append(it.pk)
- return render_to_response('config_items/entry_status.html',
- {'entry': item,
- 'items': items,
- 'timestamp': timestamp},
- context_instance=RequestContext(request))
-
+ return render(request, 'config_items/entry_status.html',
+ {'entry': item,
+ 'items': items,
+ 'timestamp': timestamp})
@timeview
def common_problems(request, timestamp=None, threshold=None, group=None):
@@ -262,11 +258,10 @@ def common_problems(request, timestamp=None, threshold=None, group=None):
# Property doesn't render properly..
lists.append((etype.ENTRY_TYPE, ldata))
- return render_to_response('config_items/common.html',
- {'lists': lists,
- 'timestamp': timestamp,
- 'threshold': threshold},
- context_instance=RequestContext(request))
+ return render(request, 'config_items/common.html',
+ {'lists': lists,
+ 'timestamp': timestamp,
+ 'threshold': threshold})
@timeview
@@ -281,10 +276,9 @@ def client_index(request, timestamp=None, **kwargs):
list = _handle_filters(Interaction.objects.recent(timestamp), **kwargs).\
select_related('client').order_by("client__name").all()
- return render_to_response('clients/index.html',
- {'inter_list': list,
- 'timestamp': timestamp},
- context_instance=RequestContext(request))
+ return render(request, 'clients/index.html',
+ {'inter_list': list,
+ 'timestamp': timestamp})
@timeview
@@ -374,9 +368,9 @@ def client_manage(request):
client_name = "<none>"
message = "Couldn't find client \"%s\"" % client_name
- return render_to_response('clients/manage.html',
- {'clients': Client.objects.order_by('name').all(), 'message': message},
- context_instance=RequestContext(request))
+ return render(request, 'clients/manage.html',
+ {'clients': Client.objects.order_by('name').all(),
+ 'message': message})
@timeview
@@ -429,10 +423,10 @@ def display_summary(request, timestamp=None):
summary_data.append(get_dict('stale',
'nodes did not run within the last 24 hours.'))
- return render_to_response('displays/summary.html',
- {'summary_data': summary_data, 'node_count': node_count,
- 'timestamp': timestamp},
- context_instance=RequestContext(request))
+ return render(request, 'displays/summary.html',
+ {'summary_data': summary_data,
+ 'node_count': node_count,
+ 'timestamp': timestamp})
@timeview
@@ -447,10 +441,9 @@ def display_timing(request, timestamp=None):
mdict[client] = { 'name': client }
mdict[client][perf.metric] = perf.value
- return render_to_response('displays/timing.html',
- {'metrics': list(mdict.values()),
- 'timestamp': timestamp},
- context_instance=RequestContext(request))
+ return render(request, 'displays/timing.html',
+ {'metrics': list(mdict.values()),
+ 'timestamp': timestamp})
def render_history_view(request, template='clients/history.html', **kwargs):
@@ -525,8 +518,7 @@ def render_history_view(request, template='clients/history.html', **kwargs):
else:
context['entry_list'] = iquery.all()
- return render_to_response(template, context,
- context_instance=RequestContext(request))
+ return render(request, template, context)
def prepare_paginated_list(request, context, paged_list, page=1, max_results=25):
diff --git a/src/lib/Bcfg2/Server/Admin.py b/src/lib/Bcfg2/Server/Admin.py
index c294e6be5..77bca88eb 100644
--- a/src/lib/Bcfg2/Server/Admin.py
+++ b/src/lib/Bcfg2/Server/Admin.py
@@ -25,15 +25,19 @@ import Bcfg2.Server.Plugins.Metadata
try:
from django.core.exceptions import ImproperlyConfigured
from django.core import management
+ import django
import django.conf
import Bcfg2.Server.models
HAS_DJANGO = True
- try:
- import south # pylint: disable=W0611
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
HAS_REPORTS = True
- except ImportError:
- HAS_REPORTS = False
+ else:
+ try:
+ import south # pylint: disable=W0611
+ HAS_REPORTS = True
+ except ImportError:
+ HAS_REPORTS = False
except ImportError:
HAS_DJANGO = False
HAS_REPORTS = False
@@ -439,6 +443,25 @@ class Compare(AdminCmd):
print("")
+class ExpireCache(_ProxyAdminCmd):
+ """ Expire the metadata cache """
+
+ options = _ProxyAdminCmd.options + [
+ Bcfg2.Options.PositionalArgument(
+ "hostname", nargs="*", default=[],
+ help="Expire cache for the given host(s)")]
+
+ def run(self, setup):
+ clients = None
+ if setup.hostname is not None and len(setup.hostname) > 0:
+ clients = setup.hostname
+
+ try:
+ self.proxy.expire_metadata_cache(clients)
+ except Bcfg2.Client.Proxy.ProxyError:
+ self.errExit("Proxy Error: %s" % sys.exc_info()[1])
+
+
class Init(AdminCmd):
"""Interactively initialize a new repository."""
@@ -643,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",
@@ -877,6 +900,7 @@ if HAS_DJANGO:
Django management system """
command = None
args = []
+ kwargs = {}
def run(self, _):
'''Call a django command'''
@@ -885,7 +909,7 @@ if HAS_DJANGO:
else:
command = self.__class__.__name__.lower()
args = [command] + self.args
- management.call_command(*args)
+ management.call_command(*args, **self.kwargs)
class DBShell(_DjangoProxyCmd):
""" Call the Django 'dbshell' command on the database """
@@ -901,7 +925,6 @@ if HAS_DJANGO:
""" Sync the Django ORM with the configured database """
def run(self, setup):
- Bcfg2.Server.models.load_models()
try:
Bcfg2.DBSettings.sync_databases(
interactive=False,
@@ -915,6 +938,17 @@ if HAS_DJANGO:
self.logger.error("Database update failed: %s" % err)
raise SystemExit(1)
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ class Makemigrations(_DjangoProxyCmd):
+ """ Call the 'makemigrations' command on the database """
+ args = ['Reporting']
+
+ else:
+ class Schemamigration(_DjangoProxyCmd):
+ """ Call the South 'schemamigration' command on the database """
+ args = ['Bcfg2.Reporting']
+ kwargs = {'auto': True}
+
if HAS_REPORTS:
import datetime
diff --git a/src/lib/Bcfg2/Server/BuiltinCore.py b/src/lib/Bcfg2/Server/BuiltinCore.py
index e138c57e4..dc5cc46fb 100644
--- a/src/lib/Bcfg2/Server/BuiltinCore.py
+++ b/src/lib/Bcfg2/Server/BuiltinCore.py
@@ -34,7 +34,8 @@ class BuiltinCore(NetworkCore):
daemon_args = dict(uid=Bcfg2.Options.setup.daemon_uid,
gid=Bcfg2.Options.setup.daemon_gid,
umask=int(Bcfg2.Options.setup.umask, 8),
- detach_process=True)
+ detach_process=True,
+ files_preserve=self._logfilehandles())
if Bcfg2.Options.setup.daemon:
daemon_args['pidfile'] = TimeoutPIDLockFile(
Bcfg2.Options.setup.daemon, acquire_timeout=5)
@@ -44,6 +45,24 @@ class BuiltinCore(NetworkCore):
self.context = daemon.DaemonContext(**daemon_args)
__init__.__doc__ = NetworkCore.__init__.__doc__.split('.. -----')[0]
+ def _logfilehandles(self, logger=None):
+ """ Get a list of all filehandles logger, that have to be handled
+ with DaemonContext.files_preserve to keep looging working.
+
+ :param logger: The logger to get the file handles of. By default,
+ self.logger is used.
+ :type logger: logging.Logger
+ """
+ if logger is None:
+ logger = self.logger
+
+ handles = [handler.stream.fileno()
+ for handler in logger.handlers
+ if hasattr(handler, 'stream')]
+ if logger.parent:
+ handles += self._logfilehandles(logger.parent)
+ return handles
+
def _dispatch(self, method, args, dispatch_dict):
""" Dispatch XML-RPC method calls
diff --git a/src/lib/Bcfg2/Server/Cache.py b/src/lib/Bcfg2/Server/Cache.py
index d05eb0bf6..b3b906b2c 100644
--- a/src/lib/Bcfg2/Server/Cache.py
+++ b/src/lib/Bcfg2/Server/Cache.py
@@ -96,15 +96,19 @@ class _Cache(MutableMapping):
return len(list(iter(self)))
def expire(self, key=None):
- """ expire all items, or a specific item, from the cache """
+ """ expire all items, or a specific item, from the cache
+
+ :returns: number of expired entries
+ """
+
if key is None:
- expire(*self._tags)
+ return expire(*self._tags)
else:
tags = self._tags | set([key])
# py 2.5 doesn't support mixing *args and explicit keyword
# args
kwargs = dict(exact=True)
- expire(*tags, **kwargs)
+ return expire(*tags, **kwargs)
def __repr__(self):
return repr(dict(self))
@@ -152,7 +156,10 @@ def expire(*tags, **kwargs):
""" Expire all items, a set of items, or one specific item from
the cache. If ``exact`` is set to True, then if the given tag set
doesn't match exactly one item in the cache, nothing will be
- expired. """
+ expired.
+
+ :returns: number of expired entries
+ """
exact = kwargs.pop("exact", False)
count = 0
if not tags:
@@ -170,6 +177,8 @@ def expire(*tags, **kwargs):
for hook in _hooks:
hook(tags, exact, count)
+ return count
+
def add_expire_hook(func):
""" Add a hook that will be called when an item is expired from
diff --git a/src/lib/Bcfg2/Server/CherrypyCore.py b/src/lib/Bcfg2/Server/CherrypyCore.py
index 3cb0e291b..05c6c5a94 100644
--- a/src/lib/Bcfg2/Server/CherrypyCore.py
+++ b/src/lib/Bcfg2/Server/CherrypyCore.py
@@ -110,17 +110,21 @@ class CherrypyCore(NetworkCore):
return cherrypy.serving.response.body
def _daemonize(self):
- """ Drop privileges with
- :class:`cherrypy.process.plugins.DropPrivileges`, daemonize
- with :class:`cherrypy.process.plugins.Daemonizer`, and write a
+ """ Drop privileges, daemonize
+ with :class:`cherrypy.process.plugins.Daemonizer` and write a
PID file with :class:`cherrypy.process.plugins.PIDFile`. """
+ self._drop_privileges()
+ Daemonizer(cherrypy.engine).subscribe()
+ PIDFile(cherrypy.engine, Bcfg2.Options.setup.daemon).subscribe()
+ return True
+
+ def _drop_privileges(self):
+ """ Drop privileges with
+ :class:`cherrypy.process.plugins.DropPrivileges` """
DropPrivileges(cherrypy.engine,
uid=Bcfg2.Options.setup.daemon_uid,
gid=Bcfg2.Options.setup.daemon_gid,
umask=int(Bcfg2.Options.setup.umask, 8)).subscribe()
- Daemonizer(cherrypy.engine).subscribe()
- PIDFile(cherrypy.engine, Bcfg2.Options.setup.daemon).subscribe()
- return True
def _run(self):
""" Start the server listening. """
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index 03ab40343..a1ee24e18 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -11,6 +11,7 @@ import threading
import time
import inspect
import lxml.etree
+import daemon
import Bcfg2.Server
import Bcfg2.Logger
import Bcfg2.Options
@@ -26,6 +27,7 @@ from Bcfg2.Server.Statistics import track_statistics
try:
from django.core.exceptions import ImproperlyConfigured
+ import django
import django.conf
HAS_DJANGO = True
except ImportError:
@@ -82,10 +84,14 @@ def close_db_connection(func):
""" The decorated function """
rv = func(self, *args, **kwargs)
if self._database_available: # pylint: disable=W0212
- from django import db
self.logger.debug("%s: Closing database connection" %
threading.current_thread().getName())
- db.close_connection()
+
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ for connection in django.db.connections.all():
+ connection.close()
+ else:
+ django.db.close_connection() # pylint: disable=E1101
return rv
return inner
@@ -113,7 +119,8 @@ class DefaultACL(Plugin, ClientACLs):
def check_acl_ip(self, address, rmi):
return (("." not in rmi and
not rmi.endswith("_debug") and
- rmi != 'get_statistics') or
+ rmi != 'get_statistics' and
+ rmi != 'expire_metadata_cache') or
address[0] == "127.0.0.1")
# in core we frequently want to catch all exceptions, regardless of
@@ -324,6 +331,10 @@ class Core(object):
select.select([famfd], [], [], 2)
elif not self.fam.pending():
terminate.wait(15)
+
+ if self.terminate.isSet():
+ break
+
if self.fam.pending():
try:
self._update_vcs_revision()
@@ -429,6 +440,7 @@ class Core(object):
self.logger.error("Unexpected instantiation failure for plugin %s"
% plugin, exc_info=1)
+ @close_db_connection
def shutdown(self):
""" Perform plugin and FAM shutdown tasks. """
if not self._running:
@@ -443,10 +455,6 @@ class Core(object):
for plugin in list(self.plugins.values()):
plugin.shutdown()
self.logger.info("%s: All plugins shut down" % self.name)
- if self._database_available:
- from django import db
- self.logger.info("%s: Closing database connection" % self.name)
- db.close_connection()
@property
def metadata_cache_mode(self):
@@ -681,7 +689,7 @@ class Core(object):
self.logger.debug("Building configuration for %s" % client)
start = time.time()
config = lxml.etree.Element("Configuration", version='2.0',
- revision=self.revision)
+ revision=str(self.revision))
try:
meta = self.build_metadata(client)
except MetadataConsistencyError:
@@ -1365,6 +1373,21 @@ class Core(object):
return "This method is deprecated and will be removed in a future " + \
"release\n%s" % self.fam.set_debug(debug)
+ @exposed
+ def expire_metadata_cache(self, _, hostnames=None):
+ """ Expire the metadata cache for one or all clients
+
+ :param hostnames: A list of hostnames to expire the metadata
+ cache for or None. If None the cache of
+ all clients will be expired.
+ :type hostnames: None or list of strings
+ """
+ if hostnames is not None:
+ for hostname in hostnames:
+ self.metadata_cache.expire(hostname)
+ else:
+ self.metadata_cache.expire()
+
class NetworkCore(Core):
""" A server core that actually listens on the network, can be
@@ -1424,9 +1447,9 @@ class NetworkCore(Core):
"\n.. automethod:: _daemonize\n"
def __str__(self):
- if hasattr(Bcfg2.Options.setup, "location"):
+ if hasattr(Bcfg2.Options.setup, "server"):
return "%s(%s)" % (self.__class__.__name__,
- Bcfg2.Options.setup.location)
+ Bcfg2.Options.setup.server)
else:
return Core.__str__(self)
@@ -1486,3 +1509,13 @@ class NetworkCore(Core):
""" Daemonize the server and write the pidfile. This must be
overridden by a core implementation. """
raise NotImplementedError
+
+ def _drop_privileges(self):
+ """ This is called if not daemonized and running as root to
+ drop the privileges to the configured daemon_uid and daemon_gid.
+ """
+ daemon.daemon.change_process_owner(
+ Bcfg2.Options.setup.daemon_uid,
+ Bcfg2.Options.setup.daemon_gid)
+ self.logger.debug("Dropped privileges to %s:%s." %
+ (os.getuid(), os.getgid()))
diff --git a/src/lib/Bcfg2/Server/Encryption.py b/src/lib/Bcfg2/Server/Encryption.py
index b60302871..c6cd4232e 100755
--- a/src/lib/Bcfg2/Server/Encryption.py
+++ b/src/lib/Bcfg2/Server/Encryption.py
@@ -176,7 +176,7 @@ def ssl_encrypt(plaintext, passwd, algorithm=None, salt=None):
def is_encrypted(val):
""" Make a best guess if the value is encrypted or not. This just
checks to see if ``val`` is a base64-encoded string whose content
- starts with "Salted__", so it may have (rare) false positives. It
+ starts with "Salted\\_\\_", so it may have (rare) false positives. It
will not have false negatives. """
try:
return b64decode(val).startswith("Salted__")
diff --git a/src/lib/Bcfg2/Server/FileMonitor/Inotify.py b/src/lib/Bcfg2/Server/FileMonitor/Inotify.py
index c4b34a469..8f6e136fd 100644
--- a/src/lib/Bcfg2/Server/FileMonitor/Inotify.py
+++ b/src/lib/Bcfg2/Server/FileMonitor/Inotify.py
@@ -214,6 +214,7 @@ class Inotify(Pseudo, pyinotify.ProcessEvent):
def shutdown(self):
if self.started and self.notifier:
self.notifier.stop()
+ Pseudo.shutdown(self)
shutdown.__doc__ = Pseudo.shutdown.__doc__
def list_watches(self):
diff --git a/src/lib/Bcfg2/Server/Info.py b/src/lib/Bcfg2/Server/Info.py
index 6af561089..044dcdf0c 100644
--- a/src/lib/Bcfg2/Server/Info.py
+++ b/src/lib/Bcfg2/Server/Info.py
@@ -1,5 +1,5 @@
-""" Subcommands and helpers for bcfg2-info """
# -*- coding: utf-8 -*-
+""" Subcommands and helpers for bcfg2-info """
import os
import sys
@@ -12,6 +12,7 @@ import fnmatch
import argparse
import operator
import lxml.etree
+import traceback
from code import InteractiveConsole
import Bcfg2.Logger
import Bcfg2.Options
@@ -142,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())
@@ -369,6 +368,7 @@ class Automatch(InfoCmd):
class ExpireCache(InfoCmd):
""" Expire the metadata cache """
+ only_interactive = True
options = [
Bcfg2.Options.PositionalArgument(
@@ -376,12 +376,20 @@ class ExpireCache(InfoCmd):
help="Expire cache for the given host(s)")]
def run(self, setup):
- if setup.clients:
- for client in self.get_client_list(setup.clients):
- self.core.expire_caches_by_type(Bcfg2.Server.Plugin.Metadata,
- key=client)
+ if setup.hostname:
+ for client in self.get_client_list(setup.hostname):
+ self.core.metadata_cache.expire(client)
else:
- self.core.expire_caches_by_type(Bcfg2.Server.Plugin.Metadata)
+ self.core.metadata_cache.expire()
+
+
+class EventDebug(InfoCmd):
+ """ Enable debugging output for FAM events """
+ only_interactive = True
+ aliases = ['event_debug']
+
+ def run(self, _):
+ self.core.fam.set_debug(True)
class Bundles(InfoCmd):
@@ -672,18 +680,35 @@ class Query(InfoCmd):
print("\n".join(res))
+class Quit(InfoCmd):
+ """ Exit program """
+ only_interactive = True
+ aliases = ['exit', 'EOF']
+
+ def run(self, _):
+ raise SystemExit(0)
+
+
class Shell(InfoCmd):
""" Open an interactive shell to run multiple bcfg2-info commands """
interactive = False
def run(self, setup):
try:
- self.core.cmdloop('Welcome to bcfg2-info\n'
- 'Type "help" for more information')
+ self.core.cli.cmdloop('Welcome to bcfg2-info\n'
+ 'Type "help" for more information')
except KeyboardInterrupt:
print("\nCtrl-C pressed, exiting...")
+class Update(InfoCmd):
+ """ Process pending filesystem events """
+ only_interactive = True
+
+ def run(self, _):
+ self.core.fam.handle_events_in_interval(0.1)
+
+
class ProfileTemplates(InfoCmd):
""" Benchmark template rendering times """
@@ -796,36 +821,18 @@ if HAS_PROFILE:
display_trace(prof)
-class InfoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
+class InfoCore(Bcfg2.Server.Core.Core):
"""Main class for bcfg2-info."""
- def __init__(self):
- cmd.Cmd.__init__(self)
+ def __init__(self, cli):
Bcfg2.Server.Core.Core.__init__(self)
- self.prompt = 'bcfg2-info> '
+ self.cli = cli
def get_locals(self):
""" Expose the local variables of the core to subcommands that
need to reference them (i.e., the interactive interpreter) """
return locals()
- def do_quit(self, _):
- """ quit|exit - Exit program """
- raise SystemExit(0)
-
- do_EOF = do_quit
- do_exit = do_quit
-
- def do_eventdebug(self, _):
- """ eventdebug - Enable debugging output for FAM events """
- self.fam.set_debug(True)
-
- do_event_debug = do_eventdebug
-
- def do_update(self, _):
- """ update - Process pending filesystem events """
- self.fam.handle_events_in_interval(0.1)
-
def run(self):
self.load_plugins()
self.block_for_fam_events(handle_events=True)
@@ -840,12 +847,15 @@ class InfoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
Bcfg2.Server.Core.Core.shutdown(self)
-class CLI(Bcfg2.Options.CommandRegistry):
+class CLI(cmd.Cmd, Bcfg2.Options.CommandRegistry):
""" The bcfg2-info CLI """
options = [Bcfg2.Options.BooleanOption("-p", "--profile", help="Profile")]
def __init__(self):
+ cmd.Cmd.__init__(self)
Bcfg2.Options.CommandRegistry.__init__(self)
+ self.prompt = 'bcfg2-info> '
+
self.register_commands(globals().values(), parent=InfoCmd)
parser = Bcfg2.Options.get_parser(
description="Inspect a running Bcfg2 server",
@@ -860,7 +870,7 @@ class CLI(Bcfg2.Options.CommandRegistry):
else:
if Bcfg2.Options.setup.profile:
print("Profiling functionality not available.")
- self.core = InfoCore()
+ self.core = InfoCore(self)
for command in self.commands.values():
command.core = self.core
@@ -877,3 +887,22 @@ class CLI(Bcfg2.Options.CommandRegistry):
def shutdown(self):
Bcfg2.Options.CommandRegistry.shutdown(self)
self.core.shutdown()
+
+ def get_names(self):
+ """ Overwrite cmd.Cmd.get_names to use the instance to get the
+ methods and not the class, because the CommandRegistry
+ dynamically adds methods for the registed subcommands. """
+ return dir(self)
+
+ def onecmd(self, line):
+ """ Overwrite cmd.Cmd.onecmd to catch all exceptions (except
+ SystemExit) print an error message and continue cmdloop. """
+ try:
+ cmd.Cmd.onecmd(self, line)
+ except SystemExit:
+ raise
+ except: # pylint: disable=W0702
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ lines = traceback.format_exception(exc_type, exc_value,
+ exc_traceback)
+ self.stdout.write(''.join(lines))
diff --git a/src/lib/Bcfg2/Server/Lint/Bundler.py b/src/lib/Bcfg2/Server/Lint/Bundler.py
index 576e157ad..7b024229b 100644
--- a/src/lib/Bcfg2/Server/Lint/Bundler.py
+++ b/src/lib/Bcfg2/Server/Lint/Bundler.py
@@ -29,8 +29,12 @@ class Bundler(ServerPlugin):
# when given a list of files on stdin, this check is
# useless, so skip it
groupdata = self.metadata.groups_xml.xdata
+
ref_bundles = set([b.get("name")
for b in groupdata.findall("//Bundle")])
+ for bundle in self.core.plugins['Bundler'].bundles.values():
+ ref_bundles |= set([rb.get("name") for rb in
+ bundle.xdata.findall(".//RequiredBundle")])
allbundles = self.core.plugins['Bundler'].bundles.keys()
for bundle in ref_bundles:
diff --git a/src/lib/Bcfg2/Server/Lint/MergeFiles.py b/src/lib/Bcfg2/Server/Lint/MergeFiles.py
index 8e6a926ae..3a6251594 100644
--- a/src/lib/Bcfg2/Server/Lint/MergeFiles.py
+++ b/src/lib/Bcfg2/Server/Lint/MergeFiles.py
@@ -6,6 +6,7 @@ import copy
from difflib import SequenceMatcher
import Bcfg2.Server.Lint
from Bcfg2.Server.Plugins.Cfg import CfgGenerator
+from Bcfg2.Utils import is_string
def threshold(val):
@@ -50,6 +51,8 @@ class MergeFiles(Bcfg2.Server.Lint.ServerPlugin):
for filename, entryset in self.core.plugins['Cfg'].entries.items():
candidates = dict([(f, e) for f, e in entryset.entries.items()
if (isinstance(e, CfgGenerator) and
+ is_string(e.data,
+ Bcfg2.Options.setup.encoding) and
f not in ignore and
not f.endswith(".crypt"))])
similar, identical = self.get_similar(candidates)
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/Lint/TemplateHelper.py b/src/lib/Bcfg2/Server/Lint/TemplateHelper.py
index 9d05516f1..98faa269d 100644
--- a/src/lib/Bcfg2/Server/Lint/TemplateHelper.py
+++ b/src/lib/Bcfg2/Server/Lint/TemplateHelper.py
@@ -4,8 +4,8 @@
import sys
import imp
from Bcfg2.Server.Lint import ServerPlugin
-from Bcfg2.Server.Plugins.TemplateHelper import HelperModule, MODULE_RE, \
- safe_module_name
+from Bcfg2.Server.Plugins.TemplateHelper import HelperModule, MODULE_RE
+from Bcfg2.Utils import safe_module_name
class TemplateHelper(ServerPlugin):
@@ -26,7 +26,7 @@ class TemplateHelper(ServerPlugin):
ServerPlugin.__init__(self, *args, **kwargs)
# we instantiate a dummy helper to discover which keywords and
# defaults are reserved
- dummy = HelperModule("foo.py")
+ dummy = HelperModule("foo.py", None)
self.reserved_keywords = dir(dummy)
self.reserved_defaults = dummy.reserved_defaults
@@ -44,7 +44,8 @@ class TemplateHelper(ServerPlugin):
module_name = MODULE_RE.search(helper).group(1)
try:
- module = imp.load_source(safe_module_name(module_name), helper)
+ module = imp.load_source(
+ safe_module_name('TemplateHelper', module_name), helper)
except: # pylint: disable=W0702
err = sys.exc_info()[1]
self.LintError("templatehelper-import-error",
diff --git a/src/lib/Bcfg2/Server/Lint/Validate.py b/src/lib/Bcfg2/Server/Lint/Validate.py
index d6f18afbb..146f18b0c 100644
--- a/src/lib/Bcfg2/Server/Lint/Validate.py
+++ b/src/lib/Bcfg2/Server/Lint/Validate.py
@@ -61,7 +61,8 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
"AWSTags/config.xml": "awstags.xsd",
"NagiosGen/config.xml": "nagiosgen.xsd",
"FileProbes/config.xml": "fileprobes.xsd",
- "GroupLogic/groups.xml": "grouplogic.xsd"
+ "GroupLogic/groups.xml": "grouplogic.xsd",
+ "PkgVars/*.xml": "pkgvars.xsd"
}
self.filelists = {}
diff --git a/src/lib/Bcfg2/Server/Lint/__init__.py b/src/lib/Bcfg2/Server/Lint/__init__.py
index 61f704206..873e5f149 100644
--- a/src/lib/Bcfg2/Server/Lint/__init__.py
+++ b/src/lib/Bcfg2/Server/Lint/__init__.py
@@ -14,6 +14,7 @@ import time
import lxml.etree
+
import Bcfg2.Options
import Bcfg2.Server.Core
import Bcfg2.Server.Plugins
diff --git a/src/lib/Bcfg2/Server/MultiprocessingCore.py b/src/lib/Bcfg2/Server/MultiprocessingCore.py
index 724b34d8d..4bf3e4a27 100644
--- a/src/lib/Bcfg2/Server/MultiprocessingCore.py
+++ b/src/lib/Bcfg2/Server/MultiprocessingCore.py
@@ -334,9 +334,9 @@ class MultiprocessingCore(BuiltinCore):
self.children = None
def __str__(self):
- if hasattr(Bcfg2.Options.setup, "location"):
+ if hasattr(Bcfg2.Options.setup, "server"):
return "%s(%s; %s children)" % (self.__class__.__name__,
- Bcfg2.Options.setup.location,
+ Bcfg2.Options.setup.server,
len(self._all_children))
else:
return "%s(%s children)" % (self.__class__.__name__,
diff --git a/src/lib/Bcfg2/Server/Plugin/__init__.py b/src/lib/Bcfg2/Server/Plugin/__init__.py
index e28e458b3..69fc90b2f 100644
--- a/src/lib/Bcfg2/Server/Plugin/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugin/__init__.py
@@ -11,7 +11,6 @@ documentation it's not necessary to use the submodules. E.g., you can
from Bcfg2.Server.Plugin.base import Plugin
"""
-
import Bcfg2.Options
# pylint: disable=W0401
diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py
index 245cfc256..ca0fe8188 100644
--- a/src/lib/Bcfg2/Server/Plugin/helpers.py
+++ b/src/lib/Bcfg2/Server/Plugin/helpers.py
@@ -13,7 +13,7 @@ import Bcfg2.Server
import Bcfg2.Options
import Bcfg2.Server.FileMonitor
from Bcfg2.Logger import Debuggable
-from Bcfg2.Compat import CmpMixin, wraps
+from Bcfg2.Compat import CmpMixin, MutableMapping, wraps
from Bcfg2.Server.Plugin.base import Plugin
from Bcfg2.Server.Plugin.interfaces import Generator, TemplateDataProvider
from Bcfg2.Server.Plugin.exceptions import SpecificityError, \
@@ -1066,7 +1066,22 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked):
data = candidate
break
- entry.text = data.text
+ self._apply(entry, data)
+
+ def _apply(self, entry, data):
+ """ Apply all available values from data onto entry. This
+ sets the available attributes (for all attribues unset in
+ the entry), adds all children and copies the text from data
+ to entry.
+
+ :param entry: The entry to apply the changes
+ :type entry: lxml.etree._Element
+ :param data: The entry to get the data from
+ :type data: lxml.etree._Element
+ """
+
+ if data.text is not None and data.text.strip() != '':
+ entry.text = data.text
for item in data.getchildren():
entry.append(copy.copy(item))
@@ -1685,3 +1700,84 @@ class GroupSpool(Plugin, Generator):
return
reqid = self.fam.AddMonitor(name, self)
self.handles[reqid] = relative
+
+
+class CallableDict(MutableMapping):
+ """ This maps a set of keys to a set of value-getting functions;
+ the values are populated on-the-fly by the functions as the values
+ are needed (and not before). This is for example used by
+ :func:`Bcfg2.Server.Plugins.Packages.Packages.get_additional_data`;
+ see the docstring for that function for details on why.
+
+ Unlike a dict, you can specify values or functions for the
+ righthand side of this mapping. If you specify a function, it will
+ be evaluated everytime you access the value. E.g.:
+
+ .. code-block:: python
+
+ d = CallableDict(foo=load_foo,
+ bar="bar")
+ """
+
+ def __init__(self, **getters):
+ self._getters = dict(**getters)
+
+ def __getitem__(self, key):
+ if callable(self._getters[key]):
+ return self._getters[key]()
+ else:
+ return self._getters[key]
+
+ def __setitem__(self, key, getter):
+ self._getters[key] = getter
+
+ def __delitem__(self, key):
+ del self._getters[key]
+
+ def __len__(self):
+ return len(self._getters)
+
+ def __iter__(self):
+ return iter(self._getters.keys())
+
+ def _current_data(self):
+ """ Return a dict with the current available static data
+ and ``unknown`` for all callable values.
+ """
+ rv = dict()
+ for key in self._getters.keys():
+ if callable(self._getters[key]):
+ rv[key] = 'unknown'
+ else:
+ rv[key] = self._getters[key]
+ return rv
+
+ def __repr__(self):
+ return str(self._current_data())
+
+
+class OnDemandDict(CallableDict):
+ """ This is like a :class:`CallableDict` but it will cache
+ the results of the callable getters, so that it is only evaluated
+ once when you first access it.
+ """
+
+ def __init__(self, **getters):
+ CallableDict.__init__(self, **getters)
+ self._values = dict()
+
+ def __getitem__(self, key):
+ if key not in self._values:
+ self._values[key] = super(OnDemandDict, self).__getitem__(key)
+ return self._values[key]
+
+ def __delitem__(self, key):
+ super(OnDemandDict, self).__delitem__(key)
+ del self._values[key]
+
+ def _current_data(self):
+ rv = super(OnDemandDict, self)._current_data()
+ for (key, value) in rv.items():
+ if key in self._values:
+ rv[key] = value
+ return rv
diff --git a/src/lib/Bcfg2/Server/Plugins/AWSTags.py b/src/lib/Bcfg2/Server/Plugins/AWSTags.py
index 0d6eefaaa..556805bde 100644
--- a/src/lib/Bcfg2/Server/Plugins/AWSTags.py
+++ b/src/lib/Bcfg2/Server/Plugins/AWSTags.py
@@ -172,6 +172,11 @@ class AWSTags(Bcfg2.Server.Plugin.Plugin,
def start_client_run(self, metadata):
self.expire_cache(key=metadata.hostname)
+ if self.core.metadata_cache_mode == 'aggressive':
+ self.logger.warning("AWSTags is incompatible with aggressive "
+ "client metadata caching, try 'cautious' "
+ "or 'initial'")
+ self.core.metadata_cache.expire(metadata.hostname)
def get_additional_data(self, metadata):
return self.get_tags(metadata)
diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py
index e38eeea89..4f5a79465 100644
--- a/src/lib/Bcfg2/Server/Plugins/Bundler.py
+++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py
@@ -92,6 +92,10 @@ class Bundler(Plugin,
self.logger.error("Bundler: Failed to render templated bundle "
"%s: %s" % (bundlename, err))
continue
+ except:
+ self.logger.error("Bundler: Unexpected bundler error for %s" %
+ bundlename, exc_info=1)
+ continue
if data.get("independent", "false").lower() == "true":
data.tag = "Independent"
@@ -124,12 +128,12 @@ class Bundler(Plugin,
# dependent bundle -- add it to the list of
# bundles for this client
if child.get("name") not in bundles_added:
- bundles.append(child.get("name"))
+ bundles.add(child.get("name"))
bundles_added.add(child.get("name"))
if child.get('inherit_modification', 'false') == 'true':
if metadata.version_info >= \
Bcfg2VersionInfo('1.4.0pre2'):
- lxml.etree.SubElement(data, 'Bundle',
+ lxml.etree.SubElement(data, 'BoundBundle',
name=child.get('name'))
else:
self.logger.warning(
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py
index cff9ff61e..71aec7658 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py
@@ -12,14 +12,14 @@ from Bcfg2.Server.Plugins.Cfg import CfgGenerator
try:
from jinja2 import Environment, FileSystemLoader
HAS_JINJA2 = True
-except ImportError:
- HAS_JINJA2 = False
+ class RelEnvironment(Environment):
+ """Override join_path() to enable relative template paths."""
+ def join_path(self, template, parent):
+ return os.path.join(os.path.dirname(parent), template)
-class RelEnvironment(Environment):
- """Override join_path() to enable relative template paths."""
- def join_path(self, template, parent):
- return os.path.join(os.path.dirname(parent), template)
+except ImportError:
+ HAS_JINJA2 = False
class DefaultJinja2DataProvider(DefaultTemplateDataProvider):
@@ -42,15 +42,16 @@ class CfgJinja2Generator(CfgGenerator):
#: Handle .jinja2 files
__extensions__ = ['jinja2']
- #: ``__loader_cls__`` is the class that will be instantiated to
- #: load the template files. It must implement one public function,
- #: ``load()``, as :class:`genshi.template.TemplateLoader`.
- __loader_cls__ = FileSystemLoader
+ if HAS_JINJA2:
+ #: ``__loader_cls__`` is the class that will be instantiated to
+ #: load the template files. It must implement one public function,
+ #: ``load()``, as :class:`genshi.template.TemplateLoader`.
+ __loader_cls__ = FileSystemLoader
- #: ``__environment_cls__`` is the class that will be instantiated to
- #: store the jinja2 environment. It must implement one public function,
- #: ``get_template()``, as :class:`jinja2.Environment`.
- __environment_cls__ = RelEnvironment
+ #: ``__environment_cls__`` is the class that will be instantiated to
+ #: store the jinja2 environment. It must implement one public
+ #: function, ``get_template()``, as :class:`jinja2.Environment`.
+ __environment_cls__ = RelEnvironment
#: Ignore ``.jinja2_include`` files so they can be used with the
#: Jinja2 ``{% include ... %}`` directive without raising warnings.
@@ -68,7 +69,15 @@ class CfgJinja2Generator(CfgGenerator):
encoding = Bcfg2.Options.setup.encoding
self.loader = self.__loader_cls__('/',
encoding=encoding)
- self.environment = self.__environment_cls__(loader=self.loader)
+ try:
+ # keep_trailing_newline is new in Jinja2 2.7, and will
+ # fail with earlier versions
+ self.environment = \
+ self.__environment_cls__(loader=self.loader,
+ keep_trailing_newline=True)
+ except TypeError:
+ self.environment = \
+ self.__environment_cls__(loader=self.loader)
__init__.__doc__ = CfgGenerator.__init__.__doc__
def get_data(self, entry, metadata):
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/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index 355e53588..dae03144a 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -878,6 +878,7 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool,
options = Bcfg2.Server.Plugin.GroupSpool.options + [
Bcfg2.Options.BooleanOption(
'--cfg-validation', cf=('cfg', 'validation'), default=True,
+ dest="cfg_validation",
help='Run validation on Cfg files'),
Bcfg2.Options.Option(
cf=('cfg', 'category'), dest="cfg_category",
diff --git a/src/lib/Bcfg2/Server/Plugins/Defaults.py b/src/lib/Bcfg2/Server/Plugins/Defaults.py
index 79e2ca0e2..2242e3825 100644
--- a/src/lib/Bcfg2/Server/Plugins/Defaults.py
+++ b/src/lib/Bcfg2/Server/Plugins/Defaults.py
@@ -1,5 +1,6 @@
"""This generator provides rule-based entry mappings."""
+import Bcfg2.Options
import Bcfg2.Server.Plugin
import Bcfg2.Server.Plugins.Rules
@@ -9,7 +10,10 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
"""Set default attributes on bound entries"""
__author__ = 'bcfg-dev@mcs.anl.gov'
- options = Bcfg2.Server.Plugin.PrioDir.options
+ options = Bcfg2.Server.Plugin.PrioDir.options + [
+ Bcfg2.Options.BooleanOption(
+ cf=("defaults", "replace_name"), dest="defaults_replace_name",
+ help="Replace %{name} in attributes with name of target entry")]
# Rules is a Generator that happens to implement all of the
# functionality we want, so we overload it, but Defaults should
@@ -41,3 +45,9 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
def _regex_enabled(self):
""" Defaults depends on regex matching, so force it enabled """
return True
+
+ @property
+ def _replace_name_enabled(self):
+ """ Return True if the replace_name feature is enabled,
+ False otherwise """
+ return Bcfg2.Options.setup.defaults_replace_name
diff --git a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
index b60f60e65..184b362f9 100644
--- a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
+++ b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
@@ -13,6 +13,17 @@ class GroupLogicConfig(Bcfg2.Server.Plugin.StructFile):
create = lxml.etree.Element("GroupLogic",
nsmap=dict(py="http://genshi.edgewall.org/"))
+ def __init__(self, filename, core):
+ Bcfg2.Server.Plugin.StructFile.__init__(self, filename,
+ should_monitor=True)
+ self.core = core
+
+ def Index(self):
+ Bcfg2.Server.Plugin.StructFile.Index(self)
+
+ if self.core.metadata_cache_mode in ['cautious', 'aggressive']:
+ self.core.metadata_cache.expire()
+
def _match(self, item, metadata, *args):
if item.tag == 'Group' and not len(item.getchildren()):
return [item]
@@ -39,7 +50,7 @@ class GroupLogic(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.Plugin.__init__(self, core)
Bcfg2.Server.Plugin.Connector.__init__(self)
self.config = GroupLogicConfig(os.path.join(self.data, "groups.xml"),
- should_monitor=True)
+ core=core)
self._local = local()
def get_additional_groups(self, metadata):
diff --git a/src/lib/Bcfg2/Server/Plugins/Ldap.py b/src/lib/Bcfg2/Server/Plugins/Ldap.py
index 553ddbc47..770419ba5 100644
--- a/src/lib/Bcfg2/Server/Plugins/Ldap.py
+++ b/src/lib/Bcfg2/Server/Plugins/Ldap.py
@@ -1,169 +1,264 @@
+""" A plugin to fetch data from a LDAP directory """
+
import imp
-import logging
+import os
import sys
import time
import traceback
-import Bcfg2.Server.Plugin
+from functools import partial
-logger = logging.getLogger('Bcfg2.Plugins.Ldap')
+import Bcfg2.Options
+import Bcfg2.Server.Cache
+import Bcfg2.Server.Plugin
+from Bcfg2.Logger import Debuggable
+from Bcfg2.Utils import ClassName, safe_module_name
try:
import ldap
+ HAS_LDAP = True
except ImportError:
- logger.error("Unable to load ldap module. Is python-ldap installed?")
- raise ImportError
+ HAS_LDAP = False
-# time in seconds between retries after failed LDAP connection
-RETRY_DELAY = 5
-# how many times to try reaching the LDAP server if a connection is broken
-# at the very minimum, one retry is needed to handle a restarted LDAP daemon
-RETRY_COUNT = 3
-SCOPE_MAP = {
- "base": ldap.SCOPE_BASE,
- "one": ldap.SCOPE_ONELEVEL,
- "sub": ldap.SCOPE_SUBTREE,
-}
+class ConfigFile(Bcfg2.Server.Plugin.FileBacked):
+ """ Config file for the Ldap plugin """
-LDAP_QUERIES = []
+ def __init__(self, name, core, plugin):
+ Bcfg2.Server.Plugin.FileBacked.__init__(self, name)
+ self.core = core
+ self.plugin = plugin
+ self.queries = list()
+ self.fam.AddMonitor(name, self)
+ def Index(self):
+ """ Get the queries from the config file """
+ try:
+ module_name = os.path.splitext(os.path.basename(self.name))[0]
+ module = imp.load_source(safe_module_name('Ldap', module_name),
+ self.name)
+ except: # pylint: disable=W0702
+ err = sys.exc_info()[1]
+ self.logger.error("Ldap: Failed to import %s: %s" %
+ (self.name, err))
+ return
+
+ if not hasattr(module, "__queries__"):
+ self.logger.error("Ldap: %s has no __queries__ list" % self.name)
+ return
+
+ self.queries = list()
+ for query in module.__queries__:
+ try:
+ self.queries.append(getattr(module, query))
+ except AttributeError:
+ self.logger.warning(
+ "Ldap: %s exports %s, but has no such attribute" %
+ (self.name, query))
-def register_query(query):
- LDAP_QUERIES.append(query)
+ if self.core.metadata_cache_mode in ['cautious', 'aggressive']:
+ self.core.metadata_cache.expire()
+ self.plugin.expire_cache()
-class ConfigFile(Bcfg2.Server.Plugin.FileBacked):
- """
- Config file for the Ldap plugin
-
- The config file cannot be 'parsed' in the traditional sense as we would
- need some serious type checking ugliness to just get the LdapQuery
- subclasses. The alternative would be to have the user create a list with
- a predefined name that contains all queries.
- The approach implemented here is having the user call a registering
- decorator that updates a global variable in this module.
- """
- def __init__(self, filename):
- self.filename = filename
- Bcfg2.Server.Plugin.FileBacked.__init__(self, self.filename)
- self.fam.AddMonitor(self.filename, self)
-
- def Index(self):
- """
- Reregisters the queries in the config file
-
- The config will take care of actually registering the queries,
- so we just load it once and don't keep it.
- """
- global LDAP_QUERIES
- LDAP_QUERIES = []
- imp.load_source("ldap_cfg", self.filename)
+class Ldap(Bcfg2.Server.Plugin.Plugin,
+ Bcfg2.Server.Plugin.ClientRunHooks,
+ Bcfg2.Server.Plugin.Connector):
+ """ The Ldap plugin allows adding data from an LDAP server
+ to your metadata. """
+ __rmi__ = Bcfg2.Server.Plugin.Plugin.__rmi__ + ['expire_cache']
-class Ldap(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Connector):
- """
- The Ldap plugin allows adding data from an LDAP server to your metadata.
- """
- name = "Ldap"
experimental = True
- debug_flag = False
+
+ options = [
+ Bcfg2.Options.Option(
+ cf=('ldap', 'retries'), type=int, default=3,
+ dest='ldap_retries',
+ help='The number of times to retry reaching the '
+ 'LDAP server if a connection is broken'),
+ Bcfg2.Options.Option(
+ cf=('ldap', 'retry_delay'), type=float, default=5.0,
+ dest='ldap_retry_delay',
+ help='The time in seconds betreen retries'),
+ Bcfg2.Options.BooleanOption(
+ cf=('ldap', 'cache'), default=None, dest='ldap_cache',
+ help='Cache the results of the LDAP Queries until they '
+ 'are expired using the XML-RPC RMI')]
def __init__(self, core):
Bcfg2.Server.Plugin.Plugin.__init__(self, core)
Bcfg2.Server.Plugin.Connector.__init__(self)
- self.config = ConfigFile(self.data + "/config.py")
- def debug_log(self, message, flag=None):
- if (flag is None) and self.debug_flag or flag:
- self.logger.error(message)
+ if not HAS_LDAP:
+ msg = "Python ldap module is required for Ldap plugin"
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginInitError(msg)
+
+ self.config = ConfigFile(os.path.join(self.data, 'config.py'),
+ core, self)
+ self._hosts = dict()
+
+ def _cache(self, query_name):
+ """ Return the :class:`Cache <Bcfg2.Server.Cache>` for the
+ given query name. """
+ return Bcfg2.Server.Cache.Cache('Ldap', 'results', query_name)
+
+ def _execute_query(self, query, metadata):
+ """ Return the cached result of the given query for this host or
+ execute the given query and cache the result. """
+ result = None
+
+ if Bcfg2.Options.setup.ldap_cache is not False:
+ cache = self._cache(query.name)
+ result = cache.get(metadata.hostname, None)
+
+ if result is None:
+ try:
+ self.debug_log("Processing query '%s'" % query.name)
+ result = query.get_result(metadata)
+ if Bcfg2.Options.setup.ldap_cache is not False:
+ cache[metadata.hostname] = result
+ except: # pylint: disable=W0702
+ self.logger.error(
+ "Exception during processing of query named '%s', query "
+ "results will be empty and may cause bind failures" %
+ query.name)
+ for line in traceback.format_exc().split('\n'):
+ self.logger.error(line)
+ return result
def get_additional_data(self, metadata):
- query = None
- try:
- data = {}
- self.debug_log("LdapPlugin debug: found queries " +
- str(LDAP_QUERIES))
- for QueryClass in LDAP_QUERIES:
- query = QueryClass()
+ data = {}
+ self.debug_log("Found queries %s" % self.config.queries)
+ for query_class in self.config.queries:
+ try:
+ query = query_class()
if query.is_applicable(metadata):
- self.debug_log("LdapPlugin debug: processing query '" +
- query.name + "'")
- data[query.name] = query.get_result(metadata)
+ self.debug_log("Processing query '%s'" % query.name)
+ data[query.name] = partial(
+ self._execute_query, query, metadata)
else:
- self.debug_log("LdapPlugin debug: query '" + query.name +
- "' not applicable to host '" +
- metadata.hostname + "'")
- return data
- except Exception:
- if hasattr(query, "name"):
- logger.error("LdapPlugin error: " +
- "Exception during processing of query named '" +
- str(query.name) +
- "', query results will be empty" +
- " and may cause bind failures")
- for line in traceback.format_exception(sys.exc_info()[0],
- sys.exc_info()[1],
- sys.exc_info()[2]):
- logger.error("LdapPlugin error: " +
- line.replace("\n", ""))
- return {}
-
-
-class LdapConnection(object):
- """
- Connection to an LDAP server.
- """
- def __init__(self, host="localhost", port=389,
+ self.debug_log("query '%s' not applicable to host '%s'" %
+ (query.name, metadata.hostname))
+ except: # pylint: disable=W0702
+ self.logger.error(
+ "Exception during preparation of query named '%s'. "
+ "Query will be ignored." % query_class.__name__)
+ for line in traceback.format_exc().split('\n'):
+ self.logger.error(line)
+
+ return Bcfg2.Server.Plugin.CallableDict(**data)
+
+ def start_client_run(self, metadata):
+ if Bcfg2.Options.setup.ldap_cache is None:
+ self.expire_cache(hostname=metadata.hostname)
+
+ def expire_cache(self, query=None, hostname=None):
+ """ Expire the cache. You can select the items to purge
+ per query and/or per host, or you can purge all cached
+ data. This is exposed as an XML-RPC RMI. """
+
+ tags = ['Ldap', 'results']
+ if query:
+ tags.append(query)
+ if hostname:
+ tags.append(hostname)
+
+ return Bcfg2.Server.Cache.expire(*tags)
+
+
+class LdapConnection(Debuggable):
+ """ Connection to an LDAP server. """
+
+ def __init__(self, host="localhost", port=389, uri=None, options=None,
binddn=None, bindpw=None):
+ Debuggable.__init__(self)
+
+ if HAS_LDAP:
+ msg = "Python ldap module is required for Ldap plugin"
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginInitError(msg)
+
self.host = host
self.port = port
+ self.uri = uri
+ self.options = options
self.binddn = binddn
self.bindpw = bindpw
self.conn = None
+ self.__scopes__ = {
+ 'base': ldap.SCOPE_BASE,
+ 'one': ldap.SCOPE_ONELEVEL,
+ 'sub': ldap.SCOPE_SUBTREE,
+ }
+
def __del__(self):
+ """ Disconnection if the instance is destroyed. """
+ self.disconnect()
+
+ def disconnect(self):
+ """ If a connection to an LDAP server is available, disconnect it. """
if self.conn:
- self.conn.unbind()
+ self.conn.unbund()
+ self.conn = None
+
+ def connect(self):
+ """ Open a connection to the configured LDAP server, and do a simple
+ bind ff both binddn and bindpw are set. """
+ self.disconnect()
+ self.conn = ldap.initialize(self.get_uri())
+
+ if self.options is not None:
+ for (option, value) in self.options.items():
+ self.conn.set_option(option, value)
- def init_conn(self):
- self.conn = ldap.initialize(self.url)
if self.binddn is not None and self.bindpw is not None:
self.conn.simple_bind_s(self.binddn, self.bindpw)
def run_query(self, query):
- result = None
- for attempt in range(RETRY_COUNT + 1):
- if attempt >= 1:
- logger.error("LdapPlugin error: " +
- "LDAP server down (retry " + str(attempt) + "/" +
- str(RETRY_COUNT) + ")")
+ """ Connect to the server and execute the query. If the server is
+ down, wait the configured amount and try to reconnect.
+
+ :param query: The query to execute on the LDAP server.
+ :type query: Bcfg.Server.Plugins.Ldap.LdapQuery
+ """
+ for attempt in range(Bcfg2.Options.setup.ldap_retries + 1):
try:
if not self.conn:
- self.init_conn()
- result = self.conn.search_s(
- query.base,
- SCOPE_MAP[query.scope],
- query.filter.replace("\\", "\\\\"),
- query.attrs,
- )
- break
+ self.connect()
+
+ return self.conn.search_s(
+ query.base, self.__scopes__[query.scope],
+ query.filter.replace('\\', '\\\\'), query.attrs)
+
except ldap.SERVER_DOWN:
self.conn = None
- time.sleep(RETRY_DELAY)
- return result
+ self.logger.error(
+ "LdapConnection: Server %s down. Retry %d/%d in %.2fs." %
+ (self.get_uri(), attempt + 1,
+ Bcfg2.Options.setup.ldap_retries,
+ Bcfg2.Options.setup.ldap_retry_delay))
+ time.sleep(Bcfg2.Options.setup.ldap_retry_delay)
+
+ return None
- @property
- def url(self):
- return "ldap://" + self.host + ":" + str(self.port)
+ def get_uri(self):
+ """ The URL of the LDAP server. """
+ if self.uri is None:
+ if self.port == 636:
+ return "ldaps://%s" % self.host
+ return "ldap://%s:%d" % (self.host, self.port)
+ return self.uri
class LdapQuery(object):
- """
- Query referencing an LdapConnection and providing several
- methods for query manipulation.
- """
+ """ Query referencing an LdapConnection and providing several
+ methods for query manipulation. """
+
+ #: Name of the Query, used to register it in additional data.
+ name = ClassName()
- name = "unknown"
base = ""
scope = "sub"
filter = "(objectClass=*)"
@@ -172,80 +267,48 @@ class LdapQuery(object):
result = None
def __unicode__(self):
- return "LdapQuery:" + self.name
+ return "LdapQuery: %s" % self.name
- def is_applicable(self, metadata):
- """
- Overrideable method to determine if the query is to be executed for
- the given metadata object.
- Defaults to true.
- """
- return True
+ def is_applicable(self, metadata): # pylint: disable=W0613
+ """ Check is the query should be executed for a given metadata
+ object.
- def prepare_query(self, metadata):
+ :param metadata: The client metadata
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
"""
- Overrideable method to alter the query based on metadata.
- Defaults to doing nothing.
-
- In most cases, you will do something like
+ return True
- self.filter = "(cn=" + metadata.hostname + ")"
+ def prepare_query(self, metadata, **kwargs): # pylint: disable=W0613
+ """ Prepares the query based on the client metadata. You can
+ for example modify the filter based on the client hostname.
- here.
+ :param metadata: The client metadata
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
"""
pass
- def process_result(self, metadata):
- """
- Overrideable method to post-process the query result.
- Defaults to returning the unaltered result.
- """
- return self.result
+ def process_result(self, metadata, **kwargs): # pylint: disable=W0613
+ """ Post-process the query result.
- def get_result(self, metadata):
- """
- Method to handle preparing, executing and processing the query.
- """
- if isinstance(self.connection, LdapConnection):
- self.prepare_query(metadata)
- self.result = self.connection.run_query(self)
- self.result = self.process_result(metadata)
- return self.result
- else:
- logger.error("LdapPlugin error: " +
- "No valid connection defined for query " + str(self))
- return None
-
-
-class LdapSubQuery(LdapQuery):
- """
- SubQueries are meant for internal use only and are not added
- to the metadata object. They are useful for situations where
- you need to run more than one query to obtain some data.
- """
- def prepare_query(self, metadata, **kwargs):
- """
- Overrideable method to alter the query based on metadata.
- Defaults to doing nothing.
- """
- pass
-
- def process_result(self, metadata, **kwargs):
- """
- Overrideable method to post-process the query result.
- Defaults to returning the unaltered result.
+ :param metadata: The client metadata
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
"""
return self.result
def get_result(self, metadata, **kwargs):
+ """ Handle the perparation, execution and processing of the query.
+
+ :param metadata: The client metadata
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ :raises: :class:`Bcfg2.Server.Plugin.exceptions.PluginExecutionError`
"""
- Method to handle preparing, executing and processing the query.
- """
- if isinstance(self.connection, LdapConnection):
+
+ if self.connection is not None:
self.prepare_query(metadata, **kwargs)
self.result = self.connection.run_query(self)
- return self.process_result(metadata, **kwargs)
+ self.result = self.process_result(metadata, **kwargs)
else:
- logger.error("LdapPlugin error: " +
- "No valid connection defined for query " + str(self))
- return None
+ raise Bcfg2.Server.Plugin.PluginExecutionError(
+ 'No connection defined for %s' % self.name)
+
+ return self.result
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index b850c1870..b912d3725 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -21,24 +21,25 @@ 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:
- from django.db import models
- HAS_DJANGO = True
- except ImportError:
- HAS_DJANGO = False
+ if not HAS_DJANGO:
return
class MetadataClientModel(models.Model, # pylint: disable=W0621
@@ -1394,8 +1395,6 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
# look at cert.cN
client = certinfo['commonName']
self.debug_log("Got cN %s; using as client name" % client)
- auth_type = self.auth.get(client,
- Bcfg2.Options.setup.authentication)
elif user == 'root':
id_method = 'address'
try:
@@ -1417,6 +1416,13 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
# we have the client name
self.debug_log("Authenticating client %s" % client)
+ # validate id_method
+ auth_type = self.auth.get(client, Bcfg2.Options.setup.authentication)
+ if auth_type == 'cert' and id_method != 'cert':
+ self.logger.error("Client %s does not provide a cert, but only "
+ "cert auth is allowed" % client)
+ return False
+
# next we validate the address
if (id_method != 'uuid' and
not self.validate_client_address(client, address)):
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/Ohai.py b/src/lib/Bcfg2/Server/Plugins/Ohai.py
index 461be9ba8..b314e60a0 100644
--- a/src/lib/Bcfg2/Server/Plugins/Ohai.py
+++ b/src/lib/Bcfg2/Server/Plugins/Ohai.py
@@ -94,7 +94,12 @@ class Ohai(Bcfg2.Server.Plugin.Plugin,
return [self.probe]
def ReceiveData(self, meta, datalist):
- self.cache[meta.hostname] = datalist[0].text
+ if meta.hostname not in self.cache or \
+ self.cache[meta.hostname] != datalist[0].text:
+ self.cache[meta.hostname] = datalist[0].text
+
+ if self.core.metadata_cache_mode in ['cautious', 'aggressive']:
+ self.core.metadata_cache.expire(meta.hostname)
def get_additional_data(self, meta):
if meta.hostname in self.cache:
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
index 7de79e2f3..956cb9f51 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py
@@ -6,6 +6,15 @@ from Bcfg2.Server.Plugins.Packages.Collection import Collection
from Bcfg2.Server.Plugins.Packages.Source import Source
+def strip_suffix(pkgname):
+ """ Remove the ':any' suffix from a dependency name if it is present.
+ """
+ if pkgname.endswith(':any'):
+ return pkgname[:-4]
+ else:
+ return pkgname
+
+
class AptCollection(Collection):
""" Handle collections of APT sources. This is a no-op object
that simply inherits from
@@ -34,8 +43,12 @@ class AptCollection(Collection):
for source in self:
if source.rawurl:
- self.logger.info("Packages: Skipping rawurl %s" %
- source.rawurl)
+ if source.rawurl[-1] != '/':
+ source.rawurl = source.rawurl + "/"
+ index = source.rawurl.rfind("/", 0, -1)
+ lines.append("deb %s %s" %
+ (source.rawurl[:index],
+ source.rawurl[index + 1:]))
else:
lines.append("deb %s %s %s" % (source.url, source.version,
" ".join(source.components)))
@@ -44,7 +57,7 @@ class AptCollection(Collection):
(source.url,
source.version,
" ".join(source.components)))
- lines.append("")
+ lines.append("")
return "\n".join(lines)
@@ -73,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:
@@ -111,6 +125,7 @@ class AptSource(Source):
cdeps = [re.sub(r'\s+', '',
re.sub(r'\(.*\)', '', cdep))
for cdep in dep.split('|')]
+ cdeps = [strip_suffix(cdep) for cdep in cdeps]
dyn_dname = "choice-%s-%s-%s" % (pkgname,
barch,
vindex)
@@ -124,6 +139,7 @@ class AptSource(Source):
else:
raw_dep = re.sub(r'\(.*\)', '', dep)
raw_dep = raw_dep.rstrip().strip()
+ raw_dep = strip_suffix(raw_dep)
if words[0] == 'Recommends':
brecs[barch][pkgname].append(raw_dep)
else:
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
index 004e27874..e0d6e1fc3 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
@@ -289,7 +289,7 @@ class Collection(list, Debuggable):
return any(source.is_virtual_package(self.metadata, package)
for source in self)
- def get_deps(self, package, recs=None):
+ def get_deps(self, package, recs=None, pinnings=None):
""" Get a list of the dependencies of the given package.
The base implementation simply aggregates the results of
@@ -297,16 +297,35 @@ class Collection(list, Debuggable):
:param package: The name of the symbol, but see :ref:`pkg-objects`
:type package: string
+ :param pinnings: Mapping from package names to source names.
+ :type pinnings: dict
:returns: list of strings, but see :ref:`pkg-objects`
"""
recommended = None
if recs and package in recs:
recommended = recs[package]
+ pin_found = False
+ pin_source = None
+ if pinnings and package in pinnings:
+ pin_source = pinnings[package]
+
for source in self:
+ if pin_source and source.name not in pin_source:
+ continue
+ pin_found = True
+
if source.is_package(self.metadata, package):
return source.get_deps(self.metadata, package, recommended)
+ if not pin_found:
+ if pin_source:
+ self.logger.error("Packages: Source '%s' for package '%s' not found" %
+ (' or '.join(pin_source), package))
+ else:
+ self.logger.error("Packages: No source found for package '%s'" %
+ package);
+
return []
def get_essential(self):
@@ -471,12 +490,14 @@ class Collection(list, Debuggable):
@track_statistics()
def complete(self, packagelist, # pylint: disable=R0912,R0914
- recommended=None):
+ recommended=None, pinnings=None):
""" Build a complete list of all packages and their dependencies.
:param packagelist: Set of initial packages computed from the
specification.
:type packagelist: set of strings, but see :ref:`pkg-objects`
+ :param pinnings: Mapping from package names to source names.
+ :type pinnings: dict
:returns: tuple of sets - The first element contains a set of
strings (but see :ref:`pkg-objects`) describing the
complete package list, and the second element is a
@@ -535,7 +556,7 @@ class Collection(list, Debuggable):
self.debug_log("Packages: handling package requirement %s" %
(current,))
packages.add(current)
- deps = self.get_deps(current, recommended)
+ deps = self.get_deps(current, recommended, pinnings)
newdeps = set(deps).difference(examined)
if newdeps:
self.debug_log("Packages: Package %s added requirements %s"
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Dummy.py b/src/lib/Bcfg2/Server/Plugins/Packages/Dummy.py
new file mode 100644
index 000000000..f47b8f22c
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Dummy.py
@@ -0,0 +1,35 @@
+""" Dummy backend for :mod:`Bcfg2.Server.Plugins.Packages` """
+
+from Bcfg2.Server.Plugins.Packages.Collection import Collection
+from Bcfg2.Server.Plugins.Packages.Source import Source
+
+
+class DummyCollection(Collection):
+ """ Handle collections of Dummy sources. This is a no-op object
+ that simply inherits from
+ :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection`,
+ overrides nothing, and defers all operations to :class:`PacSource`
+ """
+
+ def __init__(self, metadata, sources, cachepath, basepath, debug=False):
+ # we define an __init__ that just calls the parent __init__,
+ # so that we can set the docstring on __init__ to something
+ # different from the parent __init__ -- namely, the parent
+ # __init__ docstring, minus everything after ``.. -----``,
+ # which we use to delineate the actual docs from the
+ # .. autoattribute hacks we have to do to get private
+ # attributes included in sphinx 1.0 """
+ Collection.__init__(self, metadata, sources, cachepath, basepath,
+ debug=debug)
+ __init__.__doc__ = Collection.__init__.__doc__.split(".. -----")[0]
+
+
+class DummySource(Source):
+ """ Handle Dummy sources """
+
+ #: DummySource sets the ``type`` on Package entries to "dummy"
+ ptype = 'dummy'
+
+ def __init__(self, basepath, xsource):
+ xsource.set('rawurl', 'http://example.com/')
+ Source.__init__(self, basepath, xsource)
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Layman.py b/src/lib/Bcfg2/Server/Plugins/Packages/Layman.py
new file mode 100644
index 000000000..19877d32b
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Layman.py
@@ -0,0 +1,142 @@
+import os
+import layman
+import Bcfg2.Server.Plugin
+
+class LaymanSource(Bcfg2.Server.Plugin.Debuggable):
+ basegroups = ['portage', 'gentoo', 'emerge']
+ ptype = 'layman'
+ cclass = 'Portage'
+
+ def __init__(self, basepath, xsource):
+ Bcfg2.Server.Plugin.Debuggable.__init__(self)
+ self.basepath = basepath
+ self.xsource = xsource
+
+ self.url = xsource.get('url', 'http://www.gentoo.org/proj/en/overlays/repositories.xml')
+ self.name = xsource.get('name', '')
+ self.priority = xsource.get('priority', 0)
+ self.cachefile = None
+ self.gpgkeys = []
+ self.recommended = False
+ self.essentialpkgs = set()
+ self.arches = [item.text for item in xsource.findall('Arch')]
+
+ self.url_map = [dict(version=None, component=None, arch=arch,
+ url=self.url, baseurl=self.url) for arch in self.arches]
+
+ #: A list of the the names of packages that are blacklisted
+ #: from this source
+ self.blacklist = [item.text for item in xsource.findall('Blacklist')]
+
+ #: A list of the the names of packages that are whitelisted in
+ #: this source
+ self.whitelist = [item.text for item in xsource.findall('Whitelist')]
+
+
+ # configure layman
+ base = os.path.join(basepath, 'layman')
+ storage = os.path.join(base, 'overlays')
+ config = layman.config.OptionConfig(options = {
+ 'storage': os.path.join(base, 'overlays'),
+ 'cache': os.path.join(base, 'cache'),
+ 'installed': os.path.join(base, 'installed.xml'),
+ 'local_list': os.path.join(base, 'overlays.xml'),
+ 'overlays': [self.url]
+ })
+ self.api = layman.LaymanAPI(config)
+
+ # path
+ self.dir = os.path.join(storage, self.name)
+
+ # build the set of conditions to see if this source applies to
+ # a given set of metadata
+ self.conditions = []
+ self.groups = [] # provided for some limited backwards compat
+ for el in xsource.iterancestors():
+ if el.tag == "Group":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") not in m.groups)
+ else:
+ self.groups.append(el.get("name"))
+ self.conditions.append(lambda m, el=el:
+ el.get("name") in m.groups)
+ elif el.tag == "Client":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") != m.hostname)
+ else:
+ self.conditions.append(lambda m, el=el:
+ el.get("name") == m.hostname)
+
+ def get_repo_name(self, url_map):
+ return self.name
+
+ def save_state(self):
+ pass
+
+ def load_state(self):
+ pass
+
+ def filter_unknown(self, unknown):
+ filtered = set([u for u in unknown if u.startswith('choice')])
+ unknown.difference_update(filtered)
+
+ def get_urls(self):
+ return self.url
+ urls = property(get_urls)
+
+ def setup_data(self, force_update=False):
+ self.api.fetch_remote_list()
+ if not self.api.is_repo(self.name):
+ self.logger.error("Packages: Layman overlay '%s' not"
+ " found" % self.name)
+ return False
+
+ if not self.api.is_installed(self.name):
+ self.logger.info("Packages: Adding layman overlay '%s'" %
+ self.name)
+ if not self.api.add_repos(self.name):
+ self.logger.error("Packages: Failed adding layman"
+ " overlay '%s'" % self.name)
+ return False
+
+ if force_update:
+ if not self.api.sync(self.name):
+ self.logger.error("Packages: Failed syncing layman"
+ " overlay '%s'" % self.name)
+ return False
+
+ return True
+
+
+ def applies(self, metadata):
+ """ Return true if this source applies to the given client,
+ i.e., the client is in all necessary groups.
+
+ :param metadata: The client metadata to check to see if this
+ source applies
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ :returns: bool
+ """
+ # check arch groups
+ if not self.arch_groups_match(metadata):
+ return False
+
+ # check Group/Client tags from sources.xml
+ for condition in self.conditions:
+ if not condition(metadata):
+ return False
+
+ return True
+
+ def arch_groups_match(self, metadata):
+ """ Returns True if the client is in an arch group that
+ matches the arch of this source.
+
+ :returns: bool
+ """
+ for arch in self.arches:
+ if arch in metadata.groups:
+ return True
+ return False
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/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
index 1af046ec0..9db521aae 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
@@ -106,6 +106,8 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile):
source = self.source_from_xml(xsource)
if source is not None:
self.entries.append(source)
+ sorted(self.entries, key=(lambda source: source.priority),
+ reverse=True)
Index.__doc__ = Bcfg2.Server.Plugin.StructFile.Index.__doc__ + """
``Index`` is responsible for calling :func:`source_from_xml`
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py
index 736cdcdd4..55dd4e488 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
@@ -75,9 +76,7 @@ class PkgngSource(Source):
self.logger.error("Packages: Failed to read file %s" % fname)
raise
for line in reader.readlines():
- if not isinstance(line, str):
- line = line.decode('utf-8')
- pkg = json.loads(line)
+ pkg = json.loads(unicode(line, errors='ignore'))
pkgname = pkg['name']
self.pkgnames.add(pkgname)
if 'deps' in pkg:
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Portage.py b/src/lib/Bcfg2/Server/Plugins/Packages/Portage.py
new file mode 100644
index 000000000..9df4467e0
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Portage.py
@@ -0,0 +1,333 @@
+import re
+import gzip
+import sys
+import os
+import lxml.etree
+import Bcfg2.Options
+import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugins.Packages.Collection import Collection
+from Bcfg2.Server.Plugins.Packages.Layman import LaymanSource
+
+_portage_python = '/usr/lib/portage/pym/'
+
+def _import_portage(caller):
+ # generate prefix path
+ caller.prefix = os.path.join(caller.basepath, 'cache', 'portage')
+ if not os.path.isdir(caller.prefix):
+ caller.logger.error("Packages: %s is not a dir. "
+ "Portage will not work. Please "
+ "remember to setup a EPREFIX there." %
+ caller.prefix)
+ # TODO: automatic EPREFIX setup
+ raise Exception('Invalid EPREFIX')
+
+ os.environ['PORTAGE_OVERRIDE_EPREFIX'] = caller.prefix
+
+ if not os.path.isdir(_portage_python):
+ self.logger.error("Packages: %s not found. Have you installed "
+ "the portage python modules?" % _portage_python)
+ raise Exception('Portage not found')
+
+ sys.path = sys.path + [_portage_python]
+ portage = __import__('portage', globals(), locals(),
+ ['_sets', 'dbapi.porttree' ])
+ emerge = __import__('_emerge', globals(), locals(),
+ ['RootConfig', 'depgraph', 'Package', 'actions'])
+
+ # setup profile
+ if '_setup_profile' in dir(caller):
+ caller._setup_profile(portage)
+
+ # fix some default settings
+ portage.settings.unlock()
+ portage.settings['PORTAGE_RSYNC_INITIAL_TIMEOUT'] = '0'
+ portage.settings.lock()
+
+ porttree = portage.db[portage.root]['porttree']
+ caller._import_portage(portage, emerge, porttree)
+
+
+class PortageCollection(Collection):
+ def __init__(self, metadata, sources, cachepath, basepath, debug=False):
+ Collection.__init__(self, metadata, sources, cachepath, basepath,
+ debug=debug)
+ self.portage = None
+ self.emerge = None
+ self.porttree = None
+
+ @property
+ def cachefiles(self):
+ return []
+
+ def complete(self, packagelist, pinnings=None, recommended=None):
+ if not self.portage:
+ _import_portage(self)
+
+ # calculate deps
+ setconfig = self.portage._sets.load_default_config(
+ self.portage.settings,
+ self.portage.db[self.portage.root])
+ rconfig = self.emerge.RootConfig.RootConfig(
+ self.portage.settings,
+ self.portage.db[self.portage.root],
+ setconfig)
+ self.portage.db[self.portage.root]['root_config'] = rconfig
+
+ pkgs = ["=" + j.cpv for (i, j) in packagelist if i == 'ok']
+ fail = [j for (i, j) in packagelist if i == 'fail']
+
+ x = self.emerge.depgraph.backtrack_depgraph(
+ self.portage.settings,
+ self.portage.db,
+ {'--pretend': True},
+ {'recurse': True},
+ 'merge',
+ pkgs,
+ None)
+
+ g = x[1]._dynamic_config.digraph
+ packages = [i for i in g.all_nodes() \
+ if isinstance(i, self.emerge.Package.Package)]
+
+ return (set(packages), set(fail))
+
+ def get_additional_data(self):
+ return []
+
+ def get_group(self, group):
+ self.logger.warning("Packages: Package sets are currently not supported")
+ return []
+
+ def packages_from_entry(self, entry):
+ if not self.portage:
+ _import_portage(self)
+
+ try:
+ name = entry.get('name')
+ pkgs = self.porttree.dep_bestmatch(name)
+ except self.portage.exception.AmbiguousPackageName as e:
+ self.logger.error("Packages: AmbiguousPackageName: %s" % e)
+ pkgs = ''
+
+ if pkgs == '':
+ return [('fail', name)]
+
+ return [('ok', pkgs)]
+
+ def packages_to_entry(self, pkgs, entry):
+ for pkg in pkgs:
+ if pkg.slot != '0':
+ name = "%s:%s" % (pkg.cp, pkg.slot)
+ else:
+ name = pkg.cp
+
+ lxml.etree.SubElement(entry, 'BoundPackage', name=name,
+ version=Bcfg2.Options.setup.packages_version,
+ type=self.ptype, origin='Packages')
+
+ def get_new_packages(self, initial, complete):
+ new = []
+ init = [pkg.cp for status, pkg in initial if status == 'ok']
+
+ for pkg in complete:
+ if pkg.cp not in init:
+ new.append(pkg)
+
+ return new
+
+ def setup_data(self, force_update=False):
+ pass
+
+ def _setup_profile(self, portage):
+ if 'gentoo-profile' not in self.metadata.Probes:
+ raise Exception('Unknown profile.')
+
+ profile = os.path.join(self.prefix, 'usr/portage/profiles/',
+ self.metadata.Probes['gentoo-profile'].strip())
+
+ env = portage.settings.configdict['backupenv']
+
+ # add layman overlays
+ env['PORTDIR_OVERLAY'] = ''
+ for overlay in self:
+ if isinstance(overlay, LaymanSource):
+ env['PORTDIR_OVERLAY'] += ' '
+ env['PORTDIR_OVERLAY'] += overlay.dir
+
+ portage.settings = portage.package.ebuild.config.config(
+ config_root=portage.settings['PORTAGE_CONFIGROOT'],
+ target_root=portage.settings['ROOT'],
+ env=env,
+ eprefix=portage.settings['EPREFIX'],
+ config_profile_path=profile)
+
+ portage.db[portage.root]['porttree'] \
+ = portage.dbapi.porttree.portagetree(portage.settings)
+ portage.db[portage.root]['vartree'] \
+ = portage.dbapi.vartree.vartree(portage.settings)
+
+ def _set_portage_config(self):
+ # get global use flags
+ self.portage.settings.unlock()
+ self.portage.settings['USE'] = ''
+ if 'gentoo-use-flags' in self.metadata.Probes:
+ self.portage.settings['USE'] = \
+ self.metadata.Probes['gentoo-use-flags']
+
+ # add package flags (accept_keywords, use)
+ if hasattr(self.metadata, 'PkgVars'):
+ for k in self.metadata.PkgVars['keywords']:
+ keyword = self.metadata.PkgVars['keywords'][k]
+ self.portage.settings._keywords_manager.pkeywordsdict[k] = \
+ {self.portage.dep.Atom(k): tuple(keyword)}
+
+
+ for u in self.metadata.PkgVars['use']:
+ use = self.metadata.PkgVars['use'][u]
+ self.portage.settings._use_manager._pusedict[u] = \
+ {u: tuple(use)}
+
+ self.portage.settings.lock()
+
+ def _import_portage(self, portage, emerge, porttree):
+ self.portage = portage
+ self.emerge = emerge
+ self.porttree = porttree
+ self._set_portage_config()
+
+ for s in self:
+ if isinstance(s, PortageSource):
+ s._import_portage(portage, emerge, porttree)
+
+
+class PortageSource(Bcfg2.Server.Plugin.Debuggable):
+ basegroups = ['portage', 'gentoo', 'emerge']
+ ptype = 'ebuild'
+
+ def __init__(self, basepath, xsource):
+ Bcfg2.Server.Plugin.Debuggable.__init__(self)
+ self.basepath = basepath
+ self.xsource = xsource
+
+ self.url = xsource.get('url', '')
+ self.priority = xsource.get('priority', 0)
+ self.cachefile = None
+ self.gpgkeys = []
+ self.recommended = False
+ self.essentialpkgs = set()
+ self.arches = [item.text for item in xsource.findall('Arch')]
+
+ self.url_map = [dict(version=None, component=None, arch=arch,
+ url=self.url, baseurl=self.url) for arch in self.arches]
+
+ #: A list of the the names of packages that are blacklisted
+ #: from this source
+ self.blacklist = [item.text for item in xsource.findall('Blacklist')]
+
+ #: A list of the the names of packages that are whitelisted in
+ #: this source
+ self.whitelist = [item.text for item in xsource.findall('Whitelist')]
+
+
+ self.portage = None
+ self.emerge = None
+ self.porttree = None
+
+ # build the set of conditions to see if this source applies to
+ # a given set of metadata
+ self.conditions = []
+ self.groups = [] # provided for some limited backwards compat
+ for el in xsource.iterancestors():
+ if el.tag == "Group":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") not in m.groups)
+ else:
+ self.groups.append(el.get("name"))
+ self.conditions.append(lambda m, el=el:
+ el.get("name") in m.groups)
+ elif el.tag == "Client":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") != m.hostname)
+ else:
+ self.conditions.append(lambda m, el=el:
+ el.get("name") == m.hostname)
+ def get_repo_name(self, url_map):
+ return "portage"
+
+ def _import_portage(self, portage, emerge, porttree):
+ self.portage = portage
+ self.emerge = emerge
+ self.porttree = porttree
+
+ def save_state(self):
+ pass
+
+ def load_state(self):
+ pass
+
+ def filter_unknown(self, unknown):
+ filtered = set([u for u in unknown if u.startswith('choice')])
+ unknown.difference_update(filtered)
+
+ def get_urls(self):
+ return self.url
+ urls = property(get_urls)
+
+ def setup_data(self, force_update=False):
+ if not self.porttree:
+ _import_portage(self)
+
+ timestamp = os.path.join(self.porttree.portroot, 'metadata/timestamp.chk')
+ if not os.path.isfile(timestamp):
+ self.logger.error("Packages: Timestamp not found; "
+ "falling back to sync")
+ force_update = True
+
+ if force_update:
+ # update sync url
+ self.portage.settings.unlock()
+ self.portage.settings['SYNC'] = self.url
+ self.portage.settings.lock()
+
+ # realy force the sync
+ if os.path.isfile(timestamp):
+ os.unlink(timestamp)
+
+ # sync
+ self.logger.info("Packages: Syncing with %s" % self.url)
+ self.emerge.actions.action_sync(self.portage.settings,
+ self.portage.db, None,
+ {'--quiet': True}, 'sync')
+
+ def applies(self, metadata):
+ """ Return true if this source applies to the given client,
+ i.e., the client is in all necessary groups.
+
+ :param metadata: The client metadata to check to see if this
+ source applies
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ :returns: bool
+ """
+ # check arch groups
+ if not self.arch_groups_match(metadata):
+ return False
+
+ # check Group/Client tags from sources.xml
+ for condition in self.conditions:
+ if not condition(metadata):
+ return False
+
+ return True
+
+ def arch_groups_match(self, metadata):
+ """ Returns True if the client is in an arch group that
+ matches the arch of this source.
+
+ :returns: bool
+ """
+ for arch in self.arches:
+ if arch in metadata.groups:
+ return True
+ return False
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
index c9f6ea14a..5ed809694 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py
@@ -135,22 +135,22 @@ class Source(Debuggable): # pylint: disable=R0902
#: A list of the text of all 'Component' attributes of this
#: source from XML
- self.components = [item.text for item in xsource.findall('Component')]
+ self.components = []
#: A list of the arches supported by this source
- self.arches = [item.text for item in xsource.findall('Arch')]
+ self.arches = []
#: A list of the the names of packages that are blacklisted
#: from this source
- self.blacklist = [item.text for item in xsource.findall('Blacklist')]
+ self.blacklist = []
#: A list of the the names of packages that are whitelisted in
#: this source
- self.whitelist = [item.text for item in xsource.findall('Whitelist')]
+ self.whitelist = []
#: Whether or not to include deb-src lines in the generated APT
#: configuration
- self.debsrc = xsource.get('debsrc', 'false') == 'true'
+ self.debsrc = False
#: A dict of repository options that will be included in the
#: configuration generated on the server side (if such is
@@ -162,51 +162,44 @@ class Source(Debuggable): # pylint: disable=R0902
#: configuration generated for the client (if that is
#: supported by the backend)
self.client_options = dict()
- opts = xsource.findall("Options")
- for el in opts:
- repoopts = dict([(k, v)
- for k, v in el.attrib.items()
- if k != "clientonly" and k != "serveronly"])
- if el.get("clientonly", "false").lower() == "false":
- self.server_options.update(repoopts)
- if el.get("serveronly", "false").lower() == "false":
- self.client_options.update(repoopts)
#: A list of URLs to GPG keys that apply to this source
- self.gpgkeys = [el.text for el in xsource.findall("GPGKey")]
+ self.gpgkeys = []
#: Whether or not to include essential packages from this source
- self.essential = xsource.get('essential', 'true').lower() == 'true'
+ self.essential = True
#: Whether or not to include recommended packages from this source
- self.recommended = xsource.get('recommended',
- 'false').lower() == 'true'
+ self.recommended = False
#: The "rawurl" attribute from :attr:`xsource`, if applicable.
#: A trailing slash is automatically appended to this if there
#: wasn't one already present.
- self.rawurl = xsource.get('rawurl', '')
- if self.rawurl and not self.rawurl.endswith("/"):
- self.rawurl += "/"
+ self.rawurl = None
#: The "url" attribute from :attr:`xsource`, if applicable. A
#: trailing slash is automatically appended to this if there
#: wasn't one already present.
- self.url = xsource.get('url', '')
- if self.url and not self.url.endswith("/"):
- self.url += "/"
+ self.url = None
#: The "version" attribute from :attr:`xsource`
- self.version = xsource.get('version', '')
+ self.version = None
#: The "name" attribute from :attr:`xsource`
- self.name = xsource.get('name', None)
+ self.name = None
+
+ #: The "priority" attribute from :attr:`xsource`
+ self.priority = xsource.get('priority', 500)
+
+ #: The "pin" attribute from :attr:`xsource`
+ self.pin = xsource.get('pin', '')
#: A list of predicates that are used to determine if this
#: source applies to a given
#: :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata`
#: object.
self.conditions = []
+
#: Formerly, :ref:`server-plugins-generators-packages` only
#: supported applying package sources to groups; that is, they
#: could not be assigned by more complicated logic like
@@ -214,22 +207,6 @@ class Source(Debuggable): # pylint: disable=R0902
#: attribute attempts to provide for some limited backwards
#: compat with older code that relies on this.
self.groups = []
- for el in xsource.iterancestors():
- if el.tag == "Group":
- if el.get("negate", "false").lower() == "true":
- self.conditions.append(lambda m, el=el:
- el.get("name") not in m.groups)
- else:
- self.groups.append(el.get("name"))
- self.conditions.append(lambda m, el=el:
- el.get("name") in m.groups)
- elif el.tag == "Client":
- if el.get("negate", "false").lower() == "true":
- self.conditions.append(lambda m, el=el:
- el.get("name") != m.hostname)
- else:
- self.conditions.append(lambda m, el=el:
- el.get("name") == m.hostname)
#: A set of all package names in this source. This will not
#: necessarily be populated, particularly by backends that
@@ -253,6 +230,8 @@ class Source(Debuggable): # pylint: disable=R0902
#: symbols>``. This will not necessarily be populated.
self.recommends = dict()
+ self._init_attributes(xsource)
+
#: The file (or directory) used for this source's cache data
self.cachefile = os.path.join(self.basepath,
"cache-%s" % self.cachekey)
@@ -277,11 +256,13 @@ class Source(Debuggable): # pylint: disable=R0902
for arch in self.arches:
if self.url:
usettings = [dict(version=self.version, component=comp,
- arch=arch, debsrc=self.debsrc)
+ arch=arch, debsrc=self.debsrc,
+ priority=self.priority, pin=self.pin)
for comp in self.components]
else: # rawurl given
usettings = [dict(version=self.version, component=None,
- arch=arch, debsrc=self.debsrc)]
+ arch=arch, debsrc=self.debsrc,
+ priority=self.priority, pin=self.pin)]
for setting in usettings:
if not self.rawurl:
@@ -290,8 +271,73 @@ class Source(Debuggable): # pylint: disable=R0902
setting['baseurl'] = self.rawurl
setting['url'] = baseurl % setting
setting['name'] = self.get_repo_name(setting)
+ setting['options'] = dict(server=self.server_options,
+ client=self.client_options)
self.url_map.extend(usettings)
+ def _init_attributes(self, xsource):
+ """
+ This functions evaluates the Source tag and parses all
+ attributes. Override this function in a sub class to
+ parse specific attributes. Do not use ``__init__`` because
+ ``Source.__init__`` may call other functions that already
+ need this specific fields. This functions is called before
+ any other function.
+
+ :param xsource: The XML tag that describes this source
+ :type source: lxml.etree._Element
+ """
+
+ self.components = [item.text for item in xsource.findall('Component')]
+ self.arches = [item.text for item in xsource.findall('Arch')]
+ self.blacklist = [item.text for item in xsource.findall('Blacklist')]
+ self.whitelist = [item.text for item in xsource.findall('Whitelist')]
+ self.debsrc = xsource.get('debsrc', 'false') == 'true'
+
+ opts = xsource.findall("Options")
+ for el in opts:
+ repoopts = dict([(k, v)
+ for k, v in el.attrib.items()
+ if k != "clientonly" and k != "serveronly"])
+ if el.get("clientonly", "false").lower() == "false":
+ self.server_options.update(repoopts)
+ if el.get("serveronly", "false").lower() == "false":
+ self.client_options.update(repoopts)
+
+ self.gpgkeys = [el.text for el in xsource.findall("GPGKey")]
+
+ self.essential = xsource.get('essential', 'true').lower() == 'true'
+ self.recommended = xsource.get('recommended',
+ 'false').lower() == 'true'
+
+ self.rawurl = xsource.get('rawurl', '')
+ if self.rawurl and not self.rawurl.endswith("/"):
+ self.rawurl += "/"
+
+ self.url = xsource.get('url', '')
+ if self.url and not self.url.endswith("/"):
+ self.url += "/"
+
+ self.version = xsource.get('version', '')
+ self.name = xsource.get('name', None)
+
+ for el in xsource.iterancestors():
+ if el.tag == "Group":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") not in m.groups)
+ else:
+ self.groups.append(el.get("name"))
+ self.conditions.append(lambda m, el=el:
+ el.get("name") in m.groups)
+ elif el.tag == "Client":
+ if el.get("negate", "false").lower() == "true":
+ self.conditions.append(lambda m, el=el:
+ el.get("name") != m.hostname)
+ else:
+ self.conditions.append(lambda m, el=el:
+ el.get("name") == m.hostname)
+
@property
def cachekey(self):
""" A unique key for this source that will be used to generate
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index dbe3f9ce5..acb11f1ab 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -456,16 +456,13 @@ class YumCollection(Collection):
reponame = basereponame
added = False
+ rid = 1
while not added:
try:
config.add_section(reponame)
added = True
except ConfigParser.DuplicateSectionError:
- match = re.search(r'-(\d+)', reponame)
- if match:
- rid = int(match.group(1)) + 1
- else:
- rid = 1
+ rid += 1
reponame = "%s-%d" % (basereponame, rid)
config.set(reponame, "name", reponame)
@@ -853,7 +850,7 @@ class YumCollection(Collection):
return new
@track_statistics()
- def complete(self, packagelist, recommended=None):
+ def complete(self, packagelist, recommended=None, pinnings=None):
""" Build a complete list of all packages and their dependencies.
When using the Python yum libraries, this defers to the
@@ -871,7 +868,8 @@ class YumCollection(Collection):
resolved.
"""
if not self.use_yum:
- return Collection.complete(self, packagelist, recommended)
+ return Collection.complete(self, packagelist, recommended,
+ pinnings)
lock = FileLock(os.path.join(self.cachefile, "lock"))
slept = 0
@@ -1004,8 +1002,20 @@ class YumSource(Source):
ptype = 'yum'
def __init__(self, basepath, xsource):
- Source.__init__(self, basepath, xsource)
+ self.filemap = dict()
+ self.file_to_arch = dict()
+ self.needed_paths = set()
+ self.packages = dict()
+ self.yumgroups = dict()
self.pulp_id = None
+ self.repo = None
+
+ Source.__init__(self, basepath, xsource)
+ __init__.__doc__ = Source.__init__.__doc__
+
+ def _init_attributes(self, xsource):
+ Source._init_attributes(self, xsource)
+
if HAS_PULP and xsource.get("pulp_id"):
self.pulp_id = xsource.get("pulp_id")
@@ -1034,15 +1044,11 @@ class YumSource(Source):
self.repo['relative_path'])
self.arches = [self.repo['arch']]
- self.packages = dict()
self.deps = dict([('global', dict())])
self.provides = dict([('global', dict())])
self.filemap = dict([(x, dict())
for x in ['global'] + self.arches])
- self.needed_paths = set()
- self.file_to_arch = dict()
- self.yumgroups = dict()
- __init__.__doc__ = Source.__init__.__doc__
+ _init_attributes.__doc__ = Source._init_attributes.__doc__
@property
def use_yum(self):
@@ -1130,6 +1136,94 @@ class YumSource(Source):
self.file_to_arch[self.escape_url(fullurl)] = arch
return urls
+ # pylint: disable=R0911,R0912
+ # disabling the pylint errors above because we are interesting in
+ # replicating the flow of the RPM code.
+ def _compare_rpm_versions(self, str1, str2):
+ """ Compare RPM versions.
+
+ This is an attempt to reimplement RPM's rpmvercmp method in python.
+
+ :param str1: package 1 version string
+ :param str2: package 2 version string
+ :return: 1 - str1 is newer than str2
+ 0 - str1 and str2 are the same version
+ -1 - str2 is newer than str1"""
+ if str1 == str2:
+ return 0
+
+ front_strip_re = re.compile('^[^A-Za-z0-9~]+')
+ risdigit = re.compile('(^[0-9]+)')
+ risalpha = re.compile('(^[A-Za-z])')
+ lzeroes = re.compile('^0+')
+
+ while len(str1) > 0 or len(str2) > 0:
+ str1 = front_strip_re.sub('', str1)
+ str2 = front_strip_re.sub('', str2)
+
+ if len(str1) == 0 or len(str2) == 0:
+ break
+
+ # handle the tilde separator
+ if str1[0] == '~' and str2[0] == '~':
+ str1 = str1[1:]
+ str2 = str2[1:]
+ elif str1[0] == '~':
+ return -1
+ elif str2[0] == '~':
+ return 1
+
+ # grab continuous segments from each string
+ isnum = False
+ if risdigit.match(str1):
+ segment1 = risdigit.split(str1)[1]
+ str1 = risdigit.split(str1)[2]
+ if risdigit.match(str2):
+ segment2 = risdigit.split(str2)[1]
+ str2 = risdigit.split(str2)[2]
+ else:
+ segment2 = ''
+ isnum = True
+ else:
+ segment1 = risalpha.split(str1)[1]
+ str1 = risalpha.split(str1)[2]
+ if risalpha.match(str2):
+ segment2 = risalpha.split(str2)[1]
+ str2 = risalpha.split(str2)[2]
+ else:
+ segment2 = ''
+
+ # numeric segments are always newer than alpha segments
+ if len(segment2) == 0:
+ if isnum:
+ return 1
+ return -1
+
+ if isnum:
+ # discard leading zeroes
+ segment1 = lzeroes.sub('', segment1)
+ segment2 = lzeroes.sub('', segment2)
+ # higher number has more digits
+ if len(segment1) > len(segment2):
+ return 1
+ elif len(segment2) > len(segment1):
+ return -1
+ # do a simple string comparison
+ if segment1 > segment2:
+ return 1
+ elif segment2 > segment1:
+ return -1
+
+ # if one of the strings is empty, the version of the longer
+ # string is higher
+ if len(str1) > len(str2):
+ return 1
+ elif len(str2) > len(str1):
+ return -1
+ else:
+ return 0
+ # pylint: enable=R0911,R0912
+
@track_statistics()
def read_files(self):
""" When using the builtin yum parser, read and parse locally
@@ -1198,13 +1292,33 @@ class YumSource(Source):
if arch not in self.packages:
self.packages[arch] = set()
if arch not in self.deps:
- self.deps[arch] = dict()
+ self.deps[arch] = {}
if arch not in self.provides:
- self.provides[arch] = dict()
+ self.provides[arch] = {}
+ versionmap = {}
for pkg in data.getchildren():
if not pkg.tag.endswith('package'):
continue
pkgname = pkg.find(XP + 'name').text
+ vtag = pkg.find(XP + 'version')
+ epoch = vtag.get('epoch')
+ version = vtag.get('ver')
+ release = vtag.get('rel')
+ if pkgname in self.packages[arch]:
+ # skip if version older than a previous version
+ if (self._compare_rpm_versions(
+ epoch, versionmap[pkgname]['epoch']) < 0):
+ continue
+ elif (self._compare_rpm_versions(
+ version, versionmap[pkgname]['version']) < 0):
+ continue
+ elif (self._compare_rpm_versions(
+ release, versionmap[pkgname]['release']) < 0):
+ continue
+ versionmap[pkgname] = {}
+ versionmap[pkgname]['epoch'] = epoch
+ versionmap[pkgname]['version'] = version
+ versionmap[pkgname]['release'] = release
self.packages[arch].add(pkgname)
pdata = pkg.find(XP + 'format')
@@ -1256,10 +1370,15 @@ class YumSource(Source):
arch = [a for a in self.arches if a in metadata.groups]
if not arch:
return False
- return ((package in self.packages['global'] or
- package in self.packages[arch[0]]) and
- package not in self.blacklist and
- (len(self.whitelist) == 0 or package in self.whitelist))
+ try:
+ return ((package in self.packages['global'] or
+ package in self.packages[arch[0]]) and
+ package not in self.blacklist and
+ (len(self.whitelist) == 0 or package in self.whitelist))
+ except KeyError:
+ self.logger.debug("Packages: Unable to find %s for arch %s" %
+ (package, arch[0]))
+ return False
is_package.__doc__ = Source.is_package.__doc__
def get_vpkgs(self, metadata):
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/YumHelper.py b/src/lib/Bcfg2/Server/Plugins/Packages/YumHelper.py
index b2e43bde7..89cc23090 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/YumHelper.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/YumHelper.py
@@ -274,29 +274,31 @@ class HelperSubcommand(Bcfg2.Options.Subcommand):
# whether or not this command accepts input on stdin
accept_input = True
- def __init__(self):
- Bcfg2.Options.Subcommand.__init__(self)
- self.verbosity = 0
+ # logging level
+ verbosity = 0
+
+ def run(self, setup):
if Bcfg2.Options.setup.debug:
self.verbosity = 5
elif Bcfg2.Options.setup.verbose:
self.verbosity = 1
- def run(self, setup):
- try:
- data = json.loads(sys.stdin.read())
- except ValueError:
- self.logger.error("Error decoding JSON input: %s" %
- sys.exc_info()[1])
- print(json.dumps(self.fallback))
- return 2
+ data = None
+ if self.accept_input:
+ try:
+ data = json.loads(sys.stdin.read())
+ except ValueError:
+ self.logger.error("Error decoding JSON input: %s" %
+ sys.exc_info()[1])
+ print(json.dumps(self.fallback))
+ return 2
try:
print(json.dumps(self._run(setup, data)))
except: # pylint: disable=W0702
self.logger.error("Unexpected error running %s: %s" %
- self.__class__.__name__.lower(),
- sys.exc_info()[1], exc_info=1)
+ (self.__class__.__name__.lower(),
+ sys.exc_info()[1]), exc_info=1)
print(json.dumps(self.fallback))
return 2
return 0
@@ -310,10 +312,13 @@ class DepSolverSubcommand(HelperSubcommand): # pylint: disable=W0223
""" Base class for helper commands that use the depsolver (i.e.,
only resolve dependencies, don't modify the cache) """
- def __init__(self):
- HelperSubcommand.__init__(self)
+ # DepSolver instance used in _run function
+ depsolver = None
+
+ def run(self, setup):
self.depsolver = DepSolver(Bcfg2.Options.setup.yum_config,
self.verbosity)
+ HelperSubcommand.run(self, setup)
class CacheManagerSubcommand(HelperSubcommand): # pylint: disable=W0223
@@ -322,10 +327,13 @@ class CacheManagerSubcommand(HelperSubcommand): # pylint: disable=W0223
fallback = False
accept_input = False
- def __init__(self):
- HelperSubcommand.__init__(self)
+ # CacheManager instance used in _run function
+ cachemgr = None
+
+ def run(self, setup):
self.cachemgr = CacheManager(Bcfg2.Options.setup.yum_config,
self.verbosity)
+ HelperSubcommand.run(self, setup)
class Clean(CacheManagerSubcommand):
@@ -376,10 +384,7 @@ class CLI(Bcfg2.Options.CommandRegistry):
""" The bcfg2-yum-helper CLI """
options = [
Bcfg2.Options.PathOption(
- "-c", "--yum-config", help="Yum config file"),
- Bcfg2.Options.PositionalArgument(
- "command", help="Yum helper command",
- choices=['clean', 'complete', 'get_groups'])]
+ "-c", "--yum-config", help="Yum config file")]
def __init__(self):
Bcfg2.Options.CommandRegistry.__init__(self)
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
index 3aa5c415f..3bdfddf31 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
@@ -10,7 +10,7 @@ import lxml.etree
import Bcfg2.Options
import Bcfg2.Server.Cache
import Bcfg2.Server.Plugin
-from Bcfg2.Compat import urlopen, HTTPError, URLError, MutableMapping
+from Bcfg2.Compat import urlopen, HTTPError, URLError
from Bcfg2.Server.Plugins.Packages.Collection import Collection, \
get_collection_class
from Bcfg2.Server.Plugins.Packages.PackagesSources import PackagesSources
@@ -36,52 +36,6 @@ class PackagesBackendAction(Bcfg2.Options.ComponentAction):
fail_silently = True
-class OnDemandDict(MutableMapping):
- """ This maps a set of keys to a set of value-getting functions;
- the values are populated on-the-fly by the functions as the values
- are needed (and not before). This is used by
- :func:`Bcfg2.Server.Plugins.Packages.Packages.get_additional_data`;
- see the docstring for that function for details on why.
-
- Unlike a dict, you should not specify values for for the righthand
- side of this mapping, but functions that get values. E.g.:
-
- .. code-block:: python
-
- d = OnDemandDict(foo=load_foo,
- bar=lambda: "bar");
- """
-
- def __init__(self, **getters):
- self._values = dict()
- self._getters = dict(**getters)
-
- def __getitem__(self, key):
- if key not in self._values:
- self._values[key] = self._getters[key]()
- return self._values[key]
-
- def __setitem__(self, key, getter):
- self._getters[key] = getter
-
- def __delitem__(self, key):
- del self._values[key]
- del self._getters[key]
-
- def __len__(self):
- return len(self._getters)
-
- def __iter__(self):
- return iter(self._getters.keys())
-
- def __repr__(self):
- rv = dict(self._values)
- for key in self._getters.keys():
- if key not in rv:
- rv[key] = 'unknown'
- return str(rv)
-
-
class Packages(Bcfg2.Server.Plugin.Plugin,
Bcfg2.Server.Plugin.StructureValidator,
Bcfg2.Server.Plugin.Generator,
@@ -103,7 +57,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
help="Packages backends to load",
type=Bcfg2.Options.Types.comma_list,
action=PackagesBackendAction,
- default=['Yum', 'Apt', 'Pac', 'Pkgng']),
+ default=['Yum', 'Apt', 'Pac', 'Pkgng', 'Portage', 'Layman',
+ 'Dummy']),
Bcfg2.Options.PathOption(
cf=("packages", "cache"), dest="packages_cache",
help="Path to the Packages cache",
@@ -361,6 +316,10 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
groups = []
recommended = dict()
+ pinned_src = dict()
+ if hasattr(metadata, 'PkgVars'):
+ pinned_src = metadata.PkgVars['pin']
+
for struct in structures:
for pkg in struct.xpath('//Package | //BoundPackage'):
if pkg.get("name"):
@@ -402,11 +361,11 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
base.update(collection.get_essential())
# check for this set of packages in the package cache
- pkey = hash(tuple(base))
+ pkey = hash((tuple(base), tuple(recommended), tuple(pinned_src)))
pcache = Bcfg2.Server.Cache.Cache("Packages", "pkg_sets",
collection.cachekey)
if pkey not in pcache:
- pcache[pkey] = collection.complete(base, recommended)
+ pcache[pkey] = collection.complete(base, recommended, pinned_src)
packages, unknown = pcache[pkey]
if unknown:
self.logger.info("Packages: Got %d unknown entries" % len(unknown))
@@ -548,21 +507,23 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
for source in self.sources.entries:
if source.applies(metadata):
relevant.append(source)
- sclasses.update([source.__class__])
+ if 'cclass' in dir(source):
+ sclasses.update([source.cclass])
+ else:
+ sclass = source.__class__.__name__.replace("Source", "")
+ sclasses.update([sclass])
if len(sclasses) > 1:
self.logger.warning("Packages: Multiple source types found for "
"%s: %s" %
- (metadata.hostname,
- ",".join([s.__name__ for s in sclasses])))
+ (metadata.hostname, ",".join([sclasses])))
cclass = Collection
elif len(sclasses) == 0:
self.logger.error("Packages: No sources found for %s" %
metadata.hostname)
cclass = Collection
else:
- cclass = get_collection_class(
- sclasses.pop().__name__.replace("Source", ""))
+ cclass = get_collection_class(sclasses.pop())
if self.debug_flag:
self.logger.error("Packages: Using %s for Collection of sources "
@@ -578,7 +539,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
def get_additional_data(self, metadata):
""" Return additional data for the given client. This will be
- an :class:`Bcfg2.Server.Plugins.Packages.OnDemandDict`
+ an :class:`Bcfg2.Server.Plugin.OnDemandDict`
containing two keys:
* ``sources``, whose value is a list of data returned from
@@ -610,7 +571,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
get_collection() until it's absolutely necessary. """
return self.get_collection(metadata).get_additional_data()
- return OnDemandDict(
+ return Bcfg2.Server.Plugin.OnDemandDict(
sources=get_sources,
get_config=lambda: self.get_config)
diff --git a/src/lib/Bcfg2/Server/Plugins/PkgVars.py b/src/lib/Bcfg2/Server/Plugins/PkgVars.py
new file mode 100644
index 000000000..9a2649d02
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/PkgVars.py
@@ -0,0 +1,65 @@
+import os
+import re
+import sys
+import copy
+import logging
+import lxml.etree
+import Bcfg2.Server.Plugin
+
+logger = logging.getLogger('Bcfg2.Plugins.PkgVars')
+vars = ['pin', 'use', 'keywords']
+
+class PkgVarsFile(Bcfg2.Server.Plugin.StructFile):
+ def get_additional_data(self, meta):
+ data = self.Match(meta)
+ results = {}
+ for d in data:
+ name = d.get('name', '')
+
+ for v in vars:
+ value = d.get(v, None)
+ if value:
+ if v not in results:
+ results[v] = {}
+ if name not in results[v]:
+ results[v][name] = set()
+
+ results[v][name].add(value)
+
+ return results
+
+class PkgVarsDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked):
+ __child__ = PkgVarsFile
+ patterns = re.compile(r'.*\.xml$')
+
+ def get_additional_data(self, meta):
+ results = {}
+ for v in vars:
+ results[v] = {}
+
+ for files in self.entries:
+ new = self.entries[files].get_additional_data(meta)
+ for x in vars:
+ if x in new:
+ results[x].update(new[x])
+
+ return results
+
+class PkgVars(Bcfg2.Server.Plugin.Plugin,
+ Bcfg2.Server.Plugin.Connector):
+ name = 'PkgVars'
+ version = '$Revision$'
+
+ def __init__(self, core):
+ Bcfg2.Server.Plugin.Plugin.__init__(self, core)
+ Bcfg2.Server.Plugin.Connector.__init__(self)
+ try:
+ self.store = PkgVarsDirectoryBacked(self.data)
+ except OSError:
+ e = sys.exc_info()[1]
+ self.logger.error("Error while creating PkgVars store: %s %s" %
+ (e.strerror, e.filename))
+ raise Bcfg2.Server.Plugin.PluginInitError
+
+ def get_additional_data(self, meta):
+ return self.store.get_additional_data(meta)
diff --git a/src/lib/Bcfg2/Server/Plugins/Probes.py b/src/lib/Bcfg2/Server/Plugins/Probes.py
index 76aab69b5..0a0ed9414 100644
--- a/src/lib/Bcfg2/Server/Plugins/Probes.py
+++ b/src/lib/Bcfg2/Server/Plugins/Probes.py
@@ -15,7 +15,12 @@ import Bcfg2.Server.FileMonitor
from Bcfg2.Logger import Debuggable
from Bcfg2.Server.Statistics import track_statistics
-HAS_DJANGO = False
+try:
+ from django.db import models
+ HAS_DJANGO = True
+except ImportError:
+ HAS_DJANGO = False
+
# pylint: disable=C0103
ProbesDataModel = None
ProbesGroupsModel = None
@@ -25,13 +30,10 @@ 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:
- from django.db import models
- HAS_DJANGO = True
- except ImportError:
- HAS_DJANGO = False
+
+ if not HAS_DJANGO:
return
class ProbesDataModel(models.Model, # pylint: disable=W0621,W0612
@@ -74,6 +76,7 @@ class ProbeStore(Debuggable):
def __init__(self, core, datadir): # pylint: disable=W0613
Debuggable.__init__(self)
+ self.core = core
self._groupcache = Bcfg2.Server.Cache.Cache("Probes", "probegroups")
self._datacache = Bcfg2.Server.Cache.Cache("Probes", "probedata")
@@ -134,7 +137,7 @@ class DBProbeStore(ProbeStore, Bcfg2.Server.Plugin.DatabaseBacked):
Bcfg2.Server.Cache.expire("Probes", "probegroups", hostname)
groupdata = ProbesGroupsModel.objects.filter(hostname=hostname)
self._groupcache[hostname] = list(set(r.group for r in groupdata))
- Bcfg2.Server.Cache.expire("Metadata", hostname)
+ self.core.metadata_cache.expire(hostname)
@Bcfg2.Server.Plugin.DatabaseBacked.get_db_lock
def set_groups(self, hostname, groups):
@@ -155,7 +158,7 @@ class DBProbeStore(ProbeStore, Bcfg2.Server.Plugin.DatabaseBacked):
ProbesGroupsModel.objects.filter(
hostname=hostname).exclude(group__in=groups).delete()
if olddata != groups:
- Bcfg2.Server.Cache.expire("Metadata", hostname)
+ self.core.metadata_cache.expire(hostname)
def _load_data(self, hostname):
Bcfg2.Server.Cache.expire("Probes", "probegroups", hostname)
@@ -168,7 +171,7 @@ class DBProbeStore(ProbeStore, Bcfg2.Server.Plugin.DatabaseBacked):
time.mktime(pdata.timestamp.timetuple())
ts_set = True
self._datacache[hostname][pdata.probe] = ProbeData(pdata.data)
- Bcfg2.Server.Cache.expire("Metadata", hostname)
+ self.core.metadata_cache.expire(hostname)
@Bcfg2.Server.Plugin.DatabaseBacked.get_db_lock
def set_data(self, hostname, data):
@@ -198,7 +201,7 @@ class DBProbeStore(ProbeStore, Bcfg2.Server.Plugin.DatabaseBacked):
qset.delete()
expire_metadata = True
if expire_metadata:
- Bcfg2.Server.Cache.expire("Metadata", hostname)
+ self.core.metadata_cache.expire(hostname)
class XMLProbeStore(ProbeStore):
@@ -234,7 +237,7 @@ class XMLProbeStore(ProbeStore):
self._groupcache[client.get('name')].append(
pdata.get('name'))
- Bcfg2.Server.Cache.expire("Metadata")
+ self.core.metadata_cache.expire()
def _load_groups(self, hostname):
self._load_data(hostname)
@@ -274,7 +277,7 @@ class XMLProbeStore(ProbeStore):
olddata = self._groupcache.get(hostname, [])
self._groupcache[hostname] = groups
if olddata != groups:
- Bcfg2.Server.Cache.expire("Metadata", hostname)
+ self.core.metadata_cache.expire(hostname)
def set_data(self, hostname, data):
Bcfg2.Server.Cache.expire("Probes", "probedata", hostname)
@@ -285,7 +288,7 @@ class XMLProbeStore(ProbeStore):
self._datacache[hostname][probe] = pdata
expire_metadata |= olddata != data
if expire_metadata:
- Bcfg2.Server.Cache.expire("Metadata", hostname)
+ self.core.metadata_cache.expire(hostname)
class ClientProbeDataSet(dict):
diff --git a/src/lib/Bcfg2/Server/Plugins/Properties.py b/src/lib/Bcfg2/Server/Plugins/Properties.py
index c4dd75e60..e6549b714 100644
--- a/src/lib/Bcfg2/Server/Plugins/Properties.py
+++ b/src/lib/Bcfg2/Server/Plugins/Properties.py
@@ -35,13 +35,17 @@ LOGGER = logging.getLogger(__name__)
class PropertyFile(object):
""" Base Properties file handler """
- def __init__(self, name):
+ def __init__(self, name, core):
"""
:param name: The filename of this properties file.
+ :type name: string
+ :param core: The Bcfg2.Server.Core initializing the Properties plugin
+ :type core: Bcfg2.Server.Core
.. automethod:: _write
"""
self.name = name
+ self.core = core
def write(self):
""" Write the data in this data structure back to the property
@@ -69,6 +73,12 @@ class PropertyFile(object):
file. """
raise NotImplementedError
+ def _expire_metadata_cache(self):
+ """ Expires the metadata cache, if it is required by the caching
+ mode. """
+ if self.core.metadata_cache_mode in ['cautious', 'aggressive']:
+ self.core.metadata_cache.expire()
+
def validate_data(self):
""" Verify that the data in this file is valid. """
raise NotImplementedError
@@ -81,9 +91,9 @@ class PropertyFile(object):
class JSONPropertyFile(Bcfg2.Server.Plugin.FileBacked, PropertyFile):
"""Handle JSON Properties files."""
- def __init__(self, name):
+ def __init__(self, name, core):
Bcfg2.Server.Plugin.FileBacked.__init__(self, name)
- PropertyFile.__init__(self, name)
+ PropertyFile.__init__(self, name, core)
self.json = None
def Index(self):
@@ -93,10 +103,13 @@ class JSONPropertyFile(Bcfg2.Server.Plugin.FileBacked, PropertyFile):
err = sys.exc_info()[1]
raise PluginExecutionError("Could not load JSON data from %s: %s" %
(self.name, err))
+ self._expire_metadata_cache()
+ Index.__doc__ = Bcfg2.Server.Plugin.FileBacked.Index.__doc__
def _write(self):
json.dump(self.json, open(self.name, 'wb'))
return True
+ _write.__doc__ = PropertyFile._write.__doc__
def validate_data(self):
try:
@@ -105,6 +118,7 @@ class JSONPropertyFile(Bcfg2.Server.Plugin.FileBacked, PropertyFile):
err = sys.exc_info()[1]
raise PluginExecutionError("Data for %s cannot be dumped to JSON: "
"%s" % (self.name, err))
+ validate_data.__doc__ = PropertyFile.validate_data.__doc__
def __str__(self):
return str(self.json)
@@ -116,11 +130,10 @@ class JSONPropertyFile(Bcfg2.Server.Plugin.FileBacked, PropertyFile):
class YAMLPropertyFile(Bcfg2.Server.Plugin.FileBacked, PropertyFile):
""" Handle YAML Properties files. """
- def __init__(self, name):
+ def __init__(self, name, core):
Bcfg2.Server.Plugin.FileBacked.__init__(self, name)
- PropertyFile.__init__(self, name)
+ PropertyFile.__init__(self, name, core)
self.yaml = None
- __init__.__doc__ = Bcfg2.Server.Plugin.FileBacked.__init__.__doc__
def Index(self):
try:
@@ -129,6 +142,7 @@ class YAMLPropertyFile(Bcfg2.Server.Plugin.FileBacked, PropertyFile):
err = sys.exc_info()[1]
raise PluginExecutionError("Could not load YAML data from %s: %s" %
(self.name, err))
+ self._expire_metadata_cache()
Index.__doc__ = Bcfg2.Server.Plugin.FileBacked.Index.__doc__
def _write(self):
@@ -155,10 +169,15 @@ class YAMLPropertyFile(Bcfg2.Server.Plugin.FileBacked, PropertyFile):
class XMLPropertyFile(Bcfg2.Server.Plugin.StructFile, PropertyFile):
""" Handle XML Properties files. """
- def __init__(self, name, should_monitor=False):
+ def __init__(self, name, core, should_monitor=False):
Bcfg2.Server.Plugin.StructFile.__init__(self, name,
should_monitor=should_monitor)
- PropertyFile.__init__(self, name)
+ PropertyFile.__init__(self, name, core)
+
+ def Index(self):
+ Bcfg2.Server.Plugin.StructFile.Index(self)
+ self._expire_metadata_cache()
+ Index.__doc__ = Bcfg2.Server.Plugin.StructFile.Index.__doc__
def _write(self):
open(self.name, "wb").write(
@@ -258,11 +277,11 @@ class Properties(Bcfg2.Server.Plugin.Plugin,
:class:`PropertyFile`
"""
if fname.endswith(".xml"):
- return XMLPropertyFile(fname)
+ return XMLPropertyFile(fname, self.core)
elif HAS_JSON and fname.endswith(".json"):
- return JSONPropertyFile(fname)
+ return JSONPropertyFile(fname, self.core)
elif HAS_YAML and (fname.endswith(".yaml") or fname.endswith(".yml")):
- return YAMLPropertyFile(fname)
+ return YAMLPropertyFile(fname, self.core)
else:
raise Bcfg2.Server.Plugin.PluginExecutionError(
"Properties: Unknown extension %s" % fname)
diff --git a/src/lib/Bcfg2/Server/Plugins/PuppetENC.py b/src/lib/Bcfg2/Server/Plugins/PuppetENC.py
index 59fbe6f03..e2d8a058f 100644
--- a/src/lib/Bcfg2/Server/Plugins/PuppetENC.py
+++ b/src/lib/Bcfg2/Server/Plugins/PuppetENC.py
@@ -117,7 +117,7 @@ class PuppetENC(Bcfg2.Server.Plugin.Plugin,
self.logger.warning("PuppetENC is incompatible with aggressive "
"client metadata caching, try 'cautious' or "
"'initial' instead")
- self.core.expire_caches_by_type(Bcfg2.Server.Plugin.Metadata)
+ self.core.metadata_cache.expire()
def end_statistics(self, metadata):
self.end_client_run(self, metadata)
diff --git a/src/lib/Bcfg2/Server/Plugins/Reporting.py b/src/lib/Bcfg2/Server/Plugins/Reporting.py
index 5c73546b4..e372006c7 100644
--- a/src/lib/Bcfg2/Server/Plugins/Reporting.py
+++ b/src/lib/Bcfg2/Server/Plugins/Reporting.py
@@ -9,12 +9,15 @@ from Bcfg2.Reporting.Transport.base import TransportError
from Bcfg2.Server.Plugin import Statistics, PullSource, Threaded, \
PluginInitError, PluginExecutionError
-# required for reporting
try:
- import south # pylint: disable=W0611
- HAS_SOUTH = True
+ import django
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
+ HAS_REPORTING = True
+ else:
+ import south # pylint: disable=W0611
+ HAS_REPORTING = True
except ImportError:
- HAS_SOUTH = False
+ HAS_REPORTING = False
def _rpc_call(method):
@@ -48,8 +51,8 @@ class Reporting(Statistics, Threaded, PullSource):
self.whoami = platform.node()
self.transport = None
- if not HAS_SOUTH:
- msg = "Django south is required for Reporting"
+ if not HAS_REPORTING:
+ msg = "Django 1.7+ or Django south is required for Reporting"
self.logger.error(msg)
raise PluginInitError(msg)
diff --git a/src/lib/Bcfg2/Server/Plugins/Rules.py b/src/lib/Bcfg2/Server/Plugins/Rules.py
index a3f682ed6..cf659251c 100644
--- a/src/lib/Bcfg2/Server/Plugins/Rules.py
+++ b/src/lib/Bcfg2/Server/Plugins/Rules.py
@@ -1,10 +1,17 @@
"""This generator provides rule-based entry mappings."""
+import copy
import re
+import string
import Bcfg2.Options
import Bcfg2.Server.Plugin
+class NameTemplate(string.Template):
+ """Simple subclass of string.Template with a custom delimiter."""
+ delimiter = '%'
+
+
class Rules(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles service assignments."""
__author__ = 'bcfg-dev@mcs.anl.gov'
@@ -12,7 +19,10 @@ class Rules(Bcfg2.Server.Plugin.PrioDir):
options = Bcfg2.Server.Plugin.PrioDir.options + [
Bcfg2.Options.BooleanOption(
cf=("rules", "regex"), dest="rules_regex",
- help="Allow regular expressions in Rules")]
+ help="Allow regular expressions in Rules"),
+ Bcfg2.Options.BooleanOption(
+ cf=("rules", "replace_name"), dest="rules_replace_name",
+ help="Replace %{name} in attributes with name of target entry")]
def __init__(self, core):
Bcfg2.Server.Plugin.PrioDir.__init__(self, core)
@@ -46,7 +56,22 @@ class Rules(Bcfg2.Server.Plugin.PrioDir):
return True
return False
+ def _apply(self, entry, data):
+ if self._replace_name_enabled:
+ data = copy.deepcopy(data)
+ for key, val in list(data.attrib.items()):
+ data.attrib[key] = NameTemplate(val).safe_substitute(
+ name=entry.get('name'))
+
+ Bcfg2.Server.Plugin.PrioDir._apply(self, entry, data)
+
@property
def _regex_enabled(self):
""" Return True if rules regexes are enabled, False otherwise """
return Bcfg2.Options.setup.rules_regex
+
+ @property
+ def _replace_name_enabled(self):
+ """ Return True if the replace_name feature is enabled,
+ False otherwise """
+ return Bcfg2.Options.setup.rules_replace_name
diff --git a/src/lib/Bcfg2/Server/Plugins/SSHbase.py b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
index e4fb9b565..08acc4d8d 100644
--- a/src/lib/Bcfg2/Server/Plugins/SSHbase.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
@@ -103,6 +103,7 @@ class KnownHostsEntrySet(Bcfg2.Server.Plugin.EntrySet):
class SSHbase(Bcfg2.Server.Plugin.Plugin,
+ Bcfg2.Server.Plugin.Connector,
Bcfg2.Server.Plugin.Generator,
Bcfg2.Server.Plugin.PullTarget):
"""
@@ -120,6 +121,10 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
private key for (hostname)
ssh_host_(ec)(dr)sa_key.pub.H_(hostname) -> the v2 ssh host
public key for (hostname)
+ ssh_host_ed25519_key.H_(hostname) -> the v2 ssh host
+ private key for (hostname)
+ ssh_host_ed25519_key.pub.H_(hostname) -> the v2 ssh host
+ public key for (hostname)
ssh_known_hosts -> the current known hosts file. this
is regenerated each time a new key is generated.
@@ -127,10 +132,12 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
__author__ = 'bcfg-dev@mcs.anl.gov'
keypatterns = ["ssh_host_dsa_key",
"ssh_host_ecdsa_key",
+ "ssh_host_ed25519_key",
"ssh_host_rsa_key",
"ssh_host_key",
"ssh_host_dsa_key.pub",
"ssh_host_ecdsa_key.pub",
+ "ssh_host_ed25519_key.pub",
"ssh_host_rsa_key.pub",
"ssh_host_key.pub"]
@@ -141,6 +148,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
def __init__(self, core):
Bcfg2.Server.Plugin.Plugin.__init__(self, core)
+ Bcfg2.Server.Plugin.Connector.__init__(self)
Bcfg2.Server.Plugin.Generator.__init__(self)
Bcfg2.Server.Plugin.PullTarget.__init__(self)
self.ipcache = {}
@@ -210,7 +218,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
try:
names[cmeta.hostname].update(
self.get_namecache_entry(ip))
- except socket.gaierror:
+ except socket.herror:
continue
names[cmeta.hostname] = sorted(names[cmeta.hostname])
@@ -284,6 +292,10 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
self.debug_log("New public key %s; invalidating "
"ssh_known_hosts cache" % event.filename)
self.skn = False
+
+ if self.core.metadata_cache_mode in ['cautious',
+ 'aggressive']:
+ self.core.metadata_cache.expire()
return
if event.filename == 'info.xml':
@@ -332,7 +344,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
self.ipcache[client] = False
msg = "Failed to find IP address for %s: %s" % (client,
result.error)
- self.logger(msg)
+ self.logger.error(msg)
raise PluginExecutionError(msg)
def get_namecache_entry(self, cip):
@@ -342,7 +354,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
if self.namecache[cip]:
return self.namecache[cip]
else:
- raise socket.gaierror
+ raise socket.herror
else:
# add an entry that has not been cached
try:
@@ -353,7 +365,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
self.namecache[cip] = []
self.namecache[cip].extend(rvlookup[1])
return self.namecache[cip]
- except socket.gaierror:
+ except socket.herror:
self.namecache[cip] = False
self.logger.error("Failed to find any names associated with "
"IP address %s" % cip)
@@ -415,7 +427,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
def GenerateHostKeyPair(self, client, filename):
"""Generate new host key pair for client."""
- match = re.search(r'(ssh_host_(?:((?:ecd|d|r)sa)_)?key)', filename)
+ match = re.search(r'(ssh_host_(?:((?:ecd|d|r)sa|ed25519)_)?key)',
+ filename)
if match:
hostkey = "%s.H_%s" % (match.group(1), client)
if match.group(2):
@@ -489,3 +502,15 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
self.logger.error("Failed to pull %s. This file does not "
"currently exist on the client" %
entry.get('name'))
+
+ def get_additional_data(self, metadata):
+ data = dict()
+ for key in self.keypatterns:
+ if key.endswith(".pub"):
+ try:
+ keyfile = "/etc/ssh/" + key
+ entry = self.entries[keyfile].best_matching(metadata)
+ data[key] = entry.data
+ except Bcfg2.Server.Plugin.PluginExecutionError:
+ pass
+ return data
diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
index cec2de297..ff67571fa 100644
--- a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
+++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
@@ -7,24 +7,18 @@ import lxml.etree
from Bcfg2.Server.Plugin import Plugin, Connector, DirectoryBacked, \
TemplateDataProvider, DefaultTemplateDataProvider
from Bcfg2.Logger import Debuggable
+from Bcfg2.Utils import safe_module_name
MODULE_RE = re.compile(r'(?P<filename>(?P<module>[^\/]+)\.py)$')
-def safe_module_name(module):
- """ Munge the name of a TemplateHelper module to avoid collisions
- with other Python modules. E.g., if someone has a helper named
- 'ldap.py', it should not be added to ``sys.modules`` as ``ldap``,
- but rather as something more obscure. """
- return '__TemplateHelper_%s' % module
-
-
class HelperModule(Debuggable):
""" Representation of a TemplateHelper module """
- def __init__(self, name):
+ def __init__(self, name, core):
Debuggable.__init__(self)
self.name = name
+ self.core = core
#: The name of the module as used by get_additional_data().
#: the name of the file with .py stripped off.
@@ -51,9 +45,14 @@ class HelperModule(Debuggable):
if event and event.code2str() not in ['exists', 'changed', 'created']:
return
+ # expire the metadata cache, because the module might have changed
+ if self.core.metadata_cache_mode in ['cautious', 'aggressive']:
+ self.core.metadata_cache.expire()
+
try:
- module = imp.load_source(safe_module_name(self._module_name),
- self.name)
+ module = imp.load_source(
+ safe_module_name('TemplateHelper', self._module_name),
+ self.name)
except: # pylint: disable=W0702
# this needs to be a blanket except because the
# imp.load_source() call can raise literally any error,
@@ -107,7 +106,6 @@ class TemplateHelper(Plugin, Connector, DirectoryBacked, TemplateDataProvider):
__author__ = 'chris.a.st.pierre@gmail.com'
ignore = re.compile(r'^(\.#.*|.*~|\..*\.(sw[px])|.*\.py[co])$')
patterns = MODULE_RE
- __child__ = HelperModule
def __init__(self, core):
Plugin.__init__(self, core)
@@ -115,6 +113,10 @@ class TemplateHelper(Plugin, Connector, DirectoryBacked, TemplateDataProvider):
DirectoryBacked.__init__(self, self.data)
TemplateDataProvider.__init__(self)
+ # The HelperModule needs access to the core, so we have to construct
+ # it manually and add the custom argument.
+ self.__child__ = lambda fname: HelperModule(fname, core)
+
def get_additional_data(self, _):
return dict([(h._module_name, h) # pylint: disable=W0212
for h in self.entries.values()])
diff --git a/src/lib/Bcfg2/Server/Reports/reports/models.py b/src/lib/Bcfg2/Server/Reports/reports/models.py
index ac4c8eac4..67aa425d9 100644
--- a/src/lib/Bcfg2/Server/Reports/reports/models.py
+++ b/src/lib/Bcfg2/Server/Reports/reports/models.py
@@ -266,7 +266,7 @@ class Reason(models.Model):
current_to = models.CharField(max_length=1024, blank=True)
version = models.CharField(max_length=1024, blank=True)
current_version = models.CharField(max_length=1024, blank=True)
- current_exists = models.BooleanField() # False means its missing. Default True
+ current_exists = models.BooleanField(default=True) # False means its missing.
current_diff = models.TextField(max_length=1024*1024, blank=True)
is_binary = models.BooleanField(default=False)
is_sensitive = models.BooleanField(default=False)
diff --git a/src/lib/Bcfg2/Server/Reports/updatefix.py b/src/lib/Bcfg2/Server/Reports/updatefix.py
index 91c370994..09b218464 100644
--- a/src/lib/Bcfg2/Server/Reports/updatefix.py
+++ b/src/lib/Bcfg2/Server/Reports/updatefix.py
@@ -4,7 +4,7 @@ import django.core.management
import sys
import logging
import traceback
-from Bcfg2.Server.models import InternalDatabaseVersion
+from Bcfg2.Server.models import internal_database_version
logger = logging.getLogger('Bcfg2.Server.Reports.UpdateFix')
@@ -138,7 +138,7 @@ def rollupdate(current_version):
exc_info=1)
# since array start at 0 but version start at 1
# we add 1 to the normal count
- ret = InternalDatabaseVersion.objects.create(version=i + 1)
+ ret = internal_database_version().create(version=i + 1)
return ret
else:
return None
@@ -149,10 +149,10 @@ def update_database():
try:
logger.debug("Running upgrade of models to the new one")
django.core.management.call_command("syncdb", interactive=False, verbosity=0)
- know_version = InternalDatabaseVersion.objects.order_by('-version')
+ know_version = internal_database_version().order_by('-version')
if not know_version:
logger.debug("No version, creating initial version")
- know_version = InternalDatabaseVersion.objects.create(version=lastversion)
+ know_version = internal_database_version().create(version=lastversion)
else:
know_version = know_version[0]
logger.debug("Presently at %s" % know_version)
diff --git a/src/lib/Bcfg2/Server/migrations/0001_initial.py b/src/lib/Bcfg2/Server/migrations/0001_initial.py
new file mode 100644
index 000000000..3b3dca455
--- /dev/null
+++ b/src/lib/Bcfg2/Server/migrations/0001_initial.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.9 on 2016-08-17 18:52
+from __future__ import unicode_literals
+
+import Bcfg2.Server.Plugin.helpers
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='MetadataClientModel',
+ fields=[
+ ('hostname', models.CharField(max_length=255, primary_key=True, serialize=False)),
+ ('version', models.CharField(max_length=31, null=True)),
+ ],
+ bases=(models.Model, Bcfg2.Server.Plugin.helpers.PluginDatabaseModel),
+ ),
+ migrations.CreateModel(
+ name='ProbesDataModel',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('hostname', models.CharField(max_length=255)),
+ ('probe', models.CharField(max_length=255)),
+ ('timestamp', models.DateTimeField(auto_now=True)),
+ ('data', models.TextField(null=True)),
+ ],
+ bases=(models.Model, Bcfg2.Server.Plugin.helpers.PluginDatabaseModel),
+ ),
+ migrations.CreateModel(
+ name='ProbesGroupsModel',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('hostname', models.CharField(max_length=255)),
+ ('group', models.CharField(max_length=255)),
+ ],
+ bases=(models.Model, Bcfg2.Server.Plugin.helpers.PluginDatabaseModel),
+ ),
+ ]
diff --git a/src/lib/Bcfg2/Server/migrations/__init__.py b/src/lib/Bcfg2/Server/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/Bcfg2/Server/migrations/__init__.py
diff --git a/src/lib/Bcfg2/Server/models.py b/src/lib/Bcfg2/Server/models.py
index 8d6642a25..9c0153c74 100644
--- a/src/lib/Bcfg2/Server/models.py
+++ b/src/lib/Bcfg2/Server/models.py
@@ -8,6 +8,7 @@ import Bcfg2.Server.Plugins
LOGGER = logging.getLogger(__name__)
MODELS = []
+INTERNAL_DATABASE_VERSION = None
class _OptionContainer(object):
@@ -56,15 +57,23 @@ def load_models(plugins=None):
setattr(sys.modules[__name__], sym, obj)
MODELS.append(sym)
- class InternalDatabaseVersion(models.Model):
- """ Object that tell us to which version the database is """
- version = models.IntegerField()
- updated = models.DateTimeField(auto_now_add=True)
+def internal_database_version():
+ global INTERNAL_DATABASE_VERSION
- def __str__(self):
- return "version %d updated %s" % (self.version,
- self.updated.isoformat())
+ if INTERNAL_DATABASE_VERSION is None:
+ from django.db import models
+ class InternalDatabaseVersion(models.Model):
+ """ Object that tell us to which version the database is """
+ version = models.IntegerField()
+ updated = models.DateTimeField(auto_now_add=True)
- class Meta: # pylint: disable=C0111,W0232
- app_label = "reports"
- get_latest_by = "version"
+ def __str__(self):
+ return "version %d updated %s" % (self.version,
+ self.updated.isoformat())
+
+ class Meta: # pylint: disable=C0111,W0232
+ app_label = "reports"
+ get_latest_by = "version"
+ INTERNAL_DATABASE_VERSION = InternalDatabaseVersion
+
+ return INTERNAL_DATABASE_VERSION.objects
diff --git a/src/lib/Bcfg2/Server/south_migrations/0001_initial.py b/src/lib/Bcfg2/Server/south_migrations/0001_initial.py
new file mode 100644
index 000000000..864c311e5
--- /dev/null
+++ b/src/lib/Bcfg2/Server/south_migrations/0001_initial.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+from south.utils import datetime_utils as datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'MetadataClientModel'
+ db.create_table(u'Server_metadataclientmodel', (
+ ('hostname', self.gf('django.db.models.fields.CharField')(max_length=255, primary_key=True)),
+ ('version', self.gf('django.db.models.fields.CharField')(max_length=31, null=True)),
+ ))
+ db.send_create_signal('Server', ['MetadataClientModel'])
+
+ # Adding model 'ProbesDataModel'
+ db.create_table(u'Server_probesdatamodel', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('hostname', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('probe', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
+ ('data', self.gf('django.db.models.fields.TextField')(null=True)),
+ ))
+ db.send_create_signal('Server', ['ProbesDataModel'])
+
+ # Adding model 'ProbesGroupsModel'
+ db.create_table(u'Server_probesgroupsmodel', (
+ (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('hostname', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('group', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('Server', ['ProbesGroupsModel'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'MetadataClientModel'
+ db.delete_table(u'Server_metadataclientmodel')
+
+ # Deleting model 'ProbesDataModel'
+ db.delete_table(u'Server_probesdatamodel')
+
+ # Deleting model 'ProbesGroupsModel'
+ db.delete_table(u'Server_probesgroupsmodel')
+
+
+ models = {
+ 'Server.metadataclientmodel': {
+ 'Meta': {'object_name': 'MetadataClientModel'},
+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '31', 'null': 'True'})
+ },
+ 'Server.probesdatamodel': {
+ 'Meta': {'object_name': 'ProbesDataModel'},
+ 'data': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'probe': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
+ },
+ 'Server.probesgroupsmodel': {
+ 'Meta': {'object_name': 'ProbesGroupsModel'},
+ 'group': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'hostname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ }
+ }
+
+ complete_apps = ['Server'] \ No newline at end of file
diff --git a/src/lib/Bcfg2/Server/south_migrations/__init__.py b/src/lib/Bcfg2/Server/south_migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/Bcfg2/Server/south_migrations/__init__.py
diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py
index 10057b63e..b043fd11c 100644
--- a/src/lib/Bcfg2/Utils.py
+++ b/src/lib/Bcfg2/Utils.py
@@ -321,6 +321,15 @@ def safe_input(msg):
return input(msg)
+def safe_module_name(prefix, module):
+ """ Munge the name of a module with prefix to avoid collisions
+ with other Python modules. E.g., if you want to import user
+ defined helper modules and someone has a helper named 'ldap.py',
+ it should not be added to ``sys.modules`` as ``ldap``, but rather
+ as something more obscure. """
+ return '__%s_%s' % (prefix, module)
+
+
class classproperty(object): # pylint: disable=C0103
""" Decorator that can be used to create read-only class
properties. """
@@ -329,4 +338,20 @@ class classproperty(object): # pylint: disable=C0103
self.getter = getter
def __get__(self, instance, owner):
- return self.getter(owner)
+ return classmethod(self.getter).__get__(None, owner)()
+
+
+def is_string(strng, encoding):
+ """ Returns true if the string contains no ASCII control
+ characters and can be decoded from the specified encoding. """
+ for char in strng:
+ if ord(char) < 9 or ord(char) > 13 and ord(char) < 32:
+ return False
+ if not hasattr(strng, "decode"):
+ # py3k
+ return True
+ try:
+ strng.decode(encoding)
+ return True
+ except: # pylint: disable=W0702
+ return False
diff --git a/src/lib/Bcfg2/manage.py b/src/lib/Bcfg2/manage.py
index 3e4eedc9f..9675a3db1 100755
--- a/src/lib/Bcfg2/manage.py
+++ b/src/lib/Bcfg2/manage.py
@@ -1,14 +1,31 @@
#!/usr/bin/env python
-from django.core.management import execute_manager
-import imp
+""" Wrapper for the django manage.py with the Bcfg2 Opitons parsing. """
+
+import sys
+import django
+import Bcfg2.Options
+import Bcfg2.DBSettings
+
try:
- imp.find_module('settings') # Assumed to be in the same directory.
+ import Bcfg2.Server.models
except ImportError:
- import sys
- sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
- sys.exit(1)
+ pass
+
+
+def main():
+ parser = Bcfg2.Options.get_parser()
+ parser.add_options([
+ Bcfg2.Options.PositionalArgument('django_command', nargs='*')])
+ parser.parse()
+
+ if django.VERSION[0] == 1 and django.VERSION[1] >= 6:
+ from django.core.management import execute_from_command_line
+ execute_from_command_line(
+ sys.argv[:1] + Bcfg2.Options.setup.django_command)
+ else:
+ from django.core.management import execute_manager
+ execute_manager(Bcfg2.DBSettings.settings)
-import settings
if __name__ == "__main__":
- execute_manager(settings)
+ main()
diff --git a/src/lib/Bcfg2/version.py b/src/lib/Bcfg2/version.py
index 196d77273..ab5439528 100644
--- a/src/lib/Bcfg2/version.py
+++ b/src/lib/Bcfg2/version.py
@@ -2,7 +2,7 @@
import re
-__version__ = "1.4.0pre1"
+__version__ = "1.4.0pre2"
class Bcfg2VersionInfo(tuple): # pylint: disable=E0012,R0924