summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Reporting
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Bcfg2/Reporting')
-rw-r--r--src/lib/Bcfg2/Reporting/Storage/DjangoORM.py3
-rw-r--r--src/lib/Bcfg2/Reporting/models.py58
2 files changed, 45 insertions, 16 deletions
diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
index 3b2c0ccfa..aea5e9d4b 100644
--- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
+++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
@@ -197,7 +197,8 @@ class DjangoORM(StorageBase):
def _import_Service(self, entry, state):
return self._import_default(entry, state,
defaults=dict(status='',
- current_status=''),
+ current_status='',
+ target_status=''),
mapping=dict(status='target_status'))
def _import_SEBoolean(self, entry, state):
diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py
index e63c180a8..598e1c6ec 100644
--- a/src/lib/Bcfg2/Reporting/models.py
+++ b/src/lib/Bcfg2/Reporting/models.py
@@ -3,7 +3,7 @@ import sys
from django.core.exceptions import ImproperlyConfigured
try:
- from django.db import models
+ from django.db import models, backend, connection
except ImproperlyConfigured:
e = sys.exc_info()[1]
print("Reports: unable to import django models: %s" % e)
@@ -26,6 +26,8 @@ TYPE_CHOICES = (
(TYPE_EXTRA, 'Extra'),
)
+_our_backend = None
+
def convert_entry_type_to_id(type_name):
"""Convert a entry type to its entry id"""
@@ -49,6 +51,22 @@ def hash_entry(entry_dict):
return hash(cPickle.dumps(dataset))
+def _quote(value):
+ """
+ Quote a string to use as a table name or column
+
+ Newer versions and various drivers require an argument
+ https://code.djangoproject.com/ticket/13630
+ """
+ global _our_backend
+ if not _our_backend:
+ try:
+ _our_backend = backend.DatabaseOperations(connection)
+ except TypeError:
+ _our_backend = backend.DatabaseOperations()
+ return _our_backend.quote_name(value)
+
+
class Client(models.Model):
"""Object representing every client we have seen stats for."""
creation = models.DateTimeField(auto_now_add=True)
@@ -77,16 +95,20 @@ class InteractionManager(models.Manager):
cursor = connection.cursor()
cfilter = "expiration is null"
- sql = 'select ri.id, x.client_id from (select client_id, MAX(timestamp) ' + \
- 'as timer from Reporting_interaction'
+ sql = 'select ri.id, x.client_id from ' + \
+ '(select client_id, MAX(timestamp) as timer from ' + \
+ _quote('Reporting_interaction')
if maxdate:
if not isinstance(maxdate, datetime):
raise ValueError('Expected a datetime object')
sql = sql + " where timestamp <= '%s' " % maxdate
cfilter = "(expiration is null or expiration > '%s') and creation <= '%s'" % (maxdate, maxdate)
- sql = sql + ' GROUP BY client_id) x, Reporting_interaction ri where ' + \
- 'ri.client_id = x.client_id AND ri.timestamp = x.timer'
- sql = sql + " and x.client_id in (select id from Reporting_client where %s)" % cfilter
+ sql = sql + ' GROUP BY client_id) x, ' + \
+ _quote('Reporting_interaction') + \
+ ' ri where ri.client_id = x.client_id AND' + \
+ ' ri.timestamp = x.timer and x.client_id in' + \
+ ' (select id from %s where %s)' % \
+ (_quote('Reporting_client'), cfilter)
try:
cursor.execute(sql)
return [item[0] for item in cursor.fetchall()]
@@ -95,7 +117,6 @@ class InteractionManager(models.Manager):
pass
return []
-
def recent(self, maxdate=None):
"""
Returns the most recent interactions for clients as of a date
@@ -115,7 +136,7 @@ class Interaction(models.Model):
timestamp = models.DateTimeField(db_index=True) # Timestamp for this record
state = models.CharField(max_length=32) # good/bad/modified/etc
repo_rev_code = models.CharField(max_length=64) # repo revision at time of interaction
- server = models.CharField(max_length=256) # Name of the server used for the interaction
+ server = models.CharField(max_length=256) # server used for interaction
good_count = models.IntegerField() # of good config-items
total_count = models.IntegerField() # of total config-items
bad_count = models.IntegerField(default=0)
@@ -211,18 +232,24 @@ class Interaction(models.Model):
def bad(self):
rv = []
for entry in self.entry_types:
+ if entry == 'failures':
+ continue
rv.extend(getattr(self, entry).filter(state=TYPE_BAD))
return rv
def modified(self):
rv = []
for entry in self.entry_types:
+ if entry == 'failures':
+ continue
rv.extend(getattr(self, entry).filter(state=TYPE_MODIFIED))
return rv
def extra(self):
rv = []
for entry in self.entry_types:
+ if entry == 'failures':
+ continue
rv.extend(getattr(self, entry).filter(state=TYPE_EXTRA))
return rv
@@ -234,7 +261,8 @@ class Interaction(models.Model):
class Performance(models.Model):
"""Object representing performance data for any interaction."""
- interaction = models.ForeignKey(Interaction, related_name="performance_items")
+ interaction = models.ForeignKey(Interaction,
+ related_name="performance_items")
metric = models.CharField(max_length=128)
value = models.DecimalField(max_digits=32, decimal_places=16)
@@ -267,11 +295,11 @@ class Group(models.Model):
class Meta:
ordering = ('name',)
-
@staticmethod
def prune_orphans():
'''Prune unused groups'''
- Group.objects.filter(interaction__isnull=True, group__isnull=True).delete()
+ Group.objects.filter(interaction__isnull=True,
+ group__isnull=True).delete()
class Bundle(models.Model):
@@ -289,11 +317,11 @@ class Bundle(models.Model):
class Meta:
ordering = ('name',)
-
@staticmethod
def prune_orphans():
'''Prune unused bundles'''
- Bundle.objects.filter(interaction__isnull=True, group__isnull=True).delete()
+ Bundle.objects.filter(interaction__isnull=True,
+ group__isnull=True).delete()
# new interaction models
@@ -396,7 +424,7 @@ class BaseEntry(models.Model):
def prune_orphans(cls):
'''Remove unused entries'''
# yeat another sqlite hack
- cls_orphans = [x['id'] \
+ cls_orphans = [x['id']
for x in cls.objects.filter(interaction__isnull=True).values("id")]
i = 0
while i < len(cls_orphans):
@@ -671,7 +699,7 @@ class PathEntry(SuccessEntry):
acls = models.ManyToManyField(FileAcl)
detail_type = models.IntegerField(default=0,
- choices=DETAIL_CHOICES)
+ choices=DETAIL_CHOICES)
details = models.TextField(default='')
ENTRY_TYPE = r"Path"