[CalendarServer-changes] [574] CalendarServer/branches/caladmin-tool/caladmin

source_changes at macosforge.org source_changes at macosforge.org
Wed Nov 22 16:03:33 PST 2006


Revision: 574
          http://trac.macosforge.org/projects/calendarserver/changeset/574
Author:   dreid at apple.com
Date:     2006-11-22 16:03:32 -0800 (Wed, 22 Nov 2006)

Log Message:
-----------
better, more flexible reporting, removal of quotas subcommand quota information is now conveyed in the principal subcommands (users, resources, groups)

Modified Paths:
--------------
    CalendarServer/branches/caladmin-tool/caladmin/formatters.py
    CalendarServer/branches/caladmin-tool/caladmin/options.py
    CalendarServer/branches/caladmin-tool/caladmin/principals.py
    CalendarServer/branches/caladmin-tool/caladmin/script.py
    CalendarServer/branches/caladmin-tool/caladmin/stats.py
    CalendarServer/branches/caladmin-tool/caladmin/util.py

Removed Paths:
-------------
    CalendarServer/branches/caladmin-tool/caladmin/quotas.py

Modified: CalendarServer/branches/caladmin-tool/caladmin/formatters.py
===================================================================
--- CalendarServer/branches/caladmin-tool/caladmin/formatters.py	2006-11-22 23:50:23 UTC (rev 573)
+++ CalendarServer/branches/caladmin-tool/caladmin/formatters.py	2006-11-23 00:03:32 UTC (rev 574)
@@ -20,8 +20,8 @@
 
 FORMATTERS = {}
 
-def registerFormatter(short, formatter):
-    FORMATTERS[short] = formatter
+def registerFormatter(formatter):
+    FORMATTERS[formatter.name] = formatter
 
 def listFormatters():
     return FORMATTERS.keys()
@@ -31,32 +31,119 @@
 
 
 class BaseFormatter(object):
-    def __init__(self, dst=None):
-        self.dst = dst
+    config = None
+
+    def __init__(self, dest=None, options=None):
+        self.dest = dest
         
-        if not self.dst:
-            self.dst = sys.stdout
+        if not self.dest:
+            self.dest = sys.stdout
 
+        self.options = options
 
+        if not options:
+            self.options = {}
+
+        self.reportTypes = []
+
+        for attr in self.__dict__:
+            if attr.startswith('report_'):
+                self.reportTypes.append(attr.split('_', 1)[1])
+
+    def write(self, data):
+        self.dest.write(data)
+        self.dest.flush()
+
+    def close(self):
+        self.dest.close()
+
+    def printReport(self, report):
+        reportPrinter = getattr(self, 'report_%s' % (report['type'],), None)
+
+        if reportPrinter:
+            reportPrinter(report)
+
+        else:
+            self.report_default(report)
+    
+    def report_default(self, report):
+        import pprint
+
+        preport = pprint.pformat(report)
+
+        self.write(''.join([preport, '\n']))
+        self.close()
+
+
 class PlainFormatter(BaseFormatter):
-    def printRow(self, row, spacelen):
-        for el in row:
-            self.dst.write(str(el))
-            self.dst.write(' '*(spacelen - len(str(el))))
-            
-        self.dst.write('\n')
+    name = "plain"
 
-registerFormatter('plain', PlainFormatter)
+registerFormatter(PlainFormatter)
 
 
+import csv
+
 class CsvFormatter(BaseFormatter):
-    def printRow(self, row, spacelen):
-        for el in row:
-            self.dst.write(str(el))
-            self.dst.write(',')
+    name = "csv"
+
+    def writeList(self, fieldnames, l):
+        dw = csv.DictWriter(self.dest,
+                            **self.options)
+
+        dw.writerow(dict(zip(fieldnames,
+                             fieldnames)))
+
+        dw.writerows(l)
+
+    def report_principals(self, report):
+        if 'fieldnames' not in self.options:
+            self.options['fieldnames'] = [
+                'principalName',
+                'calendarHome',
+                'calendarCount',
+                'eventCount',
+                'todoCount',
+                'disabled',
+                'diskUsage',
+                'quotaRoot',
+                'quotaUsed',
+                'quotaAvail',
+                'quotaFree']
+            
+        self.writeDict(self.options['fieldnames'],
+                       report['records'])
         
