>From 5eb0ea8554567ae3b03cac66a8e79b46622a6d9f Mon Sep 17 00:00:00 2001
From: Anderson Silva <ansilva@bean2test.re.redhat.com>
Date: Tue, 16 Sep 2008 14:54:49 -0400
Subject: [PATCH] cobbler report changes

---
 cobbler/action_report.py      |  339 +++++++++++++++++++++++++++++++++++++++++
 cobbler/api.py                |    9 +
 cobbler/commands.py           |   32 +----
 cobbler/modules/cli_misc.py   |   52 -------
 cobbler/modules/cli_report.py |   66 ++++++++
 5 files changed, 418 insertions(+), 80 deletions(-)
 create mode 100644 cobbler/action_report.py
 create mode 100644 cobbler/modules/cli_report.py

diff --git a/cobbler/action_report.py b/cobbler/action_report.py
new file mode 100644
index 0000000..b56e582
--- /dev/null
+++ b/cobbler/action_report.py
@@ -0,0 +1,339 @@
+"""
+Report from a cobbler master.
+
+Copyright 2007-2008, Red Hat, Inc
+Anderson Silva <ansilva@redhat.com>
+
+
+This software may be freely redistributed under the terms of the GNU
+general public license.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+import os
+import os.path
+import xmlrpclib
+import api as cobbler_api
+from cexceptions import *
+from utils import _
+
+
+class Report:
+
+    def __init__(self, config):
+        """
+        Constructor
+        """
+        self.config = config
+        self.settings = config.settings()
+        self.api = config.api
+        self.report_type = None
+        self.report_what = None
+        self.report_name = None
+        self.report_fields = None
+
+    def reporting_csv(self, info, order):
+        """
+        Formats data on 'info' for csv output
+        """
+        outputheaders = ''
+        outputbody = ''
+        sep = ','
+
+        info_count = 0
+        for item in info:
+
+            item_count = 0
+            for key in order:
+
+                if info_count == 0:
+                    outputheaders += str(key) + sep
+
+                outputbody += str(item[key]) + sep
+
+                item_count = item_count + 1
+
+            info_count = info_count + 1
+            outputbody += '\n'
+
+        outputheaders += '\n'
+        return outputheaders + outputbody
+ 
+    def reporting_trac(self, info, order):
+        """
+        Formats data on 'info' for trac wiki table output
+        """        
+        outputheaders = ''
+        outputbody = ''
+        sep = '||'
+
+        print info
+        print order
+
+        info_count = 0
+        for item in info:
+            
+            item_count = 0
+            for key in order:
+
+
+                if info_count == 0:
+                    outputheaders += sep + str(key)
+
+                outputbody += sep + str(item[key])
+
+                item_count = item_count + 1
+
+            info_count = info_count + 1
+            outputbody += '||\n'
+
+        outputheaders += '||\n'
+        return outputheaders + outputbody
+
+    def reporting_doku(self, info, order):
+        """
+        Formats data on 'info' for doku wiki table output
+        """      
+        outputheaders = ''
+        outputbody = ''
+        sep1 = '^'
+        sep2 = '|'
+
+
+        info_count = 0
+        for item in info:
+            
+            item_count = 0
+            for key in order:
+
+                if info_count == 0:
+                    outputheaders += sep1 + key
+
+                outputbody += sep2 + item[key]
+
+                item_count = item_count + 1
+
+            info_count = info_count + 1
+            outputbody += sep2 + '\n'
+
+        outputheaders += sep1 + '\n'
+        return outputheaders + outputbody
+
+    def reporting_mediawiki(self, info, order):
+        """
+        Formats data on 'info' for mediawiki table output
+        """
+        outputheaders = ''
+        outputbody = ''
+        opentable = '{| border="1"\n'
+        closetable = '|}\n'
+        sep1 = '||'
+        sep2 = '|'
+        sep3 = '|-'
+
+
+        info_count = 0
+        for item in info:
+            
+            item_count = 0
+            for key in order:
+
+                if info_count == 0 and item_count == 0:
+                    outputheaders += sep2 + key
+                elif info_count == 0:
+                    outputheaders += sep1 + key
+
+                if item_count == 0:
+                    outputbody += sep2 + item[key]
+                else:
+                    outputbody += sep1 + item[key]
+
+                item_count = item_count + 1
+
+            info_count = info_count + 1
+            outputbody += '\n' + sep3 + '\n'
+
+        outputheaders += '\n' + sep3 + '\n'
+        return opentable + outputheaders + outputbody + closetable
+    
+    def print_formatted_data(self, data, order, report_type):
+        """
+        Used for picking the correct format to output data as
+        """
+        if report_type == "csv":
+            print self.reporting_csv(data, order)
+        if report_type == "mediawiki":
+            print self.reporting_mediawiki(data, order)
+        if report_type == "trac":
+            print self.reporting_trac(data, order)
+        if report_type == "doku":
+            print self.reporting_doku(data, order)
+
+        return True
+
+    def reporting_sorter(self, a, b):
+        """
+        Used for sorting cobbler objects for report commands
+        """
+        return cmp(a.name, b.name)
+
+    def reporting_print_sorted(self, collection):
+        """
+        Prints all objects in a collection sorted by name
+        """
+        collection = [x for x in collection]
+        collection.sort(self.reporting_sorter)
+        for x in collection:
+            print x.printable()
+        return True
+
+    def reporting_list_names2(self, collection, name):
+        """
+        Prints a specific object in a collection.
+        """
+        obj = collection.find(name=name)
+        if obj is not None:
+            print obj.printable()
+        return True
+    
+    def reporting_print_all_fields(self, collection, report_type):
+        """
+        Prints all fields in a collection as a table given the report type
+        """
+        collection = [x for x in collection]
+        collection.sort(self.reporting_sorter)
+        data = []
+        out_order = []
+        count = 0
+        for x in collection:
+            item = {}
+            structure = x.to_datastruct()
+           
+            for (key, value) in structure.iteritems():
+
+                # exception for systems which could have > 1 interface
+                if key == "interfaces":
+                    for (device, info) in value.iteritems():
+                        for (info_header, info_value) in info.iteritems():
+                            item[info_header] = device + ': ' + info_value
+                            # needs to create order list for print_formatted_fields
+                            if count == 0:
+                                out_order.append(info_header)
+                else: 
+                    item[key] = value
+                    # needs to create order list for print_formatted_fields
+                    if count == 0:
+                        out_order.append(key)                  
+
+            count = count + 1
+  
+            data.append(item) 
+
+            self.print_formatted_data(data = data, order = out_order, report_type = report_type)
+        
+        return True
+    
+    def reporting_print_x_fields(self, collection, report_type, report_fields):
+        """
+        Prints specific fields in a collection as a table given the report type
+        """
+        collection = [x for x in collection]
+        collection.sort(self.reporting_sorter)
+        data = []
+        fields_list = report_fields.replace(' ', '').split(',')
+        
+        for x in collection:
+            structure = x.to_datastruct()
+            item = {}
+            for field in fields_list:
+
+                if field in structure.keys():
+                    item[field] = structure[field]
+ 
+                # exception for systems which could have > 1 interface
+                if "interfaces" in structure.keys():
+                    for device in structure['interfaces'].keys():
+                        if field in structure['interfaces'][device]:
+                            item[field] = device + ': ' + structure['interfaces'][device][field]                    
+                else: 
+                    raise CX(_("The field you are looking for most likely doesn't exist. Try dumpvars."))
+
+            data.append(item)
+         
+            self.print_formatted_data(data = data, order = fields_list, report_type = report_type)
+                        
+        return True
+        
+    # -------------------------------------------------------
+
+    def run(self, report_what = None, report_name = None, report_type = None, report_fields = None):
+        """
+        Get remote profiles and distros and sync them locally
+        """
+               
+        """
+        1. Handles original report output
+        2. Handles all fields of report outputs as table given a format
+        3. Handles specific fields of report outputs as table given a format
+        """        
+        
+
+        if report_type == 'text' and report_fields == 'all':
+
+            if report_what in [ "all", "distros", "distro" ]:
+                if report_name:
+                    self.reporting_list_names2(self.api.distros(), report_name)
+                else:
+                    self.reporting_print_sorted(self.api.distros())
+
+            if report_what in [ "all", "profiles", "profile" ]:
+                if report_name:
+                    self.reporting_list_names2(self.api.profiles(), report_name)
+                else:
+                    self.reporting_print_sorted(self.api.profiles())
+
+            if report_what in [ "all", "systems", "system" ]:
+                if report_name:
+                    self.reporting_list_names2(self.api.systems(), report_name)
+                else:
+                    self.reporting_print_sorted(self.api.systems())
+
+            if report_what in [ "all", "repos", "repo" ]:
+                if report_name is not None:
+                    self.reporting_list_names2(self.api.repos(), report_name)
+                else:
+                    self.reporting_print_sorted(self.api.repos())
+                   
+        elif report_type == 'text' and report_fields != 'all':
+            raise CX(_("The 'text' type can only be used with field set to 'all'"))
+ 
+        elif report_type != 'text' and report_fields == 'all':
+            
+            if report_what in [ "all", "distros", "distro" ]:
+                self.reporting_print_all_fields(self.api.distros(), report_type)
+
+            if report_what in [ "all", "profiles", "profile" ]:
+                self.reporting_print_all_fields(self.api.profiles(), report_type)
+
+            if report_what in [ "all", "systems", "system" ]:
+                self.reporting_print_all_fields(self.api.systems(), report_type)
+
+            if report_what in [ "all", "repos", "repo" ]:
+                self.reporting_print_all_fields(self.api.repos(), report_type) 
+        
+        else:
+            
+            if report_what in [ "all", "distros", "distro" ]:
+                self.reporting_print_x_fields(self.api.distros(), report_type, report_fields)
+
+            if report_what in [ "all", "profiles", "profile" ]:
+                self.reporting_print_x_fields(self.api.profiles(), report_type, report_fields)
+
+            if report_what in [ "all", "systems", "system" ]:
+                self.reporting_print_x_fields(self.api.systems(), report_type, report_fields)
+
+            if report_what in [ "all", "repos", "repo" ]:
+                self.reporting_print_x_fields(self.api.repos(), report_type, report_fields)
diff --git a/cobbler/api.py b/cobbler/api.py
index f0ade5d..eaf71b6 100644
--- a/cobbler/api.py
+++ b/cobbler/api.py
@@ -33,6 +33,7 @@ import action_validate
 import action_buildiso
 import action_replicate
 import action_acl
+import action_report
 from cexceptions import *
 import sub_process
 import module_loader
@@ -521,6 +522,14 @@ class BootAPI:
               include_systems = systems
         )
 
+    def report(self, report_what = None, report_name = None, report_type = None, report_fields = None):
+        """
+        Report functionality for cobbler
+        """
+        reporter = action_report.Report(self._config)
+        return reporter.run(report_what = report_what, report_name = report_name,\
+                            report_type = report_type, report_fields = report_fields)
+
     def get_kickstart_templates(self):
         return utils.get_kickstar_templates(self)
 
diff --git a/cobbler/commands.py b/cobbler/commands.py
index bee233c..6a3c38d 100644
--- a/cobbler/commands.py
+++ b/cobbler/commands.py
@@ -291,10 +291,11 @@ class CobblerFunction:
 
         if "report" in self.args:
             if self.options.name is None:
-                self.reporting_print_sorted(collect_fn())
+                return self.api.report(report_what = self.args[1], report_name = self.options.name, \
+                               report_type = 'text', report_fields = 'all')
             else:
-                self.reporting_list_names2(collect_fn(),self.options.name)
-            return None
+                return self.api.report(report_what = self.args[1], report_name = 'all', \
+                                report_type = 'text', report_fields = 'all')
 
         if "getks" in self.args:
             if not self.options.name:
@@ -396,31 +397,6 @@ class CobblerFunction:
 
         return rc
 
-    def reporting_sorter(self, a, b):
-        """
-        Used for sorting cobbler objects for report commands
-        """
-        return cmp(a.name, b.name)
-
-    def reporting_print_sorted(self, collection):
-        """
-        Prints all objects in a collection sorted by name
-        """
-        collection = [x for x in collection]
-        collection.sort(self.reporting_sorter)
-        for x in collection:
-            print x.printable()
-        return True
-
-    def reporting_list_names2(self, collection, name):
-        """
-        Prints a specific object in a collection.
-        """
-        obj = collection.find(name=name)
-        if obj is not None:
-            print obj.printable()
-        return True
-
     def list_tree(self,collection,level):
         """
         Print cobbler object tree as a, well, tree.
