A while ago I was seeing funny effects when syncing with the SAP UM driver to a SAP CUA (Central User Administration) system that distributes IDM-managed accounts further down to its own child system. Seems like timestamps in SAP have a precision of one second only and therefore do not preserve the order of events when they’re coming in too fast. Under certain circumstances accounts were getting disabled and split-seconds later re-enabled again by IDM in the CUA system (no problem here) but the CUA system regularly mixed up the disable/enable order when syncing to its own child systems and accounts ended up disabled there.

Of course I should’ve re-written my policies to avoid that situation in the first place, but who’s ever got time for such a boring thing? Instead I explored the much more interesting problem of how to write a rule that delays subsequent modifies to the same object to ensure a minimum interval. First thought of tracking last modification time per application on the edir object, but that would cause a lot of overhead and simply looked like a bad idea (apart from being plain ugly as well). Instead I used a driver-scope variable to keep track of recent object changes and if necessary let the driver sleep a bit to enforce the desired minimum interval:

<rule>
	<description>Delay subsequent modifications of the same object to a minimum of 5 seconds</description>
	<conditions>
		<or>
			<if-operation mode="regex" op="equal">add|modify|modify-password|rename|move|delete</if-operation>
		</or>
	</conditions>
	<actions>
		<do-set-local-variable name="now" scope="policy">
			<arg-string>
				<token-time format="!CTIME" tz="UTC"/>
			</arg-string>
		</do-set-local-variable>
		<do-if>
			<arg-conditions>
				<and>
					<if-xpath op="true">$LastModifiedObjects/list</if-xpath>
				</and>
			</arg-conditions>
			<arg-actions>
				<do-strip-xpath expression="$LastModifiedObjects/list/object[@timestamp < $now - 5]"/>
			</arg-actions>
			<arg-actions>
				<do-set-local-variable name="LastModifiedObjects" scope="driver">
					<arg-node-set>
						<token-xml-parse>
							<token-text xml:space="preserve"><list/></token-text>
						</token-xml-parse>
					</arg-node-set>
				</do-set-local-variable>
			</arg-actions>
		</do-if>
		<do-for-each>
			<arg-node-set>
				<token-xpath expression="$LastModifiedObjects/list/object[@association = $current-op/association and @timestamp >= $now - 5][last()]/@timestamp"/>
			</arg-node-set>
			<arg-actions>
				<do-set-local-variable name="sleep-result" scope="policy">
					<arg-object>
						<token-xpath expression="java.lang.Thread:sleep(($current-node + 5 - $now) * 1000)"/>
					</arg-object>
				</do-set-local-variable>
			</arg-actions>
		</do-for-each>
		<do-append-xml-element expression="$LastModifiedObjects/list" name="object"/>
		<do-set-xml-attr expression="$LastModifiedObjects/list/object[last()]" name="association">
			<arg-string>
				<token-association/>
			</arg-string>
		</do-set-xml-attr>
		<do-set-xml-attr expression="$LastModifiedObjects/list/object[last()]" name="timestamp">
			<arg-string>
				<token-time format="!CTIME" tz="UTC"/>
			</arg-string>
		</do-set-xml-attr>
	</actions>
</rule>

That worked quite well, but I was curious how much time the driver will actually waste while sleeping. So I added a few more lines to keep track of that and provide some basic stats. And of course I want to be able to set the minimum interval as a GCV (right, Geoff?), so this is what finally made it into production:

<rule>
	<description>Delay subsequent modifications of the same object to the driver shim to a minimum delta</description>
	<comment xml:space="preserve">The SAP CUA does not keep track of the modification order of events when they are following each other too closely. As a result modifications for child systems can get mixed up in order and produce unwanted results. Delaying subsequent modifcations for the same object to a minimum interval will ensure that the original order of events is maintained on the SAP side. 
Requires the following GCV to be defined on the driver or driverset object:
&lt;definition display-name="Minimum Modification Delta" name="MinModDelta" range-hi="60" range-lo="0" type="integer">
	&lt;description>Minimum time between subsequent modifications of the same object on the application side</description>
	&lt;value>5</value>
