diff options
Diffstat (limited to 'src/lib/Server/Reports')
33 files changed, 0 insertions, 2778 deletions
diff --git a/src/lib/Server/Reports/__init__.py b/src/lib/Server/Reports/__init__.py deleted file mode 100644 index bdf908f4a..000000000 --- a/src/lib/Server/Reports/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ['manage', 'nisauth', 'reports', 'settings', 'backends', 'urls', 'importscript'] diff --git a/src/lib/Server/Reports/backends.py b/src/lib/Server/Reports/backends.py deleted file mode 100644 index 85241932f..000000000 --- a/src/lib/Server/Reports/backends.py +++ /dev/null @@ -1,34 +0,0 @@ -from django.contrib.auth.models import User -from nisauth import * - - -class NISBackend(object): - - def authenticate(self, username=None, password=None): - try: - print("start nis authenticate") - n = nisauth(username, password) - temp_pass = User.objects.make_random_password(100) - nis_user = dict(username=username, - ) - - user_session_obj = dict(email=username, - first_name=None, - last_name=None, - uid=n.uid) - user, created = User.objects.get_or_create(username=username) - - return user - - except NISAUTHError: - e = sys.exc_info()[1] - print(e) - return None - - def get_user(self, user_id): - try: - return User.objects.get(pk=user_id) - except User.DoesNotExist: - e = sys.exc_info()[1] - print(e) - return None diff --git a/src/lib/Server/Reports/importscript.py b/src/lib/Server/Reports/importscript.py deleted file mode 100755 index cbdf019f5..000000000 --- a/src/lib/Server/Reports/importscript.py +++ /dev/null @@ -1,310 +0,0 @@ -#! /usr/bin/env python -""" -Imports statistics.xml and clients.xml files in to database backend for -new statistics engine -""" - -import binascii -import os -import sys -try: - import Bcfg2.Server.Reports.settings -except Exception: - e = sys.exc_info()[1] - sys.stderr.write("Failed to load configuration settings. %s\n" % e) - sys.exit(1) - -project_directory = os.path.dirname(Bcfg2.Server.Reports.settings.__file__) -project_name = os.path.basename(project_directory) -sys.path.append(os.path.join(project_directory, '..')) -project_module = __import__(project_name, '', '', ['']) -sys.path.pop() -# Set DJANGO_SETTINGS_MODULE appropriately. -os.environ['DJANGO_SETTINGS_MODULE'] = '%s.settings' % project_name - -from Bcfg2.Server.Reports.reports.models import * -from lxml.etree import XML, XMLSyntaxError -from getopt import getopt, GetoptError -from datetime import datetime -from time import strptime -from django.db import connection -from Bcfg2.Server.Reports.updatefix import update_database -import logging -import Bcfg2.Logger -import platform - -# Compatibility import -from Bcfg2.Bcfg2Py3k import ConfigParser - - -def build_reason_kwargs(r_ent, encoding, logger): - binary_file = False - sensitive_file = False - if r_ent.get('sensitive') in ['true', 'True']: - sensitive_file = True - rc_diff = '' - elif r_ent.get('current_bfile', False): - binary_file = True - rc_diff = r_ent.get('current_bfile') - if len(rc_diff) > 1024 * 1024: - rc_diff = '' - elif len(rc_diff) == 0: - # No point in flagging binary if we have no data - binary_file = False - elif r_ent.get('current_bdiff', False): - rc_diff = binascii.a2b_base64(r_ent.get('current_bdiff')) - elif r_ent.get('current_diff', False): - rc_diff = r_ent.get('current_diff') - else: - rc_diff = '' - if not binary_file: - try: - rc_diff = rc_diff.decode(encoding) - except: - logger.error("Reason isn't %s encoded, cannot decode it" % encoding) - rc_diff = '' - return dict(owner=r_ent.get('owner', default=""), - current_owner=r_ent.get('current_owner', default=""), - group=r_ent.get('group', default=""), - current_group=r_ent.get('current_group', default=""), - perms=r_ent.get('perms', default=""), - current_perms=r_ent.get('current_perms', default=""), - status=r_ent.get('status', default=""), - current_status=r_ent.get('current_status', default=""), - to=r_ent.get('to', default=""), - current_to=r_ent.get('current_to', default=""), - version=r_ent.get('version', default=""), - current_version=r_ent.get('current_version', default=""), - current_exists=r_ent.get('current_exists', default="True").capitalize() == "True", - current_diff=rc_diff, - is_binary=binary_file, - is_sensitive=sensitive_file) - - -def load_stats(cdata, sdata, encoding, vlevel, logger, quick=False, location=''): - clients = {} - [clients.__setitem__(c.name, c) \ - for c in Client.objects.all()] - - pingability = {} - [pingability.__setitem__(n.get('name'), n.get('pingable', default='N')) \ - for n in cdata.findall('Client')] - - for node in sdata.findall('Node'): - name = node.get('name') - c_inst, created = Client.objects.get_or_create(name=name) - if vlevel > 0: - logger.info("Client %s added to db" % name) - clients[name] = c_inst - try: - pingability[name] - except KeyError: - pingability[name] = 'N' - for statistics in node.findall('Statistics'): - timestamp = datetime(*strptime(statistics.get('time'))[0:6]) - ilist = Interaction.objects.filter(client=c_inst, - timestamp=timestamp) - if ilist: - current_interaction = ilist[0] - if vlevel > 0: - logger.info("Interaction for %s at %s with id %s already exists" % \ - (c_inst.id, timestamp, current_interaction.id)) - continue - else: - newint = Interaction(client=c_inst, - timestamp=timestamp, - state=statistics.get('state', - default="unknown"), - repo_rev_code=statistics.get('revision', - default="unknown"), - client_version=statistics.get('client_version', - default="unknown"), - goodcount=statistics.get('good', - default="0"), - totalcount=statistics.get('total', - default="0"), - server=location) - newint.save() - current_interaction = newint - if vlevel > 0: - logger.info("Interaction for %s at %s with id %s INSERTED in to db" % (c_inst.id, - timestamp, current_interaction.id)) - - counter_fields = {TYPE_CHOICES[0]: 0, - TYPE_CHOICES[1]: 0, - TYPE_CHOICES[2]: 0} - pattern = [('Bad/*', TYPE_CHOICES[0]), - ('Extra/*', TYPE_CHOICES[2]), - ('Modified/*', TYPE_CHOICES[1])] - for (xpath, type) in pattern: - for x in statistics.findall(xpath): - counter_fields[type] = counter_fields[type] + 1 - kargs = build_reason_kwargs(x, encoding, logger) - - try: - rr = None - try: - rr = Reason.objects.filter(**kargs)[0] - except IndexError: - rr = Reason(**kargs) - rr.save() - if vlevel > 0: - logger.info("Created reason: %s" % rr.id) - except Exception: - ex = sys.exc_info()[1] - logger.error("Failed to create reason for %s: %s" % (x.get('name'), ex)) - rr = Reason(current_exists=x.get('current_exists', - default="True").capitalize() == "True") - rr.save() - - entry, created = Entries.objects.get_or_create(\ - name=x.get('name'), kind=x.tag) - - Entries_interactions(entry=entry, reason=rr, - interaction=current_interaction, - type=type[0]).save() - if vlevel > 0: - logger.info("%s interaction created with reason id %s and entry %s" % (xpath, rr.id, entry.id)) - - # Update interaction counters - current_interaction.bad_entries = counter_fields[TYPE_CHOICES[0]] - current_interaction.modified_entries = counter_fields[TYPE_CHOICES[1]] - current_interaction.extra_entries = counter_fields[TYPE_CHOICES[2]] - current_interaction.save() - - mperfs = [] - for times in statistics.findall('OpStamps'): - for metric, value in list(times.items()): - mmatch = [] - if not quick: - mmatch = Performance.objects.filter(metric=metric, value=value) - - if mmatch: - mperf = mmatch[0] - else: - mperf = Performance(metric=metric, value=value) - mperf.save() - mperfs.append(mperf) - current_interaction.performance_items.add(*mperfs) - - for key in list(pingability.keys()): - if key not in clients: - continue - try: - pmatch = Ping.objects.filter(client=clients[key]).order_by('-endtime')[0] - if pmatch.status == pingability[key]: - pmatch.endtime = datetime.now() - pmatch.save() - continue - except IndexError: - pass - Ping(client=clients[key], status=pingability[key], - starttime=datetime.now(), - endtime=datetime.now()).save() - - if vlevel > 1: - logger.info("---------------PINGDATA SYNCED---------------------") - - #Clients are consistent - -if __name__ == '__main__': - from sys import argv - verb = 0 - cpath = "/etc/bcfg2.conf" - clientpath = False - statpath = False - syslog = False - - try: - opts, args = getopt(argv[1:], "hvudc:s:CS", ["help", - "verbose", - "updates", - "debug", - "clients=", - "stats=", - "config=", - "syslog"]) - except GetoptError: - mesg = sys.exc_info()[1] - # print help information and exit: - print("%s\nUsage:\nimportscript.py [-h] [-v] [-u] [-d] [-S] [-C bcfg2 config file] [-c clients-file] [-s statistics-file]" % (mesg)) - raise SystemExit(2) - - for o, a in opts: - if o in ("-h", "--help"): - print("Usage:\nimportscript.py [-h] [-v] -c <clients-file> -s <statistics-file> \n") - print("h : help; this message") - print("v : verbose; print messages on record insertion/skip") - print("u : updates; print status messages as items inserted semi-verbose") - print("d : debug; print most SQL used to manipulate database") - print("C : path to bcfg2.conf config file.") - print("c : clients.xml file") - print("s : statistics.xml file") - print("S : syslog; output to syslog") - raise SystemExit - if o in ["-C", "--config"]: - cpath = a - - if o in ("-v", "--verbose"): - verb = 1 - if o in ("-u", "--updates"): - verb = 2 - if o in ("-d", "--debug"): - verb = 3 - if o in ("-c", "--clients"): - clientspath = a - - if o in ("-s", "--stats"): - statpath = a - if o in ("-S", "--syslog"): - syslog = True - - logger = logging.getLogger('importscript.py') - logging.getLogger().setLevel(logging.INFO) - Bcfg2.Logger.setup_logging('importscript.py', - True, - syslog) - - cf = ConfigParser.ConfigParser() - cf.read([cpath]) - - if not statpath: - try: - statpath = "%s/etc/statistics.xml" % cf.get('server', 'repository') - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - print("Could not read bcfg2.conf; exiting") - raise SystemExit(1) - try: - statsdata = XML(open(statpath).read()) - except (IOError, XMLSyntaxError): - print("StatReports: Failed to parse %s" % (statpath)) - raise SystemExit(1) - - try: - encoding = cf.get('components', 'encoding') - except: - encoding = 'UTF-8' - - if not clientpath: - try: - clientspath = "%s/Metadata/clients.xml" % \ - cf.get('server', 'repository') - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): - print("Could not read bcfg2.conf; exiting") - raise SystemExit(1) - try: - clientsdata = XML(open(clientspath).read()) - except (IOError, XMLSyntaxError): - print("StatReports: Failed to parse %s" % (clientspath)) - raise SystemExit(1) - - q = '-O3' in sys.argv - # Be sure the database is ready for new schema - update_database() - load_stats(clientsdata, - statsdata, - encoding, - verb, - logger, - quick=q, - location=platform.node()) diff --git a/src/lib/Server/Reports/manage.py b/src/lib/Server/Reports/manage.py deleted file mode 100755 index 858bddeca..000000000 --- a/src/lib/Server/Reports/manage.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -from django.core.management import execute_manager -try: - import settings # Assumed to be in the same directory. -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(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) - sys.exit(1) - -if __name__ == "__main__": - execute_manager(settings) diff --git a/src/lib/Server/Reports/nisauth.py b/src/lib/Server/Reports/nisauth.py deleted file mode 100644 index b3e37113b..000000000 --- a/src/lib/Server/Reports/nisauth.py +++ /dev/null @@ -1,44 +0,0 @@ -import crypt -import nis -from Bcfg2.Server.Reports.settings import AUTHORIZED_GROUP - -"""Checks with NIS to see if the current user is in the support group""" - - -class NISAUTHError(Exception): - """NISAUTHError is raised when somehting goes boom.""" - pass - - -class nisauth(object): - group_test = False - samAcctName = None - distinguishedName = None - sAMAccountName = None - telephoneNumber = None - title = None - memberOf = None - department = None # this will be a list - mail = None - extensionAttribute1 = None # badgenumber - badge_no = None - uid = None - - def __init__(self, login, passwd=None): - """get user profile from NIS""" - try: - p = nis.match(login, 'passwd.byname').split(":") - print(p) - except: - raise NISAUTHError('username') - # check user password using crypt and 2 character salt from passwd file - if p[1] == crypt.crypt(passwd, p[1][:2]): - # check to see if user is in valid support groups - # will have to include these groups in a settings file eventually - if not login in nis.match(AUTHORIZED_GROUP, - 'group.byname').split(':')[-1].split(','): - raise NISAUTHError('group') - self.uid = p[2] - print(self.uid) - else: - raise NISAUTHError('password') diff --git a/src/lib/Server/Reports/reports/__init__.py b/src/lib/Server/Reports/reports/__init__.py deleted file mode 100644 index ccdce8943..000000000 --- a/src/lib/Server/Reports/reports/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ['templatetags'] diff --git a/src/lib/Server/Reports/reports/fixtures/initial_version.xml b/src/lib/Server/Reports/reports/fixtures/initial_version.xml deleted file mode 100644 index 919265d48..000000000 --- a/src/lib/Server/Reports/reports/fixtures/initial_version.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version='1.0' encoding='utf-8' ?> -<django-objects version="1.0"> - <object pk="1" model="reports.internaldatabaseversion"> - <field type="IntegerField" name="version">0</field> - <field type="DateTimeField" name="updated">2008-08-05 11:03:50</field> - </object> - <object pk="2" model="reports.internaldatabaseversion"> - <field type="IntegerField" name="version">1</field> - <field type="DateTimeField" name="updated">2008-08-05 11:04:10</field> - </object> - <object pk="3" model="reports.internaldatabaseversion"> - <field type="IntegerField" name="version">2</field> - <field type="DateTimeField" name="updated">2008-08-05 13:37:19</field> - </object> - <object pk="4" model="reports.internaldatabaseversion"> - <field type='IntegerField' name='version'>3</field> - <field type='DateTimeField' name='updated'>2008-08-11 08:44:36</field> - </object> - <object pk="5" model="reports.internaldatabaseversion"> - <field type='IntegerField' name='version'>10</field> - <field type='DateTimeField' name='updated'>2008-08-22 11:28:50</field> - </object> - <object pk="5" model="reports.internaldatabaseversion"> - <field type='IntegerField' name='version'>11</field> - <field type='DateTimeField' name='updated'>2009-01-13 12:26:10</field> - </object> - <object pk="6" model="reports.internaldatabaseversion"> - <field type='IntegerField' name='version'>16</field> - <field type='DateTimeField' name='updated'>2010-06-01 12:26:10</field> - </object> - <object pk="7" model="reports.internaldatabaseversion"> - <field type='IntegerField' name='version'>17</field> - <field type='DateTimeField' name='updated'>2010-07-02 00:00:00</field> - </object> - <object pk="8" model="reports.internaldatabaseversion"> - <field type='IntegerField' name='version'>18</field> - <field type='DateTimeField' name='updated'>2011-06-30 00:00:00</field> - </object> -</django-objects> diff --git a/src/lib/Server/Reports/reports/models.py b/src/lib/Server/Reports/reports/models.py deleted file mode 100644 index 870239641..000000000 --- a/src/lib/Server/Reports/reports/models.py +++ /dev/null @@ -1,343 +0,0 @@ -"""Django models for Bcfg2 reports.""" -from django.db import models -from django.db import connection, transaction -from django.db.models import Q -from datetime import datetime, timedelta -from time import strptime - -KIND_CHOICES = ( - #These are the kinds of config elements - ('Package', 'Package'), - ('Path', 'directory'), - ('Path', 'file'), - ('Path', 'permissions'), - ('Path', 'symlink'), - ('Service', 'Service'), -) -PING_CHOICES = ( - #These are possible ping states - ('Up (Y)', 'Y'), - ('Down (N)', 'N') -) -TYPE_BAD = 1 -TYPE_MODIFIED = 2 -TYPE_EXTRA = 3 - -TYPE_CHOICES = ( - (TYPE_BAD, 'Bad'), - (TYPE_MODIFIED, 'Modified'), - (TYPE_EXTRA, 'Extra'), -) - - -def convert_entry_type_to_id(type_name): - """Convert a entry type to its entry id""" - for e_id, e_name in TYPE_CHOICES: - if e_name.lower() == type_name.lower(): - return e_id - return -1 - - -class ClientManager(models.Manager): - """Extended client manager functions.""" - def active(self, timestamp=None): - """returns a set of clients that have been created and have not - yet been expired as of optional timestmamp argument. Timestamp - should be a datetime object.""" - - if timestamp == None: - timestamp = datetime.now() - elif not isinstance(timestamp, datetime): - raise ValueError('Expected a datetime object') - else: - try: - timestamp = datetime(*strptime(timestamp, - "%Y-%m-%d %H:%M:%S")[0:6]) - except ValueError: - return self.none() - - return self.filter(Q(expiration__gt=timestamp) | Q(expiration__isnull=True), - creation__lt=timestamp) - - -class Client(models.Model): - """Object representing every client we have seen stats for.""" - creation = models.DateTimeField(auto_now_add=True) - name = models.CharField(max_length=128,) - current_interaction = models.ForeignKey('Interaction', - null=True, blank=True, - related_name="parent_client") - expiration = models.DateTimeField(blank=True, null=True) - - def __str__(self): - return self.name - - objects = ClientManager() - - class Admin: - pass - - -class Ping(models.Model): - """Represents a ping of a client (sparsely).""" - client = models.ForeignKey(Client, related_name="pings") - starttime = models.DateTimeField() - endtime = models.DateTimeField() - status = models.CharField(max_length=4, choices=PING_CHOICES) # up/down - - class Meta: - get_latest_by = 'endtime' - - -class InteractiveManager(models.Manager): - """Manages interactions objects.""" - - def recent_interactions_dict(self, maxdate=None, active_only=True): - """ - Return the most recent interactions for clients as of a date. - - This method uses aggregated queries to return a ValuesQueryDict object. - Faster then raw sql since this is executed as a single query. - """ - - return list(self.values('client').annotate(max_timestamp=Max('timestamp')).values()) - - def interaction_per_client(self, maxdate=None, active_only=True): - """ - Returns the most recent interactions for clients as of a date - - Arguments: - maxdate -- datetime object. Most recent date to pull. (dafault None) - active_only -- Include only active clients (default True) - - """ - - if maxdate and not isinstance(maxdate, datetime): - raise ValueError('Expected a datetime object') - return self.filter(id__in=self.get_interaction_per_client_ids(maxdate, active_only)) - - def get_interaction_per_client_ids(self, maxdate=None, active_only=True): - """ - Returns the ids of most recent interactions for clients as of a date. - - Arguments: - maxdate -- datetime object. Most recent date to pull. (dafault None) - active_only -- Include only active clients (default True) - - """ - from django.db import connection - cursor = connection.cursor() - cfilter = "expiration is null" - - sql = 'select reports_interaction.id, x.client_id from (select client_id, MAX(timestamp) ' + \ - 'as timer from reports_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, reports_interaction where ' + \ - 'reports_interaction.client_id = x.client_id AND reports_interaction.timestamp = x.timer' - if active_only: - sql = sql + " and x.client_id in (select id from reports_client where %s)" % \ - cfilter - try: - cursor.execute(sql) - return [item[0] for item in cursor.fetchall()] - except: - '''FIXME - really need some error hadling''' - pass - return [] - - -class Interaction(models.Model): - """Models each reconfiguration operation interaction between client and server.""" - client = models.ForeignKey(Client, related_name="interactions",) - timestamp = models.DateTimeField() # 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 - client_version = models.CharField(max_length=32) # Client Version - goodcount = models.IntegerField() # of good config-items - totalcount = models.IntegerField() # of total config-items - server = models.CharField(max_length=256) # Name of the server used for the interaction - bad_entries = models.IntegerField(default=-1) - modified_entries = models.IntegerField(default=-1) - extra_entries = models.IntegerField(default=-1) - - def __str__(self): - return "With " + self.client.name + " @ " + self.timestamp.isoformat() - - def percentgood(self): - if not self.totalcount == 0: - return (self.goodcount / float(self.totalcount)) * 100 - else: - return 0 - - def percentbad(self): - if not self.totalcount == 0: - return ((self.totalcount - self.goodcount) / (float(self.totalcount))) * 100 - else: - return 0 - - def isclean(self): - if (self.bad_entry_count() == 0 and self.goodcount == self.totalcount): - return True - else: - return False - - def isstale(self): - if (self == self.client.current_interaction): # Is Mostrecent - if(datetime.now() - self.timestamp > timedelta(hours=25)): - return True - else: - return False - else: - #Search for subsequent Interaction for this client - #Check if it happened more than 25 hrs ago. - if (self.client.interactions.filter(timestamp__gt=self.timestamp) - .order_by('timestamp')[0].timestamp - - self.timestamp > timedelta(hours=25)): - return True - else: - return False - - def save(self): - super(Interaction, self).save() # call the real save... - self.client.current_interaction = self.client.interactions.latest() - self.client.save() # save again post update - - def delete(self): - '''Override the default delete. Allows us to remove Performance items''' - pitems = list(self.performance_items.all()) - super(Interaction, self).delete() - for perf in pitems: - if perf.interaction.count() == 0: - perf.delete() - - def badcount(self): - return self.totalcount - self.goodcount - - def bad(self): - return Entries_interactions.objects.select_related().filter(interaction=self, type=TYPE_BAD) - - def bad_entry_count(self): - """Number of bad entries. Store the count in the interation field to save db queries.""" - if self.bad_entries < 0: - self.bad_entries = Entries_interactions.objects.filter(interaction=self, type=TYPE_BAD).count() - self.save() - return self.bad_entries - - def modified(self): - return Entries_interactions.objects.select_related().filter(interaction=self, type=TYPE_MODIFIED) - - def modified_entry_count(self): - """Number of modified entries. Store the count in the interation field to save db queries.""" - if self.modified_entries < 0: - self.modified_entries = Entries_interactions.objects.filter(interaction=self, type=TYPE_MODIFIED).count() - self.save() - return self.modified_entries - - def extra(self): - return Entries_interactions.objects.select_related().filter(interaction=self, type=TYPE_EXTRA) - - def extra_entry_count(self): - """Number of extra entries. Store the count in the interation field to save db queries.""" - if self.extra_entries < 0: - self.extra_entries = Entries_interactions.objects.filter(interaction=self, type=TYPE_EXTRA).count() - self.save() - return self.extra_entries - - objects = InteractiveManager() - - class Admin: - list_display = ('client', 'timestamp', 'state') - list_filter = ['client', 'timestamp'] - pass - - class Meta: - get_latest_by = 'timestamp' - ordering = ['-timestamp'] - unique_together = ("client", "timestamp") - - -class Reason(models.Model): - """reason why modified or bad entry did not verify, or changed.""" - owner = models.TextField(max_length=128, blank=True) - current_owner = models.TextField(max_length=128, blank=True) - group = models.TextField(max_length=128, blank=True) - current_group = models.TextField(max_length=128, blank=True) - perms = models.TextField(max_length=4, blank=True) # txt fixes typing issue - current_perms = models.TextField(max_length=4, blank=True) - status = models.TextField(max_length=3, blank=True) # on/off/(None) - current_status = models.TextField(max_length=1, blank=True) # on/off/(None) - to = models.TextField(max_length=256, blank=True) - current_to = models.TextField(max_length=256, blank=True) - version = models.TextField(max_length=128, blank=True) - current_version = models.TextField(max_length=128, blank=True) - current_exists = models.BooleanField() # False means its missing. Default True - current_diff = models.TextField(max_length=1280, blank=True) - is_binary = models.BooleanField(default=False) - is_sensitive = models.BooleanField(default=False) - - def _str_(self): - return "Reason" - - @staticmethod - @transaction.commit_on_success - def prune_orphans(): - '''Prune oprhaned rows... no good way to use the ORM''' - cursor = connection.cursor() - cursor.execute('delete from reports_reason where not exists (select rei.id from reports_entries_interactions rei where rei.reason_id = reports_reason.id)') - transaction.set_dirty() - - -class Entries(models.Model): - """Contains all the entries feed by the client.""" - name = models.CharField(max_length=128, db_index=True) - kind = models.CharField(max_length=16, choices=KIND_CHOICES, db_index=True) - - def __str__(self): - return self.name - - @staticmethod - @transaction.commit_on_success - def prune_orphans(): - '''Prune oprhaned rows... no good way to use the ORM''' - cursor = connection.cursor() - cursor.execute('delete from reports_entries where not exists (select rei.id from reports_entries_interactions rei where rei.entry_id = reports_entries.id)') - transaction.set_dirty() - - -class Entries_interactions(models.Model): - """Define the relation between the reason, the interaction and the entry.""" - entry = models.ForeignKey(Entries) - reason = models.ForeignKey(Reason) - interaction = models.ForeignKey(Interaction) - type = models.IntegerField(choices=TYPE_CHOICES) - - -class Performance(models.Model): - """Object representing performance data for any interaction.""" - interaction = models.ManyToManyField(Interaction, related_name="performance_items") - metric = models.CharField(max_length=128) - value = models.DecimalField(max_digits=32, decimal_places=16) - - def __str__(self): - return self.metric - - @staticmethod - @transaction.commit_on_success - def prune_orphans(): - '''Prune oprhaned rows... no good way to use the ORM''' - cursor = connection.cursor() - cursor.execute('delete from reports_performance where not exists (select ri.id from reports_performance_interaction ri where ri.performance_id = reports_performance.id)') - transaction.set_dirty() - - -class InternalDatabaseVersion(models.Model): - """Object that tell us to witch version is the database.""" - version = models.IntegerField() - updated = models.DateTimeField(auto_now_add=True) - - def __str__(self): - return "version %d updated the %s" % (self.version, self.updated.isoformat()) diff --git a/src/lib/Server/Reports/reports/sql/client.sql b/src/lib/Server/Reports/reports/sql/client.sql deleted file mode 100644 index 8c63754c9..000000000 --- a/src/lib/Server/Reports/reports/sql/client.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE VIEW reports_current_interactions AS SELECT x.client_id AS client_id, reports_interaction.id AS interaction_id FROM (select client_id, MAX(timestamp) as timer FROM reports_interaction GROUP BY client_id) x, reports_interaction WHERE reports_interaction.client_id = x.client_id AND reports_interaction.timestamp = x.timer; - -create index reports_interaction_client_id on reports_interaction (client_id); -create index reports_extra_interactions_client_id on reports_extra_interactions(interaction_id); -create index reports_modified_interactions_client_id on reports_modified_interactions(interaction_id); -create index reports_client_current_interaction_id on reports_client (current_interaction_id); -create index reports_performance_interaction_performance_id on reports_performance_interaction (performance_id); -create index reports_interaction_timestamp on reports_interaction (timestamp); -create index reports_performance_interation_interaction_id on reports_performance_interaction (interaction_id);
\ No newline at end of file diff --git a/src/lib/Server/Reports/reports/templates/404.html b/src/lib/Server/Reports/reports/templates/404.html deleted file mode 100644 index 168bd9fec..000000000 --- a/src/lib/Server/Reports/reports/templates/404.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends 'base.html' %} -{% block title %}Bcfg2 - Page not found{% endblock %} -{% block fullcontent %} -<h2>Page not found</h2> -<p> -The page or object requested could not be found. -</p> -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/base-timeview.html b/src/lib/Server/Reports/reports/templates/base-timeview.html deleted file mode 100644 index 842de36f0..000000000 --- a/src/lib/Server/Reports/reports/templates/base-timeview.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base.html" %} - -{% block timepiece %} -<script type="text/javascript"> -function showCalendar() { - var cal = new CalendarPopup("calendar_div"); - cal.showYearNavigation(); - cal.select(document.forms['cal_form'].cal_date,'cal_link', - 'yyyy/MM/dd' {% if timestamp %}, '{{ timestamp|date:"Y/m/d" }}'{% endif %} ); - return false; -} -function bcfg2_check_date() { - var new_date = document.getElementById('cal_date').value; - if(new_date) { - document.cal_form.submit(); - } -} -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 %} - <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'><input id='cal_date' name='cal_date' type='hidden' value=''/></form> -{% endspaceless %} -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/base.html b/src/lib/Server/Reports/reports/templates/base.html deleted file mode 100644 index f541c0d2b..000000000 --- a/src/lib/Server/Reports/reports/templates/base.html +++ /dev/null @@ -1,95 +0,0 @@ -{% load bcfg2_tags %} - -<?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"> -<head> -<title>{% block title %}Bcfg2 Reporting System{% endblock %}</title> - -<meta http-equiv="Content-type" content="text/html; charset=utf-8" /> -<meta http-equiv="Content-language" content="en" /> -<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> -<meta name="robots" content="noindex, nofollow" /> -<meta http-equiv="cache-control" content="no-cache" /> - -<link rel="stylesheet" type="text/css" href="{% to_media_url bcfg2_base.css %}" media="all" /> -<script type="text/javascript" src="{% to_media_url bcfg2.js %}"></script> -<script type="text/javascript" src="{% to_media_url date.js %}"></script> -<script type="text/javascript" src="{% to_media_url AnchorPosition.js %}"></script> -<script type="text/javascript" src="{% to_media_url CalendarPopup.js %}"></script> -<script type="text/javascript" src="{% to_media_url PopupWindow.js %}"></script> -{% block extra_header_info %}{% endblock %} - -</head> -<body onload="{% block body_onload %}{% endblock %}"> - - <div id="header"> - <a href="http://bcfg2.org"><img src='{% to_media_url bcfg2_logo.png %}' - height='115' width='300' alt='Bcfg2' style='float:left; height: 115px' /></a> - </div> - -<div id="document"> - <div id="content"><div id="contentwrapper"> - {% block fullcontent %} - <div class='page_name'> - <h1>{% block pagebanner %}Page Banner{% endblock %}</h1> - <div id="timepiece">{% block timepiece %}Rendered at {% now "Y-m-d H:i" %}{% endblock %}</div> - </div> - <div class='detail_wrapper'> - {% block content %}{% endblock %} - </div> - {% endblock %} - </div></div><!-- content --> - <div id="sidemenucontainer"><div id="sidemenu"> - {% block sidemenu %} - <ul class='menu-level1'> - <li>Overview</li> - </ul> - <ul class='menu-level2'> - <li><a href="{% url reports_summary %}">Summary</a></li> - <li><a href="{% url reports_history %}">Recent Interactions</a></li> - <li><a href="{% url reports_timing %}">Timing</a></li> - </ul> - <ul class='menu-level1'> - <li>Clients</li> - </ul> - <ul class='menu-level2'> - <li><a href="{% url reports_grid_view %}">Grid View</a></li> - <li><a href="{% url reports_detailed_list %}">Detailed List</a></li> - <li><a href="{% url reports_client_manage %}">Manage</a></li> - </ul> - <ul class='menu-level1'> - <li>Entries Configured</li> - </ul> - <ul class='menu-level2'> - <li><a href="{% url reports_item_list "bad" %}">Bad</a></li> - <li><a href="{% url reports_item_list "modified" %}">Modified</a></li> - <li><a href="{% url reports_item_list "extra" %}">Extra</a></li> - </ul> -{% comment %} - TODO - <ul class='menu-level1'> - <li>Entry Types</li> - </ul> - <ul class='menu-level2'> - <li><a href="#">Action</a></li> - <li><a href="#">Package</a></li> - <li><a href="#">Path</a></li> - <li><a href="#">Service</a></li> - </ul> -{% endcomment %} - <ul class='menu-level1'> - <li><a href="http://bcfg2.org">Homepage</a></li> - <li><a href="http://docs.bcfg2.org">Documentation</a></li> - </ul> - {% endblock %} - </div></div><!-- sidemenu --> - <div style='clear:both'></div> -</div><!-- document --> - <div id="footer"> - <span>Bcfg2 Version 1.2.2</span> - </div> - -<div id="calendar_div" style='position:absolute; visibility:hidden; background-color:white; layer-background-color:white;'></div> -</body> -</html> diff --git a/src/lib/Server/Reports/reports/templates/clients/detail.html b/src/lib/Server/Reports/reports/templates/clients/detail.html deleted file mode 100644 index dd4295f21..000000000 --- a/src/lib/Server/Reports/reports/templates/clients/detail.html +++ /dev/null @@ -1,127 +0,0 @@ -{% extends "base.html" %} -{% load bcfg2_tags %} - -{% block title %}Bcfg2 - Client {{client.name}}{% endblock %} - -{% block extra_header_info %} -<style type="text/css"> -.node_data { - border: 1px solid #98DBCC; - margin: 10px; - padding-left: 18px; -} -.node_data td { - padding: 1px 20px 1px 2px; -} -span.history_links { - font-size: 90%; - margin-left: 50px; -} -span.history_links a { - font-size: 90%; -} -</style> -{% endblock %} - -{% block body_onload %}javascript:clientdetailload(){% endblock %} - -{% block pagebanner %}Client Details{% endblock %} - -{% block content %} - <div class='detail_header'> - <h2>{{client.name}}</h2> - <a href='{% url reports_client_manage %}#{{ client.name }}'>[manage]</a> - <span class='history_links'><a href="{% url reports_client_history client.name %}">View History</a> | Jump to - <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}}</option> - {% endfor %} - </select></span> - </div> - - {% if interaction.isstale %} - <div class="warningbox"> - This node did not run within the last 24 hours — it may be out of date. - </div> - {% endif %} - <table class='node_data'> - <tr><td>Timestamp</td><td>{{interaction.timestamp}}</td></tr> - {% if interaction.server %} - <tr><td>Served by</td><td>{{interaction.server}}</td></tr> - {% endif %} - {% if interaction.repo_rev_code %} - <tr><td>Revision</td><td>{{interaction.repo_rev_code}}</td></tr> - {% endif %} - <tr><td>State</td><td class='{{interaction.state}}-lineitem'>{{interaction.state|capfirst}}</td></tr> - <tr><td>Managed entries</td><td>{{interaction.totalcount}}</td></tr> - {% if not interaction.isclean %} - <tr><td>Deviation</td><td>{{interaction.percentbad|floatformat:"3"}}%</td></tr> - {% endif %} - </table> - - {% if interaction.bad_entry_count %} - <div class='entry_list'> - <div class='entry_list_head dirty-lineitem' onclick='javascript:toggleMe("bad_table");'> - <h3>Bad Entries — {{ interaction.bad_entry_count }}</h3> - <div class='entry_expand_tab' id='plusminus_bad_table'>[+]</div> - </div> - <table id='bad_table' class='entry_list'> - {% for e in interaction.bad|sortwell %} - <tr class='{% cycle listview,listview_alt %}'> - <td class='entry_list_type'>{{e.entry.kind}}:</td> - <td><a href="{% url reports_item "bad",e.id %}"> - {{e.entry.name}}</a></td> - </tr> - {% endfor %} - </table> - </div> - {% endif %} - - {% if interaction.modified_entry_count %} - <div class='entry_list'> - <div class='entry_list_head modified-lineitem' onclick='javascript:toggleMe("modified_table");'> - <h3>Modified Entries — {{ interaction.modified_entry_count }}</h3> - <div class='entry_expand_tab' id='plusminus_modified_table'>[+]</div> - </div> - <table id='modified_table' class='entry_list'> - {% for e in interaction.modified|sortwell %} - <tr class='{% cycle listview,listview_alt %}'> - <td class='entry_list_type'>{{e.entry.kind}}:</td> - <td><a href="{% url reports_item "modified",e.id %}"> - {{e.entry.name}}</a></td> - </tr> - {% endfor %} - </table> - </div> - {% endif %} - - {% if interaction.extra_entry_count %} - <div class='entry_list'> - <div class='entry_list_head extra-lineitem' onclick='javascript:toggleMe("extra_table");'> - <h3>Extra Entries — {{ interaction.extra_entry_count }}</h3> - <div class='entry_expand_tab' id='plusminus_extra_table'>[+]</div> - </div> - <table id='extra_table' class='entry_list'> - {% for e in interaction.extra|sortwell %} - <tr class='{% cycle listview,listview_alt %}'> - <td class='entry_list_type'>{{e.entry.kind}}:</td> - <td><a href="{% url reports_item "extra",e.id %}">{{e.entry.name}}</a></td> - </tr> - {% endfor %} - </table> - </div> - {% endif %} - - {% if entry_list %} - <div class="entry_list recent_history_wrapper"> - <div class="entry_list_head" style="border-bottom: 2px solid #98DBCC;"> - <h4 style="display: inline"><a href="{% url reports_client_history client.name %}">Recent Interactions</a></h4> - </div> - <div class='recent_history_box'> - {% include "widgets/interaction_list.inc" %} - <div style='padding-left: 5px'><a href="{% url reports_client_history client.name %}">more...</a></div> - </div> - </div> - {% endif %} -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/clients/detailed-list.html b/src/lib/Server/Reports/reports/templates/clients/detailed-list.html deleted file mode 100644 index 0c1fae8d5..000000000 --- a/src/lib/Server/Reports/reports/templates/clients/detailed-list.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends "base-timeview.html" %} -{% load bcfg2_tags %} - -{% block title %}Bcfg2 - Detailed Client Listing{% endblock %} -{% block pagebanner %}Clients - Detailed View{% endblock %} - -{% block content %} -<div class='client_list_box'> -{% if entry_list %} - {% filter_navigator %} - <table cellpadding="3"> - <tr id='table_list_header' class='listview'> - <td class='left_column'>Node</td> - <td class='right_column' style='width:75px'>State</td> - <td class='right_column_narrow'>Good</td> - <td class='right_column_narrow'>Bad</td> - <td class='right_column_narrow'>Modified</td> - <td class='right_column_narrow'>Extra</td> - <td class='right_column'>Last Run</td> - <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 Bcfg2.Server.Reports.reports.views.client_detail 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 %}' - {% ifequal entry.state 'dirty' %}class='dirty-lineitem'{% endifequal %}>{{ entry.state }}</a></td> - <td class='right_column_narrow'>{{ entry.goodcount }}</td> - <td class='right_column_narrow'>{{ entry.bad_entry_count }}</td> - <td class='right_column_narrow'>{{ entry.modified_entry_count }}</td> - <td class='right_column_narrow'>{{ entry.extra_entry_count }}</td> - <td class='right_column'><span {% if entry.timestamp|isstale:entry_max %}class='dirty-lineitem'{% endif %}>{{ entry.timestamp|date:"Y-m-d\&\n\b\s\p\;H:i"|safe }}</span></td> - <td class='right_column_wide'> - {% if entry.server %} - <a href='{% add_url_filter server=entry.server %}'>{{ entry.server }}</a> - {% else %} - - {% endif %} - </td> - </tr> - {% endfor %} - </table> -{% else %} - <p>No client records are available.</p> -{% endif %} -</div> -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/clients/history.html b/src/lib/Server/Reports/reports/templates/clients/history.html deleted file mode 100644 index 01d4ec2f4..000000000 --- a/src/lib/Server/Reports/reports/templates/clients/history.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "base.html" %} -{% load bcfg2_tags %} - -{% block title %}Bcfg2 - Interaction History{% endblock %} -{% block pagebanner %}Interaction history{% if client %} for {{ client.name }}{% endif %}{% endblock %} - -{% block extra_header_info %} -{% endblock %} - -{% block content %} -<div class='client_list_box'> -{% if entry_list %} - {% filter_navigator %} - {% include "widgets/interaction_list.inc" %} -{% else %} - <p>No client records are available.</p> -{% endif %} -</div> -{% page_navigator %} -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/clients/index.html b/src/lib/Server/Reports/reports/templates/clients/index.html deleted file mode 100644 index e0c0d2d7a..000000000 --- a/src/lib/Server/Reports/reports/templates/clients/index.html +++ /dev/null @@ -1,34 +0,0 @@ -{% extends "base-timeview.html" %} - -{% block extra_header_info %} -{% endblock%} - -{% block title %}Bcfg2 - Client Grid View{% endblock %} - -{% block pagebanner %}Clients - Grid View{% endblock %} - -{% block content %} - -{% if inter_list %} - <table class='grid-view' align='center'> - {% for inter in inter_list %} - {% if forloop.first %}<tr>{% endif %} - <td class="{{inter.state}}-lineitem"> - <a href="{% spaceless %}{% if not timestamp %} - {% url reports_client_detail inter.client.name %} - {% else %} - {% url reports_client_detail_pk inter.client.name,inter.id %} - {% endif %} - {% endspaceless %}">{{ inter.client.name }}</a> - </td> - {% if forloop.last %} - </tr> - {% else %} - {% if forloop.counter|divisibleby:"4" %}</tr><tr>{% endif %} - {% endif %} - {% endfor %} - </table> -{% else %} - <p>No client records are available.</p> -{% endif %} -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/clients/manage.html b/src/lib/Server/Reports/reports/templates/clients/manage.html deleted file mode 100644 index 5725ae577..000000000 --- a/src/lib/Server/Reports/reports/templates/clients/manage.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "base.html" %} - -{% block extra_header_info %} -{% endblock%} - -{% block title %}Bcfg2 - Manage Clients{% endblock %} - -{% block pagebanner %}Clients - Manage{% endblock %} - -{% block content %} -<div class='client_list_box'> - {% if message %} - <div class="warningbox">{{ message }}</div> - {% endif %} -{% if clients %} - <table cellpadding="3"> - <tr id='table_list_header' class='listview'> - <td class='left_column'>Node</td> - <td class='right_column'>Expiration</td> - <td class='right_column_narrow'>Manage</td> - </tr> - {% for client in clients %} - <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> - <a href="{% url reports_client_detail client.name %}">{{ client.name }}</a></td> - <td>{% firstof client.expiration 'Active' %}</td> - <td> - <form method="post" action="{% url reports_client_manage %}"> - <div> {# here for no reason other then to validate #} - <input type="hidden" name="client_name" value="{{ client.name }}" /> - <input type="hidden" name="client_action" value="{% if client.expiration %}unexpire{% else %}expire{% endif %}" /> - <input type="submit" value="{% if client.expiration %}Activate{% else %}Expire Now{% endif %}" /> - </div> - </form> - </td> - </tr> - {% endfor %} - </table> - </div> -{% else %} - <p>No client records are available.</p> -{% endif %} -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/config_items/item.html b/src/lib/Server/Reports/reports/templates/config_items/item.html deleted file mode 100644 index cc99ef503..000000000 --- a/src/lib/Server/Reports/reports/templates/config_items/item.html +++ /dev/null @@ -1,115 +0,0 @@ -{% extends "base.html" %} -{% load syntax_coloring %} - - -{% block title %}Bcfg2 - Element Details{% endblock %} - - -{% block extra_header_info %} -<style type="text/css"> -#table_list_header { - font-size: 100%; -} -table.entry_list { - width: auto; -} -div.information_wrapper { - margin: 15px; -} -div.diff_wrapper { - overflow: auto; -} -div.entry_list h3 { - font-size: 90%; - padding: 5px; -} -</style> -{% endblock%} - -{% block pagebanner %}Element Details{% endblock %} - -{% block content %} - <div class='detail_header'> - <h3>{{mod_or_bad|capfirst}} {{item.entry.kind}}: {{item.entry.name}}</h3> - </div> - - <div class="information_wrapper"> - - {% if isextra %} - <p>This item exists on the host but is not defined in the configuration.</p> - {% endif %} - - {% if not item.reason.current_exists %} - <div class="warning">This item does not currently exist on the host but is specified to exist in the configuration.</div> - {% endif %} - - {% if item.reason.current_owner or item.reason.current_group or item.reason.current_perms or item.reason.current_status or item.reason.current_status or item.reason.current_to or item.reason.current_version %} - <table class='entry_list'> - <tr id='table_list_header'> - <td style='text-align: right;'>Problem Type</td><td>Expected</td><td style='border-bottom: 1px solid #98DBCC;'>Found</td></tr> - {% if item.reason.current_owner %} - <tr><td style='text-align: right'><b>Owner</b></td><td>{{item.reason.owner}}</td> - <td>{{item.reason.current_owner}}</td></tr> - {% endif %} - {% if item.reason.current_group %} - <tr><td style='text-align: right'><b>Group</b></td><td>{{item.reason.group}}</td> - <td>{{item.reason.current_group}}</td></tr> - {% endif %} - {% if item.reason.current_perms %} - <tr><td style='text-align: right'><b>Permissions</b></td><td>{{item.reason.perms}}</td> - <td>{{item.reason.current_perms}}</td></tr> - {% endif %} - {% if item.reason.current_status %} - <tr><td style='text-align: right'><b>Status</b></td><td>{{item.reason.status}}</td> - <td>{{item.reason.current_status}}</td></tr> - {% endif %} - {% if item.reason.current_to %} - <tr><td style='text-align: right'><b>Symlink Target</b></td><td>{{item.reason.to}}</td> - <td>{{item.reason.current_to}}</td></tr> - {% endif %} - {% if item.reason.current_version %} - <tr><td style='text-align: right'><b>Package Version</b></td><td>{{item.reason.version|cut:"("|cut:")"}}</td> - <td>{{item.reason.current_version|cut:"("|cut:")"}}</td></tr> - {% endif %} - </table> - {% endif %} - - {% if item.reason.current_diff or item.reason.is_sensitive %} - <div class='entry_list'> - <div class='entry_list_head'> - {% if item.reason.is_sensitive %} - <h3>File contents unavailable, as they might contain sensitive data.</h3> - {% else %} - <h3>Incorrect file contents</h3> - {% endif %} - </div> - {% if not item.reason.is_sensitive %} - <div class='diff_wrapper'> - {{ item.reason.current_diff|syntaxhilight }} - </div> - {% endif %} - </div> - {% endif %} - - - <div class='entry_list'> - <div class='entry_list_head'> - <h3>Occurences on {{ timestamp|date:"Y-m-d" }}</h3> - </div> - {% if associated_list %} - <table class="entry_list" cellpadding="3"> - {% for inter in associated_list %} - <tr><td><a href="{% url reports_client_detail inter.client.name %}" - >{{inter.client.name}}</a></td> - <td><a href="{% url reports_client_detail_pk hostname=inter.client.name,pk=inter.id %}" - >{{inter.timestamp}}</a></td> - </tr> - {% endfor %} - </table> - {% else %} - <p>Missing client list</p> - {% endif %} - </div> - - </div><!-- information_wrapper --> -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/config_items/listing.html b/src/lib/Server/Reports/reports/templates/config_items/listing.html deleted file mode 100644 index 9b1026a08..000000000 --- a/src/lib/Server/Reports/reports/templates/config_items/listing.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "base-timeview.html" %} -{% load bcfg2_tags %} - -{% block title %}Bcfg2 - Element Listing{% endblock %} - -{% block extra_header_info %} -{% endblock%} - -{% block pagebanner %}{{mod_or_bad|capfirst}} Element Listing{% endblock %} - -{% block content %} -{% if item_list_dict %} - {% for kind, entries in item_list_dict.items %} - - <div class='entry_list'> - <div class='entry_list_head element_list_head' onclick='javascript:toggleMe("table_{{ kind }}");'> - <h3>{{ kind }} — {{ entries|length }}</h3> - <div class='entry_expand_tab' id='plusminus_table_{{ kind }}'>[–]</div> - </div> - - <table id='table_{{ kind }}' class='entry_list'> - {% for e in entries %} - <tr class='{% cycle listview,listview_alt %}'> - <td><a href="{% url reports_item type=mod_or_bad,pk=e.id %}">{{e.entry.name}}</a></td> - </tr> - {% endfor %} - </table> - </div> - {% endfor %} -{% else %} - <p>There are currently no inconsistent configuration entries.</p> -{% endif %} -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/displays/summary.html b/src/lib/Server/Reports/reports/templates/displays/summary.html deleted file mode 100644 index b9847cf96..000000000 --- a/src/lib/Server/Reports/reports/templates/displays/summary.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "base-timeview.html" %} -{% load bcfg2_tags %} - -{% block title %}Bcfg2 - Client Summary{% endblock %} -{% block pagebanner %}Clients - Summary{% endblock %} - -{% block body_onload %}javascript:hide_table_array(hide_tables){% endblock %} - -{% block extra_header_info %} -<script type="text/javascript"> -var hide_tables = new Array({{ summary_data|length }}); -{% for summary in summary_data %} -hide_tables[{{ forloop.counter0 }}] = "table_{{ summary.name }}"; -{% endfor %} -</script> -{% endblock%} - -{% block content %} - <div class='detail_header'> - <h2>{{ node_count }} nodes reporting in</h2> - </div> -{% if summary_data %} - {% for summary in summary_data %} - <div class='entry_list'> - <div class='entry_list_head element_list_head' onclick='javascript:toggleMe("table_{{ summary.name }}");'> - <h3>{{ summary.nodes|length }} {{ summary.label }}</h3> - <div class='entry_expand_tab' id='plusminus_table_{{ summary.name }}'>[+]</div> - </div> - - <table id='table_{{ summary.name }}' class='entry_list'> - {% for node in summary.nodes|sort_interactions_by_name %} - <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 %} - </table> - </div> - {% endfor %} -{% else %} - <p>No data to report on</p> -{% endif %} -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/displays/timing.html b/src/lib/Server/Reports/reports/templates/displays/timing.html deleted file mode 100644 index 47accb2cb..000000000 --- a/src/lib/Server/Reports/reports/templates/displays/timing.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "base-timeview.html" %} -{% load bcfg2_tags %} - -{% block title %}Bcfg2 - Performance Metrics{% endblock %} -{% block pagebanner %}Performance Metrics{% endblock %} - - -{% block extra_header_info %} -{% endblock%} - -{% block content %} -<div class='client_list_box'> - {% if metrics %} - <table cellpadding="3"> - <tr id='table_list_header' class='listview'> - <td>Name</td> - <td>Parse</td> - <td>Probe</td> - <td>Inventory</td> - <td>Install</td> - <td>Config</td> - <td>Total</td> - </tr> - {% for metric in metrics|dictsort:"name" %} - <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 %} - <td>{{ mitem }}</td> - {% endfor %} - </tr> - {% endfor %} - </table> - {% else %} - <p>No metric data available</p> - {% endif %} -</div> -{% endblock %} diff --git a/src/lib/Server/Reports/reports/templates/widgets/filter_bar.html b/src/lib/Server/Reports/reports/templates/widgets/filter_bar.html deleted file mode 100644 index 6b57baf6a..000000000 --- a/src/lib/Server/Reports/reports/templates/widgets/filter_bar.html +++ /dev/null @@ -1,13 +0,0 @@ -{% spaceless %} -{% if filters %} -{% for filter, filter_url in filters %} - {% if forloop.first %} - <div class="filter_bar">Active filters (click to remove): - {% endif %} - <a href='{{ filter_url }}'>{{ filter|capfirst }}</a>{% if not forloop.last %}, {% endif %} - {% if forloop.last %} - </div> - {% endif %} -{% endfor %} -{% endif %} -{% endspaceless %} diff --git a/src/lib/Server/Reports/reports/templates/widgets/interaction_list.inc b/src/lib/Server/Reports/reports/templates/widgets/interaction_list.inc deleted file mode 100644 index 8f2dec1dc..000000000 --- a/src/lib/Server/Reports/reports/templates/widgets/interaction_list.inc +++ /dev/null @@ -1,38 +0,0 @@ -{% load bcfg2_tags %} -<div class='interaction_history_widget'> - <table cellpadding="3"> - <tr id='table_list_header' class='listview'> - <td class='left_column'>Timestamp</td> - {% if not client %} - <td class='right_column_wide'>Client</td> - {% endif %} - <td class='right_column' style='width:75px'>State</td> - <td class='right_column_narrow'>Good</td> - <td class='right_column_narrow'>Bad</td> - <td class='right_column_narrow'>Modified</td> - <td class='right_column_narrow'>Extra</td> - <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> - {% if not client %} - <td class='right_column_wide'><a href='{% add_url_filter hostname=entry.client.name %}'>{{ entry.client.name }}</a></td> - {% endif %} - <td class='right_column' style='width:75px'><a href='{% add_url_filter state=entry.state %}' - {% ifequal entry.state 'dirty' %}class='dirty-lineitem'{% endifequal %}>{{ entry.state }}</a></td> - <td class='right_column_narrow'>{{ entry.goodcount }}</td> - <td class='right_column_narrow'>{{ entry.bad_entry_count }}</td> - <td class='right_column_narrow'>{{ entry.modified_entry_count }}</td> - <td class='right_column_narrow'>{{ entry.extra_entry_count }}</td> - <td class='right_column_wide'> - {% if entry.server %} - <a href='{% add_url_filter server=entry.server %}'>{{ entry.server }}</a> - {% else %} - - {% endif %} - </td> - </tr> - {% endfor %} - </table> -</div> diff --git a/src/lib/Server/Reports/reports/templates/widgets/page_bar.html b/src/lib/Server/Reports/reports/templates/widgets/page_bar.html deleted file mode 100644 index aa0def83e..000000000 --- a/src/lib/Server/Reports/reports/templates/widgets/page_bar.html +++ /dev/null @@ -1,23 +0,0 @@ -{% spaceless %} -{% for page, page_url in pager %} - {% if forloop.first %} - <div class="page_bar"> - {% if prev_page %}<a href="{{ prev_page }}">< Prev</a><span> </span>{% endif %} - {% if first_page %}<a href="{{ first_page }}">1</a><span> ... </span>{% endif %} - {% endif %} - {% ifequal page current_page %} - <span class='nav_bar_current'>{{ page }}</span> - {% else %} - <a href="{{ page_url }}">{{ page }}</a> - {% endifequal %} - {% if forloop.last %} - {% if last_page %}<span> ... </span><a href="{{ last_page }}">{{ total_pages }}</a><span> </span>{% endif %} - {% if next_page %}<a href="{{ next_page }}">Next ></a><span> </span>{% endif %} - |{% for limit, limit_url in page_limits %} <a href="{{ limit_url }}">{{ limit }}</a>{% endfor %} - </div> - {% else %} - <span> </span> - {% endif %} -{% endfor %} -{% endspaceless %} -<!-- {{ path }} --> diff --git a/src/lib/Server/Reports/reports/templatetags/__init__.py b/src/lib/Server/Reports/reports/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/src/lib/Server/Reports/reports/templatetags/__init__.py +++ /dev/null diff --git a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py b/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py deleted file mode 100644 index 629984f26..000000000 --- a/src/lib/Server/Reports/reports/templatetags/bcfg2_tags.py +++ /dev/null @@ -1,276 +0,0 @@ -from django import template -from django.core.urlresolvers import resolve, reverse, Resolver404, NoReverseMatch -from django.utils.encoding import smart_unicode, smart_str -from datetime import datetime, timedelta -from Bcfg2.Server.Reports.utils import filter_list - -register = template.Library() - -__PAGE_NAV_LIMITS__ = (10, 25, 50, 100) - -@register.inclusion_tag('widgets/page_bar.html', takes_context=True) -def page_navigator(context): - """ - Creates paginated links. - - Expects the context to be a RequestContext and views.prepare_paginated_list() - to have populated page information. - """ - fragment = dict() - try: - path = context['request'].META['PATH_INFO'] - total_pages = int(context['total_pages']) - records_per_page = int(context['records_per_page']) - except KeyError: - return fragment - except ValueError: - return fragment - - if total_pages < 2: - return {} - - try: - view, args, kwargs = resolve(path) - current_page = int(kwargs.get('page_number',1)) - fragment['current_page'] = current_page - fragment['page_number'] = current_page - fragment['total_pages'] = total_pages - fragment['records_per_page'] = records_per_page - if current_page > 1: - kwargs['page_number'] = current_page - 1 - fragment['prev_page'] = reverse(view, args=args, kwargs=kwargs) - if current_page < total_pages: - kwargs['page_number'] = current_page + 1 - fragment['next_page'] = reverse(view, args=args, kwargs=kwargs) - - view_range = 5 - if total_pages > view_range: - pager_start = current_page - 2 - pager_end = current_page + 2 - if pager_start < 1: - pager_end += (1 - pager_start) - pager_start = 1 - if pager_end > total_pages: - pager_start -= (pager_end - total_pages) - pager_end = total_pages - else: - pager_start = 1 - pager_end = total_pages - - if pager_start > 1: - kwargs['page_number'] = 1 - fragment['first_page'] = reverse(view, args=args, kwargs=kwargs) - if pager_end < total_pages: - kwargs['page_number'] = total_pages - fragment['last_page'] = reverse(view, args=args, kwargs=kwargs) - - pager = [] - for page in range(pager_start, int(pager_end) + 1): - kwargs['page_number'] = page - pager.append( (page, reverse(view, args=args, kwargs=kwargs)) ) - - kwargs['page_number'] = 1 - page_limits = [] - for limit in __PAGE_NAV_LIMITS__: - kwargs['page_limit'] = limit - page_limits.append( (limit, reverse(view, args=args, kwargs=kwargs)) ) - # resolver doesn't like this - del kwargs['page_number'] - del kwargs['page_limit'] - page_limits.append( ('all', reverse(view, args=args, kwargs=kwargs) + "|all") ) - - fragment['pager'] = pager - fragment['page_limits'] = page_limits - - except Resolver404: - path = "404" - except NoReverseMatch: - nr = sys.exc_info()[1] - path = "NoReverseMatch: %s" % nr - except ValueError: - path = "ValueError" - #FIXME - Handle these - - fragment['path'] = path - return fragment - -@register.inclusion_tag('widgets/filter_bar.html', takes_context=True) -def filter_navigator(context): - try: - path = context['request'].META['PATH_INFO'] - view, args, kwargs = resolve(path) - - # Strip any page limits and numbers - if 'page_number' in kwargs: - del kwargs['page_number'] - if 'page_limit' in kwargs: - del kwargs['page_limit'] - - filters = [] - for filter in filter_list: - if filter in kwargs: - myargs = kwargs.copy() - del myargs[filter] - filters.append( (filter, reverse(view, args=args, kwargs=myargs) ) ) - filters.sort(lambda x,y: cmp(x[0], y[0])) - return { 'filters': filters } - except (Resolver404, NoReverseMatch, ValueError, KeyError): - pass - return dict() - -def _subtract_or_na(mdict, x, y): - """ - Shortcut for build_metric_list - """ - try: - return round(mdict[x] - mdict[y], 4) - except: - return "n/a" - -@register.filter -def build_metric_list(mdict): - """ - Create a list of metric table entries - - Moving this here it simplify the view. Should really handle the case where these - are missing... - """ - td_list = [] - # parse - td_list.append( _subtract_or_na(mdict, 'config_parse', 'config_download')) - #probe - td_list.append( _subtract_or_na(mdict, 'probe_upload', 'start')) - #inventory - td_list.append( _subtract_or_na(mdict, 'inventory', 'initialization')) - #install - td_list.append( _subtract_or_na(mdict, 'install', 'inventory')) - #cfg download & parse - td_list.append( _subtract_or_na(mdict, 'config_parse', 'probe_upload')) - #total - td_list.append( _subtract_or_na(mdict, 'finished', 'start')) - return td_list - -@register.filter -def isstale(timestamp, entry_max=None): - """ - Check for a stale timestamp - - Compares two timestamps and returns True if the - difference is greater then 24 hours. - """ - if not entry_max: - entry_max = datetime.now() - return entry_max - timestamp > timedelta(hours=24) - -@register.filter -def sort_interactions_by_name(value): - """ - Sort an interaction list by client name - """ - inters = list(value) - inters.sort(lambda a,b: cmp(a.client.name, b.client.name)) - return inters - -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.Server.Reports.reports.views.render_history_view' - - def render(self, context): - link = '#' - try: - path = context['request'].META['PATH_INFO'] - view, args, kwargs = resolve(path) - filter_value = self.filter_value.resolve(context, True) - if filter_value: - filter_name = smart_str(self.filter_name) - filter_value = smart_unicode(filter_value) - kwargs[filter_name] = filter_value - # These two don't make sense - if filter_name == 'server' and 'hostname' in kwargs: - del kwargs['hostname'] - elif filter_name == 'hostname' and 'server' in kwargs: - del kwargs['server'] - try: - link = reverse(view, args=args, kwargs=kwargs) - except NoReverseMatch: - link = reverse(self.fallback_view, args=None, - kwargs={ filter_name: filter_value }) - except NoReverseMatch: - rm = sys.exc_info()[1] - raise rm - except (Resolver404, ValueError): - pass - return link - -@register.tag -def add_url_filter(parser, token): - """ - Return a url with the filter added to the current view. - - Takes a new filter and resolves the current view with the new filter - applied. Resolves to Bcfg2.Server.Reports.reports.views.client_history - by default. - - {% add_url_filter server=interaction.server %} - """ - try: - tag_name, filter_pair = token.split_contents() - filter_name, filter_value = filter_pair.split('=', 1) - filter_name = filter_name.strip() - filter_value = parser.compile_filter(filter_value) - except ValueError: - raise template.TemplateSyntaxError("%r tag requires exactly one argument" % token.contents.split()[0]) - if not filter_name or not filter_value: - raise template.TemplateSyntaxError("argument should be a filter=value pair") - - return AddUrlFilter(filter_name, filter_value) - -@register.filter -def sortwell(value): - """ - Sorts a list(or evaluates queryset to list) of bad, extra, or modified items in the best - way for presentation - """ - - configItems = list(value) - configItems.sort(lambda x,y: cmp(x.entry.name, y.entry.name)) - configItems.sort(lambda x,y: cmp(x.entry.kind, y.entry.kind)) - return configItems - -class MediaTag(template.Node): - def __init__(self, filter_value): - self.filter_value = filter_value - - def render(self, context): - base = context['MEDIA_URL'] - try: - request = context['request'] - try: - base = request.environ['bcfg2.media_url'] - except: - if request.path != request.META['PATH_INFO']: - offset = request.path.find(request.META['PATH_INFO']) - if offset > 0: - base = "%s/%s" % (request.path[:offset], \ - context['MEDIA_URL'].strip('/')) - except: - pass - return "%s/%s" % (base, self.filter_value) - -@register.tag -def to_media_url(parser, token): - """ - Return a url relative to the media_url. - - {% to_media_url /bcfg2.css %} - """ - try: - tag_name, filter_value = token.split_contents() - filter_value = parser.compile_filter(filter_value) - except ValueError: - raise template.TemplateSyntaxError("%r tag requires exactly one argument" % token.contents.split()[0]) - - return MediaTag(filter_value) - diff --git a/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py b/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py deleted file mode 100644 index 2e30125f9..000000000 --- a/src/lib/Server/Reports/reports/templatetags/syntax_coloring.py +++ /dev/null @@ -1,49 +0,0 @@ -import sys -from django import template -from django.utils.encoding import smart_unicode, smart_str -from django.utils.html import conditional_escape -from django.utils.safestring import mark_safe - -register = template.Library() - -try: - from pygments import highlight - from pygments.lexers import get_lexer_by_name - from pygments.formatters import HtmlFormatter - colorize = True - -except: - colorize = False - -# py3k compatibility -def u_str(string): - if sys.hexversion >= 0x03000000: - return string - else: - return unicode(string) - -@register.filter -def syntaxhilight(value, arg="diff", autoescape=None): - """ - Returns a syntax-hilighted version of Code; requires code/language arguments - """ - - if autoescape: - value = conditional_escape(value) - arg = conditional_escape(arg) - - if colorize: - try: - output = u_str('<style type="text/css">') \ - + smart_unicode(HtmlFormatter().get_style_defs('.highlight')) \ - + u_str('</style>') - - lexer = get_lexer_by_name(arg) - output += highlight(value, lexer, HtmlFormatter()) - return mark_safe(output) - except: - return value - else: - return mark_safe(u_str('<div class="note-box">Tip: Install pygments for highlighting</div><pre>%s</pre>') % value) -syntaxhilight.needs_autoescape = True - diff --git a/src/lib/Server/Reports/reports/urls.py b/src/lib/Server/Reports/reports/urls.py deleted file mode 100644 index 434ce07b7..000000000 --- a/src/lib/Server/Reports/reports/urls.py +++ /dev/null @@ -1,55 +0,0 @@ -from django.conf.urls.defaults import * -from django.core.urlresolvers import reverse, NoReverseMatch -from django.http import HttpResponsePermanentRedirect -from Bcfg2.Server.Reports.utils import filteredUrls, paginatedUrls, timeviewUrls - -def newRoot(request): - try: - grid_view = reverse('reports_grid_view') - except NoReverseMatch: - grid_view = '/grid' - return HttpResponsePermanentRedirect(grid_view) - -urlpatterns = patterns('Bcfg2.Server.Reports.reports', - (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'^elements/(?P<type>\w+)/(?P<pk>\d+)/?$', 'views.config_item', name='reports_item'), -) - -urlpatterns += patterns('Bcfg2.Server.Reports.reports', - *timeviewUrls( - (r'^grid/?$', 'views.client_index', None, 'reports_grid_view'), - (r'^summary/?$', 'views.display_summary', None, 'reports_summary'), - (r'^timing/?$', 'views.display_timing', None, 'reports_timing'), - (r'^elements/(?P<type>\w+)/?$', 'views.config_item_list', None, 'reports_item_list'), -)) - -urlpatterns += patterns('Bcfg2.Server.Reports.reports', - *filteredUrls(*timeviewUrls( - (r'^detailed/?$', - 'views.client_detailed_list', None, 'reports_detailed_list') -))) - -urlpatterns += patterns('Bcfg2.Server.Reports.reports', - *paginatedUrls( *filteredUrls( - (r'^history/?$', - 'views.render_history_view', None, 'reports_history'), - (r'^history/(?P<hostname>[^/|]+)/?$', - '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/Server/Reports/reports/views.py b/src/lib/Server/Reports/reports/views.py deleted file mode 100644 index ccd71a60e..000000000 --- a/src/lib/Server/Reports/reports/views.py +++ /dev/null @@ -1,415 +0,0 @@ -""" -Report views - -Functions to handle all of the reporting views. -""" -from datetime import datetime, timedelta -import sys -from time import strptime - -from django.template import Context, RequestContext -from django.http import \ - HttpResponse, HttpResponseRedirect, HttpResponseServerError, Http404 -from django.shortcuts import render_to_response, get_object_or_404 -from django.core.urlresolvers import \ - resolve, reverse, Resolver404, NoReverseMatch -from django.db import connection - -from Bcfg2.Server.Reports.reports.models import * - - -class PaginationError(Exception): - """This error is raised when pagination cannot be completed.""" - pass - - -def server_error(request): - """ - 500 error handler. - - For now always return the debug response. Mailing isn't appropriate here. - - """ - from django.views import debug - return debug.technical_500_response(request, *sys.exc_info()) - - -def timeview(fn): - """ - Setup a timeview view - - Handles backend posts from the calendar and converts date pieces - into a 'timestamp' parameter - - """ - def _handle_timeview(request, **kwargs): - """Send any posts back.""" - if request.method == 'POST': - cal_date = request.POST['cal_date'] - try: - fmt = "%Y/%m/%d" - if cal_date.find(' ') > -1: - fmt += " %H:%M" - timestamp = datetime(*strptime(cal_date, fmt)[0:6]) - view, args, kw = resolve(request.META['PATH_INFO']) - kw['year'] = "%0.4d" % timestamp.year - kw['month'] = "%02.d" % timestamp.month - kw['day'] = "%02.d" % timestamp.day - if cal_date.find(' ') > -1: - kw['hour'] = timestamp.hour - kw['minute'] = timestamp.minute - return HttpResponseRedirect(reverse(view, - args=args, - kwargs=kw)) - except KeyError: - pass - except: - pass - # FIXME - Handle this - - """Extract timestamp from args.""" - timestamp = None - try: - timestamp = datetime(int(kwargs.pop('year')), - int(kwargs.pop('month')), - int(kwargs.pop('day')), int(kwargs.pop('hour', 0)), - int(kwargs.pop('minute', 0)), 0) - kwargs['timestamp'] = timestamp - except KeyError: - pass - except: - raise - return fn(request, **kwargs) - - return _handle_timeview - - -def config_item(request, pk, type="bad"): - """ - Display a single entry. - - Dispalys information about a single entry. - - """ - item = get_object_or_404(Entries_interactions, id=pk) - timestamp = item.interaction.timestamp - time_start = item.interaction.timestamp.replace(hour=0, - minute=0, - second=0, - microsecond=0) - time_end = time_start + timedelta(days=1) - - todays_data = Interaction.objects.filter(timestamp__gte=time_start, - timestamp__lt=time_end) - shared_entries = Entries_interactions.objects.filter(entry=item.entry, - reason=item.reason, - type=item.type, - interaction__in=[x['id']\ - for x in todays_data.values('id')]) - - associated_list = Interaction.objects.filter(id__in=[x['interaction']\ - for x in shared_entries.values('interaction')])\ - .order_by('client__name', 'timestamp').select_related().all() - - return render_to_response('config_items/item.html', - {'item': item, - 'isextra': item.type == TYPE_EXTRA, - 'mod_or_bad': type, - 'associated_list': associated_list, - 'timestamp': timestamp}, - context_instance=RequestContext(request)) - - -@timeview -def config_item_list(request, type, timestamp=None): - """Render a listing of affected elements""" - mod_or_bad = type.lower() - type = convert_entry_type_to_id(type) - if type < 0: - raise Http404 - - current_clients = Interaction.objects.get_interaction_per_client_ids(timestamp) - item_list_dict = {} - seen = dict() - for x in Entries_interactions.objects.filter(interaction__in=current_clients, - type=type).select_related(): - if (x.entry, x.reason) in seen: - continue - seen[(x.entry, x.reason)] = 1 - if item_list_dict.get(x.entry.kind, None): - item_list_dict[x.entry.kind].append(x) - else: - item_list_dict[x.entry.kind] = [x] - - for kind in item_list_dict: - item_list_dict[kind].sort(lambda a, b: cmp(a.entry.name, b.entry.name)) - - return render_to_response('config_items/listing.html', - {'item_list_dict': item_list_dict, - 'mod_or_bad': mod_or_bad, - 'timestamp': timestamp}, - context_instance=RequestContext(request)) - - -@timeview -def client_index(request, timestamp=None): - """ - Render a grid view of active clients. - - Keyword parameters: - timestamp -- datetime objectto render from - - """ - list = Interaction.objects.interaction_per_client(timestamp).select_related()\ - .order_by("client__name").all() - - return render_to_response('clients/index.html', - {'inter_list': list, - 'timestamp': timestamp}, - context_instance=RequestContext(request)) - - -@timeview -def client_detailed_list(request, timestamp=None, **kwargs): - """ - Provides a more detailed list view of the clients. Allows for extra - filters to be passed in. - - """ - - kwargs['interaction_base'] = Interaction.objects.interaction_per_client(timestamp).select_related() - kwargs['orderby'] = "client__name" - kwargs['page_limit'] = 0 - return render_history_view(request, 'clients/detailed-list.html', **kwargs) - - -def client_detail(request, hostname=None, pk=None): - context = dict() - client = get_object_or_404(Client, name=hostname) - if(pk == None): - context['interaction'] = client.current_interaction - return render_history_view(request, 'clients/detail.html', page_limit=5, - client=client, context=context) - else: - context['interaction'] = client.interactions.get(pk=pk) - return render_history_view(request, 'clients/detail.html', page_limit=5, - client=client, maxdate=context['interaction'].timestamp, context=context) - - -def client_manage(request): - """Manage client expiration""" - message = '' - if request.method == 'POST': - try: - client_name = request.POST.get('client_name', None) - client_action = request.POST.get('client_action', None) - client = Client.objects.get(name=client_name) - if client_action == 'expire': - client.expiration = datetime.now() - client.save() - message = "Expiration for %s set to %s." % \ - (client_name, client.expiration.strftime("%Y-%m-%d %H:%M:%S")) - elif client_action == 'unexpire': - client.expiration = None - client.save() - message = "%s is now active." % client_name - else: - message = "Missing action" - except Client.DoesNotExist: - if not client_name: - 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)) - - -@timeview -def display_summary(request, timestamp=None): - """ - Display a summary of the bcfg2 world - """ - query = Interaction.objects.interaction_per_client(timestamp).select_related() - node_count = query.count() - recent_data = query.all() - if not timestamp: - timestamp = datetime.now() - - collected_data = dict(clean=[], - bad=[], - modified=[], - extra=[], - stale=[], - pings=[]) - for node in recent_data: - if timestamp - node.timestamp > timedelta(hours=24): - collected_data['stale'].append(node) - # If stale check for uptime - try: - if node.client.pings.latest().status == 'N': - collected_data['pings'].append(node) - except Ping.DoesNotExist: - collected_data['pings'].append(node) - continue - if node.bad_entry_count() > 0: - collected_data['bad'].append(node) - else: - collected_data['clean'].append(node) - if node.modified_entry_count() > 0: - collected_data['modified'].append(node) - if node.extra_entry_count() > 0: - collected_data['extra'].append(node) - - # label, header_text, node_list - summary_data = [] - get_dict = lambda name, label: {'name': name, - 'nodes': collected_data[name], - 'label': label} - if len(collected_data['clean']) > 0: - summary_data.append(get_dict('clean', - 'nodes are clean.')) - if len(collected_data['bad']) > 0: - summary_data.append(get_dict('bad', - 'nodes are bad.')) - if len(collected_data['modified']) > 0: - summary_data.append(get_dict('modified', - 'nodes were modified.')) - if len(collected_data['extra']) > 0: - summary_data.append(get_dict('extra', - 'nodes have extra configurations.')) - if len(collected_data['stale']) > 0: - summary_data.append(get_dict('stale', - 'nodes did not run within the last 24 hours.')) - if len(collected_data['pings']) > 0: - summary_data.append(get_dict('pings', - 'are down.')) - - return render_to_response('displays/summary.html', - {'summary_data': summary_data, 'node_count': node_count, - 'timestamp': timestamp}, - context_instance=RequestContext(request)) - - -@timeview -def display_timing(request, timestamp=None): - mdict = dict() - inters = Interaction.objects.interaction_per_client(timestamp).select_related().all() - [mdict.__setitem__(inter, {'name': inter.client.name}) \ - for inter in inters] - for metric in Performance.objects.filter(interaction__in=list(mdict.keys())).all(): - for i in metric.interaction.all(): - mdict[i][metric.metric] = metric.value - return render_to_response('displays/timing.html', - {'metrics': list(mdict.values()), - 'timestamp': timestamp}, - context_instance=RequestContext(request)) - - -def render_history_view(request, template='clients/history.html', **kwargs): - """ - Provides a detailed history of a clients interactions. - - Renders a detailed history of a clients interactions. Allows for various - filters and settings. Automatically sets pagination data into the context. - - Keyword arguments: - interaction_base -- Interaction QuerySet to build on - (default Interaction.objects) - context -- Additional context data to render with - page_number -- Page to display (default 1) - page_limit -- Number of results per page, if 0 show all (default 25) - client -- Client object to render - hostname -- Client hostname to lookup and render. Returns a 404 if - not found - server -- Filter interactions by server - state -- Filter interactions by state - entry_max -- Most recent interaction to display - orderby -- Sort results using this field - - """ - - context = kwargs.get('context', dict()) - max_results = int(kwargs.get('page_limit', 25)) - page = int(kwargs.get('page_number', 1)) - - client = kwargs.get('client', None) - if not client and 'hostname' in kwargs: - client = get_object_or_404(Client, name=kwargs['hostname']) - if client: - context['client'] = client - - entry_max = kwargs.get('maxdate', None) - context['entry_max'] = entry_max - - # Either filter by client or limit by clients - iquery = kwargs.get('interaction_base', Interaction.objects) - if client: - iquery = iquery.filter(client__exact=client).select_related() - - if 'orderby' in kwargs and kwargs['orderby']: - iquery = iquery.order_by(kwargs['orderby']) - - if 'state' in kwargs and kwargs['state']: - iquery = iquery.filter(state__exact=kwargs['state']) - if 'server' in kwargs and kwargs['server']: - iquery = iquery.filter(server__exact=kwargs['server']) - - if entry_max: - iquery = iquery.filter(timestamp__lte=entry_max) - - if max_results < 0: - max_results = 1 - entry_list = [] - if max_results > 0: - try: - rec_start, rec_end = prepare_paginated_list(request, - context, - iquery, - page, - max_results) - except PaginationError: - page_error = sys.exc_info()[1] - if isinstance(page_error[0], HttpResponse): - return page_error[0] - return HttpResponseServerError(page_error) - context['entry_list'] = iquery.all()[rec_start:rec_end] - else: - context['entry_list'] = iquery.all() - - return render_to_response(template, context, - context_instance=RequestContext(request)) - - -def prepare_paginated_list(request, context, paged_list, page=1, max_results=25): - """ - Prepare context and slice an object for pagination. - """ - if max_results < 1: - raise PaginationError("Max results less then 1") - if paged_list == None: - raise PaginationError("Invalid object") - - try: - nitems = paged_list.count() - except TypeError: - nitems = len(paged_list) - - rec_start = (page - 1) * int(max_results) - try: - total_pages = (nitems / int(max_results)) + 1 - except: - total_pages = 1 - if page > total_pages: - # If we passed beyond the end send back - try: - view, args, kwargs = resolve(request.META['PATH_INFO']) - kwargs['page_number'] = total_pages - raise PaginationError(HttpResponseRedirect(reverse(view, - kwards=kwargs))) - except (Resolver404, NoReverseMatch, ValueError): - raise "Accessing beyond last page. Unable to resolve redirect." - - context['total_pages'] = total_pages - context['records_per_page'] = max_results - return (rec_start, rec_start + int(max_results)) diff --git a/src/lib/Server/Reports/settings.py b/src/lib/Server/Reports/settings.py deleted file mode 100644 index 952e3eae6..000000000 --- a/src/lib/Server/Reports/settings.py +++ /dev/null @@ -1,161 +0,0 @@ -import django -import sys - -# Compatibility import -from Bcfg2.Bcfg2Py3k import ConfigParser -# Django settings for bcfg2 reports project. -c = ConfigParser.ConfigParser() -if len(c.read(['/etc/bcfg2.conf', '/etc/bcfg2-web.conf'])) == 0: - raise ImportError("Please check that bcfg2.conf or bcfg2-web.conf exists " - "and is readable by your web server.") - -try: - DEBUG = c.getboolean('statistics', 'web_debug') -except: - DEBUG = False - -if DEBUG: - print("Warning: Setting web_debug to True causes extraordinary memory " - "leaks. Only use this setting if you know what you're doing.") - -TEMPLATE_DEBUG = DEBUG - -ADMINS = ( - ('Root', 'root'), -) - -MANAGERS = ADMINS -try: - db_engine = c.get('statistics', 'database_engine') -except ConfigParser.NoSectionError: - e = sys.exc_info()[1] - raise ImportError("Failed to determine database engine: %s" % e) -db_name = '' -if c.has_option('statistics', 'database_name'): - db_name = c.get('statistics', 'database_name') -if db_engine == 'sqlite3' and db_name == '': - db_name = "%s/etc/brpt.sqlite" % c.get('server', 'repository') - -DATABASES = { - 'default': { - 'ENGINE': "django.db.backends.%s" % db_engine, - 'NAME': db_name - } -} - -if db_engine != 'sqlite3': - DATABASES['default']['USER'] = c.get('statistics', 'database_user') - DATABASES['default']['PASSWORD'] = c.get('statistics', 'database_password') - DATABASES['default']['HOST'] = c.get('statistics', 'database_host') - try: - DATABASES['default']['PORT'] = c.get('statistics', 'database_port') - except: # An empty string tells Django to use the default port. - DATABASES['default']['PORT'] = '' - -if django.VERSION[0] == 1 and django.VERSION[1] < 2: - DATABASE_ENGINE = db_engine - DATABASE_NAME = DATABASES['default']['NAME'] - if DATABASE_ENGINE != 'sqlite3': - DATABASE_USER = DATABASES['default']['USER'] - DATABASE_PASSWORD = DATABASES['default']['PASSWORD'] - DATABASE_HOST = DATABASES['default']['HOST'] - DATABASE_PORT = DATABASES['default']['PORT'] - - -# Local time zone for this installation. All choices can be found here: -# http://docs.djangoproject.com/en/dev/ref/settings/#time-zone -try: - TIME_ZONE = c.get('statistics', 'time_zone') -except: - if django.VERSION[0] == 1 and django.VERSION[1] > 2: - TIME_ZONE = None - -# Language code for this installation. All choices can be found here: -# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes -# http://blogs.law.harvard.edu/tech/stories/storyReader$15 -LANGUAGE_CODE = 'en-us' - -SITE_ID = 1 - -# Absolute path to the directory that holds media. -# Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = '' - -# URL that handles the media served from MEDIA_ROOT. -# Example: "http://media.lawrence.com" -MEDIA_URL = '/site_media' -if c.has_option('statistics', 'web_prefix'): - MEDIA_URL = c.get('statistics', 'web_prefix').rstrip('/') + MEDIA_URL - -# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a -# trailing slash. -# Examples: "http://foo.com/media/", "/media/". -ADMIN_MEDIA_PREFIX = '/media/' - -# Make this unique, and don't share it with anybody. -SECRET_KEY = 'eb5+y%oy-qx*2+62vv=gtnnxg1yig_odu0se5$h0hh#pc*lmo7' - -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.load_template_source', - 'django.template.loaders.app_directories.load_template_source', -) - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.middleware.doc.XViewMiddleware', -) - -ROOT_URLCONF = 'Bcfg2.Server.Reports.urls' - -# Authentication Settings -# Use NIS authentication backend defined in backends.py -AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend', - 'Bcfg2.Server.Reports.backends.NISBackend') -# The NIS group authorized to login to BCFG2's reportinvg system -AUTHORIZED_GROUP = '' -#create login url area: -try: - import django.contrib.auth -except ImportError: - raise ImportError('Import of Django module failed. Is Django installed?') -django.contrib.auth.LOGIN_URL = '/login' - -SESSION_EXPIRE_AT_BROWSER_CLOSE = True - - - -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates". - # Always use forward slashes, even on Windows. - '/usr/share/python-support/python-django/django/contrib/admin/templates/', - 'Bcfg2.Server.Reports.reports' -) - -if django.VERSION[0] == 1 and django.VERSION[1] < 2: - TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.core.context_processors.auth', - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.media', - 'django.core.context_processors.request' - ) -else: - 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' - ) - -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.admin', - 'Bcfg2.Server.Reports.reports' -) diff --git a/src/lib/Server/Reports/updatefix.py b/src/lib/Server/Reports/updatefix.py deleted file mode 100644 index 7cebaaca9..000000000 --- a/src/lib/Server/Reports/updatefix.py +++ /dev/null @@ -1,190 +0,0 @@ -import Bcfg2.Server.Reports.settings - -from django.db import connection -import django.core.management -import logging -import traceback -from Bcfg2.Server.Reports.reports.models import InternalDatabaseVersion, \ - TYPE_BAD, TYPE_MODIFIED, TYPE_EXTRA -logger = logging.getLogger('Bcfg2.Server.Reports.UpdateFix') - - -# all update function should go here -def _merge_database_table_entries(): - cursor = connection.cursor() - insert_cursor = connection.cursor() - find_cursor = connection.cursor() - cursor.execute(""" - Select name, kind from reports_bad - union - select name, kind from reports_modified - union - select name, kind from reports_extra - """) - # this fetch could be better done - entries_map = {} - for row in cursor.fetchall(): - insert_cursor.execute("insert into reports_entries (name, kind) \ - values (%s, %s)", (row[0], row[1])) - entries_map[(row[0], row[1])] = insert_cursor.lastrowid - - cursor.execute(""" - Select name, kind, reason_id, interaction_id, 1 from reports_bad - inner join reports_bad_interactions on reports_bad.id=reports_bad_interactions.bad_id - union - Select name, kind, reason_id, interaction_id, 2 from reports_modified - inner join reports_modified_interactions on reports_modified.id=reports_modified_interactions.modified_id - union - Select name, kind, reason_id, interaction_id, 3 from reports_extra - inner join reports_extra_interactions on reports_extra.id=reports_extra_interactions.extra_id - """) - for row in cursor.fetchall(): - key = (row[0], row[1]) - if entries_map.get(key, None): - entry_id = entries_map[key] - else: - find_cursor.execute("Select id from reports_entries where name=%s and kind=%s", key) - rowe = find_cursor.fetchone() - entry_id = rowe[0] - insert_cursor.execute("insert into reports_entries_interactions \ - (entry_id, interaction_id, reason_id, type) values (%s, %s, %s, %s)", (entry_id, row[3], row[2], row[4])) - - -def _interactions_constraint_or_idx(): - '''sqlite doesn't support alter tables.. or constraints''' - cursor = connection.cursor() - try: - cursor.execute('alter table reports_interaction add constraint reports_interaction_20100601 unique (client_id,timestamp)') - except: - cursor.execute('create unique index reports_interaction_20100601 on reports_interaction (client_id,timestamp)') - - -def _populate_interaction_entry_counts(): - '''Populate up the type totals for the interaction table''' - cursor = connection.cursor() - count_field = {TYPE_BAD: 'bad_entries', - TYPE_MODIFIED: 'modified_entries', - TYPE_EXTRA: 'extra_entries'} - - for type in list(count_field.keys()): - cursor.execute("select count(type), interaction_id " + - "from reports_entries_interactions where type = %s group by interaction_id" % type) - updates = [] - for row in cursor.fetchall(): - updates.append(row) - try: - cursor.executemany("update reports_interaction set " + count_field[type] + "=%s where id = %s", updates) - except Exception: - e = sys.exc_info()[1] - print(e) - cursor.close() - - -# be sure to test your upgrade query before reflecting the change in the models -# the list of function and sql command to do should go here -_fixes = [_merge_database_table_entries, - # this will remove unused tables - "drop table reports_bad;", - "drop table reports_bad_interactions;", - "drop table reports_extra;", - "drop table reports_extra_interactions;", - "drop table reports_modified;", - "drop table reports_modified_interactions;", - "drop table reports_repository;", - "drop table reports_metadata;", - "alter table reports_interaction add server varchar(256) not null default 'N/A';", - # fix revision data type to support $VCS hashes - "alter table reports_interaction add repo_rev_code varchar(64) default '';", - # Performance enhancements for large sites - 'alter table reports_interaction add column bad_entries integer not null default -1;', - 'alter table reports_interaction add column modified_entries integer not null default -1;', - 'alter table reports_interaction add column extra_entries integer not null default -1;', - _populate_interaction_entry_counts, - _interactions_constraint_or_idx, - 'alter table reports_reason add is_binary bool NOT NULL default False;', - 'alter table reports_reason add is_sensitive bool NOT NULL default False;', -] - -# this will calculate the last possible version of the database -lastversion = len(_fixes) - - -def rollupdate(current_version): - """ function responsible to coordinates all the updates - need current_version as integer - """ - ret = None - if current_version < lastversion: - for i in range(current_version, lastversion): - try: - if type(_fixes[i]) == str: - connection.cursor().execute(_fixes[i]) - else: - _fixes[i]() - except: - logger.error("Failed to perform db update %s" % (_fixes[i]), 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) - return ret - else: - return None - - -def dosync(): - """Function to do the syncronisation for the models""" - # try to detect if it's a fresh new database - try: - cursor = connection.cursor() - # If this table goes missing then don't forget to change it to the new one - cursor.execute("Select * from reports_client") - # if we get here with no error then the database has existing tables - fresh = False - except: - logger.debug("there was an error while detecting the freshness of the database") - #we should get here if the database is new - fresh = True - - # ensure database connection are close, so that the management can do it's job right - try: - cursor.close() - connection.close() - except: - # ignore any errors from missing/invalid dbs - pass - # Do the syncdb according to the django version - if "call_command" in dir(django.core.management): - # this is available since django 1.0 alpha. - # not yet tested for full functionnality - django.core.management.call_command("syncdb", interactive=False, verbosity=0) - if fresh: - django.core.management.call_command("loaddata", 'initial_version.xml', verbosity=0) - elif "syncdb" in dir(django.core.management): - # this exist only for django 0.96.* - django.core.management.syncdb(interactive=False, verbosity=0) - if fresh: - logger.debug("loading the initial_version fixtures") - django.core.management.load_data(fixture_labels=['initial_version'], verbosity=0) - else: - logger.warning("Don't forget to run syncdb") - - -def update_database(): - ''' methode to search where we are in the revision of the database models and update them ''' - try: - logger.debug("Running upgrade of models to the new one") - dosync() - know_version = InternalDatabaseVersion.objects.order_by('-version') - if not know_version: - logger.debug("No version, creating initial version") - know_version = InternalDatabaseVersion.objects.create(version=0) - else: - know_version = know_version[0] - logger.debug("Presently at %s" % know_version) - if know_version.version < lastversion: - new_version = rollupdate(know_version.version) - if new_version: - logger.debug("upgraded to %s" % new_version) - except: - logger.error("Error while updating the database") - for x in traceback.format_exc().splitlines(): - logger.error(x) diff --git a/src/lib/Server/Reports/urls.py b/src/lib/Server/Reports/urls.py deleted file mode 100644 index d7ff1eee5..000000000 --- a/src/lib/Server/Reports/urls.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.conf.urls.defaults import * -from django.http import HttpResponsePermanentRedirect - -handler500 = 'Bcfg2.Server.Reports.reports.views.server_error' - -urlpatterns = patterns('', - (r'^', include('Bcfg2.Server.Reports.reports.urls')) -) - -#urlpatterns += patterns("django.views", -# url(r"media/(?P<path>.*)$", "static.serve", { -# "document_root": '/Users/tlaszlo/svn/bcfg2/reports/site_media/', -# }) -#) diff --git a/src/lib/Server/Reports/utils.py b/src/lib/Server/Reports/utils.py deleted file mode 100755 index e0b6ead59..000000000 --- a/src/lib/Server/Reports/utils.py +++ /dev/null @@ -1,124 +0,0 @@ -"""Helper functions for reports""" -from django.conf.urls.defaults import * -import re - -"""List of filters provided by filteredUrls""" -filter_list = ('server', 'state') - - -class BatchFetch(object): - """Fetch Django objects in smaller batches to save memory""" - - def __init__(self, obj, step=10000): - self.count = 0 - self.block_count = 0 - self.obj = obj - self.data = None - self.step = step - self.max = obj.count() - - def __iter__(self): - return self - - def next(self): - """Provide compatibility with python < 3.0""" - return self.__next__() - - def __next__(self): - """Return the next object from our array and fetch from the - database when needed""" - if self.block_count + self.count - self.step == self.max: - raise StopIteration - if self.block_count == 0 or self.count == self.step: - # Without list() this turns into LIMIT 1 OFFSET x queries - self.data = list(self.obj.all()[self.block_count: \ - (self.block_count + self.step)]) - self.block_count += self.step - self.count = 0 - self.count += 1 - return self.data[self.count - 1] - - -def generateUrls(fn): - """ - Parse url tuples and send to functions. - - Decorator for url generators. Handles url tuple parsing - before the actual function is called. - """ - def url_gen(*urls): - results = [] - for url_tuple in urls: - if isinstance(url_tuple, (list, tuple)): - results += fn(*url_tuple) - else: - raise ValueError("Unable to handle compiled urls") - return results - return url_gen - - -@generateUrls -def paginatedUrls(pattern, view, kwargs=None, name=None): - """ - Takes a group of url tuples and adds paginated urls. - - Extends a url tuple to include paginated urls. - Currently doesn't handle url() compiled patterns. - - """ - results = [(pattern, view, kwargs, name)] - tail = '' - mtail = re.search('(/+\+?\\*?\??\$?)$', pattern) - if mtail: - tail = mtail.group(1) - pattern = pattern[:len(pattern) - len(tail)] - results += [(pattern + "/(?P<page_number>\d+)" + tail, view, kwargs)] - results += [(pattern + "/(?P<page_number>\d+)\|(?P<page_limit>\d+)" + - tail, view, kwargs)] - if not kwargs: - kwargs = dict() - kwargs['page_limit'] = 0 - results += [(pattern + "/?\|(?P<page_limit>all)" + tail, view, kwargs)] - return results - - -@generateUrls -def filteredUrls(pattern, view, kwargs=None, name=None): - """ - Takes a url and adds filtered urls. - - Extends a url tuple to include filtered view urls. Currently doesn't - handle url() compiled patterns. - """ - results = [(pattern, view, kwargs, name)] - tail = '' - mtail = re.search('(/+\+?\\*?\??\$?)$', pattern) - if mtail: - tail = mtail.group(1) - pattern = pattern[:len(pattern) - len(tail)] - for filter in ('/state/(?P<state>\w+)', - '/server/(?P<server>[\w\-\.]+)', - '/server/(?P<server>[\w\-\.]+)/(?P<state>[A-Za-z]+)'): - results += [(pattern + filter + tail, view, kwargs)] - return results - - -@generateUrls -def timeviewUrls(pattern, view, kwargs=None, name=None): - """ - Takes a url and adds timeview urls - - Extends a url tuple to include filtered view urls. Currently doesn't - handle url() compiled patterns. - """ - results = [(pattern, view, kwargs, name)] - tail = '' - mtail = re.search('(/+\+?\\*?\??\$?)$', pattern) - if mtail: - tail = mtail.group(1) - pattern = pattern[:len(pattern) - len(tail)] - for filter in ('/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})/' + \ - '(?P<hour>\d\d)-(?P<minute>\d\d)', - '/(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'): - results += [(pattern + filter + tail, view, kwargs)] - return results |