diff --git a/cobbler/modules/cli_misc.py b/cobbler/modules/cli_misc.py
index fdc8d69..f2d9042 100644
--- a/cobbler/modules/cli_misc.py
+++ b/cobbler/modules/cli_misc.py
@@ -148,58 +148,6 @@ class ListFunction(commands.CobblerFunction):
 
 ########################################################
 
-class ReportFunction(commands.CobblerFunction):
-
-    def help_me(self):
-        return HELP_FORMAT % ("cobbler report","[ARGS|--help]")
-
-    def command_name(self):
-        return "report"
-
-    def add_options(self, p, args):
-        p.add_option("--what",              dest="what",   default="all",  help="distros/profiles/systems/repos")
-        p.add_option("--name",              dest="name",                   help="report on just this object")
-
-    def run(self):
-        if self.options.what not in [ "all", "distros", "profiles", "systems", "repos", "images" ]:
-            raise CX(_("Invalid value for --what"))
-
-        if self.options.what in [ "all", "distros"  ]:
-            if self.options.name:
-                self.reporting_list_names2(self.api.distros(),self.options.name)
-            else:
-                self.reporting_print_sorted(self.api.distros())
-
-        if self.options.what in [ "all", "profiles" ]:
-            if self.options.name:
-                self.reporting_list_names2(self.api.profiles(),self.options.name)
-            else:
-                self.reporting_print_sorted(self.api.profiles())
-
-        if self.options.what in [ "all", "systems"  ]:
-            if self.options.name:
-                self.reporting_list_names2(self.api.systems(),self.options.name)
-            else:
-                self.reporting_print_sorted(self.api.systems())
-
-        if self.options.what in [ "all", "repos"    ]:
-            if self.options.name:
-                self.reporting_list_names2(self.api.repos(),self.options.name)
-            else:
-                self.reporting_print_sorted(self.api.repos())
-
-        if self.options.what in [ "all", "images"    ]:
-            if self.options.name:
-                self.reporting_list_names2(self.api.images(),self.options.name)
-            else:
-                self.reporting_print_sorted(self.api.images())
-
-        return True
-
-
-
-########################################################
-
 class StatusFunction(commands.CobblerFunction):
 
     def help_me(self):