-        self.dst.write('\n')
+    report_users = report_groups = report_resources = report_principals
 
-registerFormatter('csv', CsvFormatter)
+    def report_stats(self, report):
+        if 'fieldnames' not in self.options:
+            self.options['fieldnames'] = report['data'].keys()
+            self.options['fieldnames'].sort()
 
+        self.writeList(self.options['fieldnames'],
+                       [report['data']])
+                
+    
+registerFormatter(CsvFormatter)
 
+import plistlib
+
+class PlistFormatter(BaseFormatter):
+    name = "plist"
+
+    def report_principals(self, report):
+        plist = plistlib.Dict()
+
+        plist[report['type']] = list(report['records'])
+
+        plistlib.writePlist(plist, self.dest)
+
+    report_users = report_groups = report_resources = report_principals
+
+    def report_stats(self, report):
+        plist = plistlib.Dict()
+        plist[report['type']] = report['data']
+
+        plistlib.writePlist(plist, self.dest)
+
+registerFormatter(PlistFormatter)

Modified: CalendarServer/branches/caladmin-tool/caladmin/options.py
===================================================================
--- CalendarServer/branches/caladmin-tool/caladmin/options.py	2006-11-22 23:50:23 UTC (rev 573)
+++ CalendarServer/branches/caladmin-tool/caladmin/options.py	2006-11-23 00:03:32 UTC (rev 574)
@@ -52,7 +52,10 @@
         self.params += rest
 
     def postOptions(self):
-        reflect.namedAny(self.action)(self).run()
+        
+        report = reflect.namedAny(self.action)(self).run()
+        self.parent.formatter.config = self
+        self.parent.formatter.printReport(report)
 
 
 PARAM_HUMAN = ['human', 'h', 'Display byte values in a human readable form.']
@@ -61,48 +64,6 @@
 PARAM_GIGA = ['gigabytes', 'g', 'Display byte values in gigabytes']
 
 
-class QuotaOptions(SubCommand):
-    name = 'quotas'
-    help = 'Retrieve quota information for principals'
-    action = 'caladmin.quotas.QuotaAction'
-
-    optFlags = [
-        PARAM_HUMAN,
-        PARAM_KILO,
-        PARAM_MEGA,
-        PARAM_GIGA,
-        ]
-         
-    def __init__(self):
-        SubCommand.__init__(self)
-
-        self['types'] = []
-
-    def opt_users(self):
-        """Show Quotas for user calendars.
-        """
-        
-        self['types'].append('users')
-    opt_u = opt_users
-
-    def opt_groups(self):
-        """Show Quotas for group calendars.
-        """
-        
-        self['types'].append('groups')
-    opt_g = opt_groups
-
-    def opt_resources(self):
-        """Show Quotas for resource calendars.
-        """
-        
-        self['types'].append('resources')
-    opt_r = opt_resources
-
-
-registerCommand(QuotaOptions)
-
-
 class PurgeOptions(SubCommand):
     name = 'purge'
     help = ('Keep your store from becoming unnecessarily large by purging '
@@ -130,6 +91,7 @@
 
 registerCommand(StatsOptions)
 
+
 from twisted.python import filepath
 from twistedcaldav.caldavd import caldavd_defaults
 
@@ -192,7 +154,8 @@
         ]
 
     def postOptions(self):
-        reflect.namedAny(self.action)(self, self.name).run()
+        report = reflect.namedAny(self.action)(self, self.name).run()
+        self.parent.formatter.printReport(report)
 
 
 class UserOptions(PrincipalOptions):

Modified: CalendarServer/branches/caladmin-tool/caladmin/principals.py
===================================================================
--- CalendarServer/branches/caladmin-tool/caladmin/principals.py	2006-11-22 23:50:23 UTC (rev 573)
+++ CalendarServer/branches/caladmin-tool/caladmin/principals.py	2006-11-23 00:03:32 UTC (rev 574)
@@ -34,16 +34,18 @@
         self.root = self.config.parent.root
         self.calendarCollection = self.config.parent.calendarCollection
         self.principalCollection = self.config.parent.principalCollection