&lt;/definition></comment>
	<conditions>
		<or>
			<if-global-variable mode="numeric" name="MinModDelta" op="gt">0</if-global-variable>
			<if-operation mode="regex" op="equal">add|modify|modify-password|rename|move|delete</if-operation>
		</or>
	</conditions>
	<actions>
		<do-set-local-variable name="now" scope="policy">
			<arg-string>
				<token-time format="!CTIME" tz="UTC"/>
			</arg-string>
		</do-set-local-variable>
		<do-if>
			<arg-conditions>
				<and>
					<if-xpath op="true">$LastModifiedObjects/list</if-xpath>
				</and>
			</arg-conditions>
			<arg-actions>
				<do-strip-xpath expression="$LastModifiedObjects/list/object[@timestamp < $now - ~MinModDelta~]"/>
			</arg-actions>
			<arg-actions>
				<do-set-local-variable name="LastModifiedObjects" scope="driver">
					<arg-node-set>
						<token-xml-parse>
							<token-text xml:space="preserve"><list/></token-text>
						</token-xml-parse>
					</arg-node-set>
				</do-set-local-variable>
				<do-set-xml-attr expression="$LastModifiedObjects/list" name="starttime">
					<arg-string>
						<token-local-variable name="now"/>
					</arg-string>
				</do-set-xml-attr>
				<do-set-xml-attr expression="$LastModifiedObjects/list" name="delaytime">
					<arg-string>
						<token-text xml:space="preserve">0</token-text>
					</arg-string>
				</do-set-xml-attr>
			</arg-actions>
		</do-if>
		<do-for-each>
			<arg-node-set>
				<token-xpath expression="$LastModifiedObjects/list/object[@association = $current-op/association and @timestamp >= $now - ~MinModDelta~][last()]/@timestamp"/>
			</arg-node-set>
			<arg-actions>
				<do-set-local-variable name="delay" scope="policy">
					<arg-string>
						<token-xpath expression="$current-node + ~MinModDelta~ - $now"/>
					</arg-string>
				</do-set-local-variable>
				<do-set-xml-attr expression="$LastModifiedObjects/list" name="delaytime">
					<arg-string>
						<token-xpath expression="$LastModifiedObjects/list/@delaytime + $delay"/>
					</arg-string>
				</do-set-xml-attr>
				<do-trace-message>
					<arg-string>
						<token-text xml:space="preserve">Delay sending current </token-text>
						<token-operation/>
						<token-text xml:space="preserve"> of </token-text>
						<token-association/>
						<token-text xml:space="preserve"> for </token-text>
						<token-local-variable name="delay"/>
						<token-text xml:space="preserve"> seconds. Total delay time during last  </token-text>
						<token-replace-all regex="(.*\..{2}).*" replace-with="$1">
							<token-xpath expression="($now - $LastModifiedObjects/list/@starttime) div 3600"/>
						</token-replace-all>
						<token-text xml:space="preserve"> hours: </token-text>
						<token-replace-all regex="(.*\..{2}).*" replace-with="$1">
							<token-xpath expression="$LastModifiedObjects/list/@delaytime div 60"/>
						</token-replace-all>
						<token-text xml:space="preserve"> minutes.</token-text>
					</arg-string>
				</do-trace-message>
				<do-set-local-variable name="sleep-result" scope="policy">
					<arg-object>
						<token-xpath expression="java.lang.Thread:sleep($delay * 1000)"/>
					</arg-object>
				</do-set-local-variable>
			</arg-actions>
		</do-for-each>
		<do-append-xml-element expression="$LastModifiedObjects/list" name="object"/>
		<do-set-xml-attr expression="$LastModifiedObjects/list/object[last()]" name="association">
			<arg-string>
				<token-association/>
			</arg-string>
		</do-set-xml-attr>
		<do-set-xml-attr expression="$LastModifiedObjects/list/object[last()]" name="timestamp">
			<arg-string>
				<token-time format="!CTIME" tz="UTC"/>
			</arg-string>
		</do-set-xml-attr>
		<do-trace-message level="3">
			<arg-string>
				<token-text xml:space="preserve">Last Modified Objects: </token-text>
				<token-xml-serialize>
					<token-local-variable name="LastModifiedObjects"/>
				</token-xml-serialize>
			</arg-string>
		</do-trace-message>
	</actions>
</rule>

This rule is meant to delay modifications on the application side and lives in the last output transform so it even kicks in when using tokens with direct=”true”.

It can be used to delay modifications to the vault as well and would have to sit in the last publisher command transform then. In that case all tokens that use direct=”true” (both from subscriber and publisher) would be missed, though.

0 votes, average: 0.00 out of 50 votes, average: 0.00 out of 50 votes, average: 0.00 out of 50 votes, average: 0.00 out of 50 votes, average: 0.00 out of 5 (0 votes, average: 0.00 out of 5)
You need to be a registered member to rate this post.
Loading...Loading...
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.

Leave a Reply

No Comments
lhaeger
By: lhaeger
May 6, 2009
4:02 pm
Reads:
1,385
Score:
Unrated