Visual LDAP Statistics



By: rkalfane

February 27, 2008 8:48 am

Reads: 206

Comments:0

Rating:0

Instant Charts with Chart API

Table of Contents

        Introduction

        Content of the attached archive

        Using the script

        Technical Details

            Easy parsing of command-line options

            Loading Data

            Preparing the statistics arrays

            Generating the charts

            Generating the final HTML output

        Summary




Introduction

As discussed in the tip from Akos about LDAP statistics, you can find interesting numbers by quering eDirectory at the root level. This article will quickly show you how to present these numbers in a nice way with graphical charts. For this example I used the Google Chart API as it is easy to use and demonstrate. If you don’t really want to submit information to Google, you can also install an equivalent software on your server. One interesting solution is to use the Eastwood product, which uses the JFreeChart library to generate graphs, and which emulates the Google Chart API.



The attached script also uses the LDIFStruct library discussed in the third part of the scripting article. Either based on a LDIF export of the Root DSE or by connecting directly to a LDAP server, the script will generate HTML content you can save to a file. Here is an overview of the result you can obtain:



LDAP Stats



The source code of the script is quite simple and could be adapted in other languages such as Php if you want to make an online tool with it. Also, instead of producing HTML content, it is very easy to modify it and generate .CSV, XML, or other type of content.




Content of the attached archive

Here is the content of the file ldapstats.zip:



\__ ./LDAPStats
    |__ generate_ldapstats
    |__ ldifstruct.py
    |__ docs
    |   |__ images_ldapstats
    |   |   \__ *.png
    |   |__ ldapstats.html
    |   \__ ldapstats.txt
    |__ ldif
    |   \__ root_dse.ldif
    |__ html
    |   |__ images
    |   |   \__ *.png
    |   |__ ldapstats.html
    |   \__ ldapstats_sample.html
    |__ odt
    |   \__ ldapstats_sample.odt
    \__ pdf
        \__ ldapstats_sample.pdf



