Skip to content

Commit

Permalink
Reworking execution of provisioning scripts in UCF (MID-5921)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Nov 27, 2019
1 parent 23b2355 commit e469089
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 134 deletions.
Expand Up @@ -84,8 +84,7 @@ public OperationResultStatus queryOperationStatus(String asynchronousOperationRe


@Override
protected String createTicketAdd(PrismObject<? extends ShadowType> object,
Collection<Operation> additionalOperations, OperationResult result) throws CommunicationException,
protected String createTicketAdd(PrismObject<? extends ShadowType> object, OperationResult result) throws CommunicationException,
GenericFrameworkException, SchemaException, ObjectAlreadyExistsException, ConfigurationException {
DummyItsm itsm = DummyItsm.getInstance();
String identifier;
Expand Down
Expand Up @@ -65,6 +65,8 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti
super.initSystem(initTask, initResult);

initDummyResourcePirate(RESOURCE_DUMMY_OPALINE_NAME, RESOURCE_DUMMY_OPALINE_FILE, RESOURCE_DUMMY_OPALINE_OID, initTask, initResult);
DummyResourceContoller opalineScriptController = DummyResourceContoller.create(RESOURCE_DUMMY_OPALINE_SCRIPT_NAME, getDummyResourceObject(RESOURCE_DUMMY_OPALINE_NAME));
dummyResourceCollection.initDummyResource(RESOURCE_DUMMY_OPALINE_SCRIPT_NAME, opalineScriptController);

repoAddObjectFromFile(SECURITY_POLICY_FILE, initResult);
repoAddObjectFromFile(PASSWORD_POLICY_BENEVOLENT_FILE, initResult);
Expand All @@ -73,7 +75,7 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti
repoAddObjectFromFile(USER_GUYBRUSH_FILE, true, initResult);
}

@Test(enabled = false) // MID-5921
@Test
public void test000Sanity() throws Exception {
final String TEST_NAME = "test000Sanity";
displayTestTitle(TEST_NAME);
Expand All @@ -93,7 +95,7 @@ public void test000Sanity() throws Exception {
assertEquals("Wrong OPALINE-SCRIPT useless string", CONF_USELESS_SCRIPT, getDummyResource(RESOURCE_DUMMY_OPALINE_SCRIPT_NAME).getUselessString());
}

@Test(enabled = false) // MID-5921
@Test
public void test100JackAssignDummyOpaline() throws Exception {
final String TEST_NAME = "test100JackAssignDummyOpaline";
displayTestTitle(TEST_NAME);
Expand Down
Expand Up @@ -35,6 +35,7 @@
import com.evolveum.midpoint.schema.util.SchemaDebugUtil;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.task.api.RunningTask;
import com.evolveum.midpoint.task.api.StateReporter;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.Tracer;
import com.evolveum.midpoint.util.DebugUtil;
Expand Down Expand Up @@ -250,21 +251,20 @@ public AsynchronousOperationReturnValue<PrismObject<ShadowType>> addResourceObje

checkForCapability(ctx, CreateCapabilityType.class, result);

Collection<Operation> additionalOperations = new ArrayList<>();
addExecuteScriptOperation(additionalOperations, ProvisioningOperationTypeType.ADD, scripts, resource, result);
executeProvisioningScripts(ctx, ProvisioningOperationTypeType.ADD, BeforeAfterType.BEFORE, scripts, result);

entitlementConverter.processEntitlementsAdd(ctx, shadowClone);

ConnectorInstance connector = ctx.getConnector(CreateCapabilityType.class, result);
AsynchronousOperationReturnValue<Collection<ResourceAttribute<?>>> connectorAsyncOpRet;
try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PROVISIONING ADD operation on resource {}\n ADD object:\n{}\n additional operations:\n{}",
resource.asPrismObject(), shadowType.asPrismObject().debugDump(),
SchemaDebugUtil.debugDump(additionalOperations, 2));
LOGGER.debug("PROVISIONING ADD operation on resource {}\n ADD object:\n{}\n",
resource.asPrismObject(), shadowType.asPrismObject().debugDump());
}
transformActivationAttributesAdd(ctx, shadowType, result);

connectorAsyncOpRet = connector.addObject(shadowClone, additionalOperations, ctx, result);
connectorAsyncOpRet = connector.addObject(shadowClone, ctx, result);
resourceAttributesAfterAdd = connectorAsyncOpRet.getReturnValue();

if (LOGGER.isDebugEnabled()) {
Expand Down Expand Up @@ -297,6 +297,8 @@ public AsynchronousOperationReturnValue<PrismObject<ShadowType>> addResourceObje

LOGGER.trace("Added resource object {}", shadow);

executeProvisioningScripts(ctx, ProvisioningOperationTypeType.ADD, BeforeAfterType.AFTER, scripts, result);

computeResultStatus(result);

AsynchronousOperationReturnValue<PrismObject<ShadowType>> asyncOpRet = AsynchronousOperationReturnValue.wrap(shadow, result);
Expand Down Expand Up @@ -391,20 +393,17 @@ public AsynchronousOperationResult deleteResourceObject(ProvisioningContext ctx,
// Execute entitlement modification on other objects (if needed)
executeEntitlementChangesDelete(ctx, shadow, scripts, connOptions, result);

Collection<Operation> additionalOperations = new ArrayList<>();
addExecuteScriptOperation(additionalOperations, ProvisioningOperationTypeType.DELETE, scripts, ctx.getResource(),
result);
executeProvisioningScripts(ctx, ProvisioningOperationTypeType.DELETE, BeforeAfterType.BEFORE, scripts, result);

ConnectorInstance connector = ctx.getConnector(DeleteCapabilityType.class, result);
AsynchronousOperationResult connectorAsyncOpRet = null;
try {

if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"PROVISIONING DELETE operation on {}\n DELETE object, object class {}, identified by:\n{}\n additional operations:\n{}",
"PROVISIONING DELETE operation on {}\n DELETE object, object class {}, identified by:\n{}",
ctx.getResource(), shadow.asObjectable().getObjectClass(),
SchemaDebugUtil.debugDump(identifiers),
SchemaDebugUtil.debugDump(additionalOperations));
SchemaDebugUtil.debugDump(identifiers));
}

if (!ResourceTypeUtil.isDeleteCapabilityEnabled(ctx.getResource())){
Expand All @@ -413,7 +412,7 @@ public AsynchronousOperationResult deleteResourceObject(ProvisioningContext ctx,
throw e;
}

connectorAsyncOpRet = connector.deleteObject(ctx.getObjectClassDefinition(), additionalOperations, shadow, identifiers, ctx, result);
connectorAsyncOpRet = connector.deleteObject(ctx.getObjectClassDefinition(), shadow, identifiers, ctx, result);

computeResultStatus(result);
LOGGER.debug("PROVISIONING DELETE: {}", result.getStatus());
Expand Down Expand Up @@ -446,8 +445,10 @@ public AsynchronousOperationResult deleteResourceObject(ProvisioningContext ctx,
throw ex;
}


LOGGER.trace("Deleted resource object {}", shadow);

executeProvisioningScripts(ctx, ProvisioningOperationTypeType.DELETE, BeforeAfterType.AFTER, scripts, result);

AsynchronousOperationResult aResult = AsynchronousOperationResult.wrap(result);
updateQuantum(ctx, connector, aResult, parentResult);
if (connectorAsyncOpRet != null) {
Expand Down Expand Up @@ -568,9 +569,6 @@ public AsynchronousOperationReturnValue<Collection<PropertyDelta<PrismPropertyVa

if (!operations.isEmpty()) {

// This must go after the skip check above. Otherwise the scripts would be executed even if there is no need to.
addExecuteScriptOperation(operations, ProvisioningOperationTypeType.MODIFY, scripts, ctx.getResource(), result);

if (InternalsConfig.isSanityChecks()) {
// MID-3964
if (MiscUtil.hasDuplicates(operations)) {
Expand All @@ -579,7 +577,7 @@ public AsynchronousOperationReturnValue<Collection<PropertyDelta<PrismPropertyVa
}

// Execute primary ICF operation on this shadow
modifyAsyncRet = executeModify(ctx, (preReadShadow == null ? repoShadow.clone() : preReadShadow), identifiers, operations, connOptions, result);
modifyAsyncRet = executeModify(ctx, (preReadShadow == null ? repoShadow.clone() : preReadShadow), identifiers, operations, scripts, result, connOptions);
if (modifyAsyncRet != null) {
sideEffectOperations = modifyAsyncRet.getReturnValue();
}
Expand Down Expand Up @@ -668,9 +666,7 @@ private Collection<PropertyDelta<PrismPropertyValue>> convertToPropertyDelta(
}

@SuppressWarnings("rawtypes")
private AsynchronousOperationReturnValue<Collection<PropertyModificationOperation>> executeModify(ProvisioningContext ctx,
PrismObject<ShadowType> currentShadow, Collection<? extends ResourceAttribute<?>> identifiers,
Collection<Operation> operations, ConnectorOperationOptions connOptions, OperationResult parentResult)
private AsynchronousOperationReturnValue<Collection<PropertyModificationOperation>> executeModify(ProvisioningContext ctx, PrismObject<ShadowType> currentShadow, Collection<? extends ResourceAttribute<?>> identifiers, Collection<Operation> operations, OperationProvisioningScriptsType scripts, OperationResult parentResult, ConnectorOperationOptions connOptions)
throws ObjectNotFoundException, CommunicationException, SchemaException, SecurityViolationException, PolicyViolationException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException {

Collection<PropertyModificationOperation> sideEffectChanges = new HashSet<>();
Expand All @@ -696,7 +692,9 @@ private AsynchronousOperationReturnValue<Collection<PropertyModificationOperatio
identifiers = allIdentifiers;
}

// Invoke ICF
executeProvisioningScripts(ctx, ProvisioningOperationTypeType.MODIFY, BeforeAfterType.BEFORE, scripts, parentResult);

// Invoke connector operation
ConnectorInstance connector = ctx.getConnector(UpdateCapabilityType.class, parentResult);
AsynchronousOperationReturnValue<Collection<PropertyModificationOperation>> connectorAsyncOpRet = null;
try {
Expand Down Expand Up @@ -734,8 +732,6 @@ private AsynchronousOperationReturnValue<Collection<PropertyModificationOperatio
} else {
LOGGER.trace("Filtering out modification {} because it has empty delta after narrow", propertyDelta);
}
} else if (origOperation instanceof ExecuteProvisioningScriptOperation) {
filteredOperations.add(origOperation);
}
}
if (filteredOperations.isEmpty()) {
Expand Down Expand Up @@ -829,6 +825,8 @@ private AsynchronousOperationReturnValue<Collection<PropertyModificationOperatio
throw ex;
}

executeProvisioningScripts(ctx, ProvisioningOperationTypeType.MODIFY, BeforeAfterType.AFTER, scripts, parentResult);

AsynchronousOperationReturnValue<Collection<PropertyModificationOperation>> asyncOpRet = AsynchronousOperationReturnValue.wrap(sideEffectChanges, parentResult);
if (connectorAsyncOpRet != null) {
asyncOpRet.setOperationType(connectorAsyncOpRet.getOperationType());
Expand Down Expand Up @@ -1207,7 +1205,7 @@ private void executeEntitlements(ProvisioningContext subjectCtx,
OperationResult result = parentResult.createMinorSubresult(OPERATION_MODIFY_ENTITLEMENT);
try {

executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), allIdentifiers, operations, connOptions, result);
executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), allIdentifiers, operations, null, result, connOptions);

result.recordSuccess();

Expand Down Expand Up @@ -2519,27 +2517,62 @@ private String getLockoutLockedValue(ActivationLockoutStatusCapabilityType capAc
return capActStatus.getLockedValue().iterator().next();
}

private void addExecuteScriptOperation(Collection<Operation> operations, ProvisioningOperationTypeType type,
OperationProvisioningScriptsType scripts, ResourceType resource, OperationResult result) throws SchemaException {
private void executeProvisioningScripts(ProvisioningContext ctx, ProvisioningOperationTypeType provisioningOperationType,
BeforeAfterType beforeAfter, OperationProvisioningScriptsType scripts, OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, ExpressionEvaluationException, GenericConnectorException {
Collection<ExecuteProvisioningScriptOperation> operations = determineExecuteScriptOperations(provisioningOperationType, beforeAfter, scripts, ctx.getResource(), result);
if (operations == null) {
return;
}
ConnectorInstance connector = ctx.getConnector(ScriptCapabilityType.class, result);
for (ExecuteProvisioningScriptOperation operation : operations) {
StateReporter reporter = new StateReporter(ctx.getResource().getOid(), ctx.getTask());

try {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PROVISIONING SCRIPT EXECUTION {} {} operation on resource {}",
beforeAfter.value(), provisioningOperationType.value(),
ctx.getResource());
}

Object returnedValue = connector.executeScript(operation, reporter, result);

if (LOGGER.isDebugEnabled()) {
LOGGER.debug("PROVISIONING SCRIPT EXECUTION {} {} successful, returned value: {}", returnedValue);
}

} catch (CommunicationException ex) {
result.recordFatalError(
"Could not execute provisioning script. Error communicating with the connector " + connector + ": " + ex.getMessage(), ex);
throw new CommunicationException("Error communicating with the connector " + connector + ": "
+ ex.getMessage(), ex);
} catch (GenericFrameworkException ex) {
result.recordFatalError("Could not execute provisioning script. Generic error in connector: " + ex.getMessage(), ex);
throw new GenericConnectorException("Generic error in connector: " + ex.getMessage(), ex);
}

}
}

private Collection<ExecuteProvisioningScriptOperation> determineExecuteScriptOperations(ProvisioningOperationTypeType provisioningOperationType,
BeforeAfterType beforeAfter, OperationProvisioningScriptsType scripts, ResourceType resource, OperationResult result) throws SchemaException {
if (scripts == null) {
// No warning needed, this is quite normal
LOGGER.trace("Skipping creating script operation to execute. Scripts was not defined.");
return;
LOGGER.trace("Skipping creating script operation to execute. No scripts were defined.");
return null;
}

Collection<ExecuteProvisioningScriptOperation> operations = new ArrayList<>();
for (OperationProvisioningScriptType script : scripts.getScript()) {
for (ProvisioningOperationTypeType operationType : script.getOperation()) {
if (type.equals(operationType)) {
if (provisioningOperationType.equals(operationType) && beforeAfter.equals(script.getOrder())) {
ExecuteProvisioningScriptOperation scriptOperation = ProvisioningUtil.convertToScriptOperation(
script, "script value for " + operationType + " in " + resource, prismContext);

scriptOperation.setScriptOrder(script.getOrder());

LOGGER.trace("Created script operation: {}", SchemaDebugUtil.prettyPrint(scriptOperation));
operations.add(scriptOperation);
}
}
}
return operations;
}

public AsynchronousOperationResult refreshOperationStatus(ProvisioningContext ctx,
Expand Down
Expand Up @@ -254,9 +254,8 @@ int count(ObjectClassComplexTypeDefinition objectClassDefinition, ObjectQuery qu
* @return created object attributes. May be null.
* @throws ObjectAlreadyExistsException object already exists on the resource
*/
AsynchronousOperationReturnValue<Collection<ResourceAttribute<?>>> addObject(PrismObject<? extends ShadowType> object, Collection<Operation> additionalOperations, StateReporter reporter,
OperationResult parentResult) throws CommunicationException, GenericFrameworkException, SchemaException,
ObjectAlreadyExistsException, ConfigurationException, SecurityViolationException, PolicyViolationException;
AsynchronousOperationReturnValue<Collection<ResourceAttribute<?>>> addObject(PrismObject<? extends ShadowType> object, StateReporter reporter, OperationResult parentResult)
throws CommunicationException, GenericFrameworkException, SchemaException, ObjectAlreadyExistsException, ConfigurationException, SecurityViolationException, PolicyViolationException;

/**
* TODO: This should return indication how the operation went, e.g. what changes were applied, what were not
Expand All @@ -281,7 +280,7 @@ AsynchronousOperationReturnValue<Collection<PropertyModificationOperation>> modi
throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException,
SecurityViolationException, PolicyViolationException, ObjectAlreadyExistsException, ConfigurationException;

AsynchronousOperationResult deleteObject(ObjectClassComplexTypeDefinition objectClass, Collection<Operation> additionalOperations, PrismObject<ShadowType> shadow, Collection<? extends ResourceAttribute<?>> identifiers, StateReporter reporter,
AsynchronousOperationResult deleteObject(ObjectClassComplexTypeDefinition objectClass, PrismObject<ShadowType> shadow, Collection<? extends ResourceAttribute<?>> identifiers, StateReporter reporter,
OperationResult parentResult)
throws ObjectNotFoundException, CommunicationException, GenericFrameworkException, SchemaException, ConfigurationException, SecurityViolationException, PolicyViolationException;

Expand Down

0 comments on commit e469089

Please sign in to comment.