/* * 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.controller.cluster.sharding; import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.concurrent.GuardedBy; import org.opendaylight.controller.cluster.databroker.actors.dds.DataStoreClient; import org.opendaylight.controller.cluster.datastore.DistributedDataStore; import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener; import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; import org.opendaylight.mdsal.dom.api.DOMDataTreeShard; import org.opendaylight.mdsal.dom.spi.shard.ChildShardContext; import org.opendaylight.mdsal.dom.spi.shard.DOMDataTreeShardProducer; import org.opendaylight.mdsal.dom.spi.shard.ForeignShardModificationContext; import org.opendaylight.mdsal.dom.spi.shard.ReadableWriteableDOMDataTreeShard; import org.opendaylight.mdsal.dom.spi.shard.SubshardProducerSpecification; import org.opendaylight.mdsal.dom.spi.shard.WriteableDOMDataTreeShard; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Proxy implementation of a shard that creates forwarding producers to the backend shard. */ class DistributedShardFrontend implements ReadableWriteableDOMDataTreeShard { private static final Logger LOG = LoggerFactory.getLogger(DistributedShardFrontend.class); private final DataStoreClient client; private final DOMDataTreeIdentifier shardRoot; @GuardedBy("this") private final Map childShards = new HashMap<>(); @GuardedBy("this") private final List producers = new ArrayList<>(); private final DistributedDataStore distributedDataStore; DistributedShardFrontend(final DistributedDataStore distributedDataStore, final DataStoreClient client, final DOMDataTreeIdentifier shardRoot) { this.distributedDataStore = Preconditions.checkNotNull(distributedDataStore); this.client = Preconditions.checkNotNull(client); this.shardRoot = Preconditions.checkNotNull(shardRoot); } @Override public synchronized DOMDataTreeShardProducer createProducer(final Collection paths) { for (final DOMDataTreeIdentifier prodPrefix : paths) { Preconditions.checkArgument(paths.contains(prodPrefix), "Prefix %s is not contained under shard root", prodPrefix, paths); } final ShardProxyProducer ret = new ShardProxyProducer(shardRoot, paths, client, createModificationFactory(paths)); producers.add(ret); return ret; } @Override public synchronized void onChildAttached(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) { LOG.debug("{} : Child shard attached at {}", shardRoot, prefix); Preconditions.checkArgument(child != this, "Attempted to attach child %s onto self", this); addChildShard(prefix, child); updateProducers(); } @Override public synchronized void onChildDetached(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) { LOG.debug("{} : Child shard detached at {}", shardRoot, prefix); childShards.remove(prefix); updateProducers(); // TODO we should grab the dataTreeSnapshot that's in the shard and apply it to this shard } private void addChildShard(final DOMDataTreeIdentifier prefix, final DOMDataTreeShard child) { Preconditions.checkArgument(child instanceof WriteableDOMDataTreeShard); childShards.put(prefix, new ChildShardContext(prefix, (WriteableDOMDataTreeShard) child)); } DistributedShardModificationFactory createModificationFactory(final Collection prefixes) { // TODO this could be abstract final Map affectedSubshards = new HashMap<>(); for (final DOMDataTreeIdentifier producerPrefix : prefixes) { for (final ChildShardContext maybeAffected : childShards.values()) { final DOMDataTreeIdentifier bindPath; if (producerPrefix.contains(maybeAffected.getPrefix())) { bindPath = maybeAffected.getPrefix(); } else if (maybeAffected.getPrefix().contains(producerPrefix)) { // Bound path is inside subshard bindPath = producerPrefix; } else { continue; } SubshardProducerSpecification spec = affectedSubshards.get(maybeAffected.getPrefix()); if (spec == null) { spec = new SubshardProducerSpecification(maybeAffected); affectedSubshards.put(maybeAffected.getPrefix(), spec); } spec.addPrefix(bindPath); } } final DistributedShardModificationFactoryBuilder builder = new DistributedShardModificationFactoryBuilder(shardRoot); for (final SubshardProducerSpecification spec : affectedSubshards.values()) { final ForeignShardModificationContext foreignContext = new ForeignShardModificationContext(spec.getPrefix(), spec.createProducer()); builder.addSubshard(foreignContext); builder.addSubshard(spec.getPrefix(), foreignContext); } return builder.build(); } private void updateProducers() { for (final ShardProxyProducer producer : producers) { producer.setModificationFactory(createModificationFactory(producer.getPrefixes())); } } @Nonnull @Override @SuppressWarnings("unchecked") public ListenerRegistration registerTreeChangeListener( final YangInstanceIdentifier treeId, final L listener) { final List toStrip = new ArrayList<>(shardRoot.getRootIdentifier().getPathArguments()); final List stripFrom = new ArrayList<>(treeId.getPathArguments()); while (!toStrip.isEmpty()) { stripFrom.remove(0); toStrip.remove(0); } return (ListenerRegistration) new ProxyRegistration(distributedDataStore .registerProxyListener(treeId, YangInstanceIdentifier.create(stripFrom), listener), listener); } private static class ProxyRegistration implements ListenerRegistration { private ListenerRegistration proxy; private DOMDataTreeChangeListener listener; private ProxyRegistration( final ListenerRegistration< org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener> proxy, final DOMDataTreeChangeListener listener) { this.proxy = proxy; this.listener = listener; } @Override public DOMDataTreeChangeListener getInstance() { return listener; } @Override public void close() { proxy.close(); } } }