AS400 Driver Additional Attributes:
Novell Identity Manager has connectors (aka drivers) available for a large number of connected systems. There are some drivers that are very specific like the Active Directory driver that only talks to Active Directory. Others are much more generic, like the LDAP driver, where one driver can talk to many of the different LDAP Directory Server platforms available on the market.
There is a family of drivers for some of the bigger systems. You know, the mainframes and midrange servers. There is a driver for IBM mainframes that support RACF, Top Secret, and ACF2 security subsystems. There is a driver for the IBM iSeries servers, sometimes called AS400, sometimes called OS400, sometimes called i5OS and sometimes just called IBM Midrange. I have had the difference between all the names explained to me several times, and it just keeps going over my head, so I just call it AS400 and i5OS and that seems to cover most cases.
For some more articles about the AS400 (or i5OS) driver, you can look at these links:
- Solving Account Disabled Issues with the AS400 Bidirectional Driver
- Association Problems when Updating the i5os Bidirectional Driver to IDM 3.5
- Limitation to AS400 Driver Support in IDM
There are actually two versions of all those above drivers, the fanout driver version and the bi-directional driver version.
The difference between the fanout and bi-dir drivers is almost philosophical. The bi-dir drivers are more like the classic notion of an Identity Manager driver, where there is a one to one relationship of a single driver for each single system. That is, for each AS400 system you would want connected to your Identity Manager solution, you would have a driver configured and running for it.
This works quite well, as we have all seen, based on the fact there are many large implementations of Novell Identity Manager running using this approach.
The Fanout driver have a different philosophy and it is a pretty neat one! Rather than a single driver to single connected system approach, there are times when a single driver connecting to a number of similar systems makes more sense. It turns out not just the IBM high end servers have both bi-dir and fanout drivers available, there is also a fanout and bi-dir driver for Linux or Unix systems.
In the case of Unix systems, odds are good there is more than one server involved, and possibly hundreds or thousands. Well mapping thousands of servers to thousands of drivers is clearly neither sustainable nor likely to succeed. Thus the need for fanout drivers. In the case of a fanout driver, there is a single driver instance (that is multi-threaded, which is absolutely critical for performance when you try and scale like this) and each connected system (or server) has a remote loader that knows to talk to that single instance. An object in eDirectory represents the configuration and setup information of each of the thousand servers, and thus the driver in a single instance can store the configuration information for thousands of systems. As we all know, having thousands, millions, or even billions of objects in eDirectory is not an issue at all, so this kind of approach can scale very well. It is a little less clear that you might have so many mainframe or midrange servers, but the real world seems to show that you either have a single one of these devices, or you have lots of them. Very rare to see something in between. Perhaps two for disaster recovery, but once you go beyond two, it seems like most sites will have many more once they grow beyond that point.
You can imagine that there are other cases where a fanout driver approach might work. It would be interesting to see if an Active Directory driver could be built around the fanout model. After all, since Active Directory is just a series of linked domains that trust each other, an Active Directory forest with many sub domains could require dozens of drivers to be supported. Perhaps there is a better way to handle this. One approach we have taken for one of our clients is to move all the rules into Libraries, at the root of the driver set, and use Global Configuration Values (GCV) to represent domain specific information.
This way, one set of rules is maintained and applies to all the domains being supported. However, that still requires the care and feeding of multiple drivers. Be very interesting to see if some sort of hybrid approach could be developed.
This article is specifically about the bi-directional AS400 (or i5os) driver.
The bi-directional driver for AS400 ships with two slightly different configurations. One has the basic attributes in AS400 mapped to eDirectory attributes, basically what you would expect with USRPRF mapped to CN, CMPNY mapped to company, FULNAM to Full Name, and so on. There is another variant where all the various fields in a User profile from AS400 are mapped to new attributes in eDirectory. They are labeled DirXML-i5osXXXXXX where XXXXXXX is the native attribute name in AS400.
Thus the JOBD (Job Description) attribute in AS400 can be mapped to DirXML-i5osJOBD and the like. So far this is just plain old straight forward Identity Manager concepts.
Alas, as always there is some complexity that pops up its sneaky head.
AS400 uses the concept of Libraries to store ‘stuff’. I am not 100 percent clear on what exactly the ‘stuff’ in a Library can be, but I do know it can include functions, descriptions, files, commands, and things like that. They are almost like subdirectories in a way (since they seem represented in the IFS file system as a series of subdirectories). The driver shim itself comes as part of the i5osdrv library, and includes all the executables, programs, and configuration files inside that Library.
Many fields that are available about a user on the AS400 are often of the form LIBRARY/ITEM. That is, you need to specify the LIBRARY containing the ITEM, and then the ITEM itself. This sort of like providing a path for a Win32 environment or on Linux.
When you look at an AS400 terminal session, often these values are represented with the ITEM on the first line, then indented below it the LIBRARY name. So what format should you be sending the attribute data over to the AS400 via the driver then? I was unable to find that in the documentation, however the developers of these drivers are really nice guys, and monitor the Novell Support forums, and you can ask this sort of question there and get a really good answer! I guess you could always open a Support incident, but why waste the incident?
If you do not know about and use the Novell Support forums, you are missing out on a great resource that really helps make the products better. There is a web interface to them (Yech! I am old school, and like to use news readers like tin or nn, but without my own reliable server to run those on, I now use Thunderbird for NNTP access) which is available at http://forums.novell.com and there is an NNTP interface available at nntp://forums.novell.com if you have an NNTP news reader (like Thunderbird, GroupWise client, Outlook Express, and others) available.
These forums are monitored by a group of volunteer sysops, now called the Novell Knowledge Partners (http://support.novell.com/community/nkp.html) and by many Novell product group employees. It is not officially supported by Novell, so you cannot be guaranteed an answer, but often you can get one. The good news is that Google indexes them, and searched will often return useful hits from those groups.
It turns out that there is a bit of a pain point in how the data is sent and returned for these attributes in AS400. On the Subscriber channel, you would want to send the value as LIBRARY/FUNCTION into the driver, which will then correctly set the value on the AS400. Coming back on the Publisher channel, from the AS400 to eDirectory, it will be returned as FUNCTION__LIBNAME where the FUNCTION name is space padded to ten characters.
For example something in one of the default libraries, *LIBL, and the function or item QSYSJOBD would be sent to the AS400 as *LIBL/QSYSJOBD. You would probably want to store it in the eDirectory attribute that way as when typing on the command line on the AS400 that is how you would enter it. Thus it makes more sense to store it that way.
If on the AS400 you were to set the value of the JOBD field to QSYSJOBD and the library it is coming from on the next line down (which is indented a bit) as *LIBL then the driver would return the value as <value>QSYSJOBD *LIBL</value>. It is important to note the two extra space characters in between, and that the ordering of the two components is reversed.
As it happens there is a bug in the Identity Manager 3.5 version of this driver, and you should upgrade the remote loader shim to the Identity Manager 3.6 version to get the bug fix. If not, you can open an incident with Novell Technical Support to get a patch for the 3.5 driver.
To handle this properly, you need a rule on the input transform to split up the two parts, swap them around, strip out the extra spaces, and stick a forward slash (/) in between them when writing back to eDirectory. While that sounds somewhat complex, it turns out to be really straight forward in DirXML Script.
Now for some examples of what this would look like. On the Subscriber channel if you wanted to change the value of the JOBD attribute to JOBBLG in the library QUSRSYS the event might look like this:
<nds dtdversion="3.5" ndsversion="8.x"> <source> <product version="18.104.22.16870315 ">DirXML</product> <contact>Novell, Inc.</contact> </source> <input> <modify class-name="User" event-id="#1#1" qualified-src-dn="O=ACME\O=NYC\OU=idmusers\CN=TStuff" src-dn="\IDM\ACME\NYC\idmusers\TStuff" src-entry-id="34897" timestamp="1238098853#2"> <association state="associated">tstuff</association> <modify-attr attr-name="DirXML-i5osJOBD"> <add-value> <value timestamp="1238098853#2" type="string">QUSRSYS/JOBBLG</value> </add-value> </modify-attr> </modify> </input> </nds>
Coming back from AS400 on the Publisher channel it would look something like this:
[03/26/09 15:27:12.065]:i5os PT: <nds dtdversion="1.1" ndsversion="8.6"> <source> <product build="20070213_1614" version="2.0"/> <contact/> </source> <input> <modify class-name="User" dest-dn="ACME\NYC\idmusers\TStore" dest-entry-id="34896" event-id="0" src-dn="TSTORE"> <association>tstore</association> <modify-attr attr-name="DirXML-i5osJOBD"> <remove-all-values/> <add-value> <value>JOBBLG QUSRSYS</value> </add-value> </modify-attr> </modify> </input> </nds>
Here you can see how the values locations have been reversed, and padded with spaces to get to 10 characters for the first field.
Here is my rule in the Input Transform to handle it:
<rule> <description>[acme] Transform attributes that are LIBRARY/FUNCTION name: JOBD</description> <conditions> <and> <if-op-attr name="JOBD" op="available"/> </and> </conditions> <actions> <do-set-local-variable name="JOBD" scope="policy"> <arg-string> <token-op-attr name="JOBD"/> </arg-string> </do-set-local-variable> <do-set-local-variable name="FUNCTION-NAME" scope="policy"> <arg-string> <token-xpath expression="normalize-space(substring($JOBD,1,10))"/> </arg-string> </do-set-local-variable> <do-set-local-variable name="LIBNAME" scope="policy"> <arg-string> <token-xpath expression="normalize-space(substring($JOBD,11,10))"/> </arg-string> </do-set-local-variable> <do-reformat-op-attr name="JOBD"> <arg-value> <token-local-variable name="LIBNAME"/> <token-text xml:space="preserve">/</token-text> <token-local-variable name="FUNCTION-NAME"/> </arg-value> </do-reformat-op-attr> </actions> </rule>
Remember that the Input Transform is before the Schema map rule, therefore attribute names are in the connected system name space, so it is still called JOBD at this point, not yet mapped to DirXML-i5osJOBD by the Schema mapping rule.
Lets break the rule down to explain what is going on:
<conditions> <and> <if-op-attr name="JOBD" op="available"/> </and> </conditions>
If the document has the JOBD attribute in it, then we fire, if not, move on, nothing to see here.
<do-set-local-variable name="JOBD" scope="policy"> <arg-string> <token-op-attr name="JOBD"/> </arg-string> </do-set-local-variable>
Here I had to make a decision, as it turns out, I can get the Operation attribute value via XPATH quite easily, as:
((add-attr|attr)[@attr-name = 'JOBD']) | (modify-attr[@attr-name = 'JOBD']/add-value))
For more information on this sort of thing, look at this article: The different attribute options in Identity Manager
However, that would have made the XPATH used later to clean up the strings VERY hard to read, and from a maintainability perspective, I decided to just store it in a variable, JOBD instead, even if that is less efficient.
<do-set-local-variable name="FUNCTION-NAME" scope="policy"> <arg-string> <token-xpath expression="normalize-space(substring($JOBD,1,10))"/> </arg-string> </do-set-local-variable>
Next lets parse apart the string we get back from the AS400 in the JOBD attribute and get the name of the function, since we know that is coming first, space padded to ten characters.
I used a couple of XPATH string processing functions to do this, normalize-space() is fun, as it gets rid of leading and trailing white spaces, if it sees multiple white spaces it converts them down to a single one, which does what I need, to get rid of any trailing white spaces. Run that against the results of the subtring($JOBD,1,10) which is the first ten characters of the variable JOBD’s contents. (From position 1, for 10 characters. In XPATH subtring you start at position 1, not 0. I had to go look it up, since I never can remember either!)
<do-set-local-variable name="LIBNAME" scope="policy"> <arg-string> <token-xpath expression="normalize-space(substring($JOBD,11,10))"/> </arg-string> </do-set-local-variable>
Do basically the same thing for the LIBNAME, which we know if the last part, and starts at position 11 in the string. Normalize-space() it just in case there are any extra white spaces, (but probably not entirely needed in this example), upon last ten characters of the string (from position 11 for 10 characters)
<do-reformat-op-attr name="JOBD"> <arg-value> <token-local-variable name="LIBNAME"/> <token-text xml:space="preserve">/</token-text> <token-local-variable name="FUNCTION-NAME"/> </arg-value> </do-reformat-op-attr>
Finally, we can reformat the operation attribute, to be LIBNAME/FUNCTION-NAME and all is done.
As I said above, I could probably do the whole thing in a single reformat operation attribute, using XPATH to get the value of the operation attribute. But as I mentioned, that would be pretty hard to read, and not very maintainable. Just for fun, I included that approach below. Tell me what you think, which approach is easier to maintain as time goes on, and you forget what exactly you were doing in this rule?
<do-reformat-op-attr name="JOBD"> <arg-value> <token-xpath expression="normalize-space(substring(((add-attr|attr)[@attr-name = 'JOBD']) | (modify-attr[@attr-name = 'JOBD']/add-value)),11,10))"/> <token-text xml:space="preserve">/</token-text> <token-xpath expression="normalize-space(substring(((add-attr|attr)[@attr-name = 'JOBD']) | (modify-attr[@attr-name = 'JOBD']/add-value)),1,10))"/> </arg-value> </do-reformat-op-attr>
I think the difference is clear, and that wasting some memory on some local variables is better than trying to do it all at once. At least if you ever expect someone else to have to look at and make changes to your code.
If you try this and the shim is returning an error message, remember to get the Identity Manager 3.6 version of this driver, as it includes a patch to make this actually work. (It is a bug in the 3.5 driver, having to do with extra single quotes being injected around the values being submitted).