diff --git a/cobbler/modules/cli_report.py b/cobbler/modules/cli_report.py
new file mode 100644
index 0000000..420da09
--- /dev/null
+++ b/cobbler/modules/cli_report.py
@@ -0,0 +1,66 @@
+"""
+Report CLI module.
+
+Copyright 2008, Red Hat, Inc
+Anderson Silva <ansilva@redhat.com
+
+This software may be freely redistributed under the terms of the GNU
+general public license.:
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+import distutils.sysconfig
+import sys
+
+plib = distutils.sysconfig.get_python_lib()
+mod_path="%s/cobbler" % plib
+sys.path.insert(0, mod_path)
+
+from utils import _, get_random_mac
+import commands
+from cexceptions import *
+HELP_FORMAT = commands.HELP_FORMAT
+
+
+class ReportFunction(commands.CobblerFunction):
+    
+    def help_me(self):
+        return HELP_FORMAT % ("cobbler report","[ARGS|--help]")
+
+    def command_name(self):
+        return "report"
+
+    def add_options(self, p, args):
+        p.add_option("--what",              dest="what",   default="all",  help="distros/profiles/systems/repos")
+        p.add_option("--name",              dest="name",                   help="report on just this object")
+        p.add_option("--type",              dest="type",   default="text", help="text/csv/trac/doku/mediawiki")
+        p.add_option("--fields",            dest="fields", default="all" , help="what fields to display")
+
+
+    def run(self):
+        if self.options.what not in [ "all", "distros", "profiles", "systems", "repos" ]:
+            raise CX(_("Invalid value for --what"))
+        if self.options.type not in ["text", "csv", "trac", "doku", "mediawiki" ]:
+            raise CX(_("Invalid vavlue for --type"))
+ 
+        return self.api.report(report_what = self.options.what, report_name = self.options.name, \
+                               report_type = self.options.type, report_fields = self.options.fields)
+    
+########################################################
+# MODULE HOOKS
+
+def register():
+    """
+    The mandatory cobbler module registration hook.
+    """
+    return "cli"
+
+def cli_functions(api):
+    return [
+       ReportFunction(api)
+    ]
+
+
-- 
1.5.4.1

