Automatic Tree Reports for IDM Projects

Python Slithering Up a Tree

Table of Contents



        Introduction

        Content of the attached archive

        Using the script

            Generating CSV reports

            Generating XML reports

            Generating HTML reports

            Getting data from LDAP query

        Steps to create a spreadsheet document

        Technical Details

            Easy parsing of command-line options

            Loading Data

            Algorithm to build the tree

        Summary


Download files:









Introduction






HTML Tree Report

Often in IDM projects there are directory objects linked together in a hierarchical way. There are sometimes organisation objects or nested groups. The manager-employee relationship is also a good example of such structure. It is not always easy to get a visual representation of these tree structures, so this article presents a tool to generate a tree representation either in .CSV, XML, or HTML format.





Using the generate_tree Python script attached to this article and a spreadsheet tool like OpenOffice Calc, you will be able to produce report very quickly even for a large structure. The script can use a LDIF export of linked objects, but it can also directly connect to a LDAP server to retrieve data. The links can either be full DNs or just the name of the linked object. The script will work in both cases if the linked objects are all in the same container.


Content of the attached archive



Download files:












Here is the content of the file Tree.zip:





\__ ./Tree
|__ generate_tree
|__ ldifstruct.py
|__ csv
| |__ groups.csv
| |__ groups_name_links.csv
| |__ managers.csv
| \__ organisations.csv
|__ docs
| |__ /communities/media/u3740
| | \__ *.png
| |__ tree.html
| \__ tree.txt
|__ html
| |__ images
| | \__ *.gif
| |__ groups.html
| |__ groups_name_links.html
| |__ managers.html
| \__ organisations.html
|__ ldif
| |__ groups.ldif
| |__ groups_name_links.ldif
| |__ managers.ldif
| \__ organisations.ldif
|__ ods
| |__ groups.ods
| |__ managers.ods
| \__ organisations.ods
|__ pdf
| |__ groups.pdf
| |__ managers.pdf
| \__ organisations.pdf
\__ xml
|__ groups.xml
|__ groups_name_links.xml
|__ managers.xml
\__ organisations.xml







