/*
* Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.groupbasedpolicy.sf;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.groupbasedpolicy.api.PolicyValidatorRegistry;
import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
import org.opendaylight.groupbasedpolicy.util.IidFactory;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ParameterName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definition.Parameter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinition;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.subject.feature.definitions.ClassifierDefinitionBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.RendererName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.Renderer;
import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.capabilities.SupportedClassifierDefinition;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
public class SupportedClassifierDefinitionListener extends DataTreeChangeHandler<SupportedClassifierDefinition> {
private static final Logger LOG = LoggerFactory.getLogger(SupportedClassifierDefinitionListener.class);
@VisibleForTesting
final Table<RendererName, ClassifierDefinitionId, ClassifierInstanceValidator> validatorByRendererAndCd =
HashBasedTable.create();
private final PolicyValidatorRegistry validatorRegistry;
public SupportedClassifierDefinitionListener(DataBroker dataProvider, PolicyValidatorRegistry validatorRegistry) {
super(dataProvider);
this.validatorRegistry = validatorRegistry;
if (validatorRegistry == null) {
LOG.info(
"{} service was NOT found. Automatic registration of simple classifier-instance validators is NOT available for renderers.",
PolicyValidatorRegistry.class.getCanonicalName());
} else {
LOG.info(
"{} service was found. Automatic registration of simple classifier-instance validators is available for renderers.",
PolicyValidatorRegistry.class.getCanonicalName());
}
registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
IidFactory.supportedClassifierDefinitionIidWildcard()));
}
@Override
protected void onWrite(DataObjectModification<SupportedClassifierDefinition> rootNode,
InstanceIdentifier<SupportedClassifierDefinition> createdSupportedCdIid) {
SupportedClassifierDefinition createdSupportedCd = rootNode.getDataAfter();
RendererName rendererName = createdSupportedCdIid.firstKeyOf(Renderer.class).getName();
ClassifierInstanceValidator ciValidator = new ClassifierInstanceValidator(createdSupportedCd, rendererName);
setParentValidators(ciValidator, false);
ClassifierDefinitionId cdId = createdSupportedCd.getClassifierDefinitionId();
validatorByRendererAndCd.put(rendererName, cdId, ciValidator);
if (validatorRegistry != null) {
validatorRegistry.register(cdId, ciValidator);
}
putOrRemoveClassifierDefinitionInOperDs(cdId, getAllSupportedParams(cdId));
}
@Override
protected void onDelete(DataObjectModification<SupportedClassifierDefinition> rootNode,
InstanceIdentifier<SupportedClassifierDefinition> removedSupportedCdIid) {
SupportedClassifierDefinition removedSupportedCd = rootNode.getDataBefore();
ClassifierDefinitionId cdId = removedSupportedCd.getClassifierDefinitionId();
RendererName rendererName = removedSupportedCdIid.firstKeyOf(Renderer.class).getName();
ClassifierInstanceValidator removedCiValidator = validatorByRendererAndCd.remove(rendererName, cdId);
if (validatorRegistry != null) {
validatorRegistry.unregister(cdId, removedCiValidator);
}
setParentValidators(removedCiValidator, true);
putOrRemoveClassifierDefinitionInOperDs(cdId, getAllSupportedParams(cdId));
}
@Override
protected void onSubtreeModified(DataObjectModification<SupportedClassifierDefinition> rootNode,
InstanceIdentifier<SupportedClassifierDefinition> modifiedSupportedCdIid) {
SupportedClassifierDefinition beforeSupportedCd = rootNode.getDataBefore();
ClassifierDefinitionId cdId = beforeSupportedCd.getClassifierDefinitionId();
RendererName rendererName = modifiedSupportedCdIid.firstKeyOf(Renderer.class).getName();
ClassifierInstanceValidator oldCiValidator = validatorByRendererAndCd.remove(rendererName, cdId);
if (validatorRegistry != null) {
validatorRegistry.unregister(cdId, oldCiValidator);
}
SupportedClassifierDefinition afterSupportedCd = rootNode.getDataAfter();
ClassifierInstanceValidator newCiValidator = new ClassifierInstanceValidator(afterSupportedCd, rendererName);
setParentValidators(newCiValidator, false);
validatorByRendererAndCd.put(rendererName, cdId, newCiValidator);
if (validatorRegistry != null) {
validatorRegistry.register(cdId, newCiValidator);
}
putOrRemoveClassifierDefinitionInOperDs(cdId, getAllSupportedParams(cdId));
}
@VisibleForTesting
void setParentValidators(ClassifierInstanceValidator ciValidator, boolean setParentToNull) {
if (ciValidator.getParentClassifierDefinitionId() != null && !setParentToNull) {
ClassifierInstanceValidator parentCiValidator = validatorByRendererAndCd.get(ciValidator.getRendererName(),
ciValidator.getParentClassifierDefinitionId());
if (parentCiValidator != null) {
ciValidator.setParentValidator(parentCiValidator);
}
}
for (ClassifierInstanceValidator existingCiValidator : getValidatorsWithParentCdForRenderer(
ciValidator.getClassifierDefinitionId(), ciValidator.getRendererName())) {
if (setParentToNull) {
existingCiValidator.setParentValidator(null);
} else {
existingCiValidator.setParentValidator(ciValidator);
}
}
}
@VisibleForTesting
Collection<ClassifierInstanceValidator> getValidatorsWithParentCdForRenderer(
final ClassifierDefinitionId parentCdId, RendererName renderer) {
return Collections2.filter(validatorByRendererAndCd.row(renderer).values(),
new Predicate<ClassifierInstanceValidator>() {
@Override
public boolean apply(ClassifierInstanceValidator ciValidator) {
if (parentCdId.equals(ciValidator.getParentClassifierDefinitionId())) {
return true;
}
return false;
}
});
}
@VisibleForTesting
List<ParameterName> getAllSupportedParams(final ClassifierDefinitionId cdId) {
return FluentIterable.from(validatorByRendererAndCd.column(cdId).values())
.transformAndConcat(new Function<ClassifierInstanceValidator, Set<ParameterName>>() {
@Override
public Set<ParameterName> apply(ClassifierInstanceValidator input) {
return input.getSupportedParameters();
}
})
.toList();
}
@VisibleForTesting
void putOrRemoveClassifierDefinitionInOperDs(ClassifierDefinitionId cdId, List<ParameterName> supportedParams) {
ReadWriteTransaction rwTx = dataProvider.newReadWriteTransaction();
Optional<ClassifierDefinition> potentialCdFromConfDs = DataStoreHelper
.readFromDs(LogicalDatastoreType.CONFIGURATION, IidFactory.classifierDefinitionIid(cdId), rwTx);
if (!potentialCdFromConfDs.isPresent()) {
LOG.error("Classifier-definition with ID {} does not exist in CONF datastore.", cdId);
DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, IidFactory.classifierDefinitionIid(cdId),
rwTx);
DataStoreHelper.submitToDs(rwTx);
return;
}
ClassifierDefinition cd =
createClassifierDefinitionWithUnionOfParams(potentialCdFromConfDs.get(), supportedParams);
if (cd != null) {
rwTx.put(LogicalDatastoreType.OPERATIONAL, IidFactory.classifierDefinitionIid(cdId), cd);
} else {
DataStoreHelper.removeIfExists(LogicalDatastoreType.OPERATIONAL, IidFactory.classifierDefinitionIid(cdId),
rwTx);
}
DataStoreHelper.submitToDs(rwTx);
}
@VisibleForTesting
static ClassifierDefinition createClassifierDefinitionWithUnionOfParams(ClassifierDefinition cd,
List<ParameterName> supportedParams) {
if (supportedParams == null || supportedParams.isEmpty()) {
LOG.debug("Classifier-definition with ID {} is not supported by any renderer.", cd.getId().getValue());
return null;
}
if (cd.getParameter() == null || cd.getParameter().isEmpty()) {
LOG.trace("Classifier-definition with ID {} does not contain any parameter in CONF datastore.",
cd.getId().getValue());
return cd;
}
List<Parameter> params = new ArrayList<>();
for (ParameterName supportedParam : supportedParams) {
for (Parameter param : cd.getParameter()) {
if (param.getName().equals(supportedParam)) {
params.add(param);
}
}
}
ClassifierDefinitionBuilder cdBuilder = new ClassifierDefinitionBuilder(cd);
return cdBuilder.setParameter(params).build();
}
}