/*
* Copyright (c) 2016 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.sxp.ep.provider.impl.dao;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.groupbasedpolicy.sxp.ep.provider.impl.DSAsyncDao;
import org.opendaylight.groupbasedpolicy.sxp.ep.provider.impl.MasterDatabaseBindingListener;
import org.opendaylight.groupbasedpolicy.sxp.ep.provider.impl.ReadableAsyncByKey;
import org.opendaylight.groupbasedpolicy.sxp.ep.provider.impl.SimpleCachedDao;
import org.opendaylight.groupbasedpolicy.sxp.ep.provider.impl.util.SxpListenerUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.MasterDatabaseFields;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.Sgt;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.database.rev160308.master.database.fields.MasterDatabaseBinding;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpDatabasesFields;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.SxpNodeIdentity;
import org.opendaylight.yang.gen.v1.urn.opendaylight.sxp.node.rev160308.network.topology.topology.node.SxpDomains;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Purpose: general dao for EndPoint templates
*/
public class MasterDatabaseBindingDaoImpl implements DSAsyncDao<IpPrefix, MasterDatabaseBinding>,
ReadableAsyncByKey<Sgt, MasterDatabaseBinding> {
private static final Logger LOG = LoggerFactory.getLogger(MasterDatabaseBindingDaoImpl.class);
private static final ListenableFuture<Optional<MasterDatabaseBinding>> READ_FUTURE_ABSENT = Futures.immediateFuture(Optional.absent());
private final DataBroker dataBroker;
private final SimpleCachedDao<IpPrefix, MasterDatabaseBinding> cachedDao;
public MasterDatabaseBindingDaoImpl(final DataBroker dataBroker,
final SimpleCachedDao<IpPrefix, MasterDatabaseBinding> cachedDao) {
this.dataBroker = dataBroker;
this.cachedDao = cachedDao;
}
@Override
public ListenableFuture<Optional<MasterDatabaseBinding>> read(@Nonnull final IpPrefix key) {
final Optional<MasterDatabaseBinding> cachedMasterDatabaseBinding = lookup(cachedDao, key);
if (cachedMasterDatabaseBinding.isPresent()) {
return Futures.immediateFuture(cachedMasterDatabaseBinding);
} else if (!cachedDao.isEmpty()) {
return READ_FUTURE_ABSENT;
} else {
final ListenableFuture<Void> cacheUpdatedFt = updateCache();
return Futures.transform(cacheUpdatedFt, new Function<Void, Optional<MasterDatabaseBinding>>() {
@Nullable
@Override
public Optional<MasterDatabaseBinding> apply(@Nullable final Void input) {
return lookup(cachedDao, key);
}
});
}
}
private ListenableFuture<Void> updateCache() {
final ReadOnlyTransaction rTx = dataBroker.newReadOnlyTransaction();
final CheckedFuture<Optional<Topology>, ReadFailedException> read =
rTx.read(LogicalDatastoreType.CONFIGURATION, buildReadPath(null));
Futures.addCallback(read, SxpListenerUtil.createTxCloseCallback(rTx));
return Futures.transform(read, new Function<Optional<Topology>, Void>() {
@Nullable
@Override
public Void apply(@Nullable final Optional<Topology> input) {
if (input != null) {
// clean cache
cachedDao.invalidateCache();
final List<Node> nodeList = java.util.Optional.ofNullable(input.orNull())
.map(Topology::getNode)
.orElseGet(() -> {
LOG.warn("failed to update cache of SxpMasterDB - no data");
return Collections.emptyList();
});
for (Node node : nodeList) {
java.util.Optional.ofNullable(node.getAugmentation(SxpNodeIdentity.class))
.map(SxpNodeIdentity::getSxpDomains)
.map(SxpDomains::getSxpDomain)
.ifPresent((sxpDomain) -> {
final List<MasterDatabaseBinding> masterDBBindings = sxpDomain.stream()
.map(SxpDatabasesFields::getMasterDatabase)
.filter(masterDb -> masterDb != null)
.map(MasterDatabaseFields::getMasterDatabaseBinding)
.filter(binding -> binding != null)
.flatMap(Collection::stream)
.collect(Collectors.toList());
for (MasterDatabaseBinding masterDBItem : masterDBBindings) {
// update all
final MasterDatabaseBinding
previousValue =
cachedDao.update(masterDBItem.getIpPrefix(), masterDBItem);
if (previousValue != null) {
LOG.warn("updated key already obtained: [node:{}, sgt:{}]",
node.getNodeId().getValue(), masterDBItem.getSecurityGroupTag());
}
}
});
}
} else {
LOG.warn("failed to update cache of SxpMasterDB - null input");
}
return null;
}
});
}
private InstanceIdentifier<Topology> buildReadPath(final Sgt key) {
return MasterDatabaseBindingListener.SXP_TOPOLOGY_PATH;
}
private Optional<MasterDatabaseBinding> lookup(final SimpleCachedDao<IpPrefix, MasterDatabaseBinding> cachedDao, final IpPrefix key) {
return cachedDao.find(key);
}
@Override
public ListenableFuture<Collection<MasterDatabaseBinding>> readBy(@Nonnull final Sgt specialKey) {
final ListenableFuture<Void> cacheUpdatedFt;
if (!cachedDao.isEmpty()) {
cacheUpdatedFt = Futures.immediateFuture(null);
} else {
cacheUpdatedFt = updateCache();
}
return Futures.transform(cacheUpdatedFt, new Function<Void, Collection<MasterDatabaseBinding>>() {
@Nullable
@Override
public Collection<MasterDatabaseBinding> apply(@Nullable final Void input) {
final Collection<MasterDatabaseBinding> foundGroups = new ArrayList<>();
for (MasterDatabaseBinding masterDBItem : cachedDao.values()) {
if (masterDBItem.getSecurityGroupTag().equals(specialKey)) {
foundGroups.add(masterDBItem);
}
}
return foundGroups;
}
});
}
}