Details:

  • generate_ldapstats: this is the main script to generate the HTML LDAP statistics report.
  • ldifstruct.py: the LDIFStruct library you will find in different languages in the third part of the scripting article (see the first part and the second part).
  • ldif/root_dse.ldif: sample LDIF file to test the script.
  • html/ldapstats_sample.html, odt/ldapstats_sample.odt, pdf/ldapstats_sample.pdf: example of HTML, OpenOffice Text and PDF report you can generate.
  • docs/ldapstats.txt: the Wiki source of this article.
  • docs/ldapstats.html: the result of the conversion from Wiki to HTML (see Wiki to CoolSolutions Converter).
  • docs/images_ldapstats/*.png: all the pictures used in this article.




Using the script

You can call the script using a number of useful and unuseful options from the command-line. You can get the list of options anytime using the -h or --help option:



/LDAPStats> ./generate_ldapstats -h
usage: generate_ldapstats [options]
       generate LDAP statistics in HTML format using Chart API
       -h or --help for help

examples: generate_ldapstats -m ldap -D cn=admin,o=org -H 127.0.0.1
              -w mypass > ldap_stats.html
          generate_ldapstats -f root_dse.ldif > ldap_stats.html

options:
  --version        show program's version number and exit
  -h, --help       show this help message and exit
  -c, --changelog  display changelog
  -m MODE          input mode: file or ldap [default: file]
  -f FILE          read data from LDIF file
  -H SERVER        LDAP server [default: localhost]
  -P PORT          LDAP server port [default: 389]
  -D BINDN         bind DN
  -w PASSWD        bind password
  -W               prompt for bind password
  -t "MY TITLE"    title of the report [default: "LDAP STATISTICS"]
  -v               verbose mode
  -q               quiet mode [default]



To generate the statistics from the ldif/root_dse.ldif LDIF export, you can use the following command:



./generate_ldapstats -f ldif/root_dse.ldif > html/ldapstats.html



To generate the statistics directly from a LDAP server, you can use the following command:



./generate_ldapstats -m ldap -H 127.0.0.1 -D cn=admin,o=org -w mypass > html/ldapstats.html
 



or to enter the password interactively:



./generate_ldapstats -m ldap -H 127.0.0.1 -D cn=admin,o=org -W
 




Technical Details

Easy Parsing of Command-line Options

Python offers a very easy and powerfull way to handle command-line arguments and options using the OptParser module. This feature has already been discussed in the Automatic Role Matrix article. Have a look at it!




Loading Data

The script can use two modes to load data: loading a LDIF file using the LDIFStruct library, or directly connecting to an LDAP server using the Python LDAP module.



The script loads data from a LDIF file or from a LDAP query, the same way as in the Automatic Role Matrix and the Automatic Tree Reports scripts.



You only need to specify a LDAP server and credentials (bind DN and password). From there, the script will query the Root DSE with the filter “(objectClass=*)” and get all the attributes.



# Load data from LDAP or from file
ldap_stats = {}
if options.mode == "ldap":
    try:
        l = ldap.initialize( "ldap://" + options.ldap_server + ":" + options.ldap_port )
        l.simple_bind_s( options.ldap_binddn, options.ldap_passwd )
        ldap_stats = dict( l.search_s( "", ldap.SCOPE_BASE, "(objectClass=*)", [] ) )
    except:
        print "Could not search in LDAP server"    
        sys.exit()
elif options.mode == "file":
    myLDIFStruct = LDIFStruct()
    myLDIFStruct.load( ldap_stats, options.filename )



Preparing the Statistics Arrays

There are six different types of statistics built in the script:



  • Searches statistics
  • Binds statistics
  • Errors statistics
  • Input/Output statistics
  • Entries statistics
  • Other Operations statistics



We can easily build these arrays extracting data from the LDIF export or the LDAP query the same way, as it produces the exact same kind of structure (you can read the first part of the scripting article to better understand how associative arrays can be used):



# Build the different statistics
# Searches
stats_search = {}
stats_search[ "searchOps" ]             = int( ldap_stats[ "" ][ "searchOps" ][ 0 ] )
stats_search[ "oneLevelSearchOps" ]     = int( ldap_stats[ "" ][ "oneLevelSearchOps" ][ 0 ] )
stats_search[ "wholeSubtreeSearchOps" ] = int( ldap_stats[ "" ][ "wholeSubtreeSearchOps" ][ 0 ] )

# Binds
stats_bind = {}
stats_bind[ "simpleAuthBinds" ]    = int( ldap_stats[ "" ][ "simpleAuthBinds" ][ 0 ] )
stats_bind[ "strongAuthBinds" ]    = int( ldap_stats[ "" ][ "strongAuthBinds" ][ 0 ] )
stats_bind[ "unAuthBinds" ]        = int( ldap_stats[ "" ][ "unAuthBinds" ][ 0 ] )
stats_bind[ "bindSecurityErrors" ] = int( ldap_stats[ "" ][ "bindSecurityErrors" ][ 0 ] )

# Errors
stats_error = {}
stats_error[ "abandonOps" ]         = int( ldap_stats[ "" ][ "abandonOps" ][ 0 ] )
stats_error[ "errors" ]             = int( ldap_stats[ "" ][ "errors" ][ 0 ] )
stats_error[ "securityErrors" ]     = int( ldap_stats[ "" ][ "securityErrors" ][ 0 ] )
stats_error[ "bindSecurityErrors" ] = int( ldap_stats[ "" ][ "bindSecurityErrors" ][ 0 ] )

# Input/output
stats_io = {}
stats_io[ "inBytes" ]  = int( ldap_stats[ "" ][ "inBytes" ][ 0 ] )
stats_io[ "outBytes" ] = int( ldap_stats[ "" ][ "outBytes" ][ 0 ] )

# Entries
stats_entry = {}
stats_entry[ "addEntryOps" ]    = int( ldap_stats[ "" ][ "addEntryOps" ][ 0 ] )
stats_entry[ "removeEntryOps" ] = int( ldap_stats[ "" ][ "removeEntryOps" ][ 0 ] )
stats_entry[ "modifyRDNOps" ]   = int( ldap_stats[ "" ][ "modifyRDNOps" ][ 0 ] )

# Other ops
stats_other = {}
stats_other[ "readOps" ]    = int( ldap_stats[ "" ][ "readOps" ][ 0 ] )
stats_other[ "compareOps" ] = int( ldap_stats[ "" ][ "compareOps" ][ 0 ] )
stats_other[ "modifyEntryOps" ] = int( ldap_stats[ "" ][ "modifyEntryOps" ][ 0 ] )
stats_other[ "listOps" ]    = int( ldap_stats[ "" ][ "listOps" ][ 0 ] )



Once we have prepared the different tables containing the statistics, we have to create a generic function building charts from them.



Generating the charts

The Google Chart API uses simple values to specify the data series. The following table shows you the correspondence between letters and their values:



A 0
B 1
C 2
X 23
Y 24
Z 25
a 26
b 27
c 28
x 49
y 50
z 51
0 52
1 53
8 60
9 61



For instance, the string “Bcz8a” can be used for the following series: 1,28,51,60,26.



Here is the Python code used to define the generic function that will generate charts:



# Chart simple values
gvalues = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

def getChart( dic ):
  # Max, chart data, steps calculation
  maxdicvalue = max( dic.values() )
  log = math.log10( maxdicvalue )
  unit = math.pow( 10, math.floor( log ) )
  maxvalue = int( math.ceil( maxdicvalue / unit ) * unit )
  if maxvalue < 10: maxvalue = 10
  data = [ gvalues[ v*61/maxvalue ] for (k,v) in sorted( dic.items(), key=itemgetter(1) ) ]
  axisSteps = [ maxvalue/4, maxvalue/2, maxvalue*3/4, maxvalue ]

  # Chart setup
  strChartData   = "s:" + "".join( data )
  strChartType   = "bhg"
  strChartBar    = "d00c0b"
  strChartBack   = "bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75"
  strChartGrid   =  "25.0,100.0"
  strChartWidth  = "400"
  strChartHeight = "150"
  strChartSize   = "%sx%s" % ( strChartWidth, strChartHeight )
  strChartAxis   = "x,y"
  strChartLabels = "0:|0|" + "|".join( [ str(v) for v in axisSteps ]  ) + "|1:|" + "|".join( [ '%s (%s)' % (k,v) for (k,v) in sorted( dic.items(), reverse=True, key=itemgetter(1) ) ] )
strChartLabels = re.sub( " ", "+", strChartLabels )

return '<img width="%s" height="%s" src="http://chart.apis.google.com/chart?cht=%s&chco=%s&chf=%s&chg=%s&chs=%s&chxt=%s&chd=%s&chxl=%s"/>' \
    % ( strChartWidth, strChartHeight, strChartType, strChartBar, strChartBack, strChartGrid, strChartSize, strChartAxis, strChartData, strChartLabels )



1. Given a dictionary holding keys and integer values for each key, the first step is to extract the max value:



  maxdicvalue = max( dic.values() )



Let’s look at the stats_bind table:



stats_bind
{
    'simpleAuthBinds': 52080,
    'bindSecurityErrors': 23, 
    'unAuthBinds': 41904, 
    'strongAuthBinds': 6700
}



In this case, the value for maxdicvalue is 52080.



2. Using a logarithm base 10 function, we can easily extract the number of digits from this value:



  log = math.log10( maxdicvalue )



In that case, if maxdicvalue is 52080, log value is 4.7166…



3. If you use the floor value of log, which is 4, you can build the unit to use for the data:



  unit = math.pow( 10, math.floor( log ) )



In our case, unit value is 10000. For instance we have the following units:

  • for maxdicvalue = 23, unit = 10
  • for maxdicvalue = 670, unit = 100
  • for maxdicvalue = 3200, unit = 1000
  • for maxdicvalue = 45000, unit = 10000
  • etc.



4. From there, you can get the next number after maxdicvalue using this unit:



  maxvalue = int( math.ceil( maxdicvalue / unit ) * unit )



In our case, maxvalue value is 60000. For instance we have the following max values:

  • for maxdicvalue = 23, maxvalue = 30
  • for maxdicvalue = 670, maxvalue = 700
  • for maxdicvalue = 3200, maxvalue = 4000
  • for maxdicvalue = 45000, maxvalue = 50000
  • etc.



5. In case the max value is lower than 10, we use 10 for the maximum:



  if maxvalue < 10: maxvalue = 10



6. From there we can transform the data series provided by the dictionary to Google API values, knowing that the maximum value is equivalent to 61:



  data = [ gvalues[ v*61/maxvalue ] for (k,v) in sorted( dic.items(), key=itemgetter(1) ) ]



In our case, we get the following result:



  data = [ "A", "G", "q", "0" ]



… which can then be transformed into the string “AGq0″:



  strChartData = "s:" + "".join( data )



7. We can then build the labels for the horizontal axis:



  axisSteps = [ maxvalue/4, maxvalue/2, maxvalue*3/4, maxvalue ]



8. We can set up the chart using the following code:



  # Chart setup
  strChartData   = "s:" + "".join( data )
  strChartType   = "bhg"
  strChartBar    = "d00c0b"
  strChartBack   = "bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75"
  strChartGrid   =  "25.0,100.0"
  strChartWidth  = "400"
  strChartHeight = "150"
  strChartSize   = "%sx%s" % ( strChartWidth, strChartHeight )
  strChartAxis   = "x,y"
  strChartLabels = "0:|0|" + "|".join( [ str(v) for v in axisSteps ]  ) + "|1:|" + "|".join( [ '%s (%s)' % (k,v) for (k,v) in sorted( dic.items(), reverse=True, key=itemgetter(1) ) ] )
  strChartLabels = re.sub( " ", "+", strChartLabels )



Here you can see graphical settings like:

  • the color of the chart bars (strChartBar),
  • the background style and color (strChartBack),
  • the size of the graph (strChartWidth, strChartHeight and strChartSize),
  • the grid to display (strChartGrid),
  • the axis labels (strChartLabels)



The following settings relate to the data:

  • the type of chart (strChartType),
  • the chart axis (strChartAxis),
  • the data series (strChartData)



9. Finally, we generate the chart URL using the Google Chart API.



return '<img width="%s" height="%s" src="http://chart.apis.google.com/chart?cht=%s&chco=%s&chf=%s&chg=%s&chs=%s&chxt=%s&chd=%s&chxl=%s"/>' \
    % ( strChartWidth, strChartHeight, strChartType, strChartBar, strChartBack, strChartGrid, strChartSize, strChartAxis, strChartData, strChartLabels )



LDAP Stats



You can get all the information about the API, the different chart types and the variables you can use to setup your charts on http://code.google.com/apis/chart/.



Generating the final HTML output
From there, we build the final output. We can prepare the title and the footer using the following code:



# Build title
title = options.title
if options.mode == "ldap":
    title = title + " (ldap://%s:%s)" % ( options.ldap_server, options.ldap_port )
else:
    title = title + " (%s)" % options.filename
footer = time.strftime("Page generated on the %d/%m/%Y at %H:%M:%S", time.localtime())



From there, we can generate the content very simply using a kind of template mechanism. It is then easy to change the content or generate a new type of content (note the different “%s” strings that will be replaced by titles and charts in the end):



# Final HTML output
print """<html>
    <head>
        <title>%s</title>
        <style>
            html { overflow: -moz-scrollbars-vertical; }
            body    { padding: 10px; background-color: #666666; }
            *       { font-family: Arial; font-size: 12px; }
            th      { height: 22px; background: #cccccc url('images/title2_bg.png') 0 0 repeat-x; }
            td      { border-width: 1px; border-style: dotted; border-color: #999999; background-color: #efefef; }
            #s_t    { margin:0 auto; background: #ffffff url('images/s_l.png') 0 0 repeat-y; width:860px; }
            #s_t_r  { margin:0 auto; background: url('images/s_r.png') 100%% 0 repeat-y; width:100%%; }
            #s_t2   { margin:0 auto; background: url('images/s_t.png') 0 0 repeat-x; width:100%%; }
            #s_t2_l { margin:0 auto; background: url('images/s_t_l.png') 0 0 no-repeat; width:100%%; }
            #s_t2_r { margin:0 auto; background: url('images/s_t_r.png') 100%% 0 no-repeat; width:100%%; }
            #s_b    { margin:0 auto; background: #ffffff url('images/s_b.png') 0 100%% repeat-x; width:860px; }
            #s_b_l  { margin:0 auto; background: url('images/s_b_l.png') 0 100%% no-repeat; width:100%%; }
            #s_b_r  { padding-bottom: 20px; margin:0 auto; background: url('images/s_b_r.png') 100%% 100%% no-repeat; width:100%%; }
        </style>
    </head>
    <body>
        <div id="s_t">
        <div id="s_t_r">
        <div id="s_t2">
        <div id="s_t2_l">
        <div id="s_t2_r">
        <table cellpadding="4" cellspacing="4" align="center" style="padding-top: 20px;">
            <tr>
                <th style="height: 32px; background: #666666 url('images/title_bg.png') 0 0 repeat-x; color: #ffffff; font-size: 20px;" colspan="2">
                    %s
                </th>
            </tr>
            <tr>
                <td colspan="2" style="padding: 0px; border: none;"><img width="824" height="100" src="images/banner.png"/></td>
            </tr>
            <tr>
                <th>Searches Statistics</th>
                <th>Binds Statistics</th>
            </tr>
            <tr>
                <td>%s</td>
                <td>%s</td>
            </tr>
            <tr>
                <th>Errors Statistics</th>
                <th>Input/Output Statistics</th>
            </tr>
            <tr>
                <td>%s</td>
                <td>%s</td>
            </tr>
            <tr>
                <th>Entries Statistics</th>
                <th>Other Ops Statistics</th>
            </tr>
            <tr>
                <td>%s</td>
                <td>%s</td>
            </tr>
            <tr>
                <th colspan="2" style="color: #999999;">%s</th>
            </tr>
        </table>
        </div>
        </div>
        </div>
        </div>
        </div>
        </div>
        <div id="s_b">
        <div id="s_b_l">
        <div id="s_b_r">
        </div>
        </div>
        </div>
    </body>
</html>""" % ( title, title, getChart( stats_search ), getChart( stats_bind ), getChart( stats_error ), getChart( stats_io ), getChart( stats_entry ), getChart( stats_other ), footer )



The final HTML code produced by the script is quite short, clean, and easy to understand or improve:



<html>
    <head>
        <title>LDAP STATISTICS (ldap://172.17.2.91:389)</title>
        <style>
            html { overflow: -moz-scrollbars-vertical; }
            body    { padding: 10px; background-color: #666666; }
            *       { font-family: Arial; font-size: 12px; }
            th      { height: 22px; background: #cccccc url('images/title2_bg.png') 0 0 repeat-x; }
            td      { border-width: 1px; border-style: dotted; border-color: #999999; background-color: #efefef; }
            #s_t    { margin:0 auto; background: #ffffff url('images/s_l.png') 0 0 repeat-y; width:860px; }
            #s_t_r  { margin:0 auto; background: url('images/s_r.png') 100% 0 repeat-y; width:100%; }
            #s_t2   { margin:0 auto; background: url('images/s_t.png') 0 0 repeat-x; width:100%; }
            #s_t2_l { margin:0 auto; background: url('images/s_t_l.png') 0 0 no-repeat; width:100%; }
            #s_t2_r { margin:0 auto; background: url('images/s_t_r.png') 100% 0 no-repeat; width:100%; }
            #s_b    { margin:0 auto; background: #ffffff url('images/s_b.png') 0 100% repeat-x; width:860px; }
            #s_b_l  { margin:0 auto; background: url('images/s_b_l.png') 0 100% no-repeat; width:100%; }
            #s_b_r  { padding-bottom: 20px; margin:0 auto; background: url('images/s_b_r.png') 100% 100% no-repeat; width:100%; }
        </style>
    </head>
    <body>
        <div id="s_t">
        <div id="s_t_r">
        <div id="s_t2">
        <div id="s_t2_l">
        <div id="s_t2_r">
        <table cellpadding="4" cellspacing="4" align="center" style="padding-top: 20px;">
            <tr>
                <th style="height: 32px; background: #666666 url('images/title_bg.png') 0 0 repeat-x; color: #ffffff; font-size: 20px;" colspan="2">
                    LDAP STATISTICS (ldap://172.17.2.91:389)
                </th>
            </tr>
            <tr>
                <td colspan="2" style="padding: 0px; border: none;"><img width="824" height="100" src="images/banner.png"/></td>
            </tr>
            <tr>
                <th>Searches Statistics</th>
                <th>Binds Statistics</th>
            </tr>
            <tr>
                <td><img width="400" height="150" src="http://chart.apis.google.com/chart?cht=bhg&chco=d00c0b&chf=bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75&chg=25.0,100.0&chs=400x150&chxt=x,y&chd=s:AUf&chxl=0:|0|50000|100000|150000|200000|1:|searchOps+(101741)|wholeSubtreeSearchOps+(66403)|oneLevelSearchOps+(1378)"/></td>
                <td><img width="400" height="150" src="http://chart.apis.google.com/chart?cht=bhg&chco=d00c0b&chf=bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75&chg=25.0,100.0&chs=400x150&chxt=x,y&chd=s:AAu5&chxl=0:|0|15000|30000|45000|60000|1:|simpleAuthBinds+(56504)|unAuthBinds+(45420)|bindSecurityErrors+(31)|strongAuthBinds+(0)"/></td>
            </tr>
            <tr>
                <th>Errors Statistics</th>
                <th>Input/Output Statistics</th>
            </tr>
            <tr>
                <td><img width="400" height="150" src="http://chart.apis.google.com/chart?cht=bhg&chco=d00c0b&chf=bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75&chg=25.0,100.0&chs=400x150&chxt=x,y&chd=s:AAAq&chxl=0:|0|5000|10000|15000|20000|1:|errors+(13920)|abandonOps+(96)|bindSecurityErrors+(31)|securityErrors+(31)"/></td>
                <td><img width="400" height="150" src="http://chart.apis.google.com/chart?cht=bhg&chco=d00c0b&chf=bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75&chg=25.0,100.0&chs=400x150&chxt=x,y&chd=s:V5&chxl=0:|0|17500000|35000000|52500000|70000000|1:|outBytes+(65982678)|inBytes+(24719440)"/></td>
            </tr>
            <tr>
                <th>Entries Statistics</th>
                <th>Other Ops Statistics</th>
            </tr>
            <tr>
                <td><img width="400" height="150" src="http://chart.apis.google.com/chart?cht=bhg&chco=d00c0b&chf=bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75&chg=25.0,100.0&chs=400x150&chxt=x,y&chd=s:AAq&chxl=0:|0|2|5|7|10|1:|addEntryOps+(7)|modifyRDNOps+(0)|removeEntryOps+(0)"/></td>
                <td><img width="400" height="150" src="http://chart.apis.google.com/chart?cht=bhg&chco=d00c0b&chf=bg,s,efefef|c,lg,45,ffffff,0,ffcccc,0.75&chg=25.0,100.0&chs=400x150&chxt=x,y&chd=s:AASz&chxl=0:|0|10000|20000|30000|40000|1:|readOps+(33960)|modifyEntryOps+(12437)|compareOps+(10)|listOps+(0)"/></td>
            </tr>
            <tr>
                <th colspan="2" style="color: #999999;">Page generated on the 20/02/2008 at 13:45:34</th>
            </tr>
        </table>
        </div>
        </div>
        </div>
        </div>
        </div>
        </div>
        <div id="s_b">
        <div id="s_b_l">
        <div id="s_b_r">
        </div>
        </div>
        </div>
    </body>
</html>



I am using some images for title backgrounds, shadows and banner but you can remove all these, as well as all the styles and “div” tags, for a simplier version of the report.




Summary

The goal of this article is not only to find a nice way to present LDAP statistics, but also to introduce you to the Chart API. As I said at the beginning of the article, you can also install your own Chart Server instead of using the Google service. If you want to produce statistics with non-confidential data, you can use the Google API without a problem. If you are more concerned about security and don’t want to let your statistics go out, you can use the Eastwood product to easily generate your charts internally.



If you have a look at the source code, you will see that it is possible to re-use and improve the chart generation function. You can even build (if it does not exist yet) a full API in your favorite language to generate any kind of charts using the Google Chart API or the Eastwood project. Happy charting!

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)

Tags: , , ,
Categories: Uncategorized

Disclaimer: As with everything else at NetIQ Cool Solutions, this content is definitely not supported by NetIQ, so Customer Support will not be able to help you if it has any adverse effect on your environment.  It just worked for at least one person, and perhaps it will be useful for you too.  Be sure to test in a non-production environment.

Comment