-        
+    
     def run(self):
+        report = {'type': self.type,
+                  'records': []}
+
         if not self.config.params:
-            principals = util.getPrincipalList(self.principalCollection, 
-                                               self.type, 
+            principals = util.getPrincipalList(self.principalCollection,
+                                               self.type,
                                                disabled=self.config['disabled'])
 
         else:
             principals = []
-
             for p in self.config.params:
                 p = self.principalCollection.child(self.type).child(p)
 
@@ -51,27 +53,37 @@
                     if self.config['disabled']:
                         if util.isPrincipalDisabled(p):
                             principals.append(p)
+
                     else:
                         principals.append(p)
 
-        if not self.config['list']:
-            self.formatter.printRow(['Name', 'Calendars', 'Events', 'Todos', 
-                                     'Disk Usage'], 16)
+        def _getRecords():
+            for p in principals:
+                precord = {}
+                
+                pcal = self.calendarCollection.child(
+                    self.type
+                    ).child(p.basename())
+            
+                precord['principalName'] = p.basename()
+                
+                precord['calendarHome'] = pcal.path
 
-        for p in principals:
-            pcal = self.calendarCollection.child(self.type).child(p.basename())
-            row = []
+                precord.update(
+                    util.getQuotaStatsForPrincipal(
+                        self.config,
+                        pcal,
+                        self.config.parent.config['UserQuotaBytes']))
 
-            row.append(p.basename())
+                precord.update(
+                    util.getCalendarDataCounts(pcal))
 
-            if not self.config['list']:
+                precord['diskUsage'] = util.getDiskUsage(self.config, pcal)
                 
-                row.extend(util.getCalendarDataCounts(pcal))
+                precord['disabled'] = util.isPrincipalDisabled(p)
                 
-                row.append(util.prepareByteValue(self.config, 
-                                                 util.getDiskUsage(pcal)))
-                
-            self.formatter.printRow(row, 16)
+                yield precord
 
-                    
-                
+        report['records'] = _getRecords()
+        
+        return report

Deleted: CalendarServer/branches/caladmin-tool/caladmin/quotas.py
===================================================================
--- CalendarServer/branches/caladmin-tool/caladmin/quotas.py	2006-11-22 23:50:23 UTC (rev 573)
+++ CalendarServer/branches/caladmin-tool/caladmin/quotas.py	2006-11-23 00:03:32 UTC (rev 574)
@@ -1,114 +0,0 @@
-##
-# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# DRI: David Reid, dreid at apple.com
-##
-
-import xattr
-
-from twisted.web2.dav.resource import TwistedQuotaRootProperty, TwistedQuotaUsedProperty
-from twisted.web import microdom
-
-from caladmin.util import prepareByteValue
-
-quotaRoot = "WebDAV:" + TwistedQuotaRootProperty.sname().replace("/", "%2F")
-quotaUsed = "WebDAV:" + TwistedQuotaUsedProperty.sname().replace("/", "%2F")
-
-def getQuotaRoot(fp):
-    x = xattr.xattr(fp.path)
-    if not x.has_key(quotaRoot):
-        return None
-
-    dom = microdom.parseString(x[quotaRoot])
-
-    qr = microdom.getElementsByTagName(dom, 'quota-root')[0]
-
-    return int(qr.firstChild().value)
-
-
-def getQuotaUsed(fp):
-    x = xattr.xattr(fp.path)
-    if not x.has_key(quotaUsed):
-        return None
-
-    dom = microdom.parseString(x[quotaUsed])
-
-    qu = microdom.getElementsByTagName(dom, 'quota-used')[0]
-
-    return int(qu.firstChild().value)
-        
-
-class QuotaAction(object):
-    def __init__(self, config):
-        self.config = config
-        self.userQuotaBytes = config.parent.config['UserQuotaBytes']
-        self.calendarCollection = config.parent.calendarCollection
-        self.principalCollection = config.parent.principalCollection
-        self.formatter = config.parent.formatter
-
-    def getQuotaStats(self):
-
-        defaultQuota = getQuotaRoot(self.calendarCollection)
-        if not defaultQuota:
-            defaultQuota = self.userQuotaBytes
-
-        for type in self.config['types']:
-
-            typeRoot = self.calendarCollection.child(type)
-
-            typePrincipals = self.principalCollection.child(type)
-
-            if not typeRoot.exists() or not typePrincipals.exists():
-                continue
-
-            typeQuota = getQuotaRoot(typeRoot)
-            if not typeQuota:
-                typeQuota = defaultQuota
-            
-            for child in typePrincipals.listdir():
-                if child in ['.db.sqlite']:
-                    continue
-
-                child = typeRoot.child(child)                
-
-                childQuota = getQuotaRoot(child)
-                if not childQuota:
-                    childQuota = typeQuota
-                
-                childUsed = getQuotaUsed(child)
-                if not childUsed:
-                    childUsed = 0
-
-                childAvailable = childQuota - childUsed
-
-                yield (child.basename(),
-                       type,
-                       prepareByteValue(self.config, childQuota),
-                       prepareByteValue(self.config, childUsed),
-                       prepareByteValue(self.config, childAvailable))
-    
-    def run(self):
-        if not self.config['types']:
-            self.config['types'] = ['users', 'groups', 'resources']
-
-        self.formatter.printRow(['Name',
-                                 'Type',
-                                 'Quota',
-                                 'Used',
-                                 'Available'],
-                                16)
-                               
-        for x in self.getQuotaStats():
-            self.formatter.printRow(x, 16)

Modified: CalendarServer/branches/caladmin-tool/caladmin/script.py
===================================================================
--- CalendarServer/branches/caladmin-tool/caladmin/script.py	2006-11-22 23:50:23 UTC (rev 573)
+++ CalendarServer/branches/caladmin-tool/caladmin/script.py	2006-11-23 00:03:32 UTC (rev 574)
@@ -54,7 +54,18 @@
         usage.Options.__init__(self)
 
         self.config = None
+        self.format_options = {}
 
+    def opt_option(self, option):
+        if '=' in option:
+            k,v = option.split('=', 1)
+        
+            self.format_options[k] = v
+        else:
+            self.format_options[option] = True
+
+    opt_o = opt_option
+
     def parseArgs(self, *rest):
         self.params += rest
 
@@ -91,7 +102,8 @@
         lf.sort()
 
         if self['format'] in lf:
-            self.formatter = formatters.getFormatter(self['format'])()
+            self.formatter = formatters.getFormatter(self['format'])
+            self.formatter = self.formatter(options=self.format_options)
         else:
             raise usage.UsageError("Please specify a valid formatter: %s" % (
                     ', '.join(lf)))

Modified: CalendarServer/branches/caladmin-tool/caladmin/stats.py
===================================================================
--- CalendarServer/branches/caladmin-tool/caladmin/stats.py	2006-11-22 23:50:23 UTC (rev 573)
+++ CalendarServer/branches/caladmin-tool/caladmin/stats.py	2006-11-23 00:03:32 UTC (rev 574)
@@ -51,59 +51,43 @@
             self.getAccountCount,
             self.getGroupCount,
             self.getResourceCount,
-            self.getCalendarCount,
-            self.getEventCount,
-            self.getTodoCount,
             self.getDiskUsage]
 
     def getDiskUsage(self):
