package org.ovirt.engine.core.bll.network.cluster; import static org.ovirt.engine.core.common.action.VdcActionType.AttachNetworkToCluster; import static org.ovirt.engine.core.common.action.VdcActionType.DetachNetworkToCluster; import static org.ovirt.engine.core.common.action.VdcActionType.UpdateNetworkOnCluster; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Named; import org.ovirt.engine.core.bll.CommandBase; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.utils.PermissionSubject; import org.ovirt.engine.core.common.action.AttachNetworkToClusterParameter; import org.ovirt.engine.core.common.action.ManageNetworkClustersParameters; import org.ovirt.engine.core.common.action.NetworkClusterParameters; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.businessentities.network.NetworkCluster; import org.ovirt.engine.core.common.businessentities.network.NetworkClusterId; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.utils.transaction.TransactionSupport; @NonTransactiveCommandAttribute public final class ManageNetworkClustersCommand extends CommandBase<ManageNetworkClustersParameters> { @Inject @Named private Predicate<NetworkCluster> managementNetworkAppointmentPredicate; @Inject @Named private Function<NetworkCluster, AttachNetworkToClusterParameter> networkClusterToAttachNetworkToClusterParameterTransformer; @Inject @Named private Function<NetworkCluster, NetworkClusterParameters> networkClusterParameterTransformer; @Inject private AttachNetworkClusterPermissionsChecker attachPermissionChecker; @Inject private DetachNetworkClusterPermissionFinder detachPermissionFinder; @Inject private UpdateNetworkClusterPermissionsChecker updatePermissionChecker; public ManageNetworkClustersCommand( ManageNetworkClustersParameters parameters, CommandContext cmdContext) { super(parameters, cmdContext); } @Override protected void executeCommand() { final Boolean dbUpdateResult = TransactionSupport.executeInNewTransaction(() -> { final Collection<NetworkCluster> attachments = getParameters().getAttachments(); final List<NetworkCluster> managementNetworkAttachments = attachments.stream().filter(managementNetworkAppointmentPredicate).collect(Collectors.toList()); final List<NetworkCluster> nonManagementNetworkAttachments = attachments.stream().filter(managementNetworkAppointmentPredicate.negate()).collect(Collectors.toList()); final Collection<NetworkCluster> updates = getParameters().getUpdates(); final List<NetworkCluster> managementNetworkUpdates = updates.stream().filter(managementNetworkAppointmentPredicate).collect(Collectors.toList()); final List<NetworkCluster> nonManagementNetworkUpdates = updates.stream().filter(managementNetworkAppointmentPredicate.negate()).collect(Collectors.toList()); boolean resultStatus = attachNetworks(managementNetworkAttachments); resultStatus = resultStatus && updateNetworkAttachments(managementNetworkUpdates); resultStatus = resultStatus && attachNetworks(nonManagementNetworkAttachments); resultStatus = resultStatus && updateNetworkAttachments(nonManagementNetworkUpdates); resultStatus = resultStatus && detachNetworks(getParameters().getDetachments()); return resultStatus; }); setSucceeded(dbUpdateResult); if (dbUpdateResult) { propagateLabeledNetworksChanges(); } } private boolean attachNetworks(Collection<NetworkCluster> attachments) { return runNetworkClusterCommands( attachments, VdcActionType.AttachNetworkToClusterInternal, networkClusterToAttachNetworkToClusterParameterTransformer); } private boolean detachNetworks(Collection<NetworkCluster> detachments) { return runNetworkClusterCommands( detachments, VdcActionType.DetachNetworkFromClusterInternal, networkClusterToAttachNetworkToClusterParameterTransformer); } private boolean updateNetworkAttachments(Collection<NetworkCluster> updates) { return runNetworkClusterCommands( updates, VdcActionType.UpdateNetworkOnCluster, networkClusterParameterTransformer); } private void propagateLabeledNetworksChanges() { runInternalAction(VdcActionType.PropagateLabeledNetworksToClusterHosts, getParameters()); } private boolean runNetworkClusterCommands( Collection<NetworkCluster> networkClusters, VdcActionType actionType, Function<NetworkCluster, ? extends VdcActionParametersBase> networkClusterToParameterTransformer) { final List<? extends VdcActionParametersBase> parameters = networkClusters.stream().map(networkClusterToParameterTransformer).collect(Collectors.toList()); return runMultipleInternalCommandsSynchronously(actionType, parameters); } /** * Runs multiple commands sequentially on the current thread. The execution stops on first failure. * * @param actionType command type * @param parameters commad parameters * @return the execution status. <code>true</code> on success and <code>false</code> on faliure. */ private boolean runMultipleInternalCommandsSynchronously( VdcActionType actionType, List<? extends VdcActionParametersBase> parameters) { for (VdcActionParametersBase param : parameters) { final VdcReturnValueBase executionResult = runInternalAction(actionType, param); if (!executionResult.getSucceeded()) { TransactionSupport.setRollbackOnly(); propagateFailure(executionResult); return false; } } return true; } @Override public List<PermissionSubject> getPermissionCheckSubjects() { final List<PermissionSubject> result = new ArrayList<>(); for (NetworkCluster attachment : getParameters().getAttachments()) { result.addAll( attachPermissionChecker.findPermissionCheckSubjects( attachment, VdcActionType.AttachNetworkToCluster)); } for (NetworkCluster detachment : getParameters().getDetachments()) { result.addAll( detachPermissionFinder.findPermissionCheckSubjects( detachment.getNetworkId(), VdcActionType.DetachNetworkToCluster)); } for (NetworkCluster update : getParameters().getUpdates()) { result.addAll( updatePermissionChecker.findPermissionCheckSubjects( update.getNetworkId(), update.getClusterId(), VdcActionType.UpdateNetworkOnCluster)); } return result; } @Override protected boolean checkPermissions(final List<PermissionSubject> permSubjects) { return checkAttachmentPermissions() && checkDetachmentsPermissions() && checkUpdatesPermissions(); } private boolean checkAttachmentPermissions() { for (NetworkCluster attachment : getParameters().getAttachments()) { final boolean isUserAllowed = attachPermissionChecker.checkPermissions( this, attachment, AttachNetworkToCluster); if (!isUserAllowed) { return false; } } return true; } private boolean checkDetachmentsPermissions() { for (NetworkCluster detachment : getParameters().getDetachments()) { final List<PermissionSubject> permissionCheckSubjects = detachPermissionFinder.findPermissionCheckSubjects( detachment.getNetworkId(), DetachNetworkToCluster); for (PermissionSubject permissionSubject : permissionCheckSubjects) { final ArrayList<String> messages = new ArrayList<>(); final boolean isUserAllowed = checkSinglePermission(permissionSubject, messages); if (!isUserAllowed) { getReturnValue().getValidationMessages().addAll(messages); return false; } } } return true; } private boolean checkUpdatesPermissions() { for (NetworkCluster update : getParameters().getUpdates()) { final boolean isUserAllowed = updatePermissionChecker.checkPermissions( this, update.getNetworkId(), update.getClusterId(), UpdateNetworkOnCluster); if (!isUserAllowed) { return false; } } return true; } @Override protected boolean validate() { return super.validate() && validate(validateInputForDuplication()); } private ValidationResult validateInputForDuplication() { final Set<NetworkClusterId> networkClusterIds = new HashSet<>(); final Iterable<NetworkCluster> inputNetworkClusters = Stream.of( getParameters().getAttachments(), getParameters().getDetachments(), getParameters().getUpdates()).flatMap(Collection::stream).collect(Collectors.toList()); for (NetworkCluster networkCluster : inputNetworkClusters) { if (networkClusterIds.contains(networkCluster.getId())) { final String networkClusterReplacement = String.format("${NetworkCluster} %s", networkCluster.getId()); return new ValidationResult( EngineMessage.ACTION_TYPE_FAILED_DUPLICATE_NETWORK_CLUSTER_INPUT, networkClusterReplacement); } else { networkClusterIds.add(networkCluster.getId()); } } return ValidationResult.VALID; } }