diff options
Diffstat (limited to 'src/lib')
118 files changed, 5263 insertions, 2763 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..172f88f24 100644 --- a/src/lib/Bcfg2/DBSettings.py +++ b/src/lib/Bcfg2/DBSettings.py @@ -21,8 +21,9 @@ try: except ImportError: HAS_SOUTH = False -settings = dict( # pylint: disable=C0103 - TIME_ZONE=None, +# pylint: disable=C0103 +settings = dict( + TIME_ZONE='UTC', TEMPLATE_DEBUG=False, DEBUG=False, ALLOWED_HOSTS=['*'], @@ -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..7e1661c5a 100755 --- a/src/lib/Bcfg2/Reporting/Reports.py +++ b/src/lib/Bcfg2/Reporting/Reports.py @@ -4,54 +4,9 @@ import sys import argparse import datetime +import django import Bcfg2.DBSettings - - -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_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")) - - print(fmt % tuple(fdata)) +from django.core.exceptions import ObjectDoesNotExist def print_entries(interaction, etype): @@ -60,6 +15,44 @@ def print_entries(interaction, etype): print("%-70s %s" % (item.entry_type + ":" + item.name, etype)) +class _FlagsFilterMixin(object): + """ Mixin that allows to filter the interactions based on the + only_important, the dry_run and/or the ready flag """ + + 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"), + Bcfg2.Options.BooleanOption( + "-r", "--ready", + help="Only consider interactions fully imported into the " + "database")] + + def get_interaction(self, client, setup): + if not setup.no_dry_run and not setup.no_only_important \ + and not setup.ready: + return client.current_interaction + + filter = {} + if setup.no_dry_run: + filter['dry_run'] = False + if setup.no_only_important: + filter['only_important'] = False + if setup.ready: + filter['ready'] = True + + 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 """ Base class for bcfg2-reports modes that take a single host as a positional argument """ @@ -74,10 +67,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 +81,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 +123,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 +135,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 +191,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 +210,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 +242,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 +272,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 +308,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..a8c8ce243 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)) @@ -363,6 +401,9 @@ class DjangoORM(StorageBase): metric=metric, value=value).save() + inter.ready = True + inter.save() + def import_interaction(self, interaction): """Import the data into the backend""" try: @@ -374,8 +415,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/migrations/0008_add_ready_flag_interaction.py b/src/lib/Bcfg2/Reporting/migrations/0008_add_ready_flag_interaction.py new file mode 100644 index 000000000..18ea6e8ba --- /dev/null +++ b/src/lib/Bcfg2/Reporting/migrations/0008_add_ready_flag_interaction.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('Reporting', '0007_add_flag_fields_interaction'), + ] + + operations = [ + migrations.AddField( + model_name='interaction', + name='ready', + 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..c28571ded 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") @@ -174,6 +181,9 @@ class Interaction(models.Model): groups = models.ManyToManyField("Group") bundles = models.ManyToManyField("Bundle") + # Is the import ready? + ready = models.BooleanField(default=False) + objects = InteractionManager() def __str__(self): @@ -630,7 +640,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/0008_add_ready_flag_interaction.py b/src/lib/Bcfg2/Reporting/south_migrations/0008_add_ready_flag_interaction.py new file mode 100644 index 000000000..cfe42f35b --- /dev/null +++ b/src/lib/Bcfg2/Reporting/south_migrations/0008_add_ready_flag_interaction.py @@ -0,0 +1,291 @@ +# -*- 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.ready' + db.add_column('Reporting_interaction', 'ready', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Interaction.ready' + db.delete_column('Reporting_interaction', 'ready') + + + 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']"}), + 'ready': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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'] 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 — {{ 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..6f86cd3a3 100644 --- a/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py +++ b/src/lib/Bcfg2/Reporting/templatetags/bcfg2_tags.py @@ -1,16 +1,17 @@ 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 from Bcfg2.Reporting.utils import filter_list from Bcfg2.Reporting.models import Group +from Bcfg2.Reporting.views import render_history_view register = template.Library() @@ -213,7 +214,7 @@ class AddUrlFilter(template.Node): def __init__(self, filter_name, filter_value): self.filter_name = filter_name self.filter_value = filter_value - self.fallback_view = 'Bcfg2.Reporting.views.render_history_view' + self.fallback_view = render_history_view def render(self, context): link = '#' @@ -394,6 +395,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 +427,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/__init__.py b/src/lib/Bcfg2/Server/Lint/__init__.py index 61f704206..66c8180f4 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 @@ -326,7 +327,10 @@ class LintPluginOption(Bcfg2.Options.Option): plugins = [p.__name__ for p in namespace.plugins] for loader, name, _is_pkg in walk_packages(path=__path__): try: - module = loader.find_module(name).load_module(name) + module_name = 'Bcfg2.Server.Lint.%s' % name + module = loader \ + .find_module(module_name) \ + .load_module(module_name) plugin = getattr(module, name) if plugin.__serverplugin__ is None or \ plugin.__serverplugin__ in 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/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/Pac.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py index 0e15d2e15..6fc084cc4 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py @@ -1,10 +1,62 @@ """ Pacman backend for :mod:`Bcfg2.Server.Plugins.Packages` """ +import os import tarfile +from Bcfg2.Compat import cPickle from Bcfg2.Server.Plugins.Packages.Collection import Collection from Bcfg2.Server.Plugins.Packages.Source import Source +def parse_db_file(pkgfile): + """ Parse a Pacman database file, returning a dictionary with + section headings for keys and lists of strings for values. + (Reference: ``sync_db_read`` in ``lib/libalpm/be_sync.c``) + """ + + pkg = {} + section = None + + for line in pkgfile: + line = line.strip() + + if section is not None: + if not line: + section = None + else: + pkg[section].append(line) + elif len(line) >= 2 and line[0] == line[-1] == '%': + section = line + pkg[section] = [] + + return pkg + + +def parse_dep(dep): + """ Parse a Pacman dependency string, returning the package name, + version restriction (or ``None``), and description (or ``None``). + (Reference: ``alpm_dep_from_string`` in ``lib/libalpm/deps.c``) + """ + + rest_desc = dep.split(': ', 1) + if len(rest_desc) == 1: + rest, desc = rest_desc[0], None + else: + rest, desc = rest_desc + + # Search for '=' last, since '<=' and '>=' are possible. + for symb in ['<', '>', '=']: + idx = rest.find(symb) + if idx >= 0: + name = rest[:idx] + version = rest[idx:] + break + else: + name = rest + version = None + + return name, version, desc + + class PacCollection(Collection): """ Handle collections of Pacman sources. This is a no-op object that simply inherits from @@ -24,6 +76,10 @@ class PacCollection(Collection): debug=debug) __init__.__doc__ = Collection.__init__.__doc__.split(".. -----")[0] + @property + def __package_groups__(self): + return True + class PacSource(Source): """ Handle Pacman sources """ @@ -31,6 +87,25 @@ class PacSource(Source): #: PacSource sets the ``type`` on Package entries to "pacman" ptype = 'pacman' + def __init__(self, basepath, xsource): + self.pacgroups = {} + + Source.__init__(self, basepath, xsource) + __init__.__doc__ = Source.__init__.__doc__ + + def load_state(self): + data = open(self.cachefile, 'rb') + (self.pkgnames, self.deps, self.provides, + self.recommends, self.pacgroups) = cPickle.load(data) + load_state.__doc__ = Source.load_state.__doc__ + + def save_state(self): + cache = open(self.cachefile, 'wb') + cPickle.dump((self.pkgnames, self.deps, self.provides, + self.recommends, self.pacgroups), cache, 2) + cache.close() + save_state.__doc__ = Source.save_state.__doc__ + @property def urls(self): """ A list of URLs to the base metadata file for each @@ -45,14 +120,12 @@ class PacSource(Source): else: raise Exception("PacSource : RAWUrl not supported (yet)") - def read_files(self): - bdeps = dict() - bprov = dict() - - depfnames = ['Depends', 'Pre-Depends'] - if self.recommended: - depfnames.append('Recommends') - + def read_files(self): # pylint: disable=R0912 + bdeps = {} + brecs = {} + bprov = {} + self.pkgnames = set() + self.pacgroups = {} for fname in self.files: if not self.rawurl: barch = [x for x in fname.split('@') if x in self.arches][0] @@ -62,8 +135,9 @@ class PacSource(Source): barch = self.arches[0] if barch not in bdeps: - bdeps[barch] = dict() - bprov[barch] = dict() + bdeps[barch] = {} + brecs[barch] = {} + bprov[barch] = {} try: self.debug_log("Packages: try to read %s" % fname) tar = tarfile.open(fname, "r") @@ -71,11 +145,52 @@ class PacSource(Source): self.logger.error("Packages: Failed to read file %s" % fname) raise + packages = {} for tarinfo in tar: - if tarinfo.isdir(): - self.pkgnames.add(tarinfo.name.rsplit("-", 2)[0]) - self.debug_log("Packages: added %s" % - tarinfo.name.rsplit("-", 2)[0]) + if not tarinfo.isfile(): + continue + prefix = os.path.dirname(tarinfo.name) + if prefix not in packages: + packages[prefix] = {} + pkg = parse_db_file(tar.extractfile(tarinfo)) + packages[prefix].update(pkg) + + for pkg in packages.values(): + pkgname = pkg['%NAME%'][0] + self.pkgnames.add(pkgname) + bdeps[barch][pkgname] = [] + brecs[barch][pkgname] = [] + + if '%DEPENDS%' in pkg: + for dep in pkg['%DEPENDS%']: + dname = parse_dep(dep)[0] + bdeps[barch][pkgname].append(dname) + + if '%OPTDEPENDS%' in pkg: + for dep in pkg['%OPTDEPENDS%']: + dname = parse_dep(dep)[0] + brecs[barch][pkgname].append(dname) + + if '%PROVIDES%' in pkg: + for dep in pkg['%PROVIDES%']: + dname = parse_dep(dep)[0] + if dname not in bprov[barch]: + bprov[barch][dname] = set() + bprov[barch][dname].add(pkgname) + + if '%GROUPS%' in pkg: + for group in pkg['%GROUPS%']: + if group not in self.pacgroups: + self.pacgroups[group] = [] + self.pacgroups[group].append(pkgname) + tar.close() - self.process_files(bdeps, bprov) + self.process_files(bdeps, bprov, brecs) read_files.__doc__ = Source.read_files.__doc__ + + def get_group(self, metadata, group, ptype=None): + try: + return self.pacgroups[group] + except KeyError: + return [] + get_group.__doc__ = Source.get_group.__doc__ diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pkgng.py index 736cdcdd4..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/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py index c9f6ea14a..86f7698f7 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,38 @@ 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 #: 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 +201,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 +224,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) @@ -292,6 +265,69 @@ class Source(Debuggable): # pylint: disable=R0902 setting['name'] = self.get_repo_name(setting) 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..846fb89cd 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) @@ -1004,8 +1001,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 +1043,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 +1135,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 +1291,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 +1369,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..95b4baa3e 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,7 @@ 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', 'Dummy']), Bcfg2.Options.PathOption( cf=("packages", "cache"), dest="packages_cache", help="Path to the Packages cache", @@ -578,7 +532,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 +564,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/Probes.py b/src/lib/Bcfg2/Server/Plugins/Probes.py index 76aab69b5..270bfa62d 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): @@ -304,7 +307,8 @@ class ProbeData(str): # pylint: disable=E0012,R0924 .json, and .yaml properties to provide convenient ways to use ProbeData objects as XML, JSON, or YAML data """ def __new__(cls, data): - if isinstance(data, unicode): + # prevent double encoding utf-8 in python3 + if isinstance(data, unicode) and not isinstance(data, str): return str.__new__(cls, data.encode('utf-8')) else: return str.__new__(cls, data) 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 |