-        return ("Disk Usage", 
-                util.prepareByteValue(self.config,
-                                      util.getDiskUsage(self.root)))
+        return ("diskUsage", 
+                util.getDiskUsage(self.config, self.root))
 
     def getAccountCount(self):
-        return ("# Accounts", 
+        return ("accountCount", 
                 len(util.getPrincipalList(
                     self.principalCollection,
                     'users')))
 
     def getGroupCount(self):
-        return ("# Groups", 
+        return ("groupCount", 
                 len(util.getPrincipalList(
                     self.principalCollection,
                     'groups')))
 
     def getResourceCount(self):
-        return ("# Resources", 
+        return ("resourceCount", 
                 len(util.getPrincipalList(
                     self.principalCollection,
                     'resources')))
 
-    def getEventCount(self):
-        return ("# Events", self.eventCount)
-
-    def getTodoCount(self):
-        return ("# Todos", self.todoCount)
-
-    def getCalendarCount(self):
-        return ("# Calendars", self.calCount)
-
-    def printStatistics(self, head, stats):
-        self.formatter.printRow([head], 16)
-
-        for stat in stats:
-            self.formatter.printRow(stat, 16)
-
     def run(self):
         assert self.root.exists()
         stats = []
 
-        (self.calCount, 
-         self.eventCount, 
-         self.todoCount) = util.getCalendarDataCounts(self.calendarCollection)
+        report = {'type': 'stats',
+                  'data': {}}
 
+        report['data'].update(
+            util.getCalendarDataCounts(
+                self.calendarCollection))
+
         for gatherer in self.gatherers:
-            stats.append(gatherer())
+            stat, value = gatherer()
+            report['data'][stat] = value
 
-        self.printStatistics("Overall Statistics", stats)
-
+        return report

Modified: CalendarServer/branches/caladmin-tool/caladmin/util.py
===================================================================
--- CalendarServer/branches/caladmin-tool/caladmin/util.py	2006-11-22 23:50:23 UTC (rev 573)
+++ CalendarServer/branches/caladmin-tool/caladmin/util.py	2006-11-23 00:03:32 UTC (rev 574)
@@ -76,15 +76,14 @@
     return pl
 
 
-def getDiskUsage(fp):
-
+def getDiskUsage(config, fp):
     status, output = commands.getstatusoutput(
         ' '.join(['/usr/bin/du', '-s', fp.path]))
     
     if status != 0:
         return 0
 
-    return int(output.split()[0])
+    return prepareByteValue(config, int(output.split()[0]))
 
 
 def getResourceType(fp):
@@ -132,8 +131,65 @@
             elif component.resourceType() == 'VTODO':
                 todoCount += 1
 
-    return (calCount, eventCount, todoCount)
+    return {'calendarCount': calCount, 
+            'eventCount': eventCount,
+            'todoCount': todoCount}
 
 
 def isPrincipalDisabled(principal):
     return False
+
+
+from twisted.web2.dav.resource import TwistedQuotaRootProperty, TwistedQuotaUsedProperty
+
+quotaRoot = "WebDAV:" + TwistedQuotaRootProperty.sname().replace("/", "%2F")
+quotaUsed = "WebDAV:" + TwistedQuotaUsedProperty.sname().replace("/", "%2F")
+
+def getQuotaRoot(fp):
+    x = xattr.xattr(fp.path)
+    if not x.has_key(quotaRoot):
+        return None
+
+    dom = microdom.parseString(x[quotaRoot])
+
+    qr = microdom.getElementsByTagName(dom, 'quota-root')[0]
+
+    return int(qr.firstChild().value)
+
+
+def getQuotaUsed(fp):
+    x = xattr.xattr(fp.path)
+    if not x.has_key(quotaUsed):
+        return None
+
+    dom = microdom.parseString(x[quotaUsed])
+
+    qu = microdom.getElementsByTagName(dom, 'quota-used')[0]
+
+    return int(qu.firstChild().value)
+
+
+def getQuotaStatsForPrincipal(config, principal, defaultQuota=None, depth=2):
+    quotaRoot = principal
+
+    principalQuota = getQuotaRoot(quotaRoot)
+
+    while not principalQuota and depth > 0:
+        depth -= 1
+        quotaRoot = quotaRoot.parent()
+        principalQuota = getQuotaRoot(quotaRoot)
+
+    if not principalQuota:
+        principalQuota = defaultQuota
+
+    principalUsed = getQuotaUsed(principal)
+    if not principalUsed:
+        principalUsed = 0
+        
+    principalAvail = principalQuota - principalUsed
+    principalFree = (float(principalAvail)/principalQuota)*100
+
+    return {'quotaRoot': prepareByteValue(config, principalQuota), 
+            'quotaUsed': prepareByteValue(config, principalUsed),
+            'quotaAvail': prepareByteValue(config, principalAvail),
+            'quotaFree': principalFree}

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20061122/ea657dca/attachment.html


More information about the calendarserver-changes mailing list