My virtual environment for this purpose was:
- Windows 2003 R2 Standard Edition Service Pack 2
- IBM WebSphere Application Server v7.0.0.9
- IBM Tivoli Identity Manager v5.1.0.0
- APISCRIPT v5.0.0.4 (available from OPAL)
The Setup
The apiscript tool (v5.0.0.4) should be downloaded from the OPAL site and deployed as per the instructions. For example, the following files need to be configured to match the ITIM environment being managed:
- etc/host/{hostname}.properties
- bin/env_master.bat
But because I'm using an ITIM v5.1 system, I need to also make a "tweak" to the apiscript.bat (or apiscript.ksh) file as the structure of the extensions directory has been updated in this release. I need to include a 5.1 directory between extensions and examples as such:
set APISCRIPT_LOGIN_CONFIG=%APISCRIPT_ITIM_HOME%\extensions\5.1\examples\apps\bin\jaas_login_was.conf
Input File
The following input file is to be used to define the organisational structure:
ParentOrgUnit|OrgUnit||internal||external|internal|unit1|internal|unit2|external|unit3|unit2|unit4|unit2|unit5|unit10|unit7|unit4|unit6|external|unit4|external|unit8|external|unit8|unit8|unit9|unit9|unit8|
Each line was terminated with a | character because early in the testing process I noticed that carriage return/line feed characters were making their way into the ITIM environment.
The Code
The code required to process such an input file should be broken down into a number of sections:
Section 1: Process The Command Line Arguments
In order to ensure that the script can process a variety of input files, the file to be processed should be passed as a command line argument. An additional argument is being processed here to enable verbose logging to take place:
try:Section 2: Read the Input File
opts, args = getopt.getopt(sys.argv[1:], "qi:", ["inputfile="])
except getopt.GetoptError, err:
print str(err)
usage()
sys.exit(2)
inputfile = ""
quietmode = "false"
for opt, arg in opts:
if opt == "-q":
print "Quiet mode enabled"
quietmode = "true"
elif opt == "-i":
inputfile = arg
The processing of the input file should be wrapped in a while loop and each line processed should be split into its constituent parts using the split method on the line object:
infile = open(inputfile,"r")At this point, we now have an Organisational Unit Name and a Parent Organisational Unit Name ready for placement in the ITIM data model.
while infile:
line = infile.readline()
if not line: break
items=line.split("|")
parentorg=items[0]
org=items[1]
Section 3: Process Each Entry
For each record retrieved above, we need to determine that the Parent Organisational Unit Name actually exists in the data model. If it does not, we cannot process the entry. If it does exist, we need to check that the child OU doesn't already exist. If it also exists, then there is no point in continuing processing of this entry, otherwise we need to get an OrganizationalUnitMO object for the Parent OU to enable us to create an OU using the child Organisational Unit Name.
if parentorg == 'ParentOrgUnit':Section 4: Check That An OU Exists
# Do Nothing - it's the header
logit('Processing Header')
else:
logit('**********************************************')
logit('Processing ' + org)
if org_exists(parentorg) != 'False':
if org_exists(org) != 'False':
logit('Child OU already exists:' + org)
else:
# Create OU
logit('Child OU does not exist:' + org)
parent_org_mo = get_org_mo(parentorg)
orgchart.do_create_ou_from_args(org, parent_org_mo)
# end if loop
else:
logit('Parent Org Unit does not exist:' + parentorg)
# end if loop
# end if
To check that an OU exists, we need to perform a search using a SearchMO object for an ORGUNIT object using a filter based on the OU name provided in the data file. If an object is found, we should return TRUE, else return FALSE:
def org_exists(orgunit):Section 5: Get the MO Of An OU
foundit = 'False'
if orgunit == "":
foundit = 'true'
else:
def_org_cont_mo = orgchart.get_default_org_mo()
myPlatform = apiscript.util.get_default_platform_ctx_and_subject()
search_mo = SearchMO(myPlatform)
search_mo.setCategory(ObjectProfileCategoryConstant.ORGUNIT)
myFilter = "(ou=" + orgunit + ")"
search_mo.setFilter(myFilter)
search_mo.setScope(SearchParameters.SUBTREE_SCOPE)
search_mo.setContext(def_org_cont_mo)
results_mo = search_mo.execute()
for result in results_mo.getResults():
if orgunit == result.name:
foundit = 'True'
return foundit
# end org_exists
An OrganizationalUnitMO object can be generated after searching for an OU by performing two additional functions:
- getDistinguishedName on the search result
- create_org_container_mo using the DN returned from the above method
def get_org_mo(orgunit):The Result
if orgunit == "":
org_mo = orgchart.get_default_org_mo()
else:
def_org_cont_mo = orgchart.get_default_org_mo()
search_mo = SearchMO( *apiscript.util.get_default_platform_ctx_and_subject())
search_mo.setCategory(ObjectProfileCategoryConstant.ORGUNIT)
myFilter = "(ou=" + orgunit + ")"
search_mo.setFilter(myFilter)
search_mo.setScope(SearchParameters.SUBTREE_SCOPE)
search_mo.setContext(def_org_cont_mo)
results_mo = search_mo.execute()
for result in results_mo.getResults():
if orgunit == result.name:
mydn = result.getDistinguishedName()
org_mo = orgchart.create_org_container_mo(mydn)
return org_mo
# End get_org_mo
The output from the script is:
C:\work\apiscript-5.0.0.4\apiscript>c:\work\apiscript-5.0.0.4\apiscript\bin\apiscript.bat -f c:\work\apiscript-5.0.0.4\apiscript\py\orgs.py -z -i c:\\work\\apiscript-5.0.0.4\\apiscript\\data\\orgs.datVisually, this gets represented in ITIM as:
Using master environment: "C:\work\apiscript-5.0.0.4\apiscript\bin\env_master.bat"
Using custom BIN_HOSTNAME: "stephen-w0ckd5b"
Using custom ETC_HOSTNAME: "stephen-w0ckd5b"
Using host properties: "C:\work\apiscript-5.0.0.4\apiscript\etc\host\stephen-w0ckd5b.properties"
Using APISCRIPT_WAS_HOME: "C:\Program Files\IBM\WebSphere\AppServer"
Using APISCRIPT_ITIM_HOME: "C:\Program Files\IBM\itim"
WASX7357I: By request, this scripting client is not connected to any server process. Certain configuration and application operations will be available in local mode.
Welcome to IBM Tivoli Identity Manager API Scripting Tool (apiscript) version: 5.0.0.4
Setting system property: java.security.auth.login.config
Setting com.ibm.CORBA properties: loginSource, loginUserid, loginPassword
WASX7303I: The following options are passed to the scripting environment and are available as arguments that are stored in the argv variable: "[-z, -i, c:\\work\\apiscript-5.0.0.4\\apiscript\\data\\orgs.dat]"
Logging configuration file is not found. All the logging information will be sent to the console.
**********************************************
Input file selected is c:\work\apiscript-5.0.0.4\apiscript\data\orgs.dat
Processing Header
**********************************************
Processing internal
Child OU does not exist:internal
**********************************************
Processing external
Child OU does not exist:external
**********************************************
Processing unit1
Child OU does not exist:unit1
**********************************************
Processing unit2
Child OU does not exist:unit2
**********************************************
Processing unit3
Child OU does not exist:unit3
**********************************************
Processing unit4
Child OU does not exist:unit4
**********************************************
Processing unit5
Child OU does not exist:unit5
**********************************************
Processing unit7
Parent Org Unit does not exist:unit10
**********************************************
Processing unit6
Child OU does not exist:unit6
**********************************************
Processing unit4
Child OU already exists:unit4
**********************************************
Processing unit8
Child OU does not exist:unit8
**********************************************
Processing unit8
Child OU already exists:unit8
**********************************************
Processing unit9
Child OU does not exist:unit9
**********************************************
Processing unit8
Child OU already exists:unit8
C:\work\apiscript-5.0.0.4\apiscript>
In conclusion, I'm not a Jython/Python sripting guru. Indeed, this was my first foray into Python. It is, however, a relatively straightforward language to learn and can be a very powerful tool in your ITIM Administrative toolset.
The full script can be downloaded at downloads/loadous.zip.