From a5b5c1926d437a8132115bd608d2fd8141982f70 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Fri, 8 Mar 2013 13:34:33 -0500 Subject: Reporting: added support for various SELinux entries --- src/lib/Bcfg2/Reporting/Storage/DjangoORM.py | 354 +++++++++------ .../migrations/0005_add_selinux_entry_support.py | 485 +++++++++++++++++++++ src/lib/Bcfg2/Reporting/models.py | 198 +++++++-- .../Reporting/templates/config_items/item.html | 31 +- src/lib/Bcfg2/Reporting/views.py | 24 +- 5 files changed, 912 insertions(+), 180 deletions(-) create mode 100644 src/lib/Bcfg2/Reporting/migrations/0005_add_selinux_entry_support.py (limited to 'src') diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py index bca4a9c1e..8826d6991 100644 --- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py +++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py @@ -16,6 +16,7 @@ from Bcfg2.Reporting.Storage.base import StorageBase, StorageError from Bcfg2.Server.Plugin.exceptions import PluginExecutionError from django.core import management from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +from django.db.models import FieldDoesNotExist from django.core.cache import cache from django.db import transaction @@ -30,6 +31,212 @@ class DjangoORM(StorageBase): super(DjangoORM, self).__init__(setup) self.size_limit = setup.get('reporting_file_limit') + def _import_default(self, entry, state, entrytype=None, defaults=None, + mapping=None, boolean=None, xforms=None): + """ Default entry importer. Maps the entry (in state + ``state``) to an appropriate *Entry object; by default, this + is determined by the entry tag, e.g., from an Action entry an + ActionEntry object is created. This can be overridden with + ``entrytype``, which should be the class to instantiate for + this entry. + + ``defaults`` is an optional mapping of : that will be used to set the default values for + various attributes. + + ``mapping`` is a mapping of : that + can be used to map fields that are named differently on the + XML entry and in the database model. + + ``boolean`` is a list of attribute names that should be + treated as booleans. + + ``xforms`` is a dict of :, where the + given function will be applied to the value of the named + attribute before trying to store it in the database. + """ + if entrytype is None: + entrytype = globals()["%sEntry" % entry.tag] + if defaults is None: + defaults = dict() + if mapping is None: + mapping = dict() + if boolean is None: + boolean = [] + if xforms is None: + xforms = dict() + mapping['exists'] = 'current_exists' + defaults['current_exists'] = 'true' + boolean.append("current_exists") + + def boolean_xform(val): + try: + return val.lower() == "true" + except AttributeError: + return False + + for attr in boolean + ["current_exists"]: + xforms[attr] = boolean_xform + act_dict = dict(state=state) + for fieldname in entrytype._meta.get_all_field_names(): + if fieldname in ['id', 'hash_key', 'state']: + continue + try: + field = entrytype._meta.get_field(fieldname) + except FieldDoesNotExist: + continue + attrname = mapping.get(fieldname, fieldname) + val = entry.get(fieldname, defaults.get(attrname)) + act_dict[fieldname] = xforms.get(attrname, lambda v: v)(val) + self.logger.debug("Adding %s:%s" % (entry.tag, entry.get("name"))) + return entrytype.entry_get_or_create(act_dict) + + def _import_Action(self, entry, state): + return self._import_default(entry, state, + defaults=dict(status='check', rc=-1), + mapping=dict(output="rc")) + + def _import_Package(self, entry, state): + name = entry.get('name') + exists = entry.get('current_exists', default="true").lower() == "true" + act_dict = dict(name=name, state=state, exists=exists, + target_version=entry.get('version', default=''), + current_version=entry.get('current_version', + default='')) + + # extra entries are a bit different. They can have Instance + # objects + if not act_dict['target_version']: + for instance in entry.findall("Instance"): + # FIXME - this probably only works for rpms + release = instance.get('release', '') + arch = instance.get('arch', '') + act_dict['current_version'] = instance.get('version') + if release: + act_dict['current_version'] += "-" + release + if arch: + act_dict['current_version'] += "." + arch + self.logger.debug("Adding package %s %s" % + (name, act_dict['current_version'])) + return PackageEntry.entry_get_or_create(act_dict) + else: + self.logger.debug("Adding package %s %s" % + (name, act_dict['target_version'])) + + # not implemented yet + act_dict['verification_details'] = \ + entry.get('verification_details', '') + return PackageEntry.entry_get_or_create(act_dict) + + def _import_Path(self, entry, state): + name = entry.get('name') + exists = entry.get('current_exists', default="true").lower() == "true" + path_type = entry.get("type").lower() + act_dict = dict(name=name, state=state, exists=exists, + path_type=path_type) + + target_dict = dict( + owner=entry.get('owner', default="root"), + group=entry.get('group', default="root"), + mode=entry.get('mode', default=entry.get('perms', + default="")) + ) + fperm, created = FilePerms.objects.get_or_create(**target_dict) + act_dict['target_perms'] = fperm + + current_dict = dict( + owner=entry.get('current_owner', default=""), + group=entry.get('current_group', default=""), + mode=entry.get('current_mode', + default=entry.get('current_perms', default="")) + ) + fperm, created = FilePerms.objects.get_or_create(**current_dict) + act_dict['current_perms'] = fperm + + if path_type in ('symlink', 'hardlink'): + act_dict['target_path'] = entry.get('to', default="") + act_dict['current_path'] = entry.get('current_to', default="") + self.logger.debug("Adding link %s" % name) + return LinkEntry.entry_get_or_create(act_dict) + elif path_type == 'device': + # TODO devices + self.logger.warn("device path types are not supported yet") + return + + # TODO - vcs output + act_dict['detail_type'] = PathEntry.DETAIL_UNUSED + if path_type == 'directory' and entry.get('prune', 'false') == 'true': + unpruned_elist = [e.get('path') for e in entry.findall('Prune')] + if unpruned_elist: + act_dict['detail_type'] = PathEntry.DETAIL_PRUNED + act_dict['details'] = "\n".join(unpruned_elist) + elif entry.get('sensitive', 'false').lower() == 'true': + act_dict['detail_type'] = PathEntry.DETAIL_SENSITIVE + else: + cdata = None + if entry.get('current_bfile', None): + act_dict['detail_type'] = PathEntry.DETAIL_BINARY + cdata = entry.get('current_bfile') + elif entry.get('current_bdiff', None): + act_dict['detail_type'] = PathEntry.DETAIL_DIFF + cdata = b64decode(entry.get('current_bdiff')) + elif entry.get('current_diff', None): + act_dict['detail_type'] = PathEntry.DETAIL_DIFF + cdata = entry.get('current_bdiff') + if cdata: + if len(cdata) > self.size_limit: + act_dict['detail_type'] = PathEntry.DETAIL_SIZE_LIMIT + act_dict['details'] = md5(cdata).hexdigest() + else: + act_dict['details'] = cdata + self.logger.debug("Adding path %s" % name) + return PathEntry.entry_get_or_create(act_dict) + # TODO - secontext + # TODO - acls + + def _import_Service(self, entry, state): + return self._import_default(entry, state, + defaults=dict(status='', + current_status=''), + mapping=dict(status='target_status')) + + def _import_SEBoolean(self, entry, state): + return self._import_default( + entry, state, + xforms=dict(value=lambda v: v.lower() == "on")) + + def _import_SEFcontext(self, entry, state): + return self._import_default(entry, state, + defaults=dict(filetype='all')) + + def _import_SEInterface(self, entry, state): + return self._import_default(entry, state) + + def _import_SEPort(self, entry, state): + return self._import_default(entry, state) + + def _import_SENode(self, entry, state): + return self._import_default(entry, state) + + def _import_SELogin(self, entry, state): + return self._import_default(entry, state) + + def _import_SEUser(self, entry, state): + return self._import_default(entry, state) + + def _import_SEPermissive(self, entry, state): + return self._import_default(entry, state) + + def _import_SEModule(self, entry, state): + return self._import_default(entry, state, + defaults=dict(disabled='false'), + boolean=['disabled', 'current_disabled']) + + def _import_unknown(self, entry, _): + self.logger.error("Unknown type %s not handled by reporting yet" % + entry.tag) + return None + @transaction.commit_on_success def _import_interaction(self, interaction): """Real import function""" @@ -46,13 +253,15 @@ class DjangoORM(StorageBase): cache.set(hostname, client) timestamp = datetime(*strptime(stats.get('time'))[0:6]) - if len(Interaction.objects.filter(client=client, timestamp=timestamp)) > 0: + if len(Interaction.objects.filter(client=client, + timestamp=timestamp)) > 0: self.logger.warn("Interaction for %s at %s already exists" % (hostname, timestamp)) return if 'profile' in metadata: - profile, created = Group.objects.get_or_create(name=metadata['profile']) + profile, created = \ + Group.objects.get_or_create(name=metadata['profile']) else: profile = None inter = Interaction(client=client, @@ -65,10 +274,10 @@ class DjangoORM(StorageBase): server=server, profile=profile) inter.save() - self.logger.debug("Interaction for %s at %s with INSERTED in to db" % + self.logger.debug("Interaction for %s at %s with INSERTED in to db" % (client.id, timestamp)) - #FIXME - this should be more efficient + # FIXME - this should be more efficient for group_name in metadata['groups']: group = cache.get("GROUP_" + group_name) if not group: @@ -76,12 +285,13 @@ class DjangoORM(StorageBase): if created: self.logger.debug("Added group %s" % group) cache.set("GROUP_" + group_name, group) - + inter.groups.add(group) - for bundle_name in metadata['bundles']: + for bundle_name in metadata.get('bundles', []): bundle = cache.get("BUNDLE_" + bundle_name) if not bundle: - bundle, created = Bundle.objects.get_or_create(name=bundle_name) + bundle, created = \ + Bundle.objects.get_or_create(name=bundle_name) if created: self.logger.debug("Added bundle %s" % bundle) cache.set("BUNDLE_" + bundle_name, bundle) @@ -94,130 +304,26 @@ class DjangoORM(StorageBase): pattern = [('Bad/*', TYPE_BAD), ('Extra/*', TYPE_EXTRA), ('Modified/*', TYPE_MODIFIED)] - updates = dict(failures=[], paths=[], packages=[], actions=[], services=[]) + updates = dict([(etype, []) for etype in Interaction.entry_types]) for (xpath, state) in pattern: for entry in stats.findall(xpath): counter_fields[state] = counter_fields[state] + 1 - entry_type = entry.tag - name = entry.get('name') - exists = entry.get('current_exists', default="true").lower() == "true" - # handle server failures differently failure = entry.get('failure', '') if failure: - act_dict = dict(name=name, entry_type=entry_type, - message=failure) + act_dict = dict(name=entry.get("name"), + entry_type=entry.tag, + message=failure) newact = FailureEntry.entry_get_or_create(act_dict) updates['failures'].append(newact) continue - act_dict = dict(name=name, state=state, exists=exists) - - if entry_type == 'Action': - act_dict['status'] = entry.get('status', default="check") - act_dict['output'] = entry.get('rc', default=-1) - self.logger.debug("Adding action %s" % name) - updates['actions'].append(ActionEntry.entry_get_or_create(act_dict)) - elif entry_type == 'Package': - act_dict['target_version'] = entry.get('version', default='') - act_dict['current_version'] = entry.get('current_version', default='') - - # extra entries are a bit different. They can have Instance objects - if not act_dict['target_version']: - for instance in entry.findall("Instance"): - #TODO - this probably only works for rpms - release = instance.get('release', '') - arch = instance.get('arch', '') - act_dict['current_version'] = instance.get('version') - if release: - act_dict['current_version'] += "-" + release - if arch: - act_dict['current_version'] += "." + arch - self.logger.debug("Adding package %s %s" % (name, act_dict['current_version'])) - updates['packages'].append(PackageEntry.entry_get_or_create(act_dict)) - else: - - self.logger.debug("Adding package %s %s" % (name, act_dict['target_version'])) - - # not implemented yet - act_dict['verification_details'] = entry.get('verification_details', '') - updates['packages'].append(PackageEntry.entry_get_or_create(act_dict)) - - elif entry_type == 'Path': - path_type = entry.get("type").lower() - act_dict['path_type'] = path_type - - target_dict = dict( - owner=entry.get('owner', default="root"), - group=entry.get('group', default="root"), - mode=entry.get('mode', default=entry.get('perms', default="")) - ) - fperm, created = FilePerms.objects.get_or_create(**target_dict) - act_dict['target_perms'] = fperm - - current_dict = dict( - owner=entry.get('current_owner', default=""), - group=entry.get('current_group', default=""), - mode=entry.get('current_mode', - default=entry.get('current_perms', default="")) - ) - fperm, created = FilePerms.objects.get_or_create(**current_dict) - act_dict['current_perms'] = fperm - - if path_type in ('symlink', 'hardlink'): - act_dict['target_path'] = entry.get('to', default="") - act_dict['current_path'] = entry.get('current_to', default="") - self.logger.debug("Adding link %s" % name) - updates['paths'].append(LinkEntry.entry_get_or_create(act_dict)) - continue - elif path_type == 'device': - #TODO devices - self.logger.warn("device path types are not supported yet") - continue - - # TODO - vcs output - act_dict['detail_type'] = PathEntry.DETAIL_UNUSED - if path_type == 'directory' and entry.get('prune', 'false') == 'true': - unpruned_elist = [e.get('path') for e in entry.findall('Prune')] - if unpruned_elist: - act_dict['detail_type'] = PathEntry.DETAIL_PRUNED - act_dict['details'] = "\n".join(unpruned_elist) - elif entry.get('sensitive', 'false').lower() == 'true': - act_dict['detail_type'] = PathEntry.DETAIL_SENSITIVE - else: - cdata = None - if entry.get('current_bfile', None): - act_dict['detail_type'] = PathEntry.DETAIL_BINARY - cdata = entry.get('current_bfile') - elif entry.get('current_bdiff', None): - act_dict['detail_type'] = PathEntry.DETAIL_DIFF - cdata = b64decode(entry.get('current_bdiff')) - elif entry.get('current_diff', None): - act_dict['detail_type'] = PathEntry.DETAIL_DIFF - cdata = entry.get('current_bdiff') - if cdata: - if len(cdata) > self.size_limit: - act_dict['detail_type'] = PathEntry.DETAIL_SIZE_LIMIT - act_dict['details'] = md5(cdata).hexdigest() - else: - act_dict['details'] = cdata - self.logger.debug("Adding path %s" % name) - updates['paths'].append(PathEntry.entry_get_or_create(act_dict)) - - - #TODO - secontext - #TODO - acls - - elif entry_type == 'Service': - act_dict['target_status'] = entry.get('status', default='') - act_dict['current_status'] = entry.get('current_status', default='') - self.logger.debug("Adding service %s" % name) - updates['services'].append(ServiceEntry.entry_get_or_create(act_dict)) - elif entry_type == 'SELinux': - self.logger.info("SELinux not implemented yet") - else: - self.logger.error("Unknown type %s not handled by reporting yet" % entry_type) + updatetype = entry.tag.lower() + "s" + update = getattr(self, "_import_%s" % entry.tag, + self._import_unknown)(entry, state) + if update is not None: + updates[updatetype].append(update) inter.bad_count = counter_fields[TYPE_BAD] inter.modified_count = counter_fields[TYPE_MODIFIED] @@ -227,15 +333,16 @@ class DjangoORM(StorageBase): # batch this for sqlite i = 0 while(i < len(updates[entry_type])): - getattr(inter, entry_type).add(*updates[entry_type][i:i+100]) + getattr(inter, entry_type).add(*updates[entry_type][i:i + 100]) i += 100 # performance metrics for times in stats.findall('OpStamps'): for metric, value in list(times.items()): - Performance(interaction=inter, metric=metric, value=value).save() + Performance(interaction=inter, + metric=metric, + value=value).save() - def import_interaction(self, interaction): """Import the data into the backend""" @@ -245,7 +352,6 @@ class DjangoORM(StorageBase): self.logger.error("Failed to import interaction: %s" % traceback.format_exc().splitlines()[-1]) - def validate(self): """Validate backend storage. Should be called once when loaded""" 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 new file mode 100644 index 000000000..d5f5d801a --- /dev/null +++ b/src/lib/Bcfg2/Reporting/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/models.py b/src/lib/Bcfg2/Reporting/models.py index ab2dc8418..a04203c73 100644 --- a/src/lib/Bcfg2/Reporting/models.py +++ b/src/lib/Bcfg2/Reporting/models.py @@ -11,21 +11,9 @@ except ImproperlyConfigured: from django.core.cache import cache from datetime import datetime, timedelta +from Bcfg2.Compat import cPickle + -try: - import cPickle as pickle -except: - import pickle - -KIND_CHOICES = ( - #These are the kinds of config elements - ('Package', 'Package'), - ('Path', 'directory'), - ('Path', 'file'), - ('Path', 'permissions'), - ('Path', 'symlink'), - ('Service', 'Service'), -) TYPE_GOOD = 0 TYPE_BAD = 1 TYPE_MODIFIED = 2 @@ -57,8 +45,8 @@ def hash_entry(entry_dict): for key in sorted(entry_dict.keys()): if key in ('id', 'hash_key') or key.startswith('_'): continue - dataset.append( (key, entry_dict[key]) ) - return hash(pickle.dumps(dataset)) + dataset.append((key, entry_dict[key])) + return hash(cPickle.dumps(dataset)) class Client(models.Model): @@ -121,7 +109,8 @@ class InteractionManager(models.Manager): class Interaction(models.Model): - """Models each reconfiguration operation interaction between client and server.""" + """ Models each reconfiguration operation interaction between + client and server. """ client = models.ForeignKey(Client, related_name="interactions") timestamp = models.DateTimeField(db_index=True) # Timestamp for this record state = models.CharField(max_length=32) # good/bad/modified/etc @@ -137,8 +126,21 @@ class Interaction(models.Model): packages = models.ManyToManyField("PackageEntry") paths = models.ManyToManyField("PathEntry") services = models.ManyToManyField("ServiceEntry") + sebooleans = models.ManyToManyField("SEBooleanEntry") + seports = models.ManyToManyField("SEPortEntry") + sefcontexts = models.ManyToManyField("SEFcontextEntry") + senodes = models.ManyToManyField("SENodeEntry") + selogins = models.ManyToManyField("SELoginEntry") + seusers = models.ManyToManyField("SEUserEntry") + seinterfaces = models.ManyToManyField("SEInterfaceEntry") + sepermissives = models.ManyToManyField("SEPermissiveEntry") + semodules = models.ManyToManyField("SEModuleEntry") failures = models.ManyToManyField("FailureEntry") + entry_types = ('actions', 'packages', 'paths', 'services', 'sebooleans', + 'seports', 'sefcontexts', 'senodes', 'selogins', 'seusers', + 'seinterfaces', 'sepermissives', 'semodules') + # Formerly InteractionMetadata profile = models.ForeignKey("Group", related_name="+", null=True) groups = models.ManyToManyField("Group") @@ -157,7 +159,8 @@ class Interaction(models.Model): def percentbad(self): if not self.total_count == 0: - return ((self.total_count - self.good_count) / (float(self.total_count))) * 100 + return ((self.total_count - self.good_count) / + (float(self.total_count))) * 100 else: return 0 @@ -189,7 +192,8 @@ class Interaction(models.Model): self.client.save() # save again post update def delete(self): - '''Override the default delete. Allows us to remove Performance items''' + '''Override the default delete. Allows us to remove + Performance items ''' pitems = list(self.performance_items.all()) super(Interaction, self).delete() for perf in pitems: @@ -201,19 +205,19 @@ class Interaction(models.Model): def bad(self): rv = [] - for entry in ('actions', 'packages', 'paths', 'services'): + for entry in self.entry_types: rv.extend(getattr(self, entry).filter(state=TYPE_BAD)) return rv def modified(self): rv = [] - for entry in ('actions', 'packages', 'paths', 'services'): + for entry in self.entry_types: rv.extend(getattr(self, entry).filter(state=TYPE_MODIFIED)) return rv def extra(self): rv = [] - for entry in ('actions', 'packages', 'paths', 'services'): + for entry in self.entry_types: rv.extend(getattr(self, entry).filter(state=TYPE_EXTRA)) return rv @@ -325,7 +329,6 @@ class BaseEntry(models.Model): self.hash_key = hash_entry(self.__dict__) super(BaseEntry, self).save(*args, **kwargs) - def class_name(self): return self.__class__.__name__ @@ -333,7 +336,6 @@ class BaseEntry(models.Model): """todo""" return [] - @classmethod def entry_from_name(cls, name): try: @@ -344,28 +346,26 @@ class BaseEntry(models.Model): except KeyError: raise ValueError("Invalid type %s" % name) - @classmethod def entry_from_type(cls, etype): - for entry_cls in (ActionEntry, PackageEntry, PathEntry, ServiceEntry): + for entry_cls in ENTRY_CLASSES: if etype == entry_cls.ENTRY_TYPE: return entry_cls else: raise ValueError("Invalid type %s" % etype) - @classmethod def entry_get_or_create(cls, act_dict): """Helper to quickly lookup an object""" cls_name = cls().__class__.__name__ act_hash = hash_entry(act_dict) - + # TODO - get form cache and validate act_key = "%s_%s" % (cls_name, act_hash) newact = cache.get(act_key) if newact: return newact - + acts = cls.objects.filter(hash_key=act_hash) if len(acts) > 0: for act in acts: @@ -375,20 +375,18 @@ class BaseEntry(models.Model): #match found newact = act break - + # worst case, its new if not newact: newact = cls(**act_dict) newact.save(hash_key=act_hash) - + cache.set(act_key, newact, 60 * 60) return newact - def is_failure(self): return isinstance(self, FailureEntry) - @classmethod def prune_orphans(cls): '''Remove unused entries''' @@ -397,7 +395,7 @@ class BaseEntry(models.Model): for x in cls.objects.filter(interaction__isnull=True).values("id")] i = 0 while i < len(cls_orphans): - cls.objects.filter(id__in=cls_orphans[i:i+100]).delete() + cls.objects.filter(id__in=cls_orphans[i:i + 100]).delete() i += 100 @@ -439,13 +437,137 @@ class FailureEntry(BaseEntry): class ActionEntry(SuccessEntry): - """ The new model for package information """ + """ Action entry """ status = models.CharField(max_length=128, default="check") output = models.IntegerField(default=0) ENTRY_TYPE = r"Action" +class SEBooleanEntry(SuccessEntry): + """ SELinux boolean """ + value = models.BooleanField(default=True) + + ENTRY_TYPE = r"SEBoolean" + + +class SEPortEntry(SuccessEntry): + """ SELinux port """ + selinuxtype = models.CharField(max_length=128) + current_selinuxtype = models.CharField(max_length=128, null=True) + + ENTRY_TYPE = r"SEPort" + + def selinuxtype_problem(self): + """Check for an selinux type problem.""" + if not self.current_selinuxtype: + return True + return self.selinuxtype != self.current_selinuxtype + + def short_list(self): + """Return a list of problems""" + rv = super(SEPortEntry, self).short_list() + if self.selinuxtype_problem(): + rv.append("Wrong SELinux type") + return rv + + +class SEFcontextEntry(SuccessEntry): + """ SELinux file context """ + selinuxtype = models.CharField(max_length=128) + current_selinuxtype = models.CharField(max_length=128, null=True) + filetype = models.CharField(max_length=16) + + ENTRY_TYPE = r"SEFcontext" + + def selinuxtype_problem(self): + """Check for an selinux type problem.""" + if not self.current_selinuxtype: + return True + return self.selinuxtype != self.current_selinuxtype + + def short_list(self): + """Return a list of problems""" + rv = super(SEFcontextEntry, self).short_list() + if self.selinuxtype_problem(): + rv.append("Wrong SELinux type") + return rv + + +class SENodeEntry(SuccessEntry): + """ SELinux node """ + selinuxtype = models.CharField(max_length=128) + current_selinuxtype = models.CharField(max_length=128, null=True) + proto = models.CharField(max_length=4) + + ENTRY_TYPE = r"SENode" + + def selinuxtype_problem(self): + """Check for an selinux type problem.""" + if not self.current_selinuxtype: + return True + return self.selinuxtype != self.current_selinuxtype + + def short_list(self): + """Return a list of problems""" + rv = super(SENodeEntry, self).short_list() + if self.selinuxtype_problem(): + rv.append("Wrong SELinux type") + return rv + + +class SELoginEntry(SuccessEntry): + """ SELinux login """ + selinuxuser = models.CharField(max_length=128) + current_selinuxuser = models.CharField(max_length=128, null=True) + + ENTRY_TYPE = r"SELogin" + + +class SEUserEntry(SuccessEntry): + """ SELinux user """ + 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) + + ENTRY_TYPE = r"SEUser" + + +class SEInterfaceEntry(SuccessEntry): + """ SELinux interface """ + selinuxtype = models.CharField(max_length=128) + current_selinuxtype = models.CharField(max_length=128, null=True) + + ENTRY_TYPE = r"SEInterface" + + def selinuxtype_problem(self): + """Check for an selinux type problem.""" + if not self.current_selinuxtype: + return True + return self.selinuxtype != self.current_selinuxtype + + def short_list(self): + """Return a list of problems""" + rv = super(SEInterfaceEntry, self).short_list() + if self.selinuxtype_problem(): + rv.append("Wrong SELinux type") + return rv + + +class SEPermissiveEntry(SuccessEntry): + """ SELinux permissive domain """ + ENTRY_TYPE = r"SEPermissive" + + +class SEModuleEntry(SuccessEntry): + """ SELinux module """ + disabled = models.BooleanField(default=False) + current_disabled = models.BooleanField(default=False) + + ENTRY_TYPE = r"SEModule" + + class PackageEntry(SuccessEntry): """ The new model for package information """ @@ -455,7 +577,7 @@ class PackageEntry(SuccessEntry): verification_details = models.TextField(default="") ENTRY_TYPE = r"Package" - #TODO - prune + # TODO - prune def version_problem(self): """Check for a version problem.""" @@ -612,3 +734,7 @@ class ServiceEntry(SuccessEntry): return rv +ENTRY_TYPES = (ActionEntry, PackageEntry, PathEntry, ServiceEntry, + SEBooleanEntry, SEPortEntry, SEFcontextEntry, SENodeEntry, + SELoginEntry, SEUserEntry, SEInterfaceEntry, SEPermissiveEntry, + SEModuleEntry) diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/item.html b/src/lib/Bcfg2/Reporting/templates/config_items/item.html index 737760252..259414399 100644 --- a/src/lib/Bcfg2/Reporting/templates/config_items/item.html +++ b/src/lib/Bcfg2/Reporting/templates/config_items/item.html @@ -45,36 +45,47 @@ div.entry_list h3 { {% endif %} {# Really need a better test here #} -{% if item.mdoe_problem or item.status_problem or item.linkentry.link_problem or item.version_problem %} +{% if item.mode_problem or item.status_problem or item.linkentry.link_problem or item.version_problem %} {% if item.mode_problem %} {% if item.current_perms.owner %} - + + {% endif %} {% if item.current_perms.group %} - + + {% endif %} {% if item.current_perms.mode%} - + {% endif %} {% endif %} {% if item.status_problem %} - - + + + {% endif %} {% if item.linkentry.link_problem %} - - + + + {% endif %} {% if item.version_problem %} - + + {% endif %} + {% if item.selinuxtype_problem %} + + + + {% endif %}
Problem TypeExpectedFound
Owner{{item.target_perms.owner}}
Owner{{item.target_perms.owner}} {{item.current_perms.owner}}
Group{{item.target_perms.group}}
Group{{item.target_perms.group}} {{item.current_perms.group}}
Mode{{item.target_perms.mode}}
Permissions + {{item.target_perms.mode}} {{item.current_perms.mode}}
Status{{item.target_status}}{{item.current_status}}
Status{{item.target_status}}{{item.current_status}}
{{item.get_path_type_display}}{{item.linkentry.target_path}}{{item.linkentry.current_path}}
{{item.get_path_type_display}}{{item.linkentry.target_path}}{{item.linkentry.current_path}}
Package Version{{item.target_version|cut:"("|cut:")"}}
Package Version{{item.target_version|cut:"("|cut:")"}} {{item.current_version|cut:"("|cut:")"}}
SELinux Type{{item.selinuxtype}}{{item.current_selinuxtype}}
{% endif %} @@ -92,7 +103,7 @@ div.entry_list h3 { {{ item.details|syntaxhilight }} {% else %} - {{ item.details }} + {{ item.details }} {% endif %} {% endif %} diff --git a/src/lib/Bcfg2/Reporting/views.py b/src/lib/Bcfg2/Reporting/views.py index 0341a18af..6cba7bf8c 100644 --- a/src/lib/Bcfg2/Reporting/views.py +++ b/src/lib/Bcfg2/Reporting/views.py @@ -161,7 +161,7 @@ def config_item(request, pk, entry_type, interaction=None): ts_end = ts_start + timedelta(days=1) associated_list = item.interaction_set.select_related('client').filter(\ timestamp__gte=ts_start, timestamp__lt=ts_end) - + if item.is_failure(): template = 'config_items/item-failure.html' else: @@ -184,7 +184,7 @@ def config_item_list(request, item_state, timestamp=None, **kwargs): current_clients = [q['id'] for q in _handle_filters(current_clients, **kwargs).values('id')] lists = [] - for etype in ActionEntry, PackageEntry, PathEntry, ServiceEntry: + 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') if len(ldata) > 0: @@ -218,7 +218,7 @@ def entry_status(request, entry_type, pk, timestamp=None, **kwargs): if it.pk not in seen: 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, @@ -254,8 +254,8 @@ def common_problems(request, timestamp=None, threshold=None, group=None): else: current_clients = Interaction.objects.recent_ids(timestamp) lists = [] - for etype in ActionEntry, PackageEntry, PathEntry, ServiceEntry: - ldata = etype.objects.exclude(state=TYPE_GOOD).filter( + for etype in ENTRY_TYPES: + ldata = etype.objects.exclude(state=TYPE_GOOD).filter( interaction__in=current_clients).annotate(num_entries=Count('id')).filter(num_entries__gte=threshold)\ .order_by('-num_entries', 'name') if len(ldata) > 0: @@ -315,7 +315,8 @@ def client_detailed_list(request, timestamp=None, **kwargs): kwargs['orderby'] = "client__name" kwargs['sort'] = "client" - kwargs['interaction_base'] = Interaction.objects.recent(timestamp).select_related() + kwargs['interaction_base'] = \ + Interaction.objects.recent(timestamp).select_related() kwargs['page_limit'] = 0 return render_history_view(request, 'clients/detailed-list.html', **kwargs) @@ -330,16 +331,18 @@ def client_detail(request, hostname=None, pk=None): inter = client.interactions.get(pk=pk) maxdate = inter.timestamp - etypes = { TYPE_BAD: 'bad', TYPE_MODIFIED: 'modified', TYPE_EXTRA: 'extra' } + etypes = {TYPE_BAD: 'bad', + TYPE_MODIFIED: 'modified', + TYPE_EXTRA: 'extra'} edict = dict() for label in etypes.values(): edict[label] = [] - for ekind in ('actions', 'packages', 'paths', 'services'): + for ekind in inter.entry_types: for ent in getattr(inter, ekind).all(): edict[etypes[ent.state]].append(ent) context['entry_types'] = edict - context['interaction']=inter + context['interaction'] = inter return render_history_view(request, 'clients/detail.html', page_limit=5, client=client, maxdate=maxdate, context=context) @@ -356,7 +359,8 @@ def client_manage(request): client.expiration = datetime.now() client.save() message = "Expiration for %s set to %s." % \ - (client_name, client.expiration.strftime("%Y-%m-%d %H:%M:%S")) + (client_name, + client.expiration.strftime("%Y-%m-%d %H:%M:%S")) elif client_action == 'unexpire': client.expiration = None client.save() -- cgit v1.2.3-1-g7c22