Details:

  • generate_tree: this is the main script to generate the tree in various formats (CSV, XML, HTML). You can either use a LDIF export of your linked objects or use a LDAP query to get the data.

  • 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/*.ldif: sample LDIF files to test the script

  • csv/*.csv, html/*.html, ods/*.ods, pdf/*.pdf, xml/*.xml: examples of CSV, HTML, OpenOffice Calc, PDF and XML reports you can generate

  • html/images/*.gif: images used to build the HTML visual tree

  • docs/tree.txt: the Wiki source of this article


  • docs//communities/media/u3740/*.png: all the pictures used in this article







Using the Script


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





/Tree> ./generate_tree -h
usage: generate_tree [options]
generate tree table in different formats (CSV, XML, HTML)
-h or --help for help

examples: generate_tree -f groups.ldif -R cn=g00001,ou=groups,o=org > groups.csv
generate_tree -f groups.ldif -R cn=g00001,ou=groups,o=org --xml > groups.xml
generate_tree -m ldap -B ou=orgs,o=org -S sub -H localhost -A mySubOrg
-D cn=admin,o=org -w mypass -F (objectClass=myOrg)
-R cn=o00001,ou=orgs,o=org -L myDesc -T myShortDesc
> organisations.csv

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
-l LINKTYPE link type: dn or name [default: dn]
-n NAMING LDAP naming attribute [default: cn]
-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
-B BASEDN base DN for search
-S SCOPE search scope: base, one or sub [default: sub]
-F FILTER LDAP filter [default: (objectClass=groupOfNames)]
-A ATTRIBUTE LDAP membership attribute [default: member]
-L LABEL LDAP attribute for nodes label [default: description]
-T OTHERATTRS LDAP attributes to include in the report, comma separated
[default: cn,description]
-R ROOTNODE root node DN
-t "MY TITLE" title of the report [default: TREE REPORT]
-o OUTPUTTYPE output mode: csv, html or xml [default: csv]
-v verbose mode
-q quiet mode [default]







You can test the script with the different LDIF files in Tree/ldif from the attached archive. You can also use the test_samples.sh script that will convert all the LDIF samples to CSV, XML and HTML.





Generating CSV reports

1. The ldif/groups.ldif file contains groups with members. Here are the details:


  • The link attribute is member, which is the default of the generate_tree script

  • The report attributes can be cn and description, which is also the default for the script

  • The title of the report should be "MY GROUPS"

  • The root node of the generated tree should be the group g00001







To generate a CSV tree report from this ldif/groups.ldif file you can use the following command:





./generate_tree -f ldif/groups.ldif -R cn=g00001,ou=groups,o=org -t "MY GROUPS" -o csv > csv/groups.csv







Here is the sample CSV result of the command (file csv/groups.csv





MY GROUPS	cn	description
\__ Direction g00001 Direction
|__ Human Resources g00002 Human Resources
|__ Finance g00003 Finance
|__ Information Technology g00004 Information Technology
| |__ Consulting g00008 Consulting
| |__ Training g00009 Training
| \__ Support g00010 Support
|__ Marketing g00005 Marketing
|__ Sales g00006 Sales
| |__ Partners g00011 Partners
| |__ Direct Sales g00012 Direct Sales
| \__ Technology Pre-Sales g00013 Technology Pre-Sales
\__ Operations g00007 Operations








2. The ldif/managers.ldif file contains manager-employees export. Here are the details:


  • The link attribute is directReports.

  • The report attributes can be cn, givenName, sn, ou.

  • The title of the report should be "MY ORGANISATION".

  • The root node of the generated tree should be the user e00001.







To generate a CSV tree report from this ldif/managers.ldif file you can use the following command:





./generate_tree -f ldif/managers.ldif -R cn=e00001,ou=users,o=org -A directReports -L fullName -T cn,givenName,sn,ou -t "MY ORGANISATION" -o csv > csv/managers.csv







Here is the sample CSV result of the command (file csv/managers.csv





MY ORGANISATION	cn	givenName	ou	sn
\__ John Smith e00001 John DIR Smith
|__ Jane doe e00002 Jane HR doe
|__ Mary Brown e00003 Mary FIN Brown
|__ Tony Williams e00004 Tony IT Williams
| |__ Holy Ewans e00008 Holy CONS Ewans
| |__ Morgan Mitchell e00009 Morgan TRN Mitchell
| \__ Ruan Alvarez e00010 Ruan SUP Alvarez
|__ Allen Young e00005 Allen MKG Young
|__ Andy Green e00006 Andy SAL Green
| |__ Anna Scott e00011 Anna PAR Scott
| |__ Jammy White e00012 Jammy DSAL White
| \__ Marty Harris e00013 Marty TPS Harris
\__ Joanna Carter e00007 Joanna OPS Carter







3. The ldif/organisations.ldif file contains the custom organisation's objects for exporting. Here are the details:


  • The link attribute is mySubOrg.

  • The report attributes can be cn and myShortDesc.

  • The title of the report should be "MY ORGANISATION".

  • The root node of the generated tree should be the organisation o00001.







To generate a CSV tree report from this ldif/organisations.ldif file you can use the following command:





./generate_tree -f ldif/organisations.ldif -R cn=o00001,ou=orgs,o=org -A mySubOrg -L myDesc -T cn,myShortDesc -t "MY organisATION" -o csv > csv/organisations.csv







Here is the sample CSV result of the command (file csv/organisations.csv





MY ORGANISATION	cn	myShortDesc
\__ Direction o00001 DIR
|__ Human Resources o00002 HR
|__ Finance o00003 FIN
|__ Information Technology o00004 IT
| |__ Consulting o00008 CONS
| |__ Training o00009 TRN
| \__ Support o00010 SUP
|__ Marketing o00005 MKG
|__ Sales o00006 SAL
| |__ Partners o00011 PAR
| |__ Direct Sales o00012 DSAL
| \__ Technology Pre-Sales o00013 TPS
\__ Operations o00007 OPS







4. The ldif/groups_name_links.ldif file contains groups with members, but instead of having the members full DN, the objects only have the members name (CN), Here are the details:


  • The link attribute is myMember.

  • The link type is "name" and not "dn".

  • The report attributes can be cn and description, which is the default for the script.

  • The title of the report should be "MY GROUPS".

  • The root node of the generated tree should be the group g00001.

  • The base DN for all groups is ou=groups,o=org.







To generate a CSV tree report from this ldif/groups_name_links.ldif file you can use the following command:





./generate_tree -f ldif/groups_name_links.ldif -R cn=g00001,ou=groups,o=org -A myMember -t "MY GROUPS" -l name -B ou=groups,o=org -o csv > csv/groups_name_links.csv







Here is the sample CSV result of the command (file csv/groups_name_links.csv. This should be the same as csv/groups.csv





MY GROUPS	cn	description
\__ Direction g00001 Direction
|__ Human Resources g00002 Human Resources
|__ Finance g00003 Finance
|__ Information Technology g00004 Information Technology
| |__ Consulting g00008 Consulting
| |__ Training g00009 Training
| \__ Support g00010 Support
|__ Marketing g00005 Marketing
|__ Sales g00006 Sales
| |__ Partners g00011 Partners
| |__ Direct Sales g00012 Direct Sales
| \__ Technology Pre-Sales g00013 Technology Pre-Sales
\__ Operations g00007 Operations







Generating XML reports
From the same LDIF files, you can generate the XML reports using the "-o xml" option:





1. To generate a XML tree report from this ldif/groups.ldif file you can use the following command:





./generate_tree -f ldif/groups.ldif -R cn=g00001,ou=groups,o=org -t "MY GROUPS" -o xml > xml/groups.xml






Here is the sample XML result of the command (file xml/groups.xml





<?xml version="1.0"?>
<report title="MY GROUPS">
<node name="g00001" label="Direction">
<attribute name="cn">g00001</attribute>
<attribute name="description">Direction</attribute>
<node name="g00002" label="Human Resources">
<attribute name="cn">g00002</attribute>
<attribute name="description">Human Resources</attribute>
</node>
<node name="g00003" label="Finance">
<attribute name="cn">g00003</attribute>
<attribute name="description">Finance</attribute>
</node>
<node name="g00004" label="Information Technology">
<attribute name="cn">g00004</attribute>
<attribute name="description">Information Technology</attribute>
<node name="g00008" label="Consulting">
<attribute name="cn">g00008</attribute>
<attribute name="description">Consulting</attribute>
</node>
<node name="g00009" label="Training">
<attribute name="cn">g00009</attribute>
<attribute name="description">Training</attribute>
</node>
<node name="g00010" label="Support">
<attribute name="cn">g00010</attribute>
<attribute name="description">Support</attribute>
</node>
</node>
<node name="g00005" label="Marketing">
<attribute name="cn">g00005</attribute>
<attribute name="description">Marketing</attribute>
</node>
<node name="g00006" label="Sales">
<attribute name="cn">g00006</attribute>
<attribute name="description">Sales</attribute>
<node name="g00011" label="Partners">
<attribute name="cn">g00011</attribute>
<attribute name="description">Partners</attribute>
</node>
<node name="g00012" label="Direct Sales">
<attribute name="cn">g00012</attribute>
<attribute name="description">Direct Sales</attribute>
</node>
<node name="g00013" label="Technology Pre-Sales">
<attribute name="cn">g00013</attribute>
<attribute name="description">Technology Pre-Sales</attribute>
</node>
</node>
<node name="g00007" label="Operations">
<attribute name="cn">g00007</attribute>
<attribute name="description">Operations</attribute>
</node>
</node>
</report>







2. To generate a XML tree report from this ldif/managers.ldif file you can use the following command:





./generate_tree -f ldif/managers.ldif -R cn=e00001,ou=users,o=org -A directReports -L fullName -T cn,givenName,sn,ou -t "MY organisATION" -o xml > xml/managers.xml







Here is the sample XML result of the command (file xml/managers.xml


<?xml version="1.0"?>
<report title="MY organisATION">
<node name="e00001" label="John Smith">
<attribute name="cn">e00001</attribute>
<attribute name="givenName">John</attribute>
<attribute name="ou">DIR</attribute>
<attribute name="sn">Smith</attribute>
<node name="e00002" label="Jane doe">
<attribute name="cn">e00002</attribute>
<attribute name="givenName">Jane</attribute>
<attribute name="ou">HR</attribute>
<attribute name="sn">doe</attribute>
</node>
<node name="e00003" label="Mary Brown">
<attribute name="cn">e00003</attribute>
<attribute name="givenName">Mary</attribute>
<attribute name="ou">FIN</attribute>
<attribute name="sn">Brown</attribute>
</node>
<node name="e00004" label="Tony Williams">
<attribute name="cn">e00004</attribute>
<attribute name="givenName">Tony</attribute>
<attribute name="ou">IT</attribute>
<attribute name="sn">Williams</attribute>
<node name="e00008" label="Holy Ewans">
<attribute name="cn">e00008</attribute>
<attribute name="givenName">Holy</attribute>
<attribute name="ou">CONS</attribute>
<attribute name="sn">Ewans</attribute>
</node>
<node name="e00009" label="Morgan Mitchell">
<attribute name="cn">e00009</attribute>
<attribute name="givenName">Morgan</attribute>
<attribute name="ou">TRN</attribute>
<attribute name="sn">Mitchell</attribute>
</node>
<node name="e00010" label="Ruan Alvarez">
<attribute name="cn">e00010</attribute>
<attribute name="givenName">Ruan</attribute>
<attribute name="ou">SUP</attribute>
<attribute name="sn">Alvarez</attribute>
</node>
</node>
<node name="e00005" label="Allen Young">
<attribute name="cn">e00005</attribute>
<attribute name="givenName">Allen</attribute>
<attribute name="ou">MKG</attribute>
<attribute name="sn">Young</attribute>
</node>
<node name="e00006" label="Andy Green">
<attribute name="cn">e00006</attribute>
<attribute name="givenName">Andy</attribute>
<attribute name="ou">SAL</attribute>
<attribute name="sn">Green</attribute>
<node name="e00011" label="Anna Scott">
<attribute name="cn">e00011</attribute>
<attribute name="givenName">Anna</attribute>
<attribute name="ou">PAR</attribute>
<attribute name="sn">Scott</attribute>
</node>
<node name="e00012" label="Jammy White">
<attribute name="cn">e00012</attribute>
<attribute name="givenName">Jammy</attribute>
<attribute name="ou">DSAL</attribute>
<attribute name="sn">White</attribute>
</node>
<node name="e00013" label="Marty Harris">
<attribute name="cn">e00013</attribute>
<attribute name="givenName">Marty</attribute>
<attribute name="ou">TPS</attribute>
<attribute name="sn">Harris</attribute>
</node>
</node>
<node name="e00007" label="Joanna Carter">
<attribute name="cn">e00007</attribute>
<attribute name="givenName">Joanna</attribute>
<attribute name="ou">OPS</attribute>
<attribute name="sn">Carter</attribute>
</node>
</node>
</report>







3. To generate a XML tree report from this ldif/organisations.ldif file you can use the following command:





./generate_tree -f ldif/organisations.ldif -R cn=o00001,ou=orgs,o=org -A mySubOrg -L myDesc -T cn,myShortDesc -t "MY organisATION" -o xml > xml/organisations.xml







Here is the sample XML result of the command (file xml/organisations.xml





<?xml version="1.0"?>
<report title="MY organisATION">
<node name="o00001" label="Direction">
<attribute name="cn">o00001</attribute>
<attribute name="myShortDesc">DIR</attribute>
<node name="o00002" label="Human Resources">
<attribute name="cn">o00002</attribute>
<attribute name="myShortDesc">HR</attribute>
</node>
<node name="o00003" label="Finance">
<attribute name="cn">o00003</attribute>
<attribute name="myShortDesc">FIN</attribute>
</node>
<node name="o00004" label="Information Technology">
<attribute name="cn">o00004</attribute>
<attribute name="myShortDesc">IT</attribute>
<node name="o00008" label="Consulting">
<attribute name="cn">o00008</attribute>
<attribute name="myShortDesc">CONS</attribute>
</node>
<node name="o00009" label="Training">
<attribute name="cn">o00009</attribute>
<attribute name="myShortDesc">TRN</attribute>
</node>
<node name="o00010" label="Support">
<attribute name="cn">o00010</attribute>
<attribute name="myShortDesc">SUP</attribute>
</node>
</node>
<node name="o00005" label="Marketing">
<attribute name="cn">o00005</attribute>
<attribute name="myShortDesc">MKG</attribute>
</node>
<node name="o00006" label="Sales">
<attribute name="cn">o00006</attribute>
<attribute name="myShortDesc">SAL</attribute>
<node name="o00011" label="Partners">
<attribute name="cn">o00011</attribute>
<attribute name="myShortDesc">PAR</attribute>
</node>
<node name="o00012" label="Direct Sales">
<attribute name="cn">o00012</attribute>
<attribute name="myShortDesc">DSAL</attribute>
</node>
<node name="o00013" label="Technology Pre-Sales">
<attribute name="cn">o00013</attribute>
<attribute name="myShortDesc">TPS</attribute>
</node>
</node>
<node name="o00007" label="Operations">
<attribute name="cn">o00007</attribute>
<attribute name="myShortDesc">OPS</attribute>
</node>
</node>
</report>








4. To generate a XML tree report from this ldif/groups_name_links.ldif file you can use the following command:





./generate_tree -f ldif/groups_name_links.ldif -R cn=g00001,ou=groups,o=org -t "MY GROUPS" -l name -B ou=groups,o=org -o xml > xml/groups_name_links.xml







Here is the sample XML result of the command (file xml/groups_name_links.xml. This should be the same as xml/groups.xml





<?xml version="1.0"?>
<report title="MY GROUPS">
<node name="g00001" label="Direction">
<attribute name="cn">g00001</attribute>
<attribute name="description">Direction</attribute>
<node name="g00002" label="Human Resources">
<attribute name="cn">g00002</attribute>
<attribute name="description">Human Resources</attribute>
</node>
<node name="g00003" label="Finance">
<attribute name="cn">g00003</attribute>
<attribute name="description">Finance</attribute>
</node>
<node name="g00004" label="Information Technology">
<attribute name="cn">g00004</attribute>
<attribute name="description">Information Technology</attribute>
<node name="g00008" label="Consulting">
<attribute name="cn">g00008</attribute>
<attribute name="description">Consulting</attribute>
</node>
<node name="g00009" label="Training">
<attribute name="cn">g00009</attribute>
<attribute name="description">Training</attribute>
</node>
<node name="g00010" label="Support">
<attribute name="cn">g00010</attribute>
<attribute name="description">Support</attribute>
</node>
</node>
<node name="g00005" label="Marketing">
<attribute name="cn">g00005</attribute>
<attribute name="description">Marketing</attribute>
</node>
<node name="g00006" label="Sales">
<attribute name="cn">g00006</attribute>
<attribute name="description">Sales</attribute>
<node name="g00011" label="Partners">
<attribute name="cn">g00011</attribute>
<attribute name="description">Partners</attribute>
</node>
<node name="g00012" label="Direct Sales">
<attribute name="cn">g00012</attribute>
<attribute name="description">Direct Sales</attribute>
</node>
<node name="g00013" label="Technology Pre-Sales">
<attribute name="cn">g00013</attribute>
<attribute name="description">Technology Pre-Sales</attribute>
</node>
</node>
<node name="g00007" label="Operations">
<attribute name="cn">g00007</attribute>
<attribute name="description">Operations</attribute>
</node>
</node>
</report>







Generating HTML reports


From the same LDIF files, you can generate the HTML reports using the "-o html" option:





1. To generate a HTML tree report from this ldif/groups.ldif file you can use the following command:





./generate_tree -f ldif/groups.ldif -R cn=g00001,ou=groups,o=org -t "MY GROUPS" -o html > html/groups.html







Here is the sample HTML result of the command (file html/groups.html





Tree Groups Report HTML





2. To generate a HTML tree report from this ldif/managers.ldif file you can use the following command:





./generate_tree -f ldif/managers.ldif -R cn=e00001,ou=users,o=org -A directReports -L fullName -T cn,givenName,sn,ou -t "MY organisATION" -o html > html/managers.html







Here is the sample HTML result of the command (file html/managers.html





Tree Managers Report HTML





3. To generate a HTML tree report from this ldif/organisations.ldif file you can use the following command:





./generate_tree -f ldif/organisations.ldif -R cn=o00001,ou=orgs,o=org -A mySubOrg -L myDesc -T cn,myShortDesc -t "MY organisATION" -o html > html/organisations.html







Here is the sample HTML result of the command (file html/organisations.html





Tree organisations Report HTML





4. To generate a HTML tree report from this ldif/groups_name_links.ldif file you can use the following command:





./generate_tree -f ldif/groups_name_links.ldif -R cn=g00001,ou=groups,o=org -t "MY GROUPS" -l name -B ou=groups,o=org -o html > html/groups_name_links.html







Here is the sample HTML result of the command (file html/groups_name_links.html





Tree Managers Report HTML





Getting data from an LDAP Query

To retrieve data from a LDAP call you need to specify these additionnal informations:

  • The mode, which should be set to "ldap"

  • The LDAP host

  • The bind DN

  • The LDAP password (or -W to enter the password interactively)

  • The LDAP scope

  • The LDAP filter







To generate a CSV tree report from LDAP you can use the following command:






./generate_tree -m ldap -H 127.0.0.1 -D cn=admin,o=org -w mypass -B ou=orgs,o=org -S sub -F \(objectClass=myOrg\) -R cn=o00001,ou=orgs,o=org -A mySubOrg -L myDesc -T cn,myShortDesc -t "MY organisATION"







Steps to Create a Spreadsheet Document



Note: The following screenshots show the steps using OpenOffice Calc.

You should ge the same result using Excel.







1. Once you have generated your CSV file using the generate_tree Python script, you can double-click it from your File Explorer. This should launch OpenOffice Calc or Excel depending on your machine. If not, you can first open your spreadsheet tool and open/import the CSV file.





Step 1





2. OpenOffice Calc will see that it is a CSV file and will ask for the separator and the text delimiter to use. The value should be "Tab" for the separator, which may be checked by default.





Step 2





3. The document should look like the following:





Step 3





4. You can change to a fix font like "Courier" and format the text:





Step 4





5. It is always nice to have a bit of color:





Step 5





6. It could also be a good idea to format the document for printing, by setting the page format, a header, a footer and the scale.





Here is how to format the page:





Step 6





7. Then you can format the header and set it:





Step 7





Step 8





8. You can format the footer and set it:





Step 9





Step 10





9. From there, you can export the result as PDF and you should get the following:





Step 11





Step 12





Do not hesitate to record all the different steps to create a macro that will automatically format the report for you. It worked pretty well on my OpenOffice Calc and on Excel.





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: by loading a LDIF file using the LDIFStruct library, or by 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 script. The difference is that the script not only loads the membership attribute but also all the other attributes contained in the ldap_otherattrs option.






treestruct = dict( l.search_s( options.ldap_basedn, scopes[ options.ldap_scope ], options.ldap_filter, list( set( [ options.ldap_attribute ] options.ldap_otherattrs.split( "," ) ) ) ) )








The format of the ldap_otherattrs option is comma separated, like for instance "cn,givenName,sn,description". The following command convert this string option into a Python list:





options.ldap_otherattrs.split( "," )







The list generated by this command is the following:





[ "cn", "givenName", "sn", "description" ]







You can also remove doubles from this list using a "set" object and converting it back to a list. If the option is "cn,givenName,sn,description,cn", the list will look like the following, with two "cn":






[ "cn", "givenName", "sn", "description", "cn" ]







You can generate a "set" object from there where all values are unique:





set( [ "cn", "givenName", "sn", "description", "cn" ] )







To convert it back to a list:





list( set( [ "cn", "givenName", "sn", "description", "cn" ] ) )







The content of the last list is then the following:





[ "cn", "givenName", "sn", "description" ]







Algorithm to build the tree

Once the data is loaded in memory (in an associative array treestruct as represented in the first part of the scripting article, keys contain the DN of all the elements. Starting from a root node, we need to display this node and cycle through each children recursively.





The pseudo-code to navigate in the tree would be the following:


function analyse( node )
| choose indentation depending on file format and if the node is the last
| display information about current node with correct indentation
| for each child in children list
| | call function analyse( child )
| end for
end function







The indentation depends on the format, and the node is stored in a dictionary:






# Tree indentation characters or images
tabs = { "csv": { "last": ( "\__ ", " " ), "default": ( "|__ ", "| " ) },
"xml": { "last": ( "\t", "\t" ), "default": ( "\t", "\t" ) },
"html": { "last": ( '<img src="images/l.gif" align="top"/>', '<img src="images/e.gif" align="top"/>' ),"default": ( '<img src="images/t.gif" align="top"/>', '<img src="images/v.gif" align="top"/>' ) } }







You can see the four different types of items we use when building a tree:


  • the "E" empty indentation ("   " for instance for CSV file format)

  • the "L" indentation for the last node in the children list ("\__ " for instance for CSV file format)

  • the "T" indentation ("|__ " for instance for CSV file format)

  • the "V" indentation ("|   " for instance for CSV file format)







For the XML format, we only use tabs ("\t" string) and for HTML format we use images (e.gif, l.gif, t.gif and v.gif).





The main function to navigate in the tree is the following:






# Recursive method to get the tree structure
def analyseNode( node, level, indent, last ):
if treestruct.has_key( node ):

# Prepare indentation
if last:
( tab1, tab2 ) = tabs[ options.output ][ "last" ]
else:
( tab1, tab2 ) = tabs[ options.output ][ "default" ]

# Prepare label
label = ""
if treestruct[ node ].has_key( options.ldap_label ):
label = treestruct[ node ][ options.ldap_label ][ 0 ]
if options.output == "xml":
label = re.sub( "&", "&amp;", label )

# Retrieve all attributes for the report
otherattrs = {}
for attr in set( options.ldap_otherattrs.split( "," ) ):
content = ""
if treestruct[ node ].has_key( attr ):
content = treestruct[ node ][ attr ][ 0 ]
otherattrs[ attr ] = content

# Display the node
if options.output == "csv":
print indent tab1 label "\t" "\t".join( [v for (k,v) in sorted( otherattrs.items() )] )
elif options.output == "xml":
print indent tab1 "<node name=\"" re.sub( "(.*?)=(.*?),.*", "\\2", node ) "\" label=\"" label "\">"
for ( attr, value ) in sorted( otherattrs.items() ):
print indent tab1 '\t<attribute name="%s">%s</attribute>' % ( attr, value )
elif options.output == "html":
print "\t\t\t<tr>"
print "\t\t\t\t<td>" indent tab1 " <b>" label "</b></td>"
for ( attr, value ) in sorted( otherattrs.items() ):
print "\t\t\t\t<td>%s</td>" % value
print "\t\t\t</tr>"

# Navigate through node children
if treestruct[ node ].has_key( options.ldap_attribute ):
children = treestruct[ node ][ options.ldap_attribute ]
count = 0
for child in children:
count = 1
last = count == len( children )
if options.linktype == "dn":
child_dn = child
elif options.linktype == "name":
child_dn = options.ldap_naming "=" child "," options.ldap_basedn
analyseNode( child_dn, level 1, indent tab2, last )

# Close XML tag
if options.output == "xml":
print indent tab1 "</node>"







The script can then start the output with specific headers depending on the chosen format, call the recursive function on a selected root node, and finish the output with specific footers:






# Start of the output
if options.output == "csv":
print '%s' % options.title "\t" "\t".join( sorted( set( options.ldap_otherattrs.split( "," ) ) ) )
if options.output == "xml":
print "<?xml version=\"1.0\"?>"
print "<report title=\"%s\">" % options.title
if options.output == "html":
print """<html>
<head>
<title>%s</title>
<style>
* { font-size: 12px; font-family: Arial; }
td { padding-left: 10px; }
tr { background-color: #eeeeee; }
</style>
</head>
<body>
<table cellspacing="0" cellpadding="0">
<tr style="background-color: #999999; color: #ffffff;">
<th>%s</th>""" % ( options.title, options.title )
for attr in sorted( set( options.ldap_otherattrs.split( "," ) ) ):
print "\t\t\t\t<th>%s</th>" % attr
print "\t\t\t</tr>"

# Display the tree structure
analyseNode( options.ldap_rootnode, 0, "", True )

# End of the output
if options.output == "xml":
print "</report>"
if options.output == "html":
print """ </table>
</body>
</html>"""







Summary



With this generate_tree tool, you can easily get a visual representation of any hierarchical structure in your directory. It can be useful to generate a tree view of specific organisations objects, nested groups or any one-to-many objects relationships, such as manager-employees relationships.





You can improve the script to generate better graphical organisational chart, or create a dynamic tree where you can expand / collapse nodes.





XML Tree Report





You can also transform the generated XML file with a XSL stylesheet to generate other nice and dynamic reports. Additionally, you can find XSL stylesheets that will transform an XML file into a folder view. Please give me feedback if you test or improve the tool, or if you find interesting XSL stylesheets for presenting the results.
Anonymous
Parents Comment Children