Automatic Tree Reports for IDM Projects



By: rkalfane

February 27, 2008 8:05 am

Reads: 334

Comments:0

Rating:0

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




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

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/tree.html: the result of the conversion from Wiki to HTML (see Wiki to CoolSolutions Converter)
  • 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.

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

Tags: ,
Categories: Identity Manager, Technical Solutions

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