--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>applications</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.opendaylight.openflowplugin.applications</groupId>
+ <artifactId>forwardingrules-sync</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-config</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller.model</groupId>
+ <artifactId>model-inventory</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.model</groupId>
+ <artifactId>model-flow-base</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.model</groupId>
+ <artifactId>model-flow-service</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin</groupId>
+ <artifactId>openflowplugin-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>concepts</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.findbugs</groupId>
+ <artifactId>jsr305</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-binding-broker-impl</artifactId>
+ <scope>test</scope>
+ <type>test-jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.sonar-plugins.java</groupId>
+ <artifactId>sonar-jacoco-listeners</artifactId>
+ <version>${sonar-jacoco-listeners.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/openflowplugin.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/openflowplugin.git</developerConnection>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_OpenFlow_Plugin:Main</url>
+ <tag>HEAD</tag>
+ </scm>
+
+</project>
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync;
+
+import java.util.concurrent.Future;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Represents a configuration item add-contract for device.
+ */
+public interface ForwardingRulesAddCommitter<D extends DataObject, A extends DataObject> {
+
+ /**
+ * Method adds the DataObject which is identified by InstanceIdentifier
+ * to device.
+ *
+ * @param identifier - the whole path to new DataObject
+ * @param add - new DataObject
+ * @param nodeIdent - Node InstanceIdentifier
+ */
+ Future<RpcResult<A>> add(InstanceIdentifier<D> identifier, D add, InstanceIdentifier<FlowCapableNode> nodeIdent);
+
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.openflowplugin.applications.frsync;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Represents configuration item contract for device.
+ * Combining add/update/remove commiters into single one.
+ */
+public interface ForwardingRulesCommitter<D extends DataObject, A extends DataObject, R extends DataObject, U extends DataObject>
+ extends ForwardingRulesAddCommitter<D, A>, ForwardingRulesRemoveCommitter<D, R>, ForwardingRulesUpdateCommitter<D, U> {
+}
+
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync;
+
+import java.util.concurrent.Future;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Represents a configuration item remove-contract for device.
+ */
+public interface ForwardingRulesRemoveCommitter<D extends DataObject, R extends DataObject> {
+
+ /**
+ * Method removes DataObject which is identified by InstanceIdentifier
+ * from device.
+ *
+ * @param identifier - the whole path to DataObject
+ * @param del - DataObject for removing
+ * @param nodeIdent - Node InstanceIdentifier
+ */
+ Future<RpcResult<R>> remove(InstanceIdentifier<D> identifier, D del, InstanceIdentifier<FlowCapableNode> nodeIdent);
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync;
+
+import java.util.concurrent.Future;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Represents a configuration item update-contract for device.
+ */
+public interface ForwardingRulesUpdateCommitter<D extends DataObject, U extends DataObject> {
+
+ /**
+ * Method updates the original DataObject to the update DataObject
+ * in device. Both are identified by same InstanceIdentifier
+ *
+ * @param identifier - the whole path to DataObject
+ * @param original - original DataObject (for update)
+ * @param update - changed DataObject (contain updates)
+ * @param nodeIdent - Node InstanceIdentifier
+ */
+ Future<RpcResult<U>> update(InstanceIdentifier<D> identifier, D original, D update,
+ InstanceIdentifier<FlowCapableNode> nodeIdent);
+
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync;
+
+import java.util.EventListener;
+
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Unifying listener for data and event changes on node.
+ */
+public interface NodeListener<T extends DataObject> extends EventListener, DataTreeChangeListener<T> {
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync;
+
+import java.util.concurrent.Semaphore;
+import javax.annotation.Nonnull;
+
+/**
+ * Proposal for how a key based semaphore provider should look like.
+ * <ul>
+ * <li>thread safe</li>
+ * <li>garbage-collect unused semaphores</li>
+ * <li>for the same key there must be always only one semaphore available</li>
+ * </ul>
+ *
+ *
+ * usage:
+ * <pre>
+ * final Semaphore guard = semaphoreKeeper.summonGuard(key);
+ * guard.acquire();
+ * // guard protected logic ...
+ * guard.release();
+ * </pre>
+ *
+ * @param <K> key type
+ */
+
+public interface SemaphoreKeeper<K> {
+ /**
+ * @param key semaphore identifier
+ * @return new or existing semaphore for given key, for one key there is always only one semaphore available
+ */
+ Semaphore summonGuard(@Nonnull K key);
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.openflowplugin.applications.frsync.impl.strategy.SynchronizationDiffInput;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+/**
+ * Prescribes common synchronization plan execution strategy.
+ * Implementations should be stateless.
+ */
+public interface SyncPlanPushStrategy {
+
+ /**
+ * @param resultVehicle bootstrap future - execution will chain it's async calls to this one
+ * @param diffInput wraps all diff data required for any strategy ({add,remove,update} x {flow,group,meter})
+ * @param counters reference to internal one-shot statistics - summary off successfully pushed items shall be recorded here
+ * @return last future of the chain
+ */
+ ListenableFuture<RpcResult<Void>> executeSyncStrategy(ListenableFuture<RpcResult<Void>> resultVehicle,
+ SynchronizationDiffInput diffInput,
+ SyncCrudCounters counters);
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Device synchronization API.
+ */
+public interface SyncReactor {
+ /**
+ * @param flowcapableNodePath path to openflow augmentation of node
+ * @param configTree configured node
+ * @param operationalTree device reflection
+ * @return synchronization outcome
+ */
+ ListenableFuture<Boolean> syncup(InstanceIdentifier<FlowCapableNode> flowcapableNodePath,
+ FlowCapableNode configTree, FlowCapableNode operationalTree) throws InterruptedException;
+
+}
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.dao;\r
+\r
+import javax.annotation.Nonnull;\r
+\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;\r
+\r
+import com.google.common.base.Optional;\r
+\r
+/**\r
+ * Implementation of data access object for {@link FlowCapableNode}.\r
+ * Contains pair of snapshot and odl DAOs.\r
+ */\r
+public class FlowCapableNodeCachedDao implements FlowCapableNodeDao {\r
+\r
+ private final FlowCapableNodeDao snapshotDao;\r
+ private final FlowCapableNodeDao odlDao;\r
+\r
+ public FlowCapableNodeCachedDao(FlowCapableNodeDao snapshotDao, FlowCapableNodeDao odlDao) {\r
+ this.snapshotDao = snapshotDao;\r
+ this.odlDao = odlDao;\r
+ }\r
+\r
+ public Optional<FlowCapableNode> loadByNodeId(@Nonnull NodeId nodeId) {\r
+ final Optional<FlowCapableNode> node = snapshotDao.loadByNodeId(nodeId);\r
+\r
+ if (node.isPresent()) {\r
+ return node;\r
+ }\r
+\r
+ return odlDao.loadByNodeId(nodeId);\r
+ }\r
+\r
+}\r
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.dao;\r
+\r
+import javax.annotation.Nonnull;\r
+\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;\r
+\r
+import com.google.common.base.Optional;\r
+\r
+/**\r
+ * Data access object for {@link FlowCapableNode}.\r
+ */\r
+public interface FlowCapableNodeDao {\r
+ Optional<FlowCapableNode> loadByNodeId(@Nonnull NodeId nodeId);\r
+}\r
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.dao;\r
+\r
+import com.google.common.base.Optional;\r
+\r
+import java.util.concurrent.TimeUnit;\r
+import java.util.concurrent.TimeoutException;\r
+\r
+import javax.annotation.Nonnull;\r
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;\r
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;\r
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+/**\r
+ * Implementation of data access object for ODL {@link FlowCapableNode}.\r
+ */\r
+public class FlowCapableNodeOdlDao implements FlowCapableNodeDao {\r
+\r
+ private static final Logger LOG = LoggerFactory.getLogger(FlowCapableNodeOdlDao.class);\r
+\r
+ private static final InstanceIdentifier<Nodes> NODES_IID = InstanceIdentifier.create(Nodes.class);\r
+ private final DataBroker dataBroker;\r
+ private final LogicalDatastoreType logicalDatastoreType;\r
+\r
+ public FlowCapableNodeOdlDao(DataBroker dataBroker, LogicalDatastoreType logicalDatastoreType) {\r
+ this.dataBroker = dataBroker;\r
+ this.logicalDatastoreType = logicalDatastoreType;\r
+ }\r
+\r
+ public Optional<FlowCapableNode> loadByNodeId(@Nonnull NodeId nodeId) {\r
+ try (final ReadOnlyTransaction roTx = dataBroker.newReadOnlyTransaction()) {\r
+ final InstanceIdentifier<FlowCapableNode> path =\r
+ NODES_IID.child(Node.class, new NodeKey(nodeId)).augmentation(FlowCapableNode.class);\r
+ return roTx.read(logicalDatastoreType, path).checkedGet(5000, TimeUnit.MILLISECONDS);\r
+ } catch (ReadFailedException | TimeoutException e) {\r
+ LOG.error("error reading {}", nodeId, e);\r
+ }\r
+\r
+ return Optional.absent();\r
+ }\r
+\r
+}\r
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.dao;\r
+\r
+import java.util.concurrent.ConcurrentHashMap;\r
+\r
+import javax.annotation.Nonnull;\r
+\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;\r
+\r
+import com.google.common.base.Optional;\r
+\r
+/**\r
+ * Adding cache to data access object of {@link FlowCapableNode}.\r
+ */\r
+public class FlowCapableNodeSnapshotDao implements FlowCapableNodeDao {\r
+\r
+ private final ConcurrentHashMap<String, FlowCapableNode> cache = new ConcurrentHashMap<>();\r
+\r
+ public void updateCache(@Nonnull NodeId nodeId, Optional<FlowCapableNode> dataAfter) {\r
+ if (dataAfter.isPresent()) {\r
+ cache.put(nodeId.getValue(), dataAfter.get());\r
+ } else {\r
+ cache.remove(nodeId.getValue());\r
+ }\r
+ }\r
+\r
+ public Optional<FlowCapableNode> loadByNodeId(@Nonnull NodeId nodeId) {\r
+ final FlowCapableNode node = cache.get(nodeId.getValue());\r
+ return Optional.fromNullable(node);\r
+ }\r
+\r
+}\r
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.openflowplugin.applications.frsync.NodeListener;
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract Listener for node changes.
+ */
+public abstract class AbstractFrmSyncListener<T extends DataObject> implements NodeListener<T> {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractFrmSyncListener.class);
+
+ @Override
+ public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<T>> modifications) {
+ for (DataTreeModification<T> modification : modifications) {
+ final NodeId nodeId = PathUtil.digNodeId(modification.getRootPath().getRootIdentifier());
+
+ try {
+ final Optional<ListenableFuture<Boolean>> optFuture = processNodeModification(modification);
+ if (optFuture.isPresent()) {
+ final ListenableFuture<Boolean> future = optFuture.get();
+ final Boolean ret = future.get(15000, TimeUnit.MILLISECONDS);
+ LOG.debug("syncup ret {} {} {} thread:{}", dsType(), ret, nodeId, threadName());
+ }
+ } catch (InterruptedException e) {
+ LOG.warn("permit for forwarding rules sync not acquired: {}", nodeId);
+ } catch (Exception e) {
+ LOG.error("error processing inventory node modification: {}", nodeId, e);
+ }
+ }
+ }
+
+ protected abstract Optional<ListenableFuture<Boolean>> processNodeModification(
+ DataTreeModification<T> modification) throws ReadFailedException, InterruptedException;
+
+ public abstract LogicalDatastoreType dsType();
+
+ static String threadName() {
+ final Thread currentThread = Thread.currentThread();
+ return currentThread.getName();
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.openflowplugin.applications.frsync.ForwardingRulesCommitter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Implements {@link ForwardingRulesCommitter} methods for processing add, update and remove of {@link Flow}.
+ */
+public class FlowForwarder implements ForwardingRulesCommitter<Flow, AddFlowOutput, RemoveFlowOutput, UpdateFlowOutput> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlowForwarder.class);
+ private SalFlowService salFlowService;
+
+ public FlowForwarder(final SalFlowService salFlowService) {
+ this.salFlowService = salFlowService;
+ }
+
+ @Override
+ public Future<RpcResult<RemoveFlowOutput>> remove(final InstanceIdentifier<Flow> identifier,
+ final Flow removeDataObj,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Forwarding Flow REMOVE request Tbl id, node Id {} {}",
+ identifier, nodeIdent);
+
+ final TableKey tableKey = identifier.firstKeyOf(Table.class, TableKey.class);
+ if (tableIdValidationPrecondition(tableKey, removeDataObj)) {
+ final RemoveFlowInputBuilder builder = new RemoveFlowInputBuilder(removeDataObj);
+ builder.setFlowRef(new FlowRef(identifier));
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setFlowTable(new FlowTableRef(nodeIdent.child(Table.class, tableKey)));
+
+ // always needs to set strict flag into remove-flow input so that
+ // only a flow entry associated with a given flow object will be removed.
+ builder.setStrict(Boolean.TRUE);
+ return salFlowService.removeFlow(builder.build());
+ } else {
+ return RpcResultBuilder.<RemoveFlowOutput>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "tableId mismatch").buildFuture();
+ }
+ }
+
+ @Override
+ public Future<RpcResult<UpdateFlowOutput>> update(final InstanceIdentifier<Flow> identifier,
+ final Flow original, final Flow update,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Forwarding Flow UPDATE request [Tbl id, node Id {} {} {}",
+ identifier, nodeIdent, update);
+
+ final Future<RpcResult<UpdateFlowOutput>> output;
+ final TableKey tableKey = identifier.firstKeyOf(Table.class, TableKey.class);
+ if (tableIdValidationPrecondition(tableKey, update)) {
+ final UpdateFlowInputBuilder builder = new UpdateFlowInputBuilder();
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setFlowRef(new FlowRef(identifier));
+
+ // always needs to set strict flag into update-flow input so that
+ // only a flow entry associated with a given flow object is updated.
+ builder.setUpdatedFlow((new UpdatedFlowBuilder(update)).setStrict(Boolean.TRUE).build());
+ builder.setOriginalFlow((new OriginalFlowBuilder(original)).setStrict(Boolean.TRUE).build());
+
+ output = salFlowService.updateFlow(builder.build());
+ } else {
+ output = RpcResultBuilder.<UpdateFlowOutput>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "tableId mismatch").buildFuture();
+ }
+
+ return output;
+ }
+
+ @Override
+ public Future<RpcResult<AddFlowOutput>> add(final InstanceIdentifier<Flow> identifier,
+ final Flow addDataObj,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Forwarding the Flow ADD request [Tbl id, node Id {} {} {}",
+ identifier, nodeIdent, addDataObj);
+
+ final Future<RpcResult<AddFlowOutput>> output;
+ final TableKey tableKey = identifier.firstKeyOf(Table.class, TableKey.class);
+ if (tableIdValidationPrecondition(tableKey, addDataObj)) {
+ final AddFlowInputBuilder builder = new AddFlowInputBuilder(addDataObj);
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setFlowRef(new FlowRef(identifier));
+ builder.setFlowTable(new FlowTableRef(nodeIdent.child(Table.class, tableKey)));
+ output = salFlowService.addFlow(builder.build());
+ } else {
+ output = RpcResultBuilder.<AddFlowOutput>failed().withError(RpcError.ErrorType.APPLICATION, "tableId mismatch").buildFuture();
+ }
+ return output;
+ }
+
+ private static boolean tableIdValidationPrecondition(final TableKey tableKey, final Flow flow) {
+ Preconditions.checkNotNull(tableKey, "TableKey can not be null or empty!");
+ Preconditions.checkNotNull(flow, "Flow can not be null or empty!");
+ if (!tableKey.getId().equals(flow.getTableId())) {
+ LOG.warn("TableID in URI tableId={} and in palyload tableId={} is not same.",
+ flow.getTableId(), tableKey.getId());
+ return false;
+ }
+ return true;
+ }
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.concurrent.Callable;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.openflowplugin.applications.frsync.NodeListener;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeCachedDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeOdlDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.openflowplugin.applications.frsync.impl.strategy.SyncPlanPushStrategyFlatBatchImpl;
+import org.opendaylight.openflowplugin.applications.frsync.util.SemaphoreKeeperGuavaImpl;
+import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.SalFlatBatchService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.SalTableService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Top provider of forwarding rules synchronization functionality.
+ */
+public class ForwardingRulesSyncProvider implements AutoCloseable, BindingAwareProvider {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ForwardingRulesSyncProvider.class);
+ public static final int STARTUP_LOOP_TICK = 500;
+ public static final int STARTUP_LOOP_MAX_RETRIES = 8;
+
+ private final DataBroker dataService;
+ private final SalFlowService salFlowService;
+ private final SalGroupService salGroupService;
+ private final SalMeterService salMeterService;
+ private final SalTableService salTableService;
+ private final FlowCapableTransactionService transactionService;
+ private final SalFlatBatchService flatBatchService;
+
+ /** wildcard path to flow-capable-node augmentation of inventory node */
+ private static final InstanceIdentifier<FlowCapableNode> FLOW_CAPABLE_NODE_WC_PATH =
+ InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class);
+ /** wildcard path to node (not flow-capable-node augmentation) of inventory node */
+ private static final InstanceIdentifier<Node> NODE_WC_PATH =
+ InstanceIdentifier.create(Nodes.class).child(Node.class);
+
+
+ private final DataTreeIdentifier<FlowCapableNode> nodeConfigDataTreePath;
+ private final DataTreeIdentifier<Node> nodeOperationalDataTreePath;
+
+ private ListenerRegistration<NodeListener> dataTreeConfigChangeListener;
+ private ListenerRegistration<NodeListener> dataTreeOperationalChangeListener;
+
+
+ public ForwardingRulesSyncProvider(final BindingAwareBroker broker,
+ final DataBroker dataBroker,
+ final RpcConsumerRegistry rpcRegistry) {
+ this.dataService = Preconditions.checkNotNull(dataBroker, "DataBroker can not be null!");
+
+ Preconditions.checkArgument(rpcRegistry != null, "RpcConsumerRegistry can not be null !");
+
+ this.salFlowService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalFlowService.class),
+ "RPC SalFlowService not found.");
+ this.salGroupService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalGroupService.class),
+ "RPC SalGroupService not found.");
+ this.salMeterService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalMeterService.class),
+ "RPC SalMeterService not found.");
+ this.salTableService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalTableService.class),
+ "RPC SalTableService not found.");
+ this.transactionService =
+ Preconditions.checkNotNull(rpcRegistry.getRpcService(FlowCapableTransactionService.class),
+ "RPC SalTableService not found.");
+ this.flatBatchService =
+ Preconditions.checkNotNull(rpcRegistry.getRpcService(SalFlatBatchService.class),
+ "RPC SalFlatBatchService not found.");
+
+ nodeConfigDataTreePath =
+ new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, FLOW_CAPABLE_NODE_WC_PATH);
+ nodeOperationalDataTreePath = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, NODE_WC_PATH);
+
+ broker.registerProvider(this);
+ }
+
+ private final ListeningExecutorService syncThreadPool = FrmExecutors.instance()
+ // TODO improve log in ThreadPoolExecutor.afterExecute
+ // TODO max bloking queue size
+ // TODO core/min pool size
+ .newFixedThreadPool(6, new ThreadFactoryBuilder()
+ .setNameFormat(SyncReactorFutureDecorator.FRM_RPC_CLIENT_PREFIX + "%d")
+ .setDaemon(false)
+ .setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread thread, Throwable e) {
+ LOG.error("uncaught exception {}", thread, e);
+ }
+ })
+ .build());
+
+ @Override
+ public void onSessionInitiated(final BindingAwareBroker.ProviderContext providerContext) {
+ final FlowForwarder flowForwarder = new FlowForwarder(salFlowService);
+ final GroupForwarder groupForwarder = new GroupForwarder(salGroupService);
+ final MeterForwarder meterForwarder = new MeterForwarder(salMeterService);
+ final TableForwarder tableForwarder = new TableForwarder(salTableService);
+
+ {
+ //TODO: make is switchable
+// final SyncPlanPushStrategy syncPlanPushStrategy = new SyncPlanPushStrategyIncrementalImpl()
+// .setFlowForwarder(flowForwarder)
+// .setGroupForwarder(groupForwarder)
+// .setMeterForwarder(meterForwarder)
+// .setTableForwarder(tableForwarder)
+// .setTransactionService(transactionService);
+
+ final SyncPlanPushStrategy syncPlanPushStrategy = new SyncPlanPushStrategyFlatBatchImpl()
+ .setFlatBatchService(flatBatchService)
+ .setTableForwarder(tableForwarder);
+
+ final SyncReactorImpl syncReactorImpl = new SyncReactorImpl(syncPlanPushStrategy);
+ final SyncReactor syncReactorGuard = new SyncReactorGuardDecorator(syncReactorImpl,
+ new SemaphoreKeeperGuavaImpl<InstanceIdentifier<FlowCapableNode>>(1, true));
+
+ final SyncReactor cfgReactor = new SyncReactorFutureWithCompressionDecorator(syncReactorGuard, syncThreadPool);
+ final SyncReactor operReactor = new SyncReactorFutureWithCompressionDecorator(syncReactorGuard, syncThreadPool);
+
+ final FlowCapableNodeSnapshotDao configSnapshot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeSnapshotDao operationalSnapshot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeDao configDao = new FlowCapableNodeCachedDao(configSnapshot,
+ new FlowCapableNodeOdlDao(dataService, LogicalDatastoreType.CONFIGURATION));
+ final FlowCapableNodeDao operationalDao = new FlowCapableNodeCachedDao(operationalSnapshot,
+ new FlowCapableNodeOdlDao(dataService, LogicalDatastoreType.OPERATIONAL));
+
+ final NodeListener<FlowCapableNode> nodeListenerConfig =
+ new SimplifiedConfigListener(
+ cfgReactor,
+ configSnapshot, operationalDao);
+ final NodeListener<Node> nodeListenerOperational =
+ new SimplifiedOperationalListener(operReactor, operationalSnapshot, configDao);
+
+ try {
+ SimpleTaskRetryLooper looper1 = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
+ dataTreeConfigChangeListener = looper1.loopUntilNoException(
+ new Callable<ListenerRegistration<NodeListener>>() {
+ @Override
+ public ListenerRegistration<NodeListener> call() throws Exception {
+ return dataService.registerDataTreeChangeListener(
+ nodeConfigDataTreePath, nodeListenerConfig);
+ }
+ });
+
+ SimpleTaskRetryLooper looper2 = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES);
+ dataTreeOperationalChangeListener = looper2.loopUntilNoException(
+ new Callable<ListenerRegistration<NodeListener>>() {
+ @Override
+ public ListenerRegistration<NodeListener> call() throws Exception {
+ return dataService.registerDataTreeChangeListener(
+ nodeOperationalDataTreePath, nodeListenerOperational);
+ }
+ });
+ } catch (final Exception e) {
+ LOG.warn("FR-Sync node DataChange listener registration fail!", e);
+ throw new IllegalStateException("FR-Sync startup fail!", e);
+ }
+ }
+ LOG.info("ForwardingRulesSync has started.");
+ }
+
+ public void close() throws Exception {
+ if (dataTreeConfigChangeListener != null) {
+ dataTreeConfigChangeListener.close();
+ dataTreeConfigChangeListener = null;
+ }
+ if (dataTreeOperationalChangeListener != null) {
+ dataTreeOperationalChangeListener.close();
+ dataTreeOperationalChangeListener = null;
+ }
+
+ syncThreadPool.shutdown();
+ }
+}
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.impl;\r
+\r
+import java.util.concurrent.ExecutorService;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.ThreadFactory;\r
+\r
+import com.google.common.annotations.VisibleForTesting;\r
+import com.google.common.util.concurrent.ListeningExecutorService;\r
+import com.google.common.util.concurrent.MoreExecutors;\r
+\r
+/**\r
+ * Static Factory for creating ExecutorServicess (because there is no dependency injection but\r
+ * static getInstance).\r
+ */\r
+public final class FrmExecutors {\r
+ public static PceExecursFactory instance() {\r
+ return DEFAULT_EXECUTORS;\r
+ }\r
+\r
+ public interface PceExecursFactory {\r
+\r
+ public ListeningExecutorService newFixedThreadPool(int nThreads, ThreadFactory factory);\r
+ }\r
+\r
+ /**\r
+ * This will be rewritten in JUnits using SynchronousExecutorService\r
+ */\r
+ @VisibleForTesting // should not be private and final\r
+ static PceExecursFactory DEFAULT_EXECUTORS = new PceExecursFactory() {\r
+\r
+ public ListeningExecutorService newFixedThreadPool(int nThreads, ThreadFactory factory) {\r
+ final ExecutorService executorService = Executors.newFixedThreadPool(nThreads, factory);\r
+ return MoreExecutors.listeningDecorator(executorService);\r
+ }\r
+ };\r
+}\r
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.concurrent.Future;
+import org.opendaylight.openflowplugin.applications.frsync.ForwardingRulesCommitter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implements {@link ForwardingRulesCommitter} methods for processing add, update and remove of {@link Group}.
+ */
+public class GroupForwarder implements ForwardingRulesCommitter<Group, AddGroupOutput, RemoveGroupOutput, UpdateGroupOutput> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(GroupForwarder.class);
+ private final SalGroupService salGroupService;
+
+ public GroupForwarder(SalGroupService salGroupService) {
+ this.salGroupService = salGroupService;
+ }
+
+ @Override
+ public Future<RpcResult<RemoveGroupOutput>> remove(final InstanceIdentifier<Group> identifier, final Group removeDataObj,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Forwarding Table REMOVE request [Tbl id, node Id {} {}",
+ identifier, nodeIdent);
+
+ final RemoveGroupInputBuilder builder = new RemoveGroupInputBuilder(removeDataObj);
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setGroupRef(new GroupRef(identifier));
+ // fix group removal - no buckets allowed
+ builder.setBuckets(null);
+ return salGroupService.removeGroup(builder.build());
+ }
+
+ @Override
+ public Future<RpcResult<UpdateGroupOutput>> update(final InstanceIdentifier<Group> identifier,
+ final Group original, final Group update,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Forwarding Group UPDATE request [Tbl id, node Id {} {} {}",
+ identifier, nodeIdent, update);
+
+ final UpdateGroupInputBuilder builder = new UpdateGroupInputBuilder();
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setGroupRef(new GroupRef(identifier));
+ builder.setUpdatedGroup((new UpdatedGroupBuilder(update)).build());
+ builder.setOriginalGroup((new OriginalGroupBuilder(original)).build());
+
+ return salGroupService.updateGroup(builder.build());
+ }
+
+ @Override
+ public Future<RpcResult<AddGroupOutput>> add(final InstanceIdentifier<Group> identifier, final Group addDataObj,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Forwarding Group ADD request [Tbl id, node Id {} {} {}",
+ identifier, nodeIdent, addDataObj);
+
+ final AddGroupInputBuilder builder = new AddGroupInputBuilder(addDataObj);
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setGroupRef(new GroupRef(identifier));
+ return salGroupService.addGroup(builder.build());
+ }
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.concurrent.Future;
+
+import org.opendaylight.openflowplugin.applications.frsync.ForwardingRulesCommitter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.OriginalMeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.UpdatedMeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterRef;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implements {@link ForwardingRulesCommitter} methods for processing add, update and remove of {@link Meter}.
+ */
+public class MeterForwarder implements ForwardingRulesCommitter<Meter, AddMeterOutput, RemoveMeterOutput, UpdateMeterOutput> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MeterForwarder.class);
+ private final SalMeterService salMeterService;
+
+ public MeterForwarder(SalMeterService salMeterService) {
+ this.salMeterService = salMeterService;
+ }
+
+ @Override
+ public Future<RpcResult<RemoveMeterOutput>> remove(final InstanceIdentifier<Meter> identifier, final Meter removeDataObj,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+
+ LOG.trace("Received the Meter REMOVE request [Tbl id, node Id {} {}",
+ identifier, nodeIdent);
+
+ final RemoveMeterInputBuilder builder = new RemoveMeterInputBuilder(removeDataObj);
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setMeterRef(new MeterRef(identifier));
+ return salMeterService.removeMeter(builder.build());
+ }
+
+ @Override
+ public Future<RpcResult<UpdateMeterOutput>> update(final InstanceIdentifier<Meter> identifier,
+ final Meter original, final Meter update,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Received the Meter UPDATE request [Tbl id, node Id {} {} {}",
+ identifier, nodeIdent, update);
+
+ final UpdateMeterInputBuilder builder = new UpdateMeterInputBuilder();
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setMeterRef(new MeterRef(identifier));
+ builder.setUpdatedMeter((new UpdatedMeterBuilder(update)).build());
+ builder.setOriginalMeter((new OriginalMeterBuilder(original)).build());
+
+ return salMeterService.updateMeter(builder.build());
+ }
+
+ @Override
+ public Future<RpcResult<AddMeterOutput>> add(final InstanceIdentifier<Meter> identifier, final Meter addDataObj,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.trace("Received the Meter ADD request [Tbl id, node Id {} {} {}",
+ identifier, nodeIdent, addDataObj);
+
+ final AddMeterInputBuilder builder = new AddMeterInputBuilder(addDataObj);
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+ builder.setMeterRef(new MeterRef(identifier));
+ return salMeterService.addMeter(builder.build());
+ }
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Listens to config changes and delegates add/remove/update/barrier to {@link SyncReactor}.
+ */
+public class SimplifiedConfigListener extends AbstractFrmSyncListener<FlowCapableNode> {
+ private static final Logger LOG = LoggerFactory.getLogger(SimplifiedConfigListener.class);
+ protected final SyncReactor reactor;
+ private final FlowCapableNodeSnapshotDao configSnaphot;
+ private final FlowCapableNodeDao operationalDao;
+
+ public SimplifiedConfigListener(final SyncReactor reactor, FlowCapableNodeSnapshotDao configSnaphot,
+ FlowCapableNodeDao operationalDao) {
+ this.reactor = reactor;
+ this.configSnaphot = configSnaphot;
+ this.operationalDao = operationalDao;
+ }
+
+ @Override
+ public void onDataTreeChanged(Collection<DataTreeModification<FlowCapableNode>> modifications) {
+ LOG.trace("Inventory Config changes {}", modifications.size());
+ super.onDataTreeChanged(modifications);
+ }
+
+ /**
+ * Compare cached operational with current config modification. If operational is not present
+ * skip calling Inventory RPCs.
+ *
+ * @throws InterruptedException from syncup
+ */
+ protected Optional<ListenableFuture<Boolean>> processNodeModification(
+ DataTreeModification<FlowCapableNode> modification) throws InterruptedException {
+ final InstanceIdentifier<FlowCapableNode> nodePath = modification.getRootPath().getRootIdentifier();
+ final NodeId nodeId = PathUtil.digNodeId(nodePath);
+
+ configSnaphot.updateCache(nodeId, Optional.fromNullable(modification.getRootNode().getDataAfter()));
+
+
+ final Optional<FlowCapableNode> operationalNode = operationalDao.loadByNodeId(nodeId);
+ if (!operationalNode.isPresent()) {
+ LOG.info("Skip syncup, {} operational is not present", nodeId.getValue());
+ return Optional.absent();// we try to reconfigure switch is alive
+ }
+
+ final DataObjectModification<FlowCapableNode> configModification = modification.getRootNode();
+ final FlowCapableNode dataBefore = configModification.getDataBefore();
+ final FlowCapableNode dataAfter = configModification.getDataAfter();
+ final ListenableFuture<Boolean> endResult;
+ if (dataBefore == null && dataAfter != null) {
+ endResult = onNodeAdded(nodePath, dataBefore, dataAfter, operationalNode.get());
+ } else if (dataBefore != null && dataAfter == null) {
+ endResult = onNodeDeleted(nodePath, dataBefore, operationalNode.get());
+ } else {
+ endResult = onNodeUpdated(nodePath, dataBefore, dataAfter, operationalNode.get());
+ }
+
+ return Optional.of(endResult);
+ }
+
+ /**
+ * Add only what is missing in operational store. Config. node could be added in two situations:
+ * <ul>
+ * <li>Note very first time after restart was handled by operational listener. Syncup should
+ * calculate no delta (we don want to reconfigure switch if not necessary).</li>
+ * <li>But later the config. node could be deleted, after that config node added again. Syncup
+ * should calculate that everything needs to be added. Operational store should be empty in
+ * optimal case (but the switch could be reprogrammed by another person/system.</li>
+ * </ul>
+ */
+ protected ListenableFuture<Boolean> onNodeAdded(InstanceIdentifier<FlowCapableNode> nodePath,
+ FlowCapableNode dataBefore, FlowCapableNode dataAfter, FlowCapableNode operationalNode)
+ throws InterruptedException {
+ LOG.trace("onNodeAdded {}", nodePath);
+
+ final ListenableFuture<Boolean> endResult =
+ reactor.syncup(nodePath, dataAfter, operationalNode);
+ return endResult;
+ }
+
+ /**
+ * Apply minimal changes very fast. For better performance needed just compare config
+ * after+before. Config listener should not be dependent on operational flows/groups while
+ * updating config because operational store is highly async and it depends on another module in
+ * system which is updating operational store (that components is also trying to solve
+ * scale/performance issues on several layers).
+ */
+ protected ListenableFuture<Boolean> onNodeUpdated(InstanceIdentifier<FlowCapableNode> nodePath,
+ FlowCapableNode dataBefore, FlowCapableNode dataAfter, FlowCapableNode operationalNodeNode)
+ throws InterruptedException {
+ LOG.trace("onNodeUpdated {}", nodePath);
+
+ final ListenableFuture<Boolean> endResult =
+ reactor.syncup(nodePath, dataAfter, dataBefore);
+ return endResult;
+ }
+
+ /**
+ * Remove values that are being deleted in the config from the switch. Note, this could be
+ * probably optimized using dedicated wipe-out RPC, but it has impact on switch if it is
+ * programmed by two person/system
+ */
+ protected ListenableFuture<Boolean> onNodeDeleted(InstanceIdentifier<FlowCapableNode> nodePath,
+ FlowCapableNode dataBefore, FlowCapableNode operationalNode) throws InterruptedException {
+ LOG.trace("onNodeDeleted {}", nodePath);
+
+ final ListenableFuture<Boolean> endResult =
+ reactor.syncup(nodePath, null, dataBefore);
+ return endResult;
+ }
+
+ @Override
+ public LogicalDatastoreType dsType() {
+ return LogicalDatastoreType.CONFIGURATION;
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Listens to operational new nodes and delegates add/remove/update/barrier to {@link SyncReactor}.
+ */
+public class SimplifiedOperationalListener extends AbstractFrmSyncListener<Node> {
+ private static final Logger LOG = LoggerFactory.getLogger(SimplifiedOperationalListener.class);
+
+ protected final SyncReactor reactor;
+
+ private FlowCapableNodeSnapshotDao operationalSnaphot;
+
+ private FlowCapableNodeDao configDao;
+
+ public SimplifiedOperationalListener(SyncReactor reactor,
+ FlowCapableNodeSnapshotDao operationalSnaphot, FlowCapableNodeDao configDao) {
+ this.reactor = reactor;
+ this.operationalSnaphot = operationalSnaphot;
+ this.configDao = configDao;
+ }
+
+ @Override
+ public void onDataTreeChanged(Collection<DataTreeModification<Node>> modifications) {
+ LOG.trace("Inventory Operational changes {}", modifications.size());
+ super.onDataTreeChanged(modifications);
+ }
+
+ /**
+ * This method behaves like this:
+ * <ul>
+ * <li>If node is added to operational store then reconciliation.</li>
+ * <li>Node is deleted from operational cache is removed.</li>
+ * <li>Skip this event otherwise.</li>
+ * </ul>
+ *
+ * @throws InterruptedException from syncup
+ */
+ protected Optional<ListenableFuture<Boolean>> processNodeModification(
+ DataTreeModification<Node> modification) throws ReadFailedException, InterruptedException {
+ updateCache(modification);
+
+ if (isAdd(modification) || isAddLogical(modification)) {
+ return reconciliation(modification);
+ }
+ // TODO: else = explicit reconciliation required
+
+ return skipModification(modification);
+ }
+
+ /**
+ * Remove if delete. Update only if FlowCapableNode Augmentation modified.
+ *
+ * @param modification
+ */
+ protected void updateCache(DataTreeModification<Node> modification) {
+ try {
+ boolean isDelete = isDelete(modification) || isDeleteLogical(modification);
+ if (isDelete) {
+ operationalSnaphot.updateCache(nodeId(modification), Optional.<FlowCapableNode>absent());
+ return;
+ }
+
+ operationalSnaphot.updateCache(nodeId(modification), Optional.fromNullable(flowCapableNodeAfter(modification)));
+ } catch(Exception e) {
+ LOG.error("update cache failed {}", nodeId(modification), e);
+ }
+ }
+
+ protected Optional<ListenableFuture<Boolean>> skipModification(DataTreeModification<Node> modification) {
+ LOG.trace("Skipping Inventory Operational modification {}, before {}, after {}", nodeIdValue(modification),
+ modification.getRootNode().getDataBefore() == null ? "null" : "nonnull",
+ modification.getRootNode().getDataAfter() == null ? "null" : "nonnull");
+ return Optional.absent();// skip otherwise event
+ }
+
+ /**
+ * ModificationType.DELETE
+ */
+ protected boolean isDelete(DataTreeModification<Node> modification) {
+ if (ModificationType.DELETE == modification.getRootNode().getModificationType()) {
+ LOG.trace("Delete {} (physical)", nodeIdValue(modification));
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * All connectors disappeared from operational store (logical delete).
+ */
+ protected boolean isDeleteLogical(DataTreeModification<Node> modification) {
+ final DataObjectModification<Node> rootNode = modification.getRootNode();
+ if (!safeConnectorsEmpty(rootNode.getDataBefore()) && safeConnectorsEmpty(rootNode.getDataAfter())) {
+ LOG.trace("Delete {} (logical)", nodeIdValue(modification));
+ return true;
+ }
+
+ return false;
+ }
+
+ protected boolean isAdd(DataTreeModification<Node> modification) {
+ final DataObjectModification<Node> rootNode = modification.getRootNode();
+ final Node dataAfter = rootNode.getDataAfter();
+ final Node dataBefore = rootNode.getDataBefore();
+
+ final boolean nodeAppearedInOperational = dataBefore == null && dataAfter != null;
+ if (nodeAppearedInOperational) {
+ LOG.trace("Add {} (physical)", nodeIdValue(modification));
+ }
+ return nodeAppearedInOperational;
+ }
+
+ /**
+ * All connectors appeared in operational store (logical add).
+ */
+ protected boolean isAddLogical(DataTreeModification<Node> modification) {
+ final DataObjectModification<Node> rootNode = modification.getRootNode();
+ if (safeConnectorsEmpty(rootNode.getDataBefore()) && !safeConnectorsEmpty(rootNode.getDataAfter())) {
+ LOG.trace("Add {} (logical)", nodeIdValue(modification));
+ return true;
+ }
+
+ return false;
+ }
+
+ protected Optional<ListenableFuture<Boolean>> reconciliation(
+ DataTreeModification<Node> modification) throws InterruptedException {
+ final NodeId nodeId = nodeId(modification);
+
+ LOG.debug("reconciliation {}", nodeId.getValue());
+
+ final Optional<FlowCapableNode> nodeConfiguration = configDao.loadByNodeId(nodeId);
+ final InstanceIdentifier<FlowCapableNode> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(nodeId(modification))).augmentation(FlowCapableNode.class);
+ final ListenableFuture<Boolean> rpcResult =
+ reactor.syncup(nodePath, nodeConfiguration.orNull(), flowCapableNodeAfter(modification));
+ return Optional.of(rpcResult);
+ }
+
+ static FlowCapableNode flowCapableNodeAfter(DataTreeModification<Node> modification) {
+ final Node dataAfter = modification.getRootNode().getDataAfter();
+ if (dataAfter == null) {
+ return null;
+ }
+ return dataAfter.getAugmentation(FlowCapableNode.class);
+ }
+
+ static boolean safeConnectorsEmpty(Node node) {
+ if (node == null) {
+ return true;
+ }
+
+ final List<NodeConnector> nodeConnectors = node.getNodeConnector();
+ if (nodeConnectors == null || nodeConnectors.isEmpty()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ static String nodeIdValue(DataTreeModification<Node> modification) {
+ final NodeId nodeId = nodeId(modification);
+
+ if (nodeId == null) {
+ return null;
+ }
+
+ return nodeId.getValue();
+ }
+
+ static NodeId nodeId(DataTreeModification<Node> modification) {
+ final DataObjectModification<Node> rootNode = modification.getRootNode();
+ final Node dataAfter = rootNode.getDataAfter();
+
+
+ if (dataAfter != null) {
+ return dataAfter.getId();
+ }
+
+ final Node dataBefore = rootNode.getDataBefore();
+ if (dataBefore != null) {
+ return dataBefore.getId();
+ }
+
+ return null;
+ }
+
+ @Override
+ public LogicalDatastoreType dsType() {
+ return LogicalDatastoreType.OPERATIONAL;
+ }
+}
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.impl;\r
+\r
+import java.util.concurrent.Callable;\r
+import java.util.concurrent.TimeUnit;\r
+import java.util.concurrent.TimeoutException;\r
+\r
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;\r
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;\r
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.google.common.util.concurrent.ListenableFuture;\r
+import com.google.common.util.concurrent.ListeningExecutorService;\r
+\r
+/**\r
+ * Decorator for running delegate syncup in Future.\r
+ */\r
+public class SyncReactorFutureDecorator implements SyncReactor {\r
+\r
+ private static final Logger LOG = LoggerFactory.getLogger(SyncReactorFutureDecorator.class);\r
+\r
+ private final SyncReactor delegate;\r
+ private final ListeningExecutorService executorService;\r
+\r
+ public static final String FRM_RPC_CLIENT_PREFIX = "FRM-RPC-client-";\r
+\r
+ public SyncReactorFutureDecorator(SyncReactor delegate, ListeningExecutorService executorService) {\r
+ this.delegate = delegate;\r
+ this.executorService = executorService;\r
+ }\r
+\r
+ public ListenableFuture<Boolean> syncup(final InstanceIdentifier<FlowCapableNode> flowcapableNodePath,\r
+ final FlowCapableNode configTree, final FlowCapableNode operationalTree) throws InterruptedException {\r
+ final NodeId nodeId = PathUtil.digNodeId(flowcapableNodePath);\r
+ LOG.trace("syncup {}", nodeId.getValue());\r
+\r
+ final ListenableFuture<Boolean> syncup = executorService.submit(new Callable<Boolean>() {\r
+ public Boolean call() throws Exception {\r
+ final String oldThreadName = updateThreadName(nodeId);\r
+\r
+ try {\r
+ final Boolean ret = doSyncupInFuture(flowcapableNodePath, configTree, operationalTree)\r
+ .get(10000, TimeUnit.MILLISECONDS);\r
+ LOG.trace("ret {} {}", nodeId.getValue(), ret);\r
+ return true;\r
+ } catch (TimeoutException e) {\r
+ LOG.error("doSyncupInFuture timeout occured {}", nodeId.getValue(), e);\r
+ return false;\r
+ } finally {\r
+ updateThreadName(oldThreadName);\r
+ }\r
+ }\r
+ });\r
+ \r
+ return syncup;\r
+ }\r
+\r
+ protected ListenableFuture<Boolean> doSyncupInFuture(final InstanceIdentifier<FlowCapableNode> flowcapableNodePath,\r
+ final FlowCapableNode configTree, final FlowCapableNode operationalTree)\r
+ throws InterruptedException {\r
+ final NodeId nodeId = PathUtil.digNodeId(flowcapableNodePath);\r
+ LOG.trace("doSyncupInFuture {}", nodeId.getValue());\r
+\r
+ return delegate.syncup(flowcapableNodePath, configTree, operationalTree);\r
+ }\r
+\r
+ static String threadName() {\r
+ final Thread currentThread = Thread.currentThread();\r
+ return currentThread.getName();\r
+ }\r
+\r
+ protected String updateThreadName(NodeId nodeId) {\r
+ final Thread currentThread = Thread.currentThread();\r
+ final String oldName = currentThread.getName();\r
+ try {\r
+ if (oldName.startsWith(SyncReactorFutureDecorator.FRM_RPC_CLIENT_PREFIX)) {\r
+ currentThread.setName(oldName + "@" + nodeId.getValue());\r
+ } else {\r
+ LOG.warn("try to update foreign thread name {} {}", nodeId, oldName);\r
+ }\r
+ } catch (Exception e) {\r
+ LOG.error("failed updating threadName {}", nodeId, e);\r
+ }\r
+ return oldName;\r
+ }\r
+\r
+ protected String updateThreadName(String name) {\r
+ final Thread currentThread = Thread.currentThread();\r
+ final String oldName = currentThread.getName();\r
+ try {\r
+ if (oldName.startsWith(SyncReactorFutureDecorator.FRM_RPC_CLIENT_PREFIX)) {\r
+ currentThread.setName(name);\r
+ } else {\r
+ LOG.warn("try to update foreign thread name {} {}", oldName, name);\r
+ }\r
+ } catch (Exception e) {\r
+ LOG.error("failed updating threadName {}", name, e);\r
+ }\r
+ return oldName;\r
+ }\r
+}\r
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.impl;\r
+\r
+import java.util.HashMap;\r
+import java.util.Map;\r
+import java.util.concurrent.Semaphore;\r
+\r
+import javax.annotation.concurrent.GuardedBy;\r
+\r
+import org.apache.commons.lang3.tuple.Pair;\r
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;\r
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;\r
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.google.common.util.concurrent.Futures;\r
+import com.google.common.util.concurrent.ListenableFuture;\r
+import com.google.common.util.concurrent.ListeningExecutorService;\r
+\r
+/**\r
+ * Enriches {@link SyncReactorFutureDecorator} with state compression.\r
+ */\r
+public class SyncReactorFutureWithCompressionDecorator extends SyncReactorFutureDecorator {\r
+\r
+ private static final Logger LOG = LoggerFactory.getLogger(SyncReactorFutureWithCompressionDecorator.class);\r
+\r
+ @GuardedBy("beforeCompressionGuard")\r
+ final Map<InstanceIdentifier<FlowCapableNode>, Pair<FlowCapableNode, FlowCapableNode>> beforeCompression =\r
+ new HashMap<>();\r
+ final Semaphore beforeCompressionGuard = new Semaphore(1, false);\r
+\r
+ public SyncReactorFutureWithCompressionDecorator(SyncReactor delegate, ListeningExecutorService executorService) {\r
+ super(delegate, executorService);\r
+ }\r
+\r
+ public ListenableFuture<Boolean> syncup(final InstanceIdentifier<FlowCapableNode> flowcapableNodePath,\r
+ final FlowCapableNode configTree, final FlowCapableNode operationalTree) throws InterruptedException {\r
+ final NodeId nodeId = PathUtil.digNodeId(flowcapableNodePath);\r
+ LOG.trace("syncup {}", nodeId.getValue());\r
+\r
+ try {\r
+ beforeCompressionGuard.acquire();\r
+\r
+ final boolean newFutureNecessary = updateCompressionState(flowcapableNodePath, configTree, operationalTree);\r
+ if (newFutureNecessary) {\r
+ super.syncup(flowcapableNodePath, configTree, operationalTree);\r
+ }\r
+ return Futures.immediateFuture(true);\r
+ } finally {\r
+ beforeCompressionGuard.release();\r
+ }\r
+ }\r
+\r
+ protected ListenableFuture<Boolean> doSyncupInFuture(final InstanceIdentifier<FlowCapableNode> flowcapableNodePath,\r
+ final FlowCapableNode configTree, final FlowCapableNode operationalTree)\r
+ throws InterruptedException {\r
+ final NodeId nodeId = PathUtil.digNodeId(flowcapableNodePath);\r
+ LOG.trace("doSyncupInFuture {}", nodeId.getValue());\r
+\r
+ final Pair<FlowCapableNode, FlowCapableNode> lastCompressionState =\r
+ removeLastCompressionState(flowcapableNodePath);\r
+ if (lastCompressionState == null) {\r
+ return Futures.immediateFuture(true);\r
+ } else {\r
+ return super.doSyncupInFuture(flowcapableNodePath,\r
+ lastCompressionState.getLeft(), lastCompressionState.getRight());\r
+ }\r
+ }\r
+\r
+ protected boolean updateCompressionState(final InstanceIdentifier<FlowCapableNode> flowcapableNodePath,\r
+ final FlowCapableNode configTree, final FlowCapableNode operationalTree) {\r
+ final Pair<FlowCapableNode, FlowCapableNode> previous = beforeCompression.get(flowcapableNodePath);\r
+ if (previous != null) {\r
+ final FlowCapableNode previousOperational = previous.getRight();\r
+ beforeCompression.put(flowcapableNodePath, Pair.of(configTree, previousOperational));\r
+ return false;\r
+ } else {\r
+ beforeCompression.put(flowcapableNodePath, Pair.of(configTree, operationalTree));\r
+ return true;\r
+ }\r
+ }\r
+\r
+ protected Pair<FlowCapableNode/* config */, FlowCapableNode/* operational */> removeLastCompressionState(\r
+ final InstanceIdentifier<FlowCapableNode> flowcapableNodePath) {\r
+ try {\r
+ try {\r
+ beforeCompressionGuard.acquire();\r
+ } catch (InterruptedException e) {\r
+ return null;\r
+ }\r
+\r
+ return beforeCompression.remove(flowcapableNodePath);\r
+ } finally {\r
+ beforeCompressionGuard.release();\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.impl;\r
+\r
+import java.util.concurrent.Semaphore;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import javax.annotation.Nullable;\r
+\r
+import org.opendaylight.openflowplugin.applications.frsync.SemaphoreKeeper;\r
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;\r
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;\r
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.google.common.base.Preconditions;\r
+import com.google.common.util.concurrent.FutureCallback;\r
+import com.google.common.util.concurrent.Futures;\r
+import com.google.common.util.concurrent.ListenableFuture;\r
+\r
+/**\r
+ * Decorator for NodeId level syncup locking.\r
+ */\r
+public class SyncReactorGuardDecorator implements SyncReactor {\r
+\r
+ private static final Logger LOG = LoggerFactory.getLogger(SyncReactorGuardDecorator.class);\r
+\r
+ private final SyncReactor delegate;\r
+ private final SemaphoreKeeper<InstanceIdentifier<FlowCapableNode>> semaphoreKeeper;\r
+\r
+ public SyncReactorGuardDecorator(SyncReactor delegate,\r
+ SemaphoreKeeper<InstanceIdentifier<FlowCapableNode>> semaphoreKeeper) {\r
+ this.delegate = delegate;\r
+ this.semaphoreKeeper = semaphoreKeeper;\r
+ }\r
+\r
+ public ListenableFuture<Boolean> syncup(final InstanceIdentifier<FlowCapableNode> flowcapableNodePath,\r
+ final FlowCapableNode configTree, final FlowCapableNode operationalTree) throws InterruptedException {\r
+ final NodeId nodeId = PathUtil.digNodeId(flowcapableNodePath);\r
+ LOG.trace("syncup {}", nodeId.getValue());\r
+\r
+ final long stampBeforeGuard = System.nanoTime();\r
+ final Semaphore guard = summonGuardAndAcquire(flowcapableNodePath);//TODO handle InteruptedException\r
+\r
+ try {\r
+ final long stampAfterGuard = System.nanoTime();\r
+ if (LOG.isDebugEnabled()) {\r
+ LOG.debug("syncup start {} waiting:{} guard:{} thread:{}", nodeId.getValue(),\r
+ formatNanos(stampAfterGuard - stampBeforeGuard),\r
+ guard, threadName());\r
+ }\r
+ \r
+ final ListenableFuture<Boolean> endResult =\r
+ delegate.syncup(flowcapableNodePath, configTree, operationalTree);//TODO handle InteruptedException\r
+ \r
+ Futures.addCallback(endResult, new FutureCallback<Boolean>() {\r
+ @Override\r
+ public void onSuccess(@Nullable final Boolean result) {\r
+ if (LOG.isDebugEnabled()) {\r
+ final long stampFinished = System.nanoTime();\r
+ LOG.debug("syncup finished {} took:{} rpc:{} wait:{} guard:{}, thread:{}", nodeId.getValue(),\r
+ formatNanos(stampFinished - stampBeforeGuard),\r
+ formatNanos(stampFinished - stampAfterGuard),\r
+ formatNanos(stampAfterGuard - stampBeforeGuard),\r
+ guard, threadName());\r
+ }\r
+ \r
+ lockReleaseForNodeId(nodeId, guard);\r
+ }\r
+ \r
+ @Override\r
+ public void onFailure(final Throwable t) {\r
+ if (LOG.isDebugEnabled()) {\r
+ final long stampFinished = System.nanoTime();\r
+ LOG.warn("syncup failed {} took:{} rpc:{} wait:{} guard:{} thread:{}", nodeId.getValue(),\r
+ formatNanos(stampFinished - stampBeforeGuard),\r
+ formatNanos(stampFinished - stampAfterGuard),\r
+ formatNanos(stampAfterGuard - stampBeforeGuard),\r
+ guard, threadName());\r
+ }\r
+ \r
+ lockReleaseForNodeId(nodeId, guard);\r
+ }\r
+ });\r
+ return endResult;\r
+ } catch(InterruptedException e) {\r
+ lockReleaseForNodeId(nodeId, guard);\r
+ throw e;\r
+ }\r
+ }\r
+\r
+ protected String formatNanos(long nanos) {\r
+ return "'" + TimeUnit.NANOSECONDS.toMillis(nanos) + " ms'";\r
+ }\r
+\r
+ /**\r
+ * get guard\r
+ *\r
+ * @param flowcapableNodePath\r
+ * @return\r
+ */\r
+ protected Semaphore summonGuardAndAcquire(final InstanceIdentifier<FlowCapableNode> flowcapableNodePath)\r
+ throws InterruptedException {\r
+ final Semaphore guard = Preconditions.checkNotNull(semaphoreKeeper.summonGuard(flowcapableNodePath),\r
+ "no guard for " + flowcapableNodePath);\r
+\r
+ if (LOG.isDebugEnabled()) {\r
+ final NodeId nodeId = PathUtil.digNodeId(flowcapableNodePath);\r
+ try {\r
+ LOG.debug("syncup summon {} guard:{} thread:{}", nodeId.getValue(), guard, threadName());\r
+ } catch (Exception e) {\r
+ LOG.error("error logging guard after summon before aquiring {}", nodeId);\r
+ }\r
+ }\r
+\r
+ guard.acquire();\r
+ return guard;\r
+ }\r
+\r
+ /**\r
+ * unlock per node\r
+ *\r
+ * @param nodeId\r
+ * @param guard\r
+ */\r
+ protected void lockReleaseForNodeId(final NodeId nodeId,\r
+ final Semaphore guard) {\r
+ if (guard == null) {\r
+ return;\r
+ }\r
+ guard.release();\r
+ }\r
+\r
+ static String threadName() {\r
+ final Thread currentThread = Thread.currentThread();\r
+ return currentThread.getName();\r
+ }\r
+\r
+}\r
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.impl.strategy.SynchronizationDiffInput;
+import org.opendaylight.openflowplugin.applications.frsync.util.CrudCounts;
+import org.opendaylight.openflowplugin.applications.frsync.util.FlowCapableNodeLookups;
+import org.opendaylight.openflowplugin.applications.frsync.util.FxChainUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ReconcileUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Synchronization reactor implementation, applicable for both - syncup and reconciliation.
+ */
+public class SyncReactorImpl implements SyncReactor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncReactorImpl.class);
+ private final SyncPlanPushStrategy syncPlanPushStrategy;
+
+ public SyncReactorImpl(SyncPlanPushStrategy syncPlanPushStrategy) {
+ this.syncPlanPushStrategy = Preconditions.checkNotNull(syncPlanPushStrategy, "execution strategy is mandatory");
+ }
+
+ @Override
+ public ListenableFuture<Boolean> syncup(final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final FlowCapableNode configTree, final FlowCapableNode operationalTree) {
+
+ LOG.trace("syncup {} cfg:{} oper:{}", nodeIdent, configTree == null ? "is null" : "non null", operationalTree == null ? "is null" : "non null");
+ final SyncCrudCounters counters = new SyncCrudCounters();
+ /**
+ * instructions:
+ * - extract diff changes and prepare change steps in safe order
+ * - optimization: decide if updates needed
+ * - execute chosen implementation (e.g. conventional API, bulk API, flat bulk API)
+ * - recommended order follows:
+ * reconciliation strategy - phase 1: - add/update missing objects in following order:
+ * - table features - groups (reordered) - meters - flows
+ *
+ * reconciliation strategy - phase 2: - remove redundant objects in following order:
+ * - flows - meters - groups (reordered)
+ **/
+
+ final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
+
+ final List<ItemSyncBox<Group>> groupsToAddOrUpdate = extractGroupsToAddOrUpdate(nodeId, configTree, operationalTree);
+ final ItemSyncBox<Meter> metersToAddOrUpdate = extractMetersToAddOrUpdate(nodeId, configTree, operationalTree);
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate = extractFlowsToAddOrUpdate(nodeId, configTree, operationalTree);
+
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToRemove = extractFlowsToRemove(nodeId, configTree, operationalTree);
+ final ItemSyncBox<Meter> metersToRemove = extractMetersToRemove(nodeId, configTree, operationalTree);
+ final List<ItemSyncBox<Group>> groupsToRemove = extractGroupsToRemove(nodeId, configTree, operationalTree);
+
+ final SynchronizationDiffInput input = new SynchronizationDiffInput(nodeIdent,
+ groupsToAddOrUpdate, metersToAddOrUpdate, flowsToAddOrUpdate,
+ flowsToRemove, metersToRemove, groupsToRemove);
+
+ counters.setStartNano(System.nanoTime());
+ final ListenableFuture<RpcResult<Void>> bootstrapResultFuture = RpcResultBuilder.<Void>success().buildFuture();
+ final ListenableFuture<RpcResult<Void>> resultVehicle = syncPlanPushStrategy.executeSyncStrategy(
+ bootstrapResultFuture, input, counters);
+
+ // log final result
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "final result"));
+
+ return Futures.transform(resultVehicle, new Function<RpcResult<Void>, Boolean>() {
+ @Override
+ public Boolean apply(RpcResult<Void> input) {
+ if (input == null) {
+ return false;
+ }
+
+ if (LOG.isDebugEnabled()) {
+ final CrudCounts flowCrudCounts = counters.getFlowCrudCounts();
+ final CrudCounts meterCrudCounts = counters.getMeterCrudCounts();
+ final CrudCounts groupCrudCounts = counters.getGroupCrudCounts();
+ LOG.debug("sync-outcome[{}] (added/updated/removed): flow={}/{}/{}, meter={}/{}/{}, group={}/{}/{}, took={} ms",
+ nodeId.getValue(),
+ flowCrudCounts.getAdded(),
+ flowCrudCounts.getUpdated(),
+ flowCrudCounts.getRemoved(),
+ meterCrudCounts.getAdded(),
+ meterCrudCounts.getUpdated(),
+ meterCrudCounts.getRemoved(),
+ groupCrudCounts.getAdded(),
+ groupCrudCounts.getUpdated(),
+ groupCrudCounts.getRemoved(),
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - counters.getStartNano())
+ );
+ }
+
+ return input.isSuccessful();
+ }
+ });
+ }
+
+ @VisibleForTesting
+ static List<ItemSyncBox<Group>> extractGroupsToAddOrUpdate(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Group> groupsConfigured = ReconcileUtil.safeGroups(flowCapableNodeConfigured);
+ final List<Group> groupsOperational = ReconcileUtil.safeGroups(flowCapableNodeOperational);
+ final Map<Long, Group> groupOperationalMap = FlowCapableNodeLookups.wrapGroupsToMap(groupsOperational);
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.addAll(groupsConfigured);
+
+ return ReconcileUtil.resolveAndDivideGroupDiffs(nodeId, groupOperationalMap, pendingGroups, true);
+ }
+
+ @VisibleForTesting
+ static ItemSyncBox<Meter> extractMetersToAddOrUpdate(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Meter> metersConfigured = ReconcileUtil.safeMeters(flowCapableNodeConfigured);
+ final List<Meter> metersOperational = ReconcileUtil.safeMeters(flowCapableNodeOperational);
+ final Map<MeterId, Meter> meterOperationalMap = FlowCapableNodeLookups.wrapMetersToMap(metersOperational);
+
+ return ReconcileUtil.resolveMeterDiffs(nodeId, meterOperationalMap, metersConfigured, true);
+ }
+
+ @VisibleForTesting
+ static Map<TableKey, ItemSyncBox<Flow>> extractFlowsToAddOrUpdate(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Table> tablesConfigured = ReconcileUtil.safeTables(flowCapableNodeConfigured);
+ if (tablesConfigured.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ final List<Table> tablesOperational = ReconcileUtil.safeTables(flowCapableNodeOperational);
+ final Map<Short, Table> tableOperationalMap = FlowCapableNodeLookups.wrapTablesToMap(tablesOperational);
+
+ return ReconcileUtil.resolveFlowDiffsInAllTables(nodeId, tableOperationalMap, tablesConfigured, true);
+ }
+
+ @VisibleForTesting
+ static Map<TableKey, ItemSyncBox<Flow>> extractFlowsToRemove(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Table> tablesOperational = ReconcileUtil.safeTables(flowCapableNodeOperational);
+ if (tablesOperational.isEmpty()) {
+ return Collections.emptyMap();
+ }
+
+ final List<Table> tablesConfigured = ReconcileUtil.safeTables(flowCapableNodeConfigured);
+ final Map<Short, Table> tableConfiguredMap = FlowCapableNodeLookups.wrapTablesToMap(tablesConfigured);
+
+ return ReconcileUtil.resolveFlowDiffsInAllTables(nodeId, tableConfiguredMap, tablesOperational, false);
+ }
+
+ @VisibleForTesting
+ static ItemSyncBox<Meter> extractMetersToRemove(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Meter> metersConfigured = ReconcileUtil.safeMeters(flowCapableNodeConfigured);
+ final List<Meter> metersOperational = ReconcileUtil.safeMeters(flowCapableNodeOperational);
+ final Map<MeterId, Meter> meterConfiguredMap = FlowCapableNodeLookups.wrapMetersToMap(metersConfigured);
+
+ return ReconcileUtil.resolveMeterDiffs(nodeId, meterConfiguredMap, metersOperational, false);
+ }
+
+ @VisibleForTesting
+ static List<ItemSyncBox<Group>> extractGroupsToRemove(final NodeId nodeId,
+ final FlowCapableNode flowCapableNodeConfigured,
+ final FlowCapableNode flowCapableNodeOperational) {
+ final List<Group> groupsConfigured = ReconcileUtil.safeGroups(flowCapableNodeConfigured);
+ final List<Group> groupsOperational = ReconcileUtil.safeGroups(flowCapableNodeOperational);
+ final Map<Long, Group> groupConfiguredMap = FlowCapableNodeLookups.wrapGroupsToMap(groupsConfigured);
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.addAll(groupsOperational);
+
+ return ReconcileUtil.resolveAndDivideGroupDiffs(nodeId, groupConfiguredMap, pendingGroups, false);
+ }
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.Collections;
+import java.util.concurrent.Future;
+
+import org.opendaylight.openflowplugin.applications.frsync.ForwardingRulesUpdateCommitter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.SalTableService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.table.update.OriginalTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.table.update.UpdatedTableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implements {@link ForwardingRulesUpdateCommitter} methods for processing update of {@link TableFeatures}.
+ */
+public class TableForwarder implements ForwardingRulesUpdateCommitter<TableFeatures, UpdateTableOutput> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TableForwarder.class);
+ private final SalTableService salTableService;
+
+ public TableForwarder(SalTableService salTableService) {
+ this.salTableService = salTableService;
+ }
+
+ @Override
+ public Future<RpcResult<UpdateTableOutput>> update(final InstanceIdentifier<TableFeatures> identifier,
+ final TableFeatures original, final TableFeatures update,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ LOG.debug("Forwarding Table Update request [Tbl id, node Id {} {}",
+ identifier, nodeIdent);
+
+ final UpdateTableInputBuilder builder = new UpdateTableInputBuilder();
+
+ builder.setNode(new NodeRef(nodeIdent.firstIdentifierOf(Node.class)));
+
+ final InstanceIdentifier<Table> iiToTable = nodeIdent.child(Table.class,
+ new TableKey(identifier.firstKeyOf(TableFeatures.class).getTableId()));
+ builder.setTableRef(new TableRef(iiToTable));
+
+ builder.setUpdatedTable(new UpdatedTableBuilder().setTableFeatures(
+ Collections.singletonList(update)).build());
+
+ builder.setOriginalTable(new OriginalTableBuilder().setTableFeatures(
+ Collections.singletonList(original)).build());
+ LOG.debug("Invoking SalTableService {} ", nodeIdent);
+
+ return salTableService.updateTable(builder.build());
+ }
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl.strategy;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.PeekingIterator;
+import com.google.common.collect.Range;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import javax.annotation.Nullable;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
+import org.opendaylight.openflowplugin.applications.frsync.impl.TableForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.util.FxChainUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ReconcileUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.SalFlatBatchService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.Batch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.BatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.BatchChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddFlowCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddGroupCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddMeterCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveFlowCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveGroupCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveMeterCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateFlowCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateGroupCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateMeterCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.add.flow._case.FlatBatchAddFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.add.flow._case.FlatBatchAddFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.add.group._case.FlatBatchAddGroup;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.add.group._case.FlatBatchAddGroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.add.meter._case.FlatBatchAddMeter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.add.meter._case.FlatBatchAddMeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.remove.flow._case.FlatBatchRemoveFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.remove.flow._case.FlatBatchRemoveFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.remove.group._case.FlatBatchRemoveGroup;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.remove.group._case.FlatBatchRemoveGroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.remove.meter._case.FlatBatchRemoveMeter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.remove.meter._case.FlatBatchRemoveMeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.update.flow._case.FlatBatchUpdateFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.update.flow._case.FlatBatchUpdateFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.update.group._case.FlatBatchUpdateGroup;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.update.group._case.FlatBatchUpdateGroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.update.meter._case.FlatBatchUpdateMeter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.flat.batch.update.meter._case.FlatBatchUpdateMeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.output.BatchFailure;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flows.service.rev160314.batch.flow.input.update.grouping.OriginalBatchedFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flows.service.rev160314.batch.flow.input.update.grouping.UpdatedBatchedFlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groups.service.rev160315.batch.group.input.update.grouping.OriginalBatchedGroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groups.service.rev160315.batch.group.input.update.grouping.UpdatedBatchedGroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meters.service.rev160316.batch.meter.input.update.grouping.OriginalBatchedMeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meters.service.rev160316.batch.meter.input.update.grouping.UpdatedBatchedMeterBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Execute CRUD API for flow + group + meter involving flat-batch strategy.
+ */
+public class SyncPlanPushStrategyFlatBatchImpl implements SyncPlanPushStrategy {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncPlanPushStrategyFlatBatchImpl.class);
+
+ private SalFlatBatchService flatBatchService;
+ private TableForwarder tableForwarder;
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> executeSyncStrategy(ListenableFuture<RpcResult<Void>> resultVehicle,
+ final SynchronizationDiffInput diffInput,
+ final SyncCrudCounters counters) {
+ final InstanceIdentifier<FlowCapableNode> nodeIdent = diffInput.getNodeIdent();
+ final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
+
+ // prepare default (full) counts
+ counters.getGroupCrudCounts().setAdded(ReconcileUtil.countTotalPushed(diffInput.getGroupsToAddOrUpdate()));
+ counters.getGroupCrudCounts().setUpdated(ReconcileUtil.countTotalUpdated(diffInput.getGroupsToAddOrUpdate()));
+ counters.getGroupCrudCounts().setRemoved(ReconcileUtil.countTotalPushed(diffInput.getGroupsToRemove()));
+
+ counters.getFlowCrudCounts().setAdded(ReconcileUtil.countTotalPushed(diffInput.getFlowsToAddOrUpdate().values()));
+ counters.getFlowCrudCounts().setUpdated(ReconcileUtil.countTotalUpdated(diffInput.getFlowsToAddOrUpdate().values()));
+ counters.getFlowCrudCounts().setRemoved(ReconcileUtil.countTotalPushed(diffInput.getFlowsToRemove().values()));
+
+ counters.getMeterCrudCounts().setAdded(diffInput.getMetersToAddOrUpdate().getItemsToPush().size());
+ counters.getMeterCrudCounts().setUpdated(diffInput.getMetersToAddOrUpdate().getItemsToUpdate().size());
+ counters.getMeterCrudCounts().setRemoved(diffInput.getMetersToRemove().getItemsToPush().size());
+
+ /* Tables - have to be pushed before groups */
+ // TODO enable table-update when ready
+ //resultVehicle = updateTableFeatures(nodeIdent, configTree);
+
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ //final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ // Futures.asList Arrays.asList(input, output),
+ // ReconcileUtil.<UpdateFlowOutput>createRpcResultCondenser("TODO"));
+ }
+
+ final List<Batch> batchBag = new ArrayList<>();
+ int batchOrder = 0;
+
+ batchOrder = assembleAddOrUpdateGroups(batchBag, batchOrder, diffInput.getGroupsToAddOrUpdate());
+ batchOrder = assembleAddOrUpdateMeters(batchBag, batchOrder, diffInput.getMetersToAddOrUpdate());
+ batchOrder = assembleAddOrUpdateFlows(batchBag, batchOrder, diffInput.getFlowsToAddOrUpdate());
+
+ batchOrder = assembleRemoveFlows(batchBag, batchOrder, diffInput.getFlowsToRemove());
+ batchOrder = assembleRemoveMeters(batchBag, batchOrder, diffInput.getMetersToRemove());
+ batchOrder = assembleRemoveGroups(batchBag, batchOrder, diffInput.getGroupsToRemove());
+
+ LOG.trace("Index of last batch step: {}", batchOrder);
+
+ final ProcessFlatBatchInput flatBatchInput = new ProcessFlatBatchInputBuilder()
+ .setNode(new NodeRef(PathUtil.digNodePath(diffInput.getNodeIdent())))
+ .setExitOnFirstError(false) // TODO: propagate from input
+ .setBatch(batchBag)
+ .build();
+
+ final Future<RpcResult<ProcessFlatBatchOutput>> rpcResultFuture = flatBatchService.processFlatBatch(flatBatchInput);
+
+ final int failureIndexLimit = batchOrder;
+
+ if (LOG.isDebugEnabled()) {
+ Futures.addCallback(JdkFutureAdapters.listenInPoolThread(rpcResultFuture),
+ createCounterCallback(batchBag, failureIndexLimit, counters));
+ }
+
+ return Futures.transform(JdkFutureAdapters.listenInPoolThread(rpcResultFuture),
+ ReconcileUtil.<ProcessFlatBatchOutput>createRpcResultToVoidFunction("flat-batch"));
+ }
+ });
+
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "flat-batch"));
+ return resultVehicle;
+ }
+
+ private FutureCallback<RpcResult<ProcessFlatBatchOutput>> createCounterCallback(final List<Batch> inputBatchBag,
+ final int failureIndexLimit,
+ final SyncCrudCounters counters) {
+ return new FutureCallback<RpcResult<ProcessFlatBatchOutput>>() {
+ @Override
+ public void onSuccess(@Nullable final RpcResult<ProcessFlatBatchOutput> result) {
+ if (!result.isSuccessful() && result.getResult() != null && !result.getResult().getBatchFailure().isEmpty()) {
+ Map<Range<Integer>, Batch> batchMap = mapBachesToRanges(inputBatchBag, failureIndexLimit);
+
+ for (BatchFailure batchFailure : result.getResult().getBatchFailure()) {
+ for (Map.Entry<Range<Integer>, Batch> rangeBatchEntry : batchMap.entrySet()) {
+ if (rangeBatchEntry.getKey().contains(batchFailure.getBatchOrder())) {
+ // get type and decrease
+ final BatchChoice batchChoice = rangeBatchEntry.getValue().getBatchChoice();
+ decrementCounters(batchChoice, counters);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ counters.resetAll();
+ }
+ };
+ }
+
+ static void decrementCounters(final BatchChoice batchChoice, final SyncCrudCounters counters) {
+ if (batchChoice instanceof FlatBatchAddFlowCase) {
+ counters.getFlowCrudCounts().decAdded();
+ } else if (batchChoice instanceof FlatBatchUpdateFlowCase) {
+ counters.getFlowCrudCounts().decUpdated();
+ } else if (batchChoice instanceof FlatBatchRemoveFlowCase) {
+ counters.getFlowCrudCounts().decRemoved();
+ } else if (batchChoice instanceof FlatBatchAddGroupCase) {
+ counters.getGroupCrudCounts().decAdded();
+ } else if (batchChoice instanceof FlatBatchUpdateGroupCase) {
+ counters.getGroupCrudCounts().decUpdated();
+ } else if (batchChoice instanceof FlatBatchRemoveGroupCase) {
+ counters.getGroupCrudCounts().decRemoved();
+ } else if (batchChoice instanceof FlatBatchAddMeterCase) {
+ counters.getMeterCrudCounts().decAdded();
+ } else if (batchChoice instanceof FlatBatchUpdateMeterCase) {
+ counters.getMeterCrudCounts().decUpdated();
+ } else if (batchChoice instanceof FlatBatchRemoveMeterCase) {
+ counters.getMeterCrudCounts().decRemoved();
+ }
+ }
+
+ static Map<Range<Integer>, Batch> mapBachesToRanges(final List<Batch> inputBatchBag, final int failureIndexLimit) {
+ final Map<Range<Integer>, Batch> batchMap = new LinkedHashMap<>();
+ final PeekingIterator<Batch> batchPeekingIterator = Iterators.peekingIterator(inputBatchBag.iterator());
+ while (batchPeekingIterator.hasNext()) {
+ final Batch batch = batchPeekingIterator.next();
+ final int nextBatchOrder = batchPeekingIterator.hasNext()
+ ? batchPeekingIterator.peek().getBatchOrder()
+ : failureIndexLimit;
+ batchMap.put(Range.closed(batch.getBatchOrder(), nextBatchOrder - 1), batch);
+ }
+ return batchMap;
+ }
+
+ private int getNextBatchLimit(final PeekingIterator<Batch> inputBatchIterator, final int failureIndexLimit) {
+ return inputBatchIterator.hasNext()
+ ? inputBatchIterator.peek().getBatchOrder()
+ : failureIndexLimit;
+ }
+
+ @VisibleForTesting
+ static int assembleRemoveFlows(final List<Batch> batchBag, int batchOrder, final Map<TableKey, ItemSyncBox<Flow>> flowItemSyncTableMap) {
+ // process flow remove
+ if (flowItemSyncTableMap != null) {
+ for (Map.Entry<TableKey, ItemSyncBox<Flow>> syncBoxEntry : flowItemSyncTableMap.entrySet()) {
+ final TableKey tableKey = syncBoxEntry.getKey();
+ final ItemSyncBox<Flow> flowItemSyncBox = syncBoxEntry.getValue();
+
+ if (!flowItemSyncBox.getItemsToPush().isEmpty()) {
+ final List<FlatBatchRemoveFlow> flatBatchRemoveFlowBag =
+ new ArrayList<>(flowItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (Flow flow : flowItemSyncBox.getItemsToPush()) {
+ flatBatchRemoveFlowBag.add(new FlatBatchRemoveFlowBuilder(flow)
+ .setBatchOrder(itemOrder++)
+ .setFlowId(flow.getId())
+ .build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchRemoveFlowCaseBuilder()
+ .setFlatBatchRemoveFlow(flatBatchRemoveFlowBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+ }
+ }
+ return batchOrder;
+ }
+
+ @VisibleForTesting
+ static int assembleAddOrUpdateGroups(final List<Batch> batchBag, int batchOrder, final List<ItemSyncBox<Group>> groupsToAddOrUpdate) {
+ // process group add+update
+ if (groupsToAddOrUpdate != null) {
+ for (ItemSyncBox<Group> groupItemSyncBox : groupsToAddOrUpdate) {
+ if (!groupItemSyncBox.getItemsToPush().isEmpty()) {
+ final List<FlatBatchAddGroup> flatBatchAddGroupBag =
+ new ArrayList<>(groupItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (Group group : groupItemSyncBox.getItemsToPush()) {
+ flatBatchAddGroupBag.add(new FlatBatchAddGroupBuilder(group).setBatchOrder(itemOrder++).build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchAddGroupCaseBuilder()
+ .setFlatBatchAddGroup(flatBatchAddGroupBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+
+ if (!groupItemSyncBox.getItemsToUpdate().isEmpty()) {
+ final List<FlatBatchUpdateGroup> flatBatchUpdateGroupBag =
+ new ArrayList<>(groupItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (ItemSyncBox.ItemUpdateTuple<Group> groupUpdate : groupItemSyncBox.getItemsToUpdate()) {
+ flatBatchUpdateGroupBag.add(new FlatBatchUpdateGroupBuilder()
+ .setBatchOrder(itemOrder++)
+ .setOriginalBatchedGroup(new OriginalBatchedGroupBuilder(groupUpdate.getOriginal()).build())
+ .setUpdatedBatchedGroup(new UpdatedBatchedGroupBuilder(groupUpdate.getUpdated()).build())
+ .build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchUpdateGroupCaseBuilder()
+ .setFlatBatchUpdateGroup(flatBatchUpdateGroupBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+ }
+ }
+ return batchOrder;
+ }
+
+ @VisibleForTesting
+ static int assembleRemoveGroups(final List<Batch> batchBag, int batchOrder, final List<ItemSyncBox<Group>> groupsToRemoveOrUpdate) {
+ // process group add+update
+ if (groupsToRemoveOrUpdate != null) {
+ for (ItemSyncBox<Group> groupItemSyncBox : groupsToRemoveOrUpdate) {
+ if (!groupItemSyncBox.getItemsToPush().isEmpty()) {
+ final List<FlatBatchRemoveGroup> flatBatchRemoveGroupBag =
+ new ArrayList<>(groupItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (Group group : groupItemSyncBox.getItemsToPush()) {
+ flatBatchRemoveGroupBag.add(new FlatBatchRemoveGroupBuilder(group).setBatchOrder(itemOrder++).build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchRemoveGroupCaseBuilder()
+ .setFlatBatchRemoveGroup(flatBatchRemoveGroupBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+ }
+ }
+ return batchOrder;
+ }
+
+ @VisibleForTesting
+ static int assembleAddOrUpdateMeters(final List<Batch> batchBag, int batchOrder, final ItemSyncBox<Meter> meterItemSyncBox) {
+ // process meter add+update
+ if (meterItemSyncBox != null) {
+ if (!meterItemSyncBox.getItemsToPush().isEmpty()) {
+ final List<FlatBatchAddMeter> flatBatchAddMeterBag =
+ new ArrayList<>(meterItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (Meter meter : meterItemSyncBox.getItemsToPush()) {
+ flatBatchAddMeterBag.add(new FlatBatchAddMeterBuilder(meter).setBatchOrder(itemOrder++).build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchAddMeterCaseBuilder()
+ .setFlatBatchAddMeter(flatBatchAddMeterBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+
+ if (!meterItemSyncBox.getItemsToUpdate().isEmpty()) {
+ final List<FlatBatchUpdateMeter> flatBatchUpdateMeterBag =
+ new ArrayList<>(meterItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (ItemSyncBox.ItemUpdateTuple<Meter> meterUpdate : meterItemSyncBox.getItemsToUpdate()) {
+ flatBatchUpdateMeterBag.add(new FlatBatchUpdateMeterBuilder()
+ .setBatchOrder(itemOrder++)
+ .setOriginalBatchedMeter(new OriginalBatchedMeterBuilder(meterUpdate.getOriginal()).build())
+ .setUpdatedBatchedMeter(new UpdatedBatchedMeterBuilder(meterUpdate.getUpdated()).build())
+ .build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchUpdateMeterCaseBuilder()
+ .setFlatBatchUpdateMeter(flatBatchUpdateMeterBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+ }
+ return batchOrder;
+ }
+
+ @VisibleForTesting
+ static int assembleRemoveMeters(final List<Batch> batchBag, int batchOrder, final ItemSyncBox<Meter> meterItemSyncBox) {
+ // process meter remove
+ if (meterItemSyncBox != null) {
+ if (!meterItemSyncBox.getItemsToPush().isEmpty()) {
+ final List<FlatBatchRemoveMeter> flatBatchRemoveMeterBag =
+ new ArrayList<>(meterItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (Meter meter : meterItemSyncBox.getItemsToPush()) {
+ flatBatchRemoveMeterBag.add(new FlatBatchRemoveMeterBuilder(meter).setBatchOrder(itemOrder++).build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchRemoveMeterCaseBuilder()
+ .setFlatBatchRemoveMeter(flatBatchRemoveMeterBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+ }
+ return batchOrder;
+ }
+
+ @VisibleForTesting
+ static int assembleAddOrUpdateFlows(final List<Batch> batchBag, int batchOrder, final Map<TableKey, ItemSyncBox<Flow>> flowItemSyncTableMap) {
+ // process flow add+update
+ if (flowItemSyncTableMap != null) {
+ for (Map.Entry<TableKey, ItemSyncBox<Flow>> syncBoxEntry : flowItemSyncTableMap.entrySet()) {
+ final TableKey tableKey = syncBoxEntry.getKey();
+ final ItemSyncBox<Flow> flowItemSyncBox = syncBoxEntry.getValue();
+
+ if (!flowItemSyncBox.getItemsToPush().isEmpty()) {
+ final List<FlatBatchAddFlow> flatBatchAddFlowBag =
+ new ArrayList<>(flowItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (Flow flow : flowItemSyncBox.getItemsToPush()) {
+ flatBatchAddFlowBag.add(new FlatBatchAddFlowBuilder(flow)
+ .setBatchOrder(itemOrder++)
+ .setFlowId(flow.getId())
+ .build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchAddFlowCaseBuilder()
+ .setFlatBatchAddFlow(flatBatchAddFlowBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+
+ if (!flowItemSyncBox.getItemsToUpdate().isEmpty()) {
+ final List<FlatBatchUpdateFlow> flatBatchUpdateFlowBag =
+ new ArrayList<>(flowItemSyncBox.getItemsToUpdate().size());
+ int itemOrder = 0;
+ for (ItemSyncBox.ItemUpdateTuple<Flow> flowUpdate : flowItemSyncBox.getItemsToUpdate()) {
+ flatBatchUpdateFlowBag.add(new FlatBatchUpdateFlowBuilder()
+ .setBatchOrder(itemOrder++)
+ .setFlowId(flowUpdate.getUpdated().getId())
+ .setOriginalBatchedFlow(new OriginalBatchedFlowBuilder(flowUpdate.getOriginal()).build())
+ .setUpdatedBatchedFlow(new UpdatedBatchedFlowBuilder(flowUpdate.getUpdated()).build())
+ .build());
+ }
+ final Batch batch = new BatchBuilder()
+ .setBatchChoice(new FlatBatchUpdateFlowCaseBuilder()
+ .setFlatBatchUpdateFlow(flatBatchUpdateFlowBag)
+ .build())
+ .setBatchOrder(batchOrder)
+ .build();
+ batchOrder += itemOrder;
+ batchBag.add(batch);
+ }
+ }
+ }
+ return batchOrder;
+ }
+
+ public SyncPlanPushStrategyFlatBatchImpl setFlatBatchService(final SalFlatBatchService flatBatchService) {
+ this.flatBatchService = flatBatchService;
+ return this;
+ }
+
+ public SyncPlanPushStrategyFlatBatchImpl setTableForwarder(final TableForwarder tableForwarder) {
+ this.tableForwarder = tableForwarder;
+ return this;
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl.strategy;
+
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
+import org.opendaylight.openflowplugin.applications.frsync.impl.FlowForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.GroupForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.MeterForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.TableForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.util.CrudCounts;
+import org.opendaylight.openflowplugin.applications.frsync.util.FxChainUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.openflowplugin.applications.frsync.util.PathUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.ReconcileUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Execute CRUD API for flow + group + meter involving one-by-one (incremental) strategy.
+ */
+public class SyncPlanPushStrategyIncrementalImpl implements SyncPlanPushStrategy {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncPlanPushStrategyIncrementalImpl.class);
+
+ private FlowForwarder flowForwarder;
+ private TableForwarder tableForwarder;
+ private MeterForwarder meterForwarder;
+ private GroupForwarder groupForwarder;
+ private FlowCapableTransactionService transactionService;
+
+ @Override
+ public ListenableFuture<RpcResult<Void>> executeSyncStrategy(ListenableFuture<RpcResult<Void>> resultVehicle,
+ final SynchronizationDiffInput diffInput,
+ final SyncCrudCounters counters) {
+ final InstanceIdentifier<FlowCapableNode> nodeIdent = diffInput.getNodeIdent();
+ final NodeId nodeId = PathUtil.digNodeId(nodeIdent);
+
+ /* Tables - have to be pushed before groups */
+ // TODO enable table-update when ready
+ //resultVehicle = updateTableFeatures(nodeIdent, configTree);
+
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ //final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ // Futures.asList Arrays.asList(input, output),
+ // ReconcileUtil.<UpdateFlowOutput>createRpcResultCondenser("TODO"));
+ }
+ return addMissingGroups(nodeId, nodeIdent, diffInput.getGroupsToAddOrUpdate(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "addMissingGroups"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return addMissingMeters(nodeId, nodeIdent, diffInput.getMetersToAddOrUpdate(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "addMissingMeters"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return addMissingFlows(nodeId, nodeIdent, diffInput.getFlowsToAddOrUpdate(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "addMissingFlows"));
+
+
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return removeRedundantFlows(nodeId, nodeIdent, diffInput.getFlowsToRemove(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "removeRedundantFlows"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return removeRedundantMeters(nodeId, nodeIdent, diffInput.getMetersToRemove(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "removeRedundantMeters"));
+ resultVehicle = Futures.transform(resultVehicle, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ if (!input.isSuccessful()) {
+ //TODO chain errors but not skip processing on first error return Futures.immediateFuture(input);
+ }
+ return removeRedundantGroups(nodeId, nodeIdent, diffInput.getGroupsToRemove(), counters);
+ }
+ });
+ Futures.addCallback(resultVehicle, FxChainUtil.logResultCallback(nodeId, "removeRedundantGroups"));
+ return resultVehicle;
+ }
+
+
+ protected ListenableFuture<RpcResult<Void>> addMissingFlows(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final Map<TableKey, ItemSyncBox<Flow>> flowsInTablesSyncBox,
+ final SyncCrudCounters counters) {
+ if (flowsInTablesSyncBox.isEmpty()) {
+ LOG.trace("no tables in config for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final List<ListenableFuture<RpcResult<AddFlowOutput>>> allResults = new ArrayList<>();
+ final List<ListenableFuture<RpcResult<UpdateFlowOutput>>> allUpdateResults = new ArrayList<>();
+ final CrudCounts flowCrudCounts = counters.getFlowCrudCounts();
+
+ for (Map.Entry<TableKey, ItemSyncBox<Flow>> flowsInTableBoxEntry : flowsInTablesSyncBox.entrySet()) {
+ final TableKey tableKey = flowsInTableBoxEntry.getKey();
+ final ItemSyncBox<Flow> flowSyncBox = flowsInTableBoxEntry.getValue();
+
+ final KeyedInstanceIdentifier<Table, TableKey> tableIdent = nodeIdent.child(Table.class, tableKey);
+
+ for (final Flow flow : flowSyncBox.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent = tableIdent.child(Flow.class, flow.getKey());
+
+ LOG.trace("adding flow {} in table {} - absent on device {} match{}",
+ flow.getId(), tableKey, nodeId, flow.getMatch());
+
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ flowForwarder.add(flowIdent, flow, nodeIdent)));
+ flowCrudCounts.incAdded();
+ }
+
+ for (final ItemSyncBox.ItemUpdateTuple<Flow> flowUpdate : flowSyncBox.getItemsToUpdate()) {
+ final Flow existingFlow = flowUpdate.getOriginal();
+ final Flow updatedFlow = flowUpdate.getUpdated();
+
+ final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent = tableIdent.child(Flow.class, updatedFlow.getKey());
+ LOG.trace("flow {} in table {} - needs update on device {} match{}",
+ updatedFlow.getId(), tableKey, nodeId, updatedFlow.getMatch());
+
+ allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
+ flowForwarder.update(flowIdent, existingFlow, updatedFlow, nodeIdent)));
+ flowCrudCounts.incUpdated();
+ }
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<AddFlowOutput>createRpcResultCondenser("flow adding"));
+
+ final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ Futures.allAsList(allUpdateResults),
+ ReconcileUtil.<UpdateFlowOutput>createRpcResultCondenser("flow updating"));
+
+ final ListenableFuture<RpcResult<Void>> summaryResult = Futures.transform(
+ Futures.allAsList(singleVoidAddResult, singleVoidUpdateResult),
+ ReconcileUtil.<Void>createRpcResultCondenser("flow add/update"));
+
+ return summaryResult;
+
+ /*
+ return Futures.transform(summaryResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ */
+ }
+
+ protected ListenableFuture<RpcResult<Void>> removeRedundantFlows(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final Map<TableKey, ItemSyncBox<Flow>> removalPlan,
+ final SyncCrudCounters counters) {
+ if (removalPlan.isEmpty()) {
+ LOG.trace("no tables in operational for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final List<ListenableFuture<RpcResult<RemoveFlowOutput>>> allResults = new ArrayList<>();
+ final CrudCounts flowCrudCounts = counters.getFlowCrudCounts();
+
+ for (final Map.Entry<TableKey, ItemSyncBox<Flow>> flowsPerTable : removalPlan.entrySet()) {
+ final KeyedInstanceIdentifier<Table, TableKey> tableIdent =
+ nodeIdent.child(Table.class, flowsPerTable.getKey());
+
+ // loop flows on device and check if the are configured
+ for (final Flow flow : flowsPerTable.getValue().getItemsToPush()) {
+ final KeyedInstanceIdentifier<Flow, FlowKey> flowIdent =
+ tableIdent.child(Flow.class, flow.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ flowForwarder.remove(flowIdent, flow, nodeIdent)));
+ flowCrudCounts.incRemoved();
+ }
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults), ReconcileUtil.<RemoveFlowOutput>createRpcResultCondenser("flow remove"));
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+
+ }
+
+ protected ListenableFuture<RpcResult<Void>> removeRedundantMeters(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Meter> meterRemovalPlan,
+ final SyncCrudCounters counters) {
+ if (meterRemovalPlan.isEmpty()) {
+ LOG.trace("no meters on device for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final CrudCounts meterCrudCounts = counters.getMeterCrudCounts();
+
+ final List<ListenableFuture<RpcResult<RemoveMeterOutput>>> allResults = new ArrayList<>();
+ for (Meter meter : meterRemovalPlan.getItemsToPush()) {
+ LOG.trace("removing meter {} - absent in config {}",
+ meter.getMeterId(), nodeId);
+ final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent =
+ nodeIdent.child(Meter.class, meter.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ meterForwarder.remove(meterIdent, meter, nodeIdent)));
+ meterCrudCounts.incRemoved();
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<RemoveMeterOutput>createRpcResultCondenser("meter remove"));
+ return singleVoidResult;
+ /*
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ */
+ }
+
+ ListenableFuture<RpcResult<Void>> removeRedundantGroups(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final List<ItemSyncBox<Group>> groupsRemovalPlan,
+ final SyncCrudCounters counters) {
+ if (groupsRemovalPlan.isEmpty()) {
+ LOG.trace("no groups on device for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final CrudCounts groupCrudCounts = counters.getGroupCrudCounts();
+
+ ListenableFuture<RpcResult<Void>> chainedResult = RpcResultBuilder.<Void>success().buildFuture();
+ try {
+ groupCrudCounts.setRemoved(ReconcileUtil.countTotalPushed(groupsRemovalPlan));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("removing groups: planSteps={}, toRemoveTotal={}",
+ groupsRemovalPlan.size(), groupCrudCounts.getRemoved());
+ }
+ Collections.reverse(groupsRemovalPlan);
+ for (final ItemSyncBox<Group> groupsPortion : groupsRemovalPlan) {
+ chainedResult =
+ Futures.transform(chainedResult, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input)
+ throws Exception {
+ final ListenableFuture<RpcResult<Void>> result;
+ if (input.isSuccessful()) {
+ result = flushRemoveGroupPortionAndBarrier(nodeIdent, groupsPortion);
+ } else {
+ // pass through original unsuccessful rpcResult
+ result = Futures.immediateFuture(input);
+ }
+
+ return result;
+ }
+ });
+ }
+ } catch (IllegalStateException e) {
+ chainedResult = RpcResultBuilder.<Void>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "failed to add missing groups", e)
+ .buildFuture();
+ }
+
+ return chainedResult;
+ }
+
+ private ListenableFuture<RpcResult<Void>> flushRemoveGroupPortionAndBarrier(
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Group> groupsPortion) {
+ List<ListenableFuture<RpcResult<RemoveGroupOutput>>> allResults = new ArrayList<>();
+ for (Group group : groupsPortion.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(groupForwarder.remove(groupIdent, group, nodeIdent)));
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<RemoveGroupOutput>createRpcResultCondenser("group remove"));
+
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ }
+
+ ListenableFuture<RpcResult<Void>> updateTableFeatures(final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final FlowCapableNode flowCapableNodeConfigured) {
+ // CHECK if while pushing the update, updateTableInput can be null to emulate a table add
+ final List<Table> tableList = ReconcileUtil.safeTables(flowCapableNodeConfigured);
+
+ final List<ListenableFuture<RpcResult<UpdateTableOutput>>> allResults = new ArrayList<>();
+ for (Table table : tableList) {
+ TableKey tableKey = table.getKey();
+ KeyedInstanceIdentifier<TableFeatures, TableFeaturesKey> tableFeaturesII = nodeIdent
+ .child(TableFeatures.class, new TableFeaturesKey(tableKey.getId()));
+ List<TableFeatures> tableFeatures = flowCapableNodeConfigured.getTableFeatures();
+ if (tableFeatures != null) {
+ for (TableFeatures tableFeaturesItem : tableFeatures) {
+ // TODO uncomment java.lang.NullPointerException
+ // at
+ // org.opendaylight.openflowjava.protocol.impl.serialization.match.AbstractOxmMatchEntrySerializer.serializeHeader(AbstractOxmMatchEntrySerializer.java:31
+ // allResults.add(JdkFutureAdapters.listenInPoolThread(
+ // tableForwarder.update(tableFeaturesII, null, tableFeaturesItem, nodeIdent)));
+ }
+ }
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidResult = Futures.transform(
+ Futures.allAsList(allResults),
+ ReconcileUtil.<UpdateTableOutput>createRpcResultCondenser("table update"));
+
+ return Futures.transform(singleVoidResult,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ }
+
+ private ListenableFuture<RpcResult<Void>> flushAddGroupPortionAndBarrier(
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Group> groupsPortion) {
+ final List<ListenableFuture<RpcResult<AddGroupOutput>>> allResults = new ArrayList<>();
+ final List<ListenableFuture<RpcResult<UpdateGroupOutput>>> allUpdateResults = new ArrayList<>();
+
+ for (Group group : groupsPortion.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
+ allResults.add(JdkFutureAdapters.listenInPoolThread(groupForwarder.add(groupIdent, group, nodeIdent)));
+
+ }
+
+ for (ItemSyncBox.ItemUpdateTuple<Group> groupTuple : groupsPortion.getItemsToUpdate()) {
+ final Group existingGroup = groupTuple.getOriginal();
+ final Group group = groupTuple.getUpdated();
+
+ final KeyedInstanceIdentifier<Group, GroupKey> groupIdent = nodeIdent.child(Group.class, group.getKey());
+ allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
+ groupForwarder.update(groupIdent, existingGroup, group, nodeIdent)));
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
+ Futures.allAsList(allResults), ReconcileUtil.<AddGroupOutput>createRpcResultCondenser("group add"));
+
+ final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ Futures.allAsList(allUpdateResults),
+ ReconcileUtil.<UpdateGroupOutput>createRpcResultCondenser("group update"));
+
+ final ListenableFuture<RpcResult<Void>> summaryResult = Futures.transform(
+ Futures.allAsList(singleVoidAddResult, singleVoidUpdateResult),
+ ReconcileUtil.<Void>createRpcResultCondenser("group add/update"));
+
+
+ return Futures.transform(summaryResult,
+ ReconcileUtil.chainBarrierFlush(
+ PathUtil.digNodePath(nodeIdent), transactionService));
+ }
+
+ protected ListenableFuture<RpcResult<Void>> addMissingMeters(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final ItemSyncBox<Meter> syncBox,
+ final SyncCrudCounters counters) {
+ if (syncBox.isEmpty()) {
+ LOG.trace("no meters configured for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ final CrudCounts meterCrudCounts = counters.getMeterCrudCounts();
+
+ final List<ListenableFuture<RpcResult<AddMeterOutput>>> allResults = new ArrayList<>();
+ final List<ListenableFuture<RpcResult<UpdateMeterOutput>>> allUpdateResults = new ArrayList<>();
+ for (Meter meter : syncBox.getItemsToPush()) {
+ final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent = nodeIdent.child(Meter.class, meter.getKey());
+ LOG.debug("adding meter {} - absent on device {}",
+ meter.getMeterId(), nodeId);
+ allResults.add(JdkFutureAdapters.listenInPoolThread(
+ meterForwarder.add(meterIdent, meter, nodeIdent)));
+ meterCrudCounts.incAdded();
+ }
+
+ for (ItemSyncBox.ItemUpdateTuple<Meter> meterTuple : syncBox.getItemsToUpdate()) {
+ final Meter existingMeter = meterTuple.getOriginal();
+ final Meter updated = meterTuple.getUpdated();
+ final KeyedInstanceIdentifier<Meter, MeterKey> meterIdent = nodeIdent.child(Meter.class, updated.getKey());
+ LOG.trace("meter {} - needs update on device {}", updated.getMeterId(), nodeId);
+ allUpdateResults.add(JdkFutureAdapters.listenInPoolThread(
+ meterForwarder.update(meterIdent, existingMeter, updated, nodeIdent)));
+ meterCrudCounts.incUpdated();
+ }
+
+ final ListenableFuture<RpcResult<Void>> singleVoidAddResult = Futures.transform(
+ Futures.allAsList(allResults), ReconcileUtil.<AddMeterOutput>createRpcResultCondenser("meter add"));
+
+ final ListenableFuture<RpcResult<Void>> singleVoidUpdateResult = Futures.transform(
+ Futures.allAsList(allUpdateResults),
+ ReconcileUtil.<UpdateMeterOutput>createRpcResultCondenser("meter update"));
+
+ final ListenableFuture<RpcResult<Void>> summaryResults = Futures.transform(
+ Futures.allAsList(singleVoidUpdateResult, singleVoidAddResult),
+ ReconcileUtil.<Void>createRpcResultCondenser("meter add/update"));
+
+ return summaryResults;
+
+ /*
+ return Futures.transform(summaryResults,
+ ReconcileUtil.chainBarrierFlush(PathUtil.digNodePath(nodeIdent), transactionService));
+ */
+ }
+
+ protected ListenableFuture<RpcResult<Void>> addMissingGroups(final NodeId nodeId,
+ final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final List<ItemSyncBox<Group>> groupsAddPlan,
+ final SyncCrudCounters counters) {
+ if (groupsAddPlan.isEmpty()) {
+ LOG.trace("no groups configured for node: {} -> SKIPPING", nodeId.getValue());
+ return RpcResultBuilder.<Void>success().buildFuture();
+ }
+
+ ListenableFuture<RpcResult<Void>> chainedResult;
+ try {
+ if (!groupsAddPlan.isEmpty()) {
+ final CrudCounts groupCrudCounts = counters.getGroupCrudCounts();
+ groupCrudCounts.setAdded(ReconcileUtil.countTotalPushed(groupsAddPlan));
+ groupCrudCounts.setUpdated(ReconcileUtil.countTotalUpdated(groupsAddPlan));
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("adding groups: planSteps={}, toAddTotal={}, toUpdateTotal={}",
+ groupsAddPlan.size(),
+ groupCrudCounts.getAdded(),
+ groupCrudCounts.getUpdated());
+ }
+
+ chainedResult = flushAddGroupPortionAndBarrier(nodeIdent, groupsAddPlan.get(0));
+ for (final ItemSyncBox<Group> groupsPortion : Iterables.skip(groupsAddPlan, 1)) {
+ chainedResult =
+ Futures.transform(chainedResult, new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input)
+ throws Exception {
+ final ListenableFuture<RpcResult<Void>> result;
+ if (input.isSuccessful()) {
+ result = flushAddGroupPortionAndBarrier(nodeIdent, groupsPortion);
+ } else {
+ // pass through original unsuccessful rpcResult
+ result = Futures.immediateFuture(input);
+ }
+
+ return result;
+ }
+ });
+ }
+ } else {
+ chainedResult = RpcResultBuilder.<Void>success().buildFuture();
+ }
+ } catch (IllegalStateException e) {
+ chainedResult = RpcResultBuilder.<Void>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "failed to add missing groups", e)
+ .buildFuture();
+ }
+
+ return chainedResult;
+ }
+
+
+ public SyncPlanPushStrategyIncrementalImpl setFlowForwarder(final FlowForwarder flowForwarder) {
+ this.flowForwarder = flowForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setTableForwarder(final TableForwarder tableForwarder) {
+ this.tableForwarder = tableForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setMeterForwarder(final MeterForwarder meterForwarder) {
+ this.meterForwarder = meterForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setGroupForwarder(final GroupForwarder groupForwarder) {
+ this.groupForwarder = groupForwarder;
+ return this;
+ }
+
+ public SyncPlanPushStrategyIncrementalImpl setTransactionService(final FlowCapableTransactionService transactionService) {
+ this.transactionService = transactionService;
+ return this;
+ }
+
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl.strategy;
+
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Wraps all the required inputs (diffs) for synchronization strategy execution.
+ */
+public class SynchronizationDiffInput {
+
+ private final InstanceIdentifier<FlowCapableNode> nodeIdent;
+ final List<ItemSyncBox<Group>> groupsToAddOrUpdate;
+ final ItemSyncBox<Meter> metersToAddOrUpdate;
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate;
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToRemove;
+ final ItemSyncBox<Meter> metersToRemove;
+ final List<ItemSyncBox<Group>> groupsToRemove;
+
+ public SynchronizationDiffInput(final InstanceIdentifier<FlowCapableNode> nodeIdent,
+ final List<ItemSyncBox<Group>> groupsToAddOrUpdate,
+ final ItemSyncBox<Meter> metersToAddOrUpdate,
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate,
+ final Map<TableKey, ItemSyncBox<Flow>> flowsToRemove,
+ final ItemSyncBox<Meter> metersToRemove,
+ final List<ItemSyncBox<Group>> groupsToRemove) {
+ this.nodeIdent = nodeIdent;
+ this.groupsToAddOrUpdate = groupsToAddOrUpdate;
+ this.metersToAddOrUpdate = metersToAddOrUpdate;
+ this.flowsToAddOrUpdate = flowsToAddOrUpdate;
+ this.flowsToRemove = flowsToRemove;
+ this.metersToRemove = metersToRemove;
+ this.groupsToRemove = groupsToRemove;
+ }
+
+ public InstanceIdentifier<FlowCapableNode> getNodeIdent() {
+ return nodeIdent;
+ }
+
+ public List<ItemSyncBox<Group>> getGroupsToAddOrUpdate() {
+ return groupsToAddOrUpdate;
+ }
+
+ public ItemSyncBox<Meter> getMetersToAddOrUpdate() {
+ return metersToAddOrUpdate;
+ }
+
+ public Map<TableKey, ItemSyncBox<Flow>> getFlowsToAddOrUpdate() {
+ return flowsToAddOrUpdate;
+ }
+
+ public Map<TableKey, ItemSyncBox<Flow>> getFlowsToRemove() {
+ return flowsToRemove;
+ }
+
+ public ItemSyncBox<Meter> getMetersToRemove() {
+ return metersToRemove;
+ }
+
+ public List<ItemSyncBox<Group>> getGroupsToRemove() {
+ return groupsToRemove;
+ }
+}
--- /dev/null
+/**\r
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+\r
+package org.opendaylight.openflowplugin.applications.frsync.markandsweep;\r
+\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;\r
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;\r
+\r
+/**\r
+ * Identifier of {@link Flow} on device. Switch does not know about flow-id but,\r
+ * it uses combination of these unique fields: table-id, priority, match.\r
+ */\r
+public class SwitchFlowId {\r
+\r
+ private final Short tableId;\r
+\r
+ private final Integer priority;\r
+\r
+ private final Match match;\r
+\r
+ public SwitchFlowId(Flow flow) {\r
+ this.tableId = flow.getTableId();\r
+ this.priority = flow.getPriority();\r
+ this.match = flow.getMatch();\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ final int prime = 31;\r
+ int result = 1;\r
+ result = prime * result + ((match == null) ? 0 : match.hashCode());\r
+ result = prime * result + ((priority == null) ? 0 : priority.hashCode());\r
+ result = prime * result + ((tableId == null) ? 0 : tableId.hashCode());\r
+ return result;\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (this == obj)\r
+ return true;\r
+ if (obj == null)\r
+ return false;\r
+ if (getClass() != obj.getClass())\r
+ return false;\r
+ SwitchFlowId other = (SwitchFlowId) obj;\r
+ if (match == null) {\r
+ if (other.match != null)\r
+ return false;\r
+ } else if (!match.equals(other.match))\r
+ return false;\r
+ if (priority == null) {\r
+ if (other.priority != null)\r
+ return false;\r
+ } else if (!priority.equals(other.priority))\r
+ return false;\r
+ if (tableId == null) {\r
+ if (other.tableId != null)\r
+ return false;\r
+ } else if (!tableId.equals(other.tableId))\r
+ return false;\r
+ return true;\r
+ }\r
+}\r
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+/**
+ * General placeholder for add/update/remove counts.
+ */
+public class CrudCounts {
+ private int added;
+ private int updated;
+ private int removed;
+
+ public int getAdded() {
+ return added;
+ }
+
+ public void setAdded(final int added) {
+ this.added = added;
+ }
+
+ public int getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(final int updated) {
+ this.updated = updated;
+ }
+
+ public int getRemoved() {
+ return removed;
+ }
+
+ public void setRemoved(final int removed) {
+ this.removed = removed;
+ }
+
+ public void incAdded() {
+ added++;
+ }
+
+ public void incUpdated() {
+ updated++;
+ }
+
+ public void incRemoved() {
+ removed++;
+ }
+
+ public void decAdded() {
+ added--;
+ }
+
+ public void decUpdated() {
+ updated--;
+ }
+
+ public void decRemoved() {
+ removed--;
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opendaylight.openflowplugin.applications.frsync.markandsweep.SwitchFlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helpers for flow lookups in {@link FlowCapableNode}.
+ */
+public final class FlowCapableNodeLookups {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlowCapableNodeLookups.class);
+
+ private FlowCapableNodeLookups() {
+ throw new IllegalAccessError("non instantiable util class");
+ }
+
+ @Nonnull
+ public static Map<Short, Table> wrapTablesToMap(@Nullable final List<Table> tables) {
+ final Map<Short, Table> tableMap;
+
+ if (tables == null) {
+ tableMap = Collections.emptyMap();
+ } else {
+ LOG.trace("tables found: {}", tables.size());
+ tableMap = new HashMap<>();
+ for (Table table : tables) {
+ tableMap.put(table.getId(), table);
+ }
+ }
+
+ return tableMap;
+ }
+
+ @Nonnull
+ public static Map<SwitchFlowId, Flow> wrapFlowsToMap(@Nullable final List<Flow> flows) {
+ final Map<SwitchFlowId, Flow> flowMap;
+
+ if (flows == null) {
+ flowMap = Collections.emptyMap();
+ } else {
+ LOG.trace("flows found: {}", flows.size());
+ flowMap = new HashMap<>();
+ for (Flow flow : flows) {
+ flowMap.put(new SwitchFlowId(flow), flow);
+ }
+ }
+
+ return flowMap;
+ }
+
+ public static Flow flowMapLookupExisting(Flow flow, Map<SwitchFlowId, Flow> flowConfigMap) {
+ return flowConfigMap.get(new SwitchFlowId(flow));
+ }
+
+ @Nonnull
+ public static Map<MeterId, Meter> wrapMetersToMap(@Nullable final List<Meter> meters) {
+ final Map<MeterId, Meter> meterMap;
+
+ if (meters == null) {
+ meterMap = Collections.emptyMap();
+ } else {
+ LOG.trace("meters found: {}", meters.size());
+ meterMap = new HashMap<>();
+ for (Meter meter : meters) {
+ meterMap.put(meter.getMeterId(), meter);
+ }
+ }
+
+ return meterMap;
+ }
+
+ @Nonnull
+ public static Map<Long, Group> wrapGroupsToMap(@Nullable final List<Group> groups) {
+ final Map<Long, Group> groupMap;
+
+ if (groups == null) {
+ groupMap = Collections.emptyMap();
+ } else {
+ LOG.trace("groups found: {}", groups.size());
+ groupMap = new HashMap<>();
+ for (Group group : groups) {
+ groupMap.put(group.getGroupId().getValue(), group);
+ }
+ }
+
+ return groupMap;
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.FutureCallback;
+import java.util.Arrays;
+import java.util.Collection;
+import javax.annotation.Nullable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util methods for {@link com.google.common.util.concurrent.ListenableFuture} chaining.
+ */
+public class FxChainUtil {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FxChainUtil.class);
+
+
+ public static FutureCallback<RpcResult<Void>> logResultCallback(final NodeId nodeId, final String prefix) {
+ return new FutureCallback<RpcResult<Void>>() {
+ @Override
+ public void onSuccess(@Nullable final RpcResult<Void> result) {
+ if (result != null) {
+ if (result.isSuccessful()) {
+ LOG.debug(prefix + " finished successfully: {}", nodeId.getValue());
+ } else {
+ final Collection<RpcError> errors = MoreObjects.firstNonNull(result.getErrors(), ImmutableList.<RpcError>of());
+ LOG.debug(prefix + " failed: {} -> {}", nodeId.getValue(), Arrays.toString(errors.toArray()));
+ }
+ } else {
+ LOG.debug(prefix + "reconciliation failed: {} -> null result", nodeId.getValue());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable t) {
+ LOG.debug(prefix + "reconciliation failed seriously: {}", nodeId.getValue(), t);
+ }
+ };
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Holder for items to be pushed to device.
+ * Contains two sets of groups -set of items to be pushed and set of tuples for update.
+ */
+public class ItemSyncBox<I> {
+
+ private Set<I> itemsToPush = new LinkedHashSet<>();
+ private Set<ItemUpdateTuple<I>> itemsToUpdate = new LinkedHashSet<>();
+
+ public Set<I> getItemsToPush() {
+ return itemsToPush;
+ }
+
+ public Set<ItemUpdateTuple<I>> getItemsToUpdate() {
+ return itemsToUpdate;
+ }
+
+ public boolean isEmpty() {
+ return itemsToPush.isEmpty() && itemsToUpdate.isEmpty();
+ }
+
+ /**
+ * Tuple holder for original and updated item
+ *
+ * @param <I> basic type
+ */
+ public static final class ItemUpdateTuple<I> {
+ private final I original;
+ private final I updated;
+
+ public ItemUpdateTuple(I original, I updated) {
+ this.original = original;
+ this.updated = updated;
+ }
+
+ public I getOriginal() {
+ return original;
+ }
+
+ public I getUpdated() {
+ return updated;
+ }
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Basic {@link InstanceIdentifier} related tools.
+ */
+public class PathUtil {
+ public static NodeId digNodeId(final InstanceIdentifier<?> nodeIdent) {
+ return nodeIdent.firstKeyOf(Node.class, NodeKey.class).getId();
+ }
+
+ public static InstanceIdentifier<Node> digNodePath(final InstanceIdentifier<FlowCapableNode> nodeIdent) {
+ return nodeIdent.firstIdentifierOf(Node.class);
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.JdkFutureAdapters;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.opendaylight.openflowplugin.applications.frsync.markandsweep.SwitchFlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util methods for group reconcil task (future chaining, transforms).
+ */
+public class ReconcileUtil {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ReconcileUtil.class);
+
+ /**
+ * @param previousItemAction description for case when the triggering future contains failure
+ * @param <D> type of rpc output (gathered in list)
+ * @return single rpc result of type Void honoring all partial rpc results
+ */
+ public static <D> Function<List<RpcResult<D>>, RpcResult<Void>> createRpcResultCondenser(final String previousItemAction) {
+ return new Function<List<RpcResult<D>>, RpcResult<Void>>() {
+ @Nullable
+ @Override
+ public RpcResult<Void> apply(@Nullable final List<RpcResult<D>> input) {
+ final RpcResultBuilder<Void> resultSink;
+ if (input != null) {
+ List<RpcError> errors = new ArrayList<>();
+ for (RpcResult<D> rpcResult : input) {
+ if (!rpcResult.isSuccessful()) {
+ errors.addAll(rpcResult.getErrors());
+ }
+ }
+ if (errors.isEmpty()) {
+ resultSink = RpcResultBuilder.success();
+ } else {
+ resultSink = RpcResultBuilder.<Void>failed().withRpcErrors(errors);
+ }
+ } else {
+ resultSink = RpcResultBuilder.<Void>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "previous " + previousItemAction + " failed");
+
+ }
+
+ return resultSink.build();
+ }
+ };
+ }
+
+ /**
+ * @param actionDescription description for case when the triggering future contains failure
+ * @param <D> type of rpc output (gathered in list)
+ * @return single rpc result of type Void honoring all partial rpc results
+ */
+ public static <D> Function<RpcResult<D>, RpcResult<Void>> createRpcResultToVoidFunction(final String actionDescription) {
+ return new Function<RpcResult<D>, RpcResult<Void>>() {
+ @Nullable
+ @Override
+ public RpcResult<Void> apply(@Nullable final RpcResult<D> input) {
+ final RpcResultBuilder<Void> resultSink;
+ if (input != null) {
+ List<RpcError> errors = new ArrayList<>();
+ if (!input.isSuccessful()) {
+ errors.addAll(input.getErrors());
+ resultSink = RpcResultBuilder.<Void>failed().withRpcErrors(errors);
+ } else {
+ resultSink = RpcResultBuilder.success();
+ }
+ } else {
+ resultSink = RpcResultBuilder.<Void>failed()
+ .withError(RpcError.ErrorType.APPLICATION, "action of " + actionDescription + " failed");
+
+ }
+
+ return resultSink.build();
+ }
+ };
+ }
+
+ /**
+ * @param nodeIdent flow capable node path - target device for routed rpc
+ * @param flowCapableTransactionService barrier rpc service
+ * @return async barrier result
+ */
+ public static AsyncFunction<RpcResult<Void>, RpcResult<Void>> chainBarrierFlush(
+ final InstanceIdentifier<Node> nodeIdent,
+ final FlowCapableTransactionService flowCapableTransactionService) {
+ return new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
+ @Override
+ public ListenableFuture<RpcResult<Void>> apply(final RpcResult<Void> input) throws Exception {
+ final SendBarrierInput barrierInput = new SendBarrierInputBuilder()
+ .setNode(new NodeRef(nodeIdent))
+ .build();
+ return JdkFutureAdapters.listenInPoolThread(flowCapableTransactionService.sendBarrier(barrierInput));
+ }
+ };
+ }
+
+ /**
+ * @param nodeId target node
+ * @param installedGroupsArg groups resent on device
+ * @param pendingGroups groups configured for device
+ * @return list of safe synchronization steps with updates
+ */
+ public static List<ItemSyncBox<Group>> resolveAndDivideGroupDiffs(final NodeId nodeId,
+ final Map<Long, Group> installedGroupsArg,
+ final Collection<Group> pendingGroups) {
+ return resolveAndDivideGroupDiffs(nodeId, installedGroupsArg, pendingGroups, true);
+ }
+
+ /**
+ * @param nodeId target node
+ * @param installedGroupsArg groups resent on device
+ * @param pendingGroups groups configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return list of safe synchronization steps
+ */
+ public static List<ItemSyncBox<Group>> resolveAndDivideGroupDiffs(final NodeId nodeId,
+ final Map<Long, Group> installedGroupsArg,
+ final Collection<Group> pendingGroups,
+ final boolean gatherUpdates) {
+
+ final Map<Long, Group> installedGroups = new HashMap<>(installedGroupsArg);
+ final List<ItemSyncBox<Group>> plan = new ArrayList<>();
+
+ while (!Iterables.isEmpty(pendingGroups)) {
+ final ItemSyncBox<Group> stepPlan = new ItemSyncBox<>();
+ final Iterator<Group> iterator = pendingGroups.iterator();
+ final Map<Long, Group> installIncrement = new HashMap<>();
+
+ while (iterator.hasNext()) {
+ final Group group = iterator.next();
+
+ final Group existingGroup = installedGroups.get(group.getGroupId().getValue());
+ if (existingGroup != null) {
+ if (!gatherUpdates) {
+ iterator.remove();
+ } else {
+ // check buckets and eventually update
+ if (group.equals(existingGroup)) {
+ iterator.remove();
+ } else {
+ if (checkGroupPrecondition(installedGroups.keySet(), group)) {
+ iterator.remove();
+ LOG.trace("Group {} on device {} differs - planned for update", group.getGroupId(), nodeId);
+ stepPlan.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(existingGroup, group));
+ }
+ }
+ }
+ } else if (checkGroupPrecondition(installedGroups.keySet(), group)) {
+ iterator.remove();
+ installIncrement.put(group.getGroupId().getValue(), group);
+ stepPlan.getItemsToPush().add(group);
+ }
+ }
+
+ if (!stepPlan.isEmpty()) {
+ // atomic update of installed flows in order to keep plan portions clean of local group dependencies
+ installedGroups.putAll(installIncrement);
+ plan.add(stepPlan);
+ } else if (!pendingGroups.isEmpty()) {
+ LOG.warn("Failed to resolve and divide groups into preconditions-match based ordered plan: {}, " +
+ "resolving stuck at level {}", nodeId.getValue(), plan.size());
+ throw new IllegalStateException("Failed to resolve and divide groups when matching preconditions");
+ }
+ }
+
+ return plan;
+ }
+
+ public static boolean checkGroupPrecondition(final Set<Long> installedGroupIds, final Group pendingGroup) {
+ boolean okToInstall = true;
+ // check each bucket in the pending group
+ for (Bucket bucket : pendingGroup.getBuckets().getBucket()) {
+ for (Action action : bucket.getAction()) {
+ // if the output action is a group
+ if (GroupActionCase.class.equals(action.getAction().getImplementedInterface())) {
+ Long groupId = ((GroupActionCase) (action.getAction())).getGroupAction().getGroupId();
+ // see if that output group is installed
+ if (!installedGroupIds.contains(groupId)) {
+ // if not installed, we have missing dependencies and cannot install this pending group
+ okToInstall = false;
+ break;
+ }
+ }
+ }
+ if (!okToInstall) {
+ break;
+ }
+ }
+ return okToInstall;
+ }
+
+ public static <E> int countTotalPushed(final Iterable<ItemSyncBox<E>> groupsAddPlan) {
+ int count = 0;
+ for (ItemSyncBox<E> groupItemSyncBox : groupsAddPlan) {
+ count += groupItemSyncBox.getItemsToPush().size();
+ }
+ return count;
+ }
+
+ public static <E> int countTotalUpdated(final Iterable<ItemSyncBox<E>> groupsAddPlan) {
+ int count = 0;
+ for (ItemSyncBox<E> groupItemSyncBox : groupsAddPlan) {
+ count += groupItemSyncBox.getItemsToUpdate().size();
+ }
+ return count;
+ }
+
+ /**
+ * @param nodeId target node
+ * @param meterOperationalMap meters present on device
+ * @param metersConfigured meters configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return synchronization box
+ */
+ public static ItemSyncBox<Meter> resolveMeterDiffs(final NodeId nodeId,
+ final Map<MeterId, Meter> meterOperationalMap,
+ final List<Meter> metersConfigured,
+ final boolean gatherUpdates) {
+ LOG.trace("resolving meters for {}", nodeId);
+ final ItemSyncBox<Meter> syncBox = new ItemSyncBox<>();
+ for (Meter meter : metersConfigured) {
+ final Meter existingMeter = meterOperationalMap.get(meter.getMeterId());
+ if (existingMeter == null) {
+ syncBox.getItemsToPush().add(meter);
+ } else {
+ // compare content and eventually update
+ if (gatherUpdates && !meter.equals(existingMeter)) {
+ syncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(existingMeter, meter));
+ }
+ }
+ }
+ return syncBox;
+ }
+
+ /**
+ * @param flowsConfigured flows resent on device
+ * @param flowOperationalMap flows configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return list of safe synchronization steps
+ */
+ @VisibleForTesting
+ static ItemSyncBox<Flow> resolveFlowDiffsInTable(final List<Flow> flowsConfigured,
+ final Map<SwitchFlowId, Flow> flowOperationalMap,
+ final boolean gatherUpdates) {
+ final ItemSyncBox<Flow> flowsSyncBox = new ItemSyncBox<>();
+ // loop configured flows and check if already present on device
+ for (final Flow flow : flowsConfigured) {
+ final Flow existingFlow = FlowCapableNodeLookups.flowMapLookupExisting(flow, flowOperationalMap);
+
+ if (existingFlow == null) {
+ flowsSyncBox.getItemsToPush().add(flow);
+ } else {
+ // check instructions and eventually update
+ if (gatherUpdates && !Objects.equals(flow.getInstructions(), existingFlow.getInstructions())) {
+ flowsSyncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(existingFlow, flow));
+ }
+ }
+ }
+ return flowsSyncBox;
+ }
+
+ /**
+ * @param nodeId target node
+ * @param tableOperationalMap flow-tables resent on device
+ * @param tablesConfigured flow-tables configured for device
+ * @param gatherUpdates check content of pending item if present on device (and create update task eventually)
+ * @return map : key={@link TableKey}, value={@link ItemSyncBox} of safe synchronization steps
+ */
+ public static Map<TableKey, ItemSyncBox<Flow>> resolveFlowDiffsInAllTables(final NodeId nodeId,
+ final Map<Short, Table> tableOperationalMap,
+ final List<Table> tablesConfigured,
+ final boolean gatherUpdates) {
+ LOG.trace("resolving flows in tables for {}", nodeId);
+ final Map<TableKey, ItemSyncBox<Flow>> tableFlowSyncBoxes = new HashMap<>();
+ for (final Table tableConfigured : tablesConfigured) {
+ final List<Flow> flowsConfigured = tableConfigured.getFlow();
+ if (flowsConfigured == null || flowsConfigured.isEmpty()) {
+ continue;
+ }
+
+ // lookup table (on device)
+ final Table tableOperational = tableOperationalMap.get(tableConfigured.getId());
+ // wrap existing (on device) flows in current table into map
+ final Map<SwitchFlowId, Flow> flowOperationalMap = FlowCapableNodeLookups.wrapFlowsToMap(
+ tableOperational != null
+ ? tableOperational.getFlow()
+ : null);
+
+
+ final ItemSyncBox<Flow> flowsSyncBox = resolveFlowDiffsInTable(
+ flowsConfigured, flowOperationalMap, gatherUpdates);
+ if (!flowsSyncBox.isEmpty()) {
+ tableFlowSyncBoxes.put(tableConfigured.getKey(), flowsSyncBox);
+ }
+ }
+ return tableFlowSyncBoxes;
+ }
+
+ public static List<Group> safeGroups(FlowCapableNode node) {
+ if (node == null) {
+ return Collections.emptyList();
+ }
+
+ return MoreObjects.firstNonNull(node.getGroup(), ImmutableList.<Group>of());
+ }
+
+ public static List<Table> safeTables(FlowCapableNode node) {
+ if (node == null) {
+ return Collections.emptyList();
+ }
+
+ return MoreObjects.firstNonNull(node.getTable(), ImmutableList.<Table>of());
+ }
+
+ public static List<Meter> safeMeters(FlowCapableNode node) {
+ if (node == null) {
+ return Collections.emptyList();
+ }
+
+ return MoreObjects.firstNonNull(node.getMeter(), ImmutableList.<Meter>of());
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import java.util.concurrent.Semaphore;
+
+import javax.annotation.Nonnull;
+
+import org.opendaylight.openflowplugin.applications.frsync.SemaphoreKeeper;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+/**
+ * Key-based semaphore provider.
+ */
+public class SemaphoreKeeperGuavaImpl<K> implements SemaphoreKeeper<K> {
+
+ private LoadingCache<K, Semaphore> semaphoreCache;
+
+ public SemaphoreKeeperGuavaImpl(final int permits, final boolean fair) {
+ semaphoreCache = CacheBuilder.newBuilder()
+ .concurrencyLevel(1)
+ .weakValues()
+ .build(new CacheLoader<K, Semaphore>() {
+ @Override
+ public Semaphore load(final K key) throws Exception {
+ return new Semaphore(permits, fair) {
+ private static final long serialVersionUID = 1L;
+ };
+ }
+ });
+ }
+
+ @Override
+ public Semaphore summonGuard(final @Nonnull K key) {
+ return semaphoreCache.getUnchecked(key);
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " size:" + (semaphoreCache == null ? null : semaphoreCache.size()) + " " + semaphoreCache;
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+/**
+ * One-shot (per sync) placeholder for counts of added/updated/removed flows/groups/meters.
+ */
+public class SyncCrudCounters {
+
+ private final CrudCounts flowCrudCounts;
+ private final CrudCounts groupCrudCounts;
+ private final CrudCounts meterCrudCounts;
+ private long startNano;
+
+ public SyncCrudCounters() {
+ flowCrudCounts = new CrudCounts();
+ groupCrudCounts = new CrudCounts();
+ meterCrudCounts = new CrudCounts();
+ }
+
+ public CrudCounts getFlowCrudCounts() {
+ return flowCrudCounts;
+ }
+
+ public CrudCounts getGroupCrudCounts() {
+ return groupCrudCounts;
+ }
+
+ public CrudCounts getMeterCrudCounts() {
+ return meterCrudCounts;
+ }
+
+
+ public long getStartNano() {
+ return startNano;
+ }
+
+ public void setStartNano(final long startNano) {
+ this.startNano = startNano;
+ }
+
+ public void resetAll() {
+ getGroupCrudCounts().setUpdated(0);
+ getGroupCrudCounts().setAdded(0);
+ getGroupCrudCounts().setRemoved(0);
+
+ getFlowCrudCounts().setUpdated(0);
+ getFlowCrudCounts().setAdded(0);
+ getFlowCrudCounts().setRemoved(0);
+
+ getMeterCrudCounts().setUpdated(0);
+ getMeterCrudCounts().setAdded(0);
+ getMeterCrudCounts().setRemoved(0);
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<features name="openflowplugin-applications-frs-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://karaf.apache.org/xmlns/features/v1.2.0 http://karaf.apache.org/xmlns/features/v1.2.0">
+ <repository>mvn:org.opendaylight.controller/features-mdsal/${mdsal.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.netconf/features-netconf-connector/${netconf.version}/xml/features</repository>
+ <repository>mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features</repository>
+
+ <!-- DEV COMMAND SUPPORT -->
+ <feature name='odl-openflowplugin-application-frsync' description="OpenDaylight :: Openflow Plugin :: FR-Synchronization" version='${project.version}'>
+ <feature version='${netconf.version}'>odl-netconf-connector-all</feature>
+ <feature version='${mdsal.version}'>odl-netconf-mdsal</feature>
+ <bundle>mvn:org.opendaylight.openflowplugin.applications/forwardingrules-sync/${project.version}</bundle>
+ <configfile finalname="etc/opendaylight/karaf/69-forwardingrules-sync.xml">mvn:org.opendaylight.openflowplugin.applications/forwardingrules-sync/${project.version}/xml/config</configfile>
+ </feature>
+
+</features>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ odl:use-default-for-reference-types="true">
+
+ <reference id="broker" interface="org.opendaylight.controller.sal.binding.api.BindingAwareBroker"/>
+ <reference id="dataBroker" interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/>
+ <reference id="rpcRegistry" interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"/>
+
+ <bean id="frSync" class="org.opendaylight.openflowplugin.applications.frsync.impl.ForwardingRulesSyncProvider"
+ destroy-method="close">
+ <argument ref="broker"/>
+ <argument ref="dataBroker"/>
+ <argument ref="rpcRegistry"/>
+ </bean>
+
+</blueprint>
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.BandId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.band.type.DropBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.MeterBandHeadersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.meter.band.headers.MeterBandHeaderBuilder;
+
+/**
+ * Provides create methods for dataObjects involved in
+ * {@link org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener} by inventory.
+ */
+public class DSInputFactory {
+ public static Group createGroup(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.<Bucket>emptyList())
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ public static Group createGroupWithAction(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(new BucketBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new OutputActionCaseBuilder()
+ .setOutputAction(new OutputActionBuilder()
+ .setOutputNodeConnector(new Uri("ut-port-1"))
+ .build())
+ .build())
+ .build()))
+ .build()))
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ public static Flow createFlow(final String flowIdValue, final int priority) {
+ return new FlowBuilder()
+ .setId(new FlowId(flowIdValue))
+ .setPriority(priority)
+ .setTableId((short) 42)
+ .setMatch(new MatchBuilder().build())
+ .build();
+ }
+
+ public static Flow createFlowWithInstruction(final String flowIdValue, final int priority) {
+ return new FlowBuilder()
+ .setId(new FlowId(flowIdValue))
+ .setPriority(priority)
+ .setTableId((short) 42)
+ .setMatch(new MatchBuilder().build())
+ .setInstructions(new InstructionsBuilder()
+ .setInstruction(Collections.singletonList(new InstructionBuilder()
+ .setInstruction(new ApplyActionsCaseBuilder()
+ .setApplyActions(new ApplyActionsBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new OutputActionCaseBuilder()
+ .setOutputAction(new OutputActionBuilder()
+ .setOutputNodeConnector(new Uri("ut-port-1"))
+ .build())
+ .build())
+ .build()))
+ .build())
+ .build())
+ .build()))
+ .build())
+ .build();
+ }
+
+ public static Meter createMeter(final Long meterIdValue) {
+ return new MeterBuilder()
+ .setMeterId(new MeterId(meterIdValue))
+ .build();
+ }
+
+ public static Meter createMeterWithBody(final Long meterIdValue) {
+ return new MeterBuilder()
+ .setMeterId(new MeterId(meterIdValue))
+ .setMeterBandHeaders(new MeterBandHeadersBuilder()
+ .setMeterBandHeader(Collections.singletonList(new MeterBandHeaderBuilder()
+ .setBandId(new BandId(42L))
+ .setBandType(new DropBuilder()
+ .setDropRate(43L)
+ .build())
+ .build()))
+ .build())
+ .build();
+ }
+
+ public static Group createGroupWithPreconditions(final long groupIdValue, final long... requiredId) {
+ final List<Action> actionBag = new ArrayList<>();
+ for (long groupIdPrecondition : requiredId) {
+ final GroupAction groupAction = new GroupActionBuilder()
+ .setGroupId(groupIdPrecondition)
+ .build();
+ final GroupActionCase groupActionCase = new GroupActionCaseBuilder()
+ .setGroupAction(groupAction)
+ .build();
+ final Action action = new ActionBuilder()
+ .setAction(groupActionCase)
+ .build();
+ actionBag.add(action);
+ }
+
+ final Bucket bucket = new BucketBuilder()
+ .setAction(actionBag)
+ .build();
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(bucket))
+ .build();
+
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.DropActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link FlowForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FlowForwarderTest {
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final TableKey tableKey = new TableKey((short) 2);
+ private final FlowId flowId = new FlowId("test_Flow");
+ private final FlowKey flowKey = new FlowKey(flowId);
+ private final Match emptyMatch = new MatchBuilder().build();
+ private final Flow flow = new FlowBuilder()
+ .setId(flowId)
+ .setTableId((short) 2)
+ .setMatch(emptyMatch)
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final InstanceIdentifier<Table> tableII = flowCapableNodePath.child(Table.class, tableKey);
+ private final InstanceIdentifier<Flow> flowPath = tableII.child(Flow.class, flowKey);
+
+ private FlowForwarder flowForwarder;
+
+ @Mock
+ private SalFlowService salFlowService;
+ @Captor
+ private ArgumentCaptor<AddFlowInput> addFlowInputCpt;
+ @Captor
+ private ArgumentCaptor<UpdateFlowInput> updateFlowInputCpt;
+ @Captor
+ private ArgumentCaptor<RemoveFlowInput> removeFlowInputCpt;
+
+
+ @Before
+ public void setUp() throws Exception {
+ flowForwarder = new FlowForwarder(salFlowService);
+ }
+
+ @Test
+ public void addTest() throws Exception {
+ Mockito.when(salFlowService.addFlow(addFlowInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new AddFlowOutputBuilder()
+ .setTransactionId(new TransactionId(BigInteger.ONE))
+ .build()).buildFuture());
+
+ final Future<RpcResult<AddFlowOutput>> addResult = flowForwarder.add(flowPath, flow, flowCapableNodePath);
+
+ Mockito.verify(salFlowService).addFlow(Matchers.<AddFlowInput>any());
+ final AddFlowInput flowInput = addFlowInputCpt.getValue();
+ Assert.assertEquals(2, flowInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowInput.getMatch());
+ Assert.assertEquals(null, flowInput.getInstructions());
+ Assert.assertEquals(nodePath, flowInput.getNode().getValue());
+ Assert.assertEquals(flowPath, flowInput.getFlowRef().getValue());
+ Assert.assertEquals(null, flowInput.isStrict());
+
+
+ final RpcResult<AddFlowOutput> addFlowOutputRpcResult = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(addFlowOutputRpcResult.isSuccessful());
+ final AddFlowOutput resultValue = addFlowOutputRpcResult.getResult();
+ Assert.assertEquals(1, resultValue.getTransactionId().getValue().intValue());
+ }
+
+ @Test
+ public void updateTest() throws Exception {
+ Mockito.when(salFlowService.updateFlow(updateFlowInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new UpdateFlowOutputBuilder()
+ .setTransactionId(new TransactionId(BigInteger.ONE))
+ .build()).buildFuture());
+
+ final Instructions originalInstructions = new InstructionsBuilder()
+ .setInstruction(Collections.singletonList(new InstructionBuilder()
+ .setInstruction(new ApplyActionsCaseBuilder()
+ .setApplyActions(new ApplyActionsBuilder()
+ .setAction(Collections.singletonList(new ActionBuilder()
+ .setAction(new DropActionCaseBuilder()
+ .build())
+ .build())
+ ).build()
+ ).build())
+ .build())
+ ).build();
+
+ final Flow flowUpdated = new FlowBuilder(flow)
+ .setInstructions(originalInstructions)
+ .setMatch(new MatchBuilder().build())
+ .build();
+
+ final Future<RpcResult<UpdateFlowOutput>> updateResult = flowForwarder.update(flowPath, flow, flowUpdated, flowCapableNodePath);
+
+ Mockito.verify(salFlowService).updateFlow(Matchers.<UpdateFlowInput>any());
+ final UpdateFlowInput updateFlowInput = updateFlowInputCpt.getValue();
+ final OriginalFlow flowOrigInput = updateFlowInput.getOriginalFlow();
+ final UpdatedFlow flowInput = updateFlowInput.getUpdatedFlow();
+
+ Assert.assertEquals(nodePath, updateFlowInput.getNode().getValue());
+ Assert.assertEquals(flowPath, updateFlowInput.getFlowRef().getValue());
+
+ Assert.assertEquals(2, flowInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowInput.getMatch());
+ Assert.assertEquals(originalInstructions, flowInput.getInstructions());
+ Assert.assertEquals(true, flowInput.isStrict());
+
+ Assert.assertEquals(2, flowOrigInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowOrigInput.getMatch());
+ Assert.assertEquals(null, flowOrigInput.getInstructions());
+ Assert.assertEquals(true, flowOrigInput.isStrict());
+
+
+ final RpcResult<UpdateFlowOutput> updateFlowOutputRpcResult = updateResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(updateFlowOutputRpcResult.isSuccessful());
+ final UpdateFlowOutput resultValue = updateFlowOutputRpcResult.getResult();
+ Assert.assertEquals(1, resultValue.getTransactionId().getValue().intValue());
+ }
+
+ @Test
+ public void removeTest() throws Exception {
+ Mockito.when(salFlowService.removeFlow(removeFlowInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new RemoveFlowOutputBuilder()
+ .setTransactionId(new TransactionId(BigInteger.ONE))
+ .build()).buildFuture());
+
+ final Flow removeFlow = new FlowBuilder(flow).build();
+ final Future<RpcResult<RemoveFlowOutput>> removeResult = flowForwarder.remove(flowPath, removeFlow, flowCapableNodePath);
+
+ Mockito.verify(salFlowService).removeFlow(Matchers.<RemoveFlowInput>any());
+ final RemoveFlowInput flowInput = removeFlowInputCpt.getValue();
+ Assert.assertEquals(2, flowInput.getTableId().shortValue());
+ Assert.assertEquals(emptyMatch, flowInput.getMatch());
+ Assert.assertEquals(null, flowInput.getInstructions());
+ Assert.assertEquals(true, flowInput.isStrict());
+
+
+ final RpcResult<RemoveFlowOutput> removeFlowOutputRpcResult = removeResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(removeFlowOutputRpcResult.isSuccessful());
+ final RemoveFlowOutput resultValue = removeFlowOutputRpcResult.getResult();
+ Assert.assertEquals(1, resultValue.getTransactionId().getValue().intValue());
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.SalTableService;
+import org.opendaylight.yangtools.yang.binding.RpcService;
+
+/**
+ * Test for {@link ForwardingRulesSyncProvider}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ForwardingRulesSyncProviderTest {
+
+ private ForwardingRulesSyncProvider provider;
+ @Mock
+ private DataBroker dataBroker;
+ @Mock
+ private RpcConsumerRegistry rpcRegistry;
+ @Mock
+ private BindingAwareBroker broker;
+ @Mock
+ private BindingAwareBroker.ProviderContext providerContext;
+
+ @Before
+ public void setUp() throws Exception {
+ Mockito.when(rpcRegistry.getRpcService(Matchers.<Class<? extends RpcService>>any()))
+ .thenAnswer(new Answer<RpcService>() {
+ @Override
+ public RpcService answer(final InvocationOnMock invocation) throws Throwable {
+ Class<? extends RpcService> serviceType = (Class<? extends RpcService>) invocation.getArguments()[0];
+ return Mockito.mock(serviceType);
+ }
+ });
+
+ provider = new ForwardingRulesSyncProvider(broker, dataBroker, rpcRegistry);
+
+ Mockito.verify(rpcRegistry).getRpcService(SalFlowService.class);
+ Mockito.verify(rpcRegistry).getRpcService(SalGroupService.class);
+ Mockito.verify(rpcRegistry).getRpcService(SalMeterService.class);
+ Mockito.verify(rpcRegistry).getRpcService(SalTableService.class);
+ Mockito.verify(rpcRegistry).getRpcService(FlowCapableTransactionService.class);
+
+ Mockito.verify(broker).registerProvider(provider);
+ }
+
+ @Test
+ public void testOnSessionInitiated() throws Exception {
+ provider.onSessionInitiated(providerContext);
+
+ Mockito.verify(dataBroker, Mockito.times(2)).registerDataTreeChangeListener(
+ Matchers.<DataTreeIdentifier<FlowCapableNode>>any(),
+ Matchers.<DataTreeChangeListener<FlowCapableNode>>any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link GroupForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class GroupForwarderTest {
+
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final GroupId groupId = new GroupId(42L);
+ private final GroupKey groupKey = new GroupKey(groupId);
+ private final Group group = new GroupBuilder()
+ .setGroupId(groupId)
+ .setGroupName("test-group")
+ .setBuckets(new BucketsBuilder().build())
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final InstanceIdentifier<Group> groupPath = flowCapableNodePath.child(Group.class, groupKey);
+
+ @Mock
+ private SalGroupService salGroupService;
+ @Captor
+ private ArgumentCaptor<AddGroupInput> addGroupInputCpt;
+ @Captor
+ private ArgumentCaptor<RemoveGroupInput> removeGroupInputCpt;
+ @Captor
+ private ArgumentCaptor<UpdateGroupInput> updateGroupInputCpt;
+
+ private TransactionId txId;
+
+ private GroupForwarder groupForwarder;
+
+ @Before
+ public void setUp() throws Exception {
+ groupForwarder = new GroupForwarder(salGroupService);
+ txId = new TransactionId(BigInteger.ONE);
+ }
+
+ @Test
+ public void testRemove() throws Exception {
+ Mockito.when(salGroupService.removeGroup(removeGroupInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new RemoveGroupOutputBuilder()
+ .setTransactionId(txId)
+ .build())
+ .buildFuture()
+ );
+
+ final Future<RpcResult<RemoveGroupOutput>> addResult = groupForwarder.remove(groupPath, group, flowCapableNodePath);
+
+ Mockito.verify(salGroupService).removeGroup(Matchers.<RemoveGroupInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<RemoveGroupOutput> result = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(result.isSuccessful());
+
+ Assert.assertEquals(1, result.getResult().getTransactionId().getValue().intValue());
+
+ final RemoveGroupInput removeGroupInput = removeGroupInputCpt.getValue();
+ Assert.assertEquals(groupPath, removeGroupInput.getGroupRef().getValue());
+ Assert.assertNull(removeGroupInput.getBuckets());
+ Assert.assertEquals(nodePath, removeGroupInput.getNode().getValue());
+ Assert.assertEquals("test-group", removeGroupInput.getGroupName());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ Mockito.when(salGroupService.updateGroup(updateGroupInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new UpdateGroupOutputBuilder()
+ .setTransactionId(txId)
+ .build())
+ .buildFuture()
+ );
+
+ Group groupOriginal = new GroupBuilder(group).build();
+ Group groupUpdate = new GroupBuilder(group)
+ .setGroupName("another-test")
+ .build();
+
+ final Future<RpcResult<UpdateGroupOutput>> addResult = groupForwarder.update(groupPath, groupOriginal, groupUpdate,
+ flowCapableNodePath);
+
+ Mockito.verify(salGroupService).updateGroup(Matchers.<UpdateGroupInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<UpdateGroupOutput> result = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(result.isSuccessful());
+
+ Assert.assertEquals(1, result.getResult().getTransactionId().getValue().intValue());
+
+ final UpdateGroupInput updateGroupInput = updateGroupInputCpt.getValue();
+ Assert.assertEquals(groupPath, updateGroupInput.getGroupRef().getValue());
+ Assert.assertEquals(nodePath, updateGroupInput.getNode().getValue());
+ Assert.assertNotNull(updateGroupInput.getOriginalGroup().getBuckets());
+ Assert.assertNotNull(updateGroupInput.getUpdatedGroup().getBuckets());
+
+ Assert.assertEquals("test-group", updateGroupInput.getOriginalGroup().getGroupName());
+ Assert.assertEquals("another-test", updateGroupInput.getUpdatedGroup().getGroupName());
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ Mockito.when(salGroupService.addGroup(addGroupInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new AddGroupOutputBuilder()
+ .setTransactionId(txId)
+ .build())
+ .buildFuture()
+ );
+
+ final Future<RpcResult<AddGroupOutput>> addResult = groupForwarder.add(groupPath, group, flowCapableNodePath);
+
+ Mockito.verify(salGroupService).addGroup(Matchers.<AddGroupInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<AddGroupOutput> result = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(result.isSuccessful());
+
+ Assert.assertEquals(1, result.getResult().getTransactionId().getValue().intValue());
+
+ final AddGroupInput addGroupInput = addGroupInputCpt.getValue();
+ Assert.assertEquals(groupPath, addGroupInput.getGroupRef().getValue());
+ Assert.assertEquals(nodePath, addGroupInput.getNode().getValue());
+ Assert.assertNotNull(addGroupInput.getBuckets());
+ Assert.assertEquals("test-group", addGroupInput.getGroupName());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link MeterForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class MeterForwarderTest {
+
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final MeterId meterId = new MeterId(42L);
+ private final MeterKey meterKey = new MeterKey(meterId);
+ private final Meter meter = new MeterBuilder()
+ .setMeterId(meterId)
+ .setMeterName("test-meter")
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final InstanceIdentifier<Meter> meterPath = flowCapableNodePath.child(Meter.class, meterKey);
+
+ @Mock
+ private SalMeterService salMeterService;
+ @Captor
+ private ArgumentCaptor<AddMeterInput> addMeterInputCpt;
+ @Captor
+ private ArgumentCaptor<RemoveMeterInput> removeMeterInputCpt;
+ @Captor
+ private ArgumentCaptor<UpdateMeterInput> updateMeterInputCpt;
+
+ private TransactionId txId;
+
+ private MeterForwarder meterForwarder;
+
+ @Before
+ public void setUp() throws Exception {
+ meterForwarder = new MeterForwarder(salMeterService);
+ txId = new TransactionId(BigInteger.ONE);
+ }
+
+ @Test
+ public void testRemove() throws Exception {
+ Mockito.when(salMeterService.removeMeter(removeMeterInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(new RemoveMeterOutputBuilder()
+ .setTransactionId(txId)
+ .build()).buildFuture()
+ );
+
+ Meter removeMeter = new MeterBuilder(meter).build();
+
+ final Future<RpcResult<RemoveMeterOutput>> removeResult = meterForwarder.remove(meterPath, removeMeter, flowCapableNodePath);
+ Mockito.verify(salMeterService).removeMeter(Matchers.<RemoveMeterInput>any());
+
+ Assert.assertTrue(removeResult.isDone());
+ final RpcResult<RemoveMeterOutput> meterResult = removeResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(meterResult.isSuccessful());
+
+ Assert.assertEquals(1, meterResult.getResult().getTransactionId().getValue().intValue());
+
+ final RemoveMeterInput removeMeterInput = removeMeterInputCpt.getValue();
+ Assert.assertEquals(meterPath, removeMeterInput.getMeterRef().getValue());
+ Assert.assertEquals(nodePath, removeMeterInput.getNode().getValue());
+ Assert.assertEquals("test-meter", removeMeterInput.getMeterName());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ Mockito.when(salMeterService.updateMeter(updateMeterInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(new UpdateMeterOutputBuilder()
+ .setTransactionId(txId)
+ .build()).buildFuture()
+ );
+
+ Meter meterOriginal = new MeterBuilder(meter).build();
+ Meter meterUpdate = new MeterBuilder(meter)
+ .setMeterName("another-test")
+ .build();
+
+ final Future<RpcResult<UpdateMeterOutput>> updateResult = meterForwarder.update(meterPath, meterOriginal, meterUpdate,
+ flowCapableNodePath);
+ Mockito.verify(salMeterService).updateMeter(Matchers.<UpdateMeterInput>any());
+
+ Assert.assertTrue(updateResult.isDone());
+ final RpcResult<UpdateMeterOutput> meterResult = updateResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(meterResult.isSuccessful());
+
+ Assert.assertEquals(1, meterResult.getResult().getTransactionId().getValue().intValue());
+
+ final UpdateMeterInput updateMeterInput = updateMeterInputCpt.getValue();
+ Assert.assertEquals(meterPath, updateMeterInput.getMeterRef().getValue());
+ Assert.assertEquals(nodePath, updateMeterInput.getNode().getValue());
+
+ Assert.assertEquals("test-meter", updateMeterInput.getOriginalMeter().getMeterName());
+ Assert.assertEquals("another-test", updateMeterInput.getUpdatedMeter().getMeterName());
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ Mockito.when(salMeterService.addMeter(addMeterInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(new AddMeterOutputBuilder()
+ .setTransactionId(txId)
+ .build()).buildFuture()
+ );
+
+ final Future<RpcResult<AddMeterOutput>> addResult = meterForwarder.add(meterPath, meter, flowCapableNodePath);
+ Mockito.verify(salMeterService).addMeter(Matchers.<AddMeterInput>any());
+
+ Assert.assertTrue(addResult.isDone());
+ final RpcResult<AddMeterOutput> meterResult = addResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(meterResult.isSuccessful());
+
+ Assert.assertEquals(1, meterResult.getResult().getTransactionId().getValue().intValue());
+
+ final AddMeterInput addMeterInput = addMeterInputCpt.getValue();
+ Assert.assertEquals(meterPath, addMeterInput.getMeterRef().getValue());
+ Assert.assertEquals(nodePath, addMeterInput.getNode().getValue());
+ Assert.assertEquals("test-meter", addMeterInput.getMeterName());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.Collections;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+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.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeCachedDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeOdlDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * Test for {@link SimplifiedConfigListener}.
+ */
+@SuppressWarnings("deprecation")
+@RunWith(MockitoJUnitRunner.class)
+public class SimplifiedConfigListenerTest {
+
+ @Mock
+ private SyncReactor reactor;
+ @Mock
+ private DataBroker db;
+ @Mock
+ private DataTreeModification<FlowCapableNode> dataTreeModification;
+ @Mock
+ private ReadOnlyTransaction roTx;
+ @Mock
+ private DataObjectModification<FlowCapableNode> configModification;
+
+ private InstanceIdentifier<FlowCapableNode> nodePath;
+ private SimplifiedConfigListener nodeListenerConfig;
+
+ @Before
+ public void setUp() throws Exception {
+ final FlowCapableNodeSnapshotDao configSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeSnapshotDao operationalSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeDao operationalDao = new FlowCapableNodeCachedDao(operationalSnaphot,
+ new FlowCapableNodeOdlDao(db, LogicalDatastoreType.OPERATIONAL));
+
+
+ nodeListenerConfig = new SimplifiedConfigListener(reactor, configSnaphot, operationalDao);
+ nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId("testNode")))
+ .augmentation(FlowCapableNode.class);
+ }
+
+ @Test
+ public void testDSLogicalType() throws Exception {
+ Assert.assertEquals(LogicalDatastoreType.CONFIGURATION, nodeListenerConfig.dsType());
+ }
+
+ @Test
+ public void testOnDataTreeChanged() throws Exception {
+ final FlowCapableNode configTree = Mockito.mock(FlowCapableNode.class);
+ final FlowCapableNode operationalTree = Mockito.mock(FlowCapableNode.class);
+ final DataTreeIdentifier<FlowCapableNode> dataTreeIdentifier =
+ new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, nodePath);
+
+ Mockito.when(dataTreeModification.getRootPath()).thenReturn(dataTreeIdentifier);
+ Mockito.when(dataTreeModification.getRootNode()).thenReturn(configModification);
+ Mockito.when(configModification.getDataAfter()).thenReturn(configTree);
+ Mockito.when(db.newReadOnlyTransaction()).thenReturn(roTx);
+ Mockito.doReturn(Futures.immediateCheckedFuture(Optional.of(operationalTree))).when(
+ roTx).read(LogicalDatastoreType.OPERATIONAL, nodePath);
+ Mockito.when(reactor.syncup(Matchers.<InstanceIdentifier<FlowCapableNode>>any(),Matchers.<FlowCapableNode>any(),Matchers.<FlowCapableNode>any()))
+ .thenReturn(Futures.immediateFuture(Boolean.TRUE));
+
+ nodeListenerConfig.onDataTreeChanged(Collections.singleton(dataTreeModification));
+
+ Mockito.verify(reactor).syncup(nodePath, configTree, operationalTree);
+ Mockito.verify(roTx).close();
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.util.Collections;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+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.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.frsync.SyncReactor;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeCachedDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeOdlDao;
+import org.opendaylight.openflowplugin.applications.frsync.dao.FlowCapableNodeSnapshotDao;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+
+/**
+ * Test for {@link SimplifiedOperationalListener}.
+ */
+@SuppressWarnings("deprecation")
+@RunWith(MockitoJUnitRunner.class)
+public class SimplifiedOperationalListenerTest {
+
+ public static final NodeId NODE_ID = new NodeId("testNode");
+ @Mock
+ private SyncReactor reactor;
+ @Mock
+ private DataBroker db;
+ @Mock
+ private DataTreeModification<Node> dataTreeModification;
+ @Mock
+ private ReadOnlyTransaction roTx;
+ @Mock
+ private DataObjectModification<Node> operationalModification;
+
+ private InstanceIdentifier<Node> nodePath;
+ private InstanceIdentifier<FlowCapableNode> fcNodePath;
+ private SimplifiedOperationalListener nodeListenerOperational;
+
+ @SuppressWarnings("deprecation")
+ @Before
+ public void setUp() throws Exception {
+ final FlowCapableNodeSnapshotDao configSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeSnapshotDao operationalSnaphot = new FlowCapableNodeSnapshotDao();
+ final FlowCapableNodeDao configDao = new FlowCapableNodeCachedDao(configSnaphot,
+ new FlowCapableNodeOdlDao(db, LogicalDatastoreType.CONFIGURATION));
+
+
+ nodeListenerOperational = new SimplifiedOperationalListener(reactor, operationalSnaphot, configDao);
+ nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID));
+ fcNodePath = nodePath.augmentation(FlowCapableNode.class);
+ }
+
+ @Test
+ public void testDSLogicalType() throws Exception {
+ Assert.assertEquals(LogicalDatastoreType.OPERATIONAL, nodeListenerOperational.dsType());
+ }
+
+ @Test
+ public void testOnDataTreeChanged() throws Exception {
+ final FlowCapableNode configTree = Mockito.mock(FlowCapableNode.class);
+ final Node mockOperationalNode = Mockito.mock(Node.class);
+ final FlowCapableNode mockOperationalFlowCapableNode = Mockito.mock(FlowCapableNode.class);
+ Mockito.when(mockOperationalNode.getAugmentation(FlowCapableNode.class))
+ .thenReturn(mockOperationalFlowCapableNode);
+ Mockito.when(mockOperationalNode.getId()).thenReturn(NODE_ID);
+
+ final DataTreeIdentifier<Node> dataTreeIdentifier =
+ new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, nodePath);
+
+ Mockito.when(dataTreeModification.getRootPath()).thenReturn(dataTreeIdentifier);
+ Mockito.when(dataTreeModification.getRootNode()).thenReturn(operationalModification);
+ Mockito.when(operationalModification.getDataAfter()).thenReturn(mockOperationalNode);
+ Mockito.when(db.newReadOnlyTransaction()).thenReturn(roTx);
+ Mockito.doReturn(Futures.immediateCheckedFuture(Optional.of(configTree))).when(
+ roTx).read(LogicalDatastoreType.CONFIGURATION, fcNodePath);
+ Mockito.when(reactor.syncup(Matchers.<InstanceIdentifier<FlowCapableNode>>any(),Matchers.<FlowCapableNode>any(),Matchers.<FlowCapableNode>any()))
+ .thenReturn(Futures.immediateFuture(Boolean.TRUE));
+
+ nodeListenerOperational.onDataTreeChanged(Collections.singleton(dataTreeModification));
+
+ Mockito.verify(reactor).syncup(fcNodePath, configTree, mockOperationalFlowCapableNode);
+ Mockito.verify(roTx).close();
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.openflowplugin.applications.frsync.SyncPlanPushStrategy;
+import org.opendaylight.openflowplugin.applications.frsync.impl.strategy.SynchronizationDiffInput;
+import org.opendaylight.openflowplugin.applications.frsync.util.ReconcileUtil;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test for {@link SyncReactorImpl}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class SyncReactorImplTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncReactorImplTest.class);
+
+ private static final NodeId NODE_ID = new NodeId("unit-nodeId");
+ private static final InstanceIdentifier<FlowCapableNode> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID)).augmentation(FlowCapableNode.class);
+
+ private SyncReactorImpl reactor;
+ @Mock
+ private DataBroker db;
+ @Mock
+ private SyncPlanPushStrategy syncPlanPushStrategy;
+
+ @Captor
+ private ArgumentCaptor<Group> groupCaptor;
+ @Captor
+ private ArgumentCaptor<Group> groupUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<TableFeatures> tableFeaturesCaptor;
+ @Captor
+ private ArgumentCaptor<SynchronizationDiffInput> syncDiffInputCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ reactor = new SyncReactorImpl(syncPlanPushStrategy);
+ }
+
+ @Test
+ public void testSyncup() throws Exception {
+ final FlowCapableNode configFcn = new FlowCapableNodeBuilder()
+ .setGroup(Collections.singletonList(DSInputFactory.createGroup(1L)))
+ .setTable(Collections.singletonList(new TableBuilder()
+ .setFlow(Collections.singletonList(DSInputFactory.createFlow("f1", 1)))
+ .build()))
+ .setMeter(Collections.singletonList(DSInputFactory.createMeter(1L)))
+ .build();
+
+ final FlowCapableNode operationalFcn = new FlowCapableNodeBuilder()
+ .setGroup(Collections.singletonList(DSInputFactory.createGroup(2L)))
+ .setTable(Collections.singletonList(new TableBuilder()
+ .setFlow(Collections.singletonList(DSInputFactory.createFlow("f2", 2)))
+ .build()))
+ .setMeter(Collections.singletonList(DSInputFactory.createMeter(2L)))
+ .build();
+
+ Mockito.when(syncPlanPushStrategy.executeSyncStrategy(
+ Matchers.<ListenableFuture<RpcResult<Void>>>any(),
+ Matchers.<SynchronizationDiffInput>any(),
+ Matchers.<SyncCrudCounters>any()))
+ .thenReturn(RpcResultBuilder.<Void>success().buildFuture());
+
+ final ListenableFuture<Boolean> syncupResult = reactor.syncup(NODE_IDENT, configFcn, operationalFcn);
+ try {
+ Assert.assertTrue(syncupResult.isDone());
+ final Boolean voidRpcResult = syncupResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(voidRpcResult);
+
+ Mockito.verify(syncPlanPushStrategy).executeSyncStrategy(
+ Matchers.<ListenableFuture<RpcResult<Void>>>any(),
+ syncDiffInputCaptor.capture(),
+ Matchers.<SyncCrudCounters>any()
+ );
+
+ final SynchronizationDiffInput diffInput = syncDiffInputCaptor.getValue();
+ Assert.assertEquals(1, ReconcileUtil.countTotalPushed(diffInput.getFlowsToAddOrUpdate().values()));
+ Assert.assertEquals(0, ReconcileUtil.countTotalUpdated(diffInput.getFlowsToAddOrUpdate().values()));
+ Assert.assertEquals(1, ReconcileUtil.countTotalPushed(diffInput.getFlowsToRemove().values()));
+
+ Assert.assertEquals(1, ReconcileUtil.countTotalPushed(diffInput.getGroupsToAddOrUpdate()));
+ Assert.assertEquals(0, ReconcileUtil.countTotalUpdated(diffInput.getGroupsToAddOrUpdate()));
+ Assert.assertEquals(1, ReconcileUtil.countTotalPushed(diffInput.getGroupsToRemove()));
+
+ Assert.assertEquals(1, diffInput.getMetersToAddOrUpdate().getItemsToPush().size());
+ Assert.assertEquals(0, diffInput.getMetersToAddOrUpdate().getItemsToUpdate().size());
+ Assert.assertEquals(1, diffInput.getMetersToRemove().getItemsToPush().size());
+ } catch (Exception e) {
+ LOG.warn("syncup failed", e);
+ Assert.fail("syncup failed: " + e.getMessage());
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl;
+
+import java.math.BigInteger;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.TransactionId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.SalTableService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link TableForwarder}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class TableForwarderTest {
+
+ private final NodeKey s1Key = new NodeKey(new NodeId("S1"));
+ private final Short tableId = (short) 42;
+ private final TableKey tableKey = new TableKey(tableId);
+ private final TableFeaturesKey tableFeaturesKey = new TableFeaturesKey(tableId);
+ private final TableFeatures tableFeatures = new TableFeaturesBuilder()
+ .setName("test-table")
+ .setTableId(tableId)
+ .build();
+
+ private final KeyedInstanceIdentifier<Node, NodeKey> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, s1Key);
+ private final InstanceIdentifier<FlowCapableNode> flowCapableNodePath = nodePath
+ .augmentation(FlowCapableNode.class);
+ private final KeyedInstanceIdentifier<Table, TableKey> tablePath = flowCapableNodePath
+ .child(Table.class, tableKey);
+ private final InstanceIdentifier<TableFeatures> tableFeaturesPath = flowCapableNodePath
+ .child(TableFeatures.class, tableFeaturesKey);
+
+ @Captor
+ private ArgumentCaptor<UpdateTableInput> updateTableInputCpt;
+ @Mock
+ private SalTableService salTableService;
+
+ private TransactionId txId;
+
+ private TableForwarder tableForwarder;
+
+
+ @Before
+ public void setUp() throws Exception {
+ tableForwarder = new TableForwarder(salTableService);
+ txId = new TransactionId(BigInteger.ONE);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ Mockito.when(salTableService.updateTable(updateTableInputCpt.capture())).thenReturn(
+ RpcResultBuilder.success(
+ new UpdateTableOutputBuilder()
+ .setTransactionId(txId)
+ .build()
+ ).buildFuture()
+ );
+
+ final TableFeatures tableFeaturesUpdate = new TableFeaturesBuilder(tableFeatures)
+ .setName("another-table")
+ .build();
+ final Future<RpcResult<UpdateTableOutput>> updateResult = tableForwarder.update(
+ tableFeaturesPath, tableFeatures, tableFeaturesUpdate, flowCapableNodePath);
+
+ Mockito.verify(salTableService).updateTable(Matchers.<UpdateTableInput>any());
+
+ Assert.assertTrue(updateResult.isDone());
+ final RpcResult<UpdateTableOutput> updateTableResult = updateResult.get(2, TimeUnit.SECONDS);
+ Assert.assertTrue(updateTableResult.isSuccessful());
+
+ Assert.assertEquals(1, updateTableResult.getResult().getTransactionId().getValue().intValue());
+
+ final UpdateTableInput updateTableInput = updateTableInputCpt.getValue();
+ Assert.assertEquals(tablePath, updateTableInput.getTableRef().getValue());
+ Assert.assertEquals(nodePath, updateTableInput.getNode().getValue());
+
+ Assert.assertEquals(1, updateTableInput.getOriginalTable().getTableFeatures().size());
+ Assert.assertEquals("test-table", updateTableInput.getOriginalTable().getTableFeatures().get(0).getName());
+ Assert.assertEquals(1, updateTableInput.getUpdatedTable().getTableFeatures().size());
+ Assert.assertEquals("another-table", updateTableInput.getUpdatedTable().getTableFeatures().get(0).getName());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl.strategy;
+
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+
+/**
+ * Provides create methods for data involved in {@link SynchronizationDiffInput}.
+ */
+public class DiffInputFactory {
+ static ItemSyncBox<Group> createGroupSyncBox(final long... groupIDs) {
+ final ItemSyncBox<Group> groupBox = new ItemSyncBox<>();
+
+ for (long gid : groupIDs) {
+ groupBox.getItemsToPush().add(createPlainGroup(gid));
+ }
+ return groupBox;
+ }
+
+ static ItemSyncBox<Group> createGroupSyncBoxWithUpdates(final long... groupIDs) {
+ final ItemSyncBox<Group> groupBox = new ItemSyncBox<>();
+
+ for (long gid : groupIDs) {
+ groupBox.getItemsToPush().add(createPlainGroup(gid));
+ groupBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(createPlainGroup(gid + 50),
+ createPlainGroup(gid + 100)));
+ }
+ return groupBox;
+ }
+
+ static Group createPlainGroup(final long gid) {
+ return new GroupBuilder().setGroupId(new GroupId(gid)).build();
+ }
+
+ static ItemSyncBox<Meter> createMeterSyncBox(final long... meterIDs) {
+ final ItemSyncBox<Meter> groupBox = new ItemSyncBox<>();
+
+ for (long gid : meterIDs) {
+ groupBox.getItemsToPush().add(createPlainMeter(gid));
+ }
+ return groupBox;
+ }
+
+ static ItemSyncBox<Meter> createMeterSyncBoxWithUpdates(final long... meterIDs) {
+ final ItemSyncBox<Meter> groupBox = new ItemSyncBox<>();
+
+ for (long mid : meterIDs) {
+ groupBox.getItemsToPush().add(createPlainMeter(mid));
+ groupBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(createPlainMeter(mid + 50),
+ createPlainMeter(mid + 100)));
+ }
+ return groupBox;
+ }
+
+ static Meter createPlainMeter(final long mid) {
+ return new MeterBuilder().setMeterId(new MeterId(mid)).build();
+ }
+
+ static ItemSyncBox<Flow> createFlowSyncBox(final String... flowIDs) {
+ final ItemSyncBox<Flow> flowBox = new ItemSyncBox<>();
+
+ for (String fid : flowIDs) {
+ flowBox.getItemsToPush().add(createPlainFlow(fid));
+ }
+ return flowBox;
+ }
+
+ static ItemSyncBox<Flow> createFlowSyncBoxWithUpdates(final String... flowIDs) {
+ final ItemSyncBox<Flow> groupBox = new ItemSyncBox<>();
+
+ for (String fid : flowIDs) {
+ groupBox.getItemsToPush().add(createPlainFlow(fid));
+ groupBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(createPlainFlow(fid + "orig"),
+ createPlainFlow(fid + "upd")));
+ }
+ return groupBox;
+ }
+
+ static Flow createPlainFlow(final String fid) {
+ return new FlowBuilder().setId(new FlowId(fid)).build();
+ }
+}
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl.strategy;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Range;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.openflowplugin.applications.frsync.impl.TableForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.ProcessFlatBatchOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.SalFlatBatchService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.Batch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.BatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddFlowCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddGroupCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchAddMeterCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveFlowCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveGroupCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchRemoveMeterCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateFlowCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateGroupCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.process.flat.batch.input.batch.batch.choice.FlatBatchUpdateMeterCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+/**
+ * Test for {@link SyncPlanPushStrategyFlatBatchImpl}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class SyncPlanPushStrategyFlatBatchImplTest {
+
+ private static final NodeId NODE_ID = new NodeId("ut-node-id");
+ private static final InstanceIdentifier<FlowCapableNode> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID))
+ .augmentation(FlowCapableNode.class);
+ @Mock
+ private SalFlatBatchService flatBatchService;
+ @Mock
+ private TableForwarder tableForwarder;
+ @Captor
+ private ArgumentCaptor<ProcessFlatBatchInput> processFlatBatchInputCpt;
+
+ private List<ItemSyncBox<Group>> groupsToAddOrUpdate;
+ private List<ItemSyncBox<Group>> groupsToRemove;
+ private ItemSyncBox<Meter> metersToAddOrUpdate;
+ private ItemSyncBox<Meter> metersToRemove;
+ private Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate;
+ private Map<TableKey, ItemSyncBox<Flow>> flowsToRemove;
+ private List<Batch> batchBag;
+
+ private SyncPlanPushStrategyFlatBatchImpl syncPlanPushStrategy;
+
+ public SyncPlanPushStrategyFlatBatchImplTest() {
+ groupsToAddOrUpdate = Lists.newArrayList(DiffInputFactory.createGroupSyncBox(1, 2, 3),
+ DiffInputFactory.createGroupSyncBoxWithUpdates(4, 5, 6));
+ groupsToRemove = Lists.newArrayList(DiffInputFactory.createGroupSyncBox(1, 2, 3),
+ DiffInputFactory.createGroupSyncBox(4, 5, 6));
+
+ metersToAddOrUpdate = DiffInputFactory.createMeterSyncBoxWithUpdates(1, 2, 3);
+ metersToRemove = DiffInputFactory.createMeterSyncBox(1, 2, 3);
+
+ flowsToAddOrUpdate = new HashMap<>();
+ flowsToAddOrUpdate.put(new TableKey((short) 0), DiffInputFactory.createFlowSyncBox("1", "2", "3"));
+ flowsToAddOrUpdate.put(new TableKey((short) 1), DiffInputFactory.createFlowSyncBoxWithUpdates("4", "5", "6"));
+ flowsToRemove = new HashMap<>();
+ flowsToRemove.put(new TableKey((short) 0), DiffInputFactory.createFlowSyncBox("1", "2", "3"));
+ flowsToRemove.put(new TableKey((short) 1), DiffInputFactory.createFlowSyncBox("4", "5", "6"));
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ syncPlanPushStrategy = new SyncPlanPushStrategyFlatBatchImpl();
+ syncPlanPushStrategy.setFlatBatchService(flatBatchService);
+ syncPlanPushStrategy.setTableForwarder(tableForwarder);
+
+ batchBag = new ArrayList<>();
+ }
+
+ @Test
+ public void testExecuteSyncStrategy() throws Exception {
+ final SynchronizationDiffInput diffInput = new SynchronizationDiffInput(NODE_IDENT,
+ groupsToAddOrUpdate, metersToAddOrUpdate, flowsToAddOrUpdate, flowsToRemove, metersToRemove, groupsToRemove);
+
+ Mockito.when(flatBatchService.processFlatBatch(Matchers.<ProcessFlatBatchInput>any()))
+ .thenReturn(RpcResultBuilder.success(new ProcessFlatBatchOutputBuilder().build()).buildFuture());
+
+ final SyncCrudCounters counters = new SyncCrudCounters();
+ final ListenableFuture<RpcResult<Void>> rpcResult = syncPlanPushStrategy.executeSyncStrategy(
+ RpcResultBuilder.<Void>success().buildFuture(), diffInput, counters);
+
+ Mockito.verify(flatBatchService).processFlatBatch(processFlatBatchInputCpt.capture());
+
+ final ProcessFlatBatchInput processFlatBatchInput = processFlatBatchInputCpt.getValue();
+ Assert.assertFalse(processFlatBatchInput.isExitOnFirstError());
+ Assert.assertEquals(13, processFlatBatchInput.getBatch().size());
+
+ Assert.assertTrue(rpcResult.isDone());
+ Assert.assertTrue(rpcResult.get().isSuccessful());
+
+ Assert.assertEquals(6, counters.getFlowCrudCounts().getAdded());
+ Assert.assertEquals(3, counters.getFlowCrudCounts().getUpdated());
+ Assert.assertEquals(6, counters.getFlowCrudCounts().getRemoved());
+
+ Assert.assertEquals(6, counters.getGroupCrudCounts().getAdded());
+ Assert.assertEquals(3, counters.getGroupCrudCounts().getUpdated());
+ Assert.assertEquals(6, counters.getGroupCrudCounts().getRemoved());
+
+ Assert.assertEquals(3, counters.getMeterCrudCounts().getAdded());
+ Assert.assertEquals(3, counters.getMeterCrudCounts().getUpdated());
+ Assert.assertEquals(3, counters.getMeterCrudCounts().getRemoved());
+ }
+
+ @Test
+ public void testAssembleRemoveFlows() throws Exception {
+ final int lastOrder = SyncPlanPushStrategyFlatBatchImpl.assembleRemoveFlows(batchBag, 0, flowsToRemove);
+
+ Assert.assertEquals(6, lastOrder);
+ Assert.assertEquals(2, batchBag.size());
+ Assert.assertEquals(FlatBatchRemoveFlowCase.class, batchBag.get(0).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchRemoveFlowCase) batchBag.get(0).getBatchChoice())
+ .getFlatBatchRemoveFlow().size());
+ Assert.assertEquals(FlatBatchRemoveFlowCase.class, batchBag.get(1).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchRemoveFlowCase) batchBag.get(1).getBatchChoice())
+ .getFlatBatchRemoveFlow().size());
+ }
+
+ @Test
+ public void testAssembleAddOrUpdateGroups() throws Exception {
+ final int lastOrder = SyncPlanPushStrategyFlatBatchImpl.assembleAddOrUpdateGroups(batchBag, 0, groupsToAddOrUpdate);
+
+ Assert.assertEquals(9, lastOrder);
+ Assert.assertEquals(3, batchBag.size());
+ Assert.assertEquals(FlatBatchAddGroupCase.class, batchBag.get(0).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchAddGroupCase) batchBag.get(0).getBatchChoice())
+ .getFlatBatchAddGroup().size());
+ Assert.assertEquals(FlatBatchAddGroupCase.class, batchBag.get(1).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchAddGroupCase) batchBag.get(1).getBatchChoice())
+ .getFlatBatchAddGroup().size());
+ Assert.assertEquals(FlatBatchUpdateGroupCase.class, batchBag.get(2).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchUpdateGroupCase) batchBag.get(2).getBatchChoice())
+ .getFlatBatchUpdateGroup().size());
+ }
+
+ @Test
+ public void testAssembleRemoveGroups() throws Exception {
+ final int lastOrder = SyncPlanPushStrategyFlatBatchImpl.assembleRemoveGroups(batchBag, 0, groupsToRemove);
+
+ Assert.assertEquals(6, lastOrder);
+ Assert.assertEquals(2, batchBag.size());
+ Assert.assertEquals(FlatBatchRemoveGroupCase.class, batchBag.get(0).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchRemoveGroupCase) batchBag.get(0).getBatchChoice())
+ .getFlatBatchRemoveGroup().size());
+ Assert.assertEquals(FlatBatchRemoveGroupCase.class, batchBag.get(1).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchRemoveGroupCase) batchBag.get(1).getBatchChoice())
+ .getFlatBatchRemoveGroup().size());
+ }
+
+ @Test
+ public void testAssembleAddOrUpdateMeters() throws Exception {
+ final int lastOrder = SyncPlanPushStrategyFlatBatchImpl.assembleAddOrUpdateMeters(batchBag, 0, metersToAddOrUpdate);
+
+ Assert.assertEquals(6, lastOrder);
+ Assert.assertEquals(2, batchBag.size());
+ Assert.assertEquals(FlatBatchAddMeterCase.class, batchBag.get(0).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchAddMeterCase) batchBag.get(0).getBatchChoice())
+ .getFlatBatchAddMeter().size());
+ Assert.assertEquals(FlatBatchUpdateMeterCase.class, batchBag.get(1).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchUpdateMeterCase) batchBag.get(1).getBatchChoice())
+ .getFlatBatchUpdateMeter().size());
+ }
+
+ @Test
+ public void testAssembleRemoveMeters() throws Exception {
+ final int lastOrder = SyncPlanPushStrategyFlatBatchImpl.assembleRemoveMeters(batchBag, 0, metersToRemove);
+
+ Assert.assertEquals(3, lastOrder);
+ Assert.assertEquals(1, batchBag.size());
+ Assert.assertEquals(FlatBatchRemoveMeterCase.class, batchBag.get(0).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchRemoveMeterCase) batchBag.get(0).getBatchChoice())
+ .getFlatBatchRemoveMeter().size());
+ }
+
+ @Test
+ public void testAssembleAddOrUpdateFlows() throws Exception {
+ final int lastOrder = SyncPlanPushStrategyFlatBatchImpl.assembleAddOrUpdateFlows(batchBag, 0, flowsToAddOrUpdate);
+
+ Assert.assertEquals(9, lastOrder);
+ Assert.assertEquals(3, batchBag.size());
+ Assert.assertEquals(FlatBatchAddFlowCase.class, batchBag.get(0).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchAddFlowCase) batchBag.get(0).getBatchChoice())
+ .getFlatBatchAddFlow().size());
+ Assert.assertEquals(FlatBatchUpdateFlowCase.class, batchBag.get(1).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchUpdateFlowCase) batchBag.get(1).getBatchChoice())
+ .getFlatBatchUpdateFlow().size());
+ Assert.assertEquals(FlatBatchAddFlowCase.class, batchBag.get(2).getBatchChoice().getImplementedInterface());
+ Assert.assertEquals(3, ((FlatBatchAddFlowCase) batchBag.get(2).getBatchChoice())
+ .getFlatBatchAddFlow().size());
+ }
+
+ @Test
+ public void testDecrementCounters() throws Exception {
+ final SyncCrudCounters counters = new SyncCrudCounters();
+ counters.getFlowCrudCounts().setAdded(100);
+ counters.getFlowCrudCounts().setUpdated(100);
+ counters.getFlowCrudCounts().setRemoved(100);
+
+ counters.getGroupCrudCounts().setAdded(100);
+ counters.getGroupCrudCounts().setUpdated(100);
+ counters.getGroupCrudCounts().setRemoved(100);
+
+ counters.getMeterCrudCounts().setAdded(100);
+ counters.getMeterCrudCounts().setUpdated(100);
+ counters.getMeterCrudCounts().setRemoved(100);
+
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchAddFlowCaseBuilder().build(), counters);
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchUpdateFlowCaseBuilder().build(), counters);
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchRemoveFlowCaseBuilder().build(), counters);
+
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchAddGroupCaseBuilder().build(), counters);
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchUpdateGroupCaseBuilder().build(), counters);
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchRemoveGroupCaseBuilder().build(), counters);
+
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchAddMeterCaseBuilder().build(), counters);
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchUpdateMeterCaseBuilder().build(), counters);
+ SyncPlanPushStrategyFlatBatchImpl.decrementCounters(new FlatBatchRemoveMeterCaseBuilder().build(), counters);
+
+ Assert.assertEquals(99, counters.getFlowCrudCounts().getAdded());
+ Assert.assertEquals(99, counters.getFlowCrudCounts().getUpdated());
+ Assert.assertEquals(99, counters.getFlowCrudCounts().getRemoved());
+
+ Assert.assertEquals(99, counters.getGroupCrudCounts().getAdded());
+ Assert.assertEquals(99, counters.getGroupCrudCounts().getUpdated());
+ Assert.assertEquals(99, counters.getGroupCrudCounts().getRemoved());
+
+ Assert.assertEquals(99, counters.getMeterCrudCounts().getAdded());
+ Assert.assertEquals(99, counters.getMeterCrudCounts().getUpdated());
+ Assert.assertEquals(99, counters.getMeterCrudCounts().getRemoved());
+ }
+
+ @Test
+ public void testMapBachesToRanges() throws Exception {
+ final List<Batch> inputBatchBag = Lists.newArrayList(
+ new BatchBuilder().setBatchOrder(0).build(),
+ new BatchBuilder().setBatchOrder(5).build(),
+ new BatchBuilder().setBatchOrder(9).build(),
+ new BatchBuilder().setBatchOrder(15).build()
+ );
+ final Map<Range<Integer>, Batch> rangeBatchMap = SyncPlanPushStrategyFlatBatchImpl.mapBachesToRanges(inputBatchBag, 42);
+
+ Assert.assertEquals(4, rangeBatchMap.size());
+ int idx = 0;
+ final int[] lower = new int[]{0, 5, 9, 15};
+ final int[] upper = new int[]{4, 8, 14, 41};
+ for (Map.Entry<Range<Integer>, Batch> rangeBatchEntry : rangeBatchMap.entrySet()) {
+ Assert.assertEquals(lower[idx], rangeBatchEntry.getKey().lowerEndpoint().intValue());
+ Assert.assertEquals(upper[idx], rangeBatchEntry.getKey().upperEndpoint().intValue());
+ Assert.assertSame(inputBatchBag.get(idx), rangeBatchEntry.getValue());
+ idx++;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.impl.strategy;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.openflowplugin.applications.frsync.impl.DSInputFactory;
+import org.opendaylight.openflowplugin.applications.frsync.impl.FlowForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.GroupForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.MeterForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.impl.TableForwarder;
+import org.opendaylight.openflowplugin.applications.frsync.util.ItemSyncBox;
+import org.opendaylight.openflowplugin.applications.frsync.util.SyncCrudCounters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.UpdateTableOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeaturesBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test for {@link SyncPlanPushStrategyIncrementalImpl}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class SyncPlanPushStrategyIncrementalImplTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SyncPlanPushStrategyIncrementalImplTest.class);
+
+ private static final NodeId NODE_ID = new NodeId("unit-nodeId");
+ private static final InstanceIdentifier<FlowCapableNode> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID)).augmentation(FlowCapableNode.class);
+
+ private SyncPlanPushStrategyIncrementalImpl syncPlanPushStrategy;
+
+ @Mock
+ private DataBroker db;
+ @Mock
+ private GroupForwarder groupCommitter;
+ @Mock
+ private FlowForwarder flowCommitter;
+ @Mock
+ private MeterForwarder meterCommitter;
+ @Mock
+ private TableForwarder tableCommitter;
+ @Mock
+ private FlowCapableTransactionService flowCapableTxService;
+
+ @Captor
+ private ArgumentCaptor<Group> groupCaptor;
+ @Captor
+ private ArgumentCaptor<Group> groupUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowCaptor;
+ @Captor
+ private ArgumentCaptor<Flow> flowUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterCaptor;
+ @Captor
+ private ArgumentCaptor<Meter> meterUpdateCaptor;
+ @Captor
+ private ArgumentCaptor<TableFeatures> tableFeaturesCaptor;
+
+ private SyncCrudCounters counters;
+
+ private List<ItemSyncBox<Group>> groupsToAddOrUpdate;
+ private List<ItemSyncBox<Group>> groupsToRemove;
+ private ItemSyncBox<Meter> metersToAddOrUpdate;
+ private ItemSyncBox<Meter> metersToRemove;
+ private Map<TableKey, ItemSyncBox<Flow>> flowsToAddOrUpdate;
+ private Map<TableKey, ItemSyncBox<Flow>> flowsToRemove;
+
+ public SyncPlanPushStrategyIncrementalImplTest() {
+ groupsToAddOrUpdate = Lists.newArrayList(DiffInputFactory.createGroupSyncBox(1, 2, 3),
+ DiffInputFactory.createGroupSyncBoxWithUpdates(4, 5, 6));
+ groupsToRemove = Lists.newArrayList(DiffInputFactory.createGroupSyncBox(1, 2, 3),
+ DiffInputFactory.createGroupSyncBox(4, 5, 6));
+
+ metersToAddOrUpdate = DiffInputFactory.createMeterSyncBoxWithUpdates(1, 2, 3);
+ metersToRemove = DiffInputFactory.createMeterSyncBox(1, 2, 3);
+
+ flowsToAddOrUpdate = new HashMap<>();
+ flowsToAddOrUpdate.put(new TableKey((short) 0), DiffInputFactory.createFlowSyncBox("1", "2", "3"));
+ flowsToAddOrUpdate.put(new TableKey((short) 1), DiffInputFactory.createFlowSyncBoxWithUpdates("4", "5", "6"));
+ flowsToRemove = new HashMap<>();
+ flowsToRemove.put(new TableKey((short) 0), DiffInputFactory.createFlowSyncBox("1", "2", "3"));
+ flowsToRemove.put(new TableKey((short) 1), DiffInputFactory.createFlowSyncBox("4", "5", "6"));
+ }
+
+ @Test
+ public void testExecuteSyncStrategy() throws Exception {
+ final SynchronizationDiffInput diffInput = new SynchronizationDiffInput(NODE_IDENT,
+ groupsToAddOrUpdate, metersToAddOrUpdate, flowsToAddOrUpdate, flowsToRemove, metersToRemove, groupsToRemove);
+
+ final SyncCrudCounters counters = new SyncCrudCounters();
+ final ListenableFuture<RpcResult<Void>> rpcResult = syncPlanPushStrategy.executeSyncStrategy(
+ RpcResultBuilder.<Void>success().buildFuture(), diffInput, counters);
+
+ Mockito.verify(groupCommitter, Mockito.times(6)).add(Matchers.<InstanceIdentifier<Group>>any(),Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(groupCommitter, Mockito.times(3)).update(Matchers.<InstanceIdentifier<Group>>any(),Matchers.<Group>any(),
+ Matchers.<Group>any(), Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(groupCommitter, Mockito.times(6)).remove(Matchers.<InstanceIdentifier<Group>>any(),Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(flowCommitter, Mockito.times(6)).add(Matchers.<InstanceIdentifier<Flow>>any(),Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(flowCommitter, Mockito.times(3)).update(Matchers.<InstanceIdentifier<Flow>>any(),Matchers.<Flow>any(),
+ Matchers.<Flow>any(), Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(flowCommitter, Mockito.times(6)).remove(Matchers.<InstanceIdentifier<Flow>>any(),Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(meterCommitter, Mockito.times(3)).add(Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(meterCommitter, Mockito.times(3)).update(Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<Meter>any(), Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.verify(meterCommitter, Mockito.times(3)).remove(Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Assert.assertTrue(rpcResult.isDone());
+ Assert.assertTrue(rpcResult.get().isSuccessful());
+
+ Assert.assertEquals(6, counters.getFlowCrudCounts().getAdded());
+ Assert.assertEquals(3, counters.getFlowCrudCounts().getUpdated());
+ Assert.assertEquals(6, counters.getFlowCrudCounts().getRemoved());
+
+ Assert.assertEquals(6, counters.getGroupCrudCounts().getAdded());
+ Assert.assertEquals(3, counters.getGroupCrudCounts().getUpdated());
+ Assert.assertEquals(6, counters.getGroupCrudCounts().getRemoved());
+
+ Assert.assertEquals(3, counters.getMeterCrudCounts().getAdded());
+ Assert.assertEquals(3, counters.getMeterCrudCounts().getUpdated());
+ Assert.assertEquals(3, counters.getMeterCrudCounts().getRemoved());
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ Mockito.when(flowCapableTxService.sendBarrier(Matchers.<SendBarrierInput>any()))
+ .thenReturn(RpcResultBuilder.success((Void) null).buildFuture());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).add(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).update(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(groupCommitter).remove(
+ Matchers.<InstanceIdentifier<Group>>any(), Matchers.<Group>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).add(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).update(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(flowCommitter).remove(
+ Matchers.<InstanceIdentifier<Flow>>any(), Matchers.<Flow>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).add(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).update(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(meterCommitter).remove(
+ Matchers.<InstanceIdentifier<Meter>>any(), Matchers.<Meter>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ Mockito.doAnswer(createSalServiceFutureAnswer()).when(tableCommitter).update(
+ Matchers.<InstanceIdentifier<TableFeatures>>any(), Matchers.<TableFeatures>any(), Matchers.<TableFeatures>any(),
+ Matchers.<InstanceIdentifier<FlowCapableNode>>any());
+
+ syncPlanPushStrategy = new SyncPlanPushStrategyIncrementalImpl()
+ .setMeterForwarder(meterCommitter)
+ .setTableForwarder(tableCommitter)
+ .setGroupForwarder(groupCommitter)
+ .setFlowForwarder(flowCommitter)
+ .setTransactionService(flowCapableTxService);
+
+ counters = new SyncCrudCounters();
+ }
+
+ private <O> Answer<Future<RpcResult<O>>> createSalServiceFutureAnswer() {
+ return new Answer<Future<RpcResult<O>>>() {
+ @Override
+ public Future<RpcResult<O>> answer(final InvocationOnMock invocation) throws Throwable {
+ return RpcResultBuilder.<O>success().buildFuture();
+ }
+ };
+ }
+
+ @Test
+ public void testAddMissingFlows() throws Exception {
+ Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Flow> flowBox = new ItemSyncBox<>();
+ flowBox.getItemsToPush().add(DSInputFactory.createFlow("f3", 3));
+ flowBox.getItemsToPush().add(DSInputFactory.createFlow("f4", 4));
+
+ final Map<TableKey, ItemSyncBox<Flow>> flowBoxMap = new LinkedHashMap<>();
+ flowBoxMap.put(new TableKey((short) 0), flowBox);
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingFlows(
+ NODE_ID, NODE_IDENT, flowBoxMap, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantFlows() throws Exception {
+ Mockito.when(flowCommitter.remove(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveFlowOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Flow> flowBox = new ItemSyncBox<>();
+ flowBox.getItemsToPush().add(DSInputFactory.createFlow("f3", 3));
+ flowBox.getItemsToPush().add(DSInputFactory.createFlow("f4", 4));
+
+ final Map<TableKey, ItemSyncBox<Flow>> flowBoxMap = new LinkedHashMap<>();
+ flowBoxMap.put(new TableKey((short) 0), flowBox);
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.removeRedundantFlows(
+ NODE_ID, NODE_IDENT, flowBoxMap, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+
+ @Test
+ public void testAddMissingFlows_withUpdate() throws Exception {
+ Mockito.when(flowCommitter.add(Matchers.<InstanceIdentifier<Flow>>any(), flowCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddFlowOutputBuilder().build()).buildFuture());
+
+ Mockito.when(flowCommitter.update(Matchers.<InstanceIdentifier<Flow>>any(),
+ flowUpdateCaptor.capture(), flowUpdateCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateFlowOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Flow> flowBox = new ItemSyncBox<>();
+ flowBox.getItemsToPush().add(DSInputFactory.createFlow("f3", 3));
+ flowBox.getItemsToPush().add(DSInputFactory.createFlow("f4", 4));
+ flowBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ DSInputFactory.createFlow("f1", 1), DSInputFactory.createFlowWithInstruction("f1", 1)));
+
+ final Map<TableKey, ItemSyncBox<Flow>> flowBoxMap = new LinkedHashMap<>();
+ flowBoxMap.put(new TableKey((short) 0), flowBox);
+
+
+ //TODO: replace null
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingFlows(
+ NODE_ID, NODE_IDENT, flowBoxMap, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Flow> flowCaptorAllValues = flowCaptor.getAllValues();
+ Assert.assertEquals(2, flowCaptorAllValues.size());
+ Assert.assertEquals("f3", flowCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f4", flowCaptorAllValues.get(1).getId().getValue());
+
+ final List<Flow> flowUpdateCaptorAllValues = flowUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, flowUpdateCaptorAllValues.size());
+ Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(0).getId().getValue());
+ Assert.assertEquals("f1", flowUpdateCaptorAllValues.get(1).getId().getValue());
+
+ final InOrder inOrderFlow = Mockito.inOrder(flowCapableTxService, flowCommitter);
+ // add f3, f4
+ inOrderFlow.verify(flowCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ // update f1
+ inOrderFlow.verify(flowCommitter).update(Matchers.<InstanceIdentifier<Flow>>any(),
+ Matchers.<Flow>any(), Matchers.<Flow>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderFlow.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderFlow.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingMeters() throws Exception {
+ Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Meter> meterSyncBox = new ItemSyncBox<>();
+ meterSyncBox.getItemsToPush().add(DSInputFactory.createMeter(2L));
+ meterSyncBox.getItemsToPush().add(DSInputFactory.createMeter(4L));
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingMeters(
+ NODE_ID, NODE_IDENT, meterSyncBox, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, metercaptorAllValues.size());
+ Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeter.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderMeter.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingMeters_withUpdate() throws Exception {
+ Mockito.when(meterCommitter.add(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddMeterOutputBuilder().build()).buildFuture());
+
+ Mockito.when(meterCommitter.update(Matchers.<InstanceIdentifier<Meter>>any(),
+ meterUpdateCaptor.capture(), meterUpdateCaptor.capture(), Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateMeterOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Meter> meterSyncBox = new ItemSyncBox<>();
+ meterSyncBox.getItemsToPush().add(DSInputFactory.createMeter(2L));
+ meterSyncBox.getItemsToPush().add(DSInputFactory.createMeter(4L));
+ meterSyncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ DSInputFactory.createMeter(1L), DSInputFactory.createMeterWithBody(1L)));
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingMeters(
+ NODE_ID, NODE_IDENT, meterSyncBox, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> meterCaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, meterCaptorAllValues.size());
+ Assert.assertEquals(2L, meterCaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, meterCaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+
+ final List<Meter> meterUpdateCaptorAllValues = meterUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, meterUpdateCaptorAllValues.size());
+ Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(1L, meterUpdateCaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeters = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeters.verify(meterCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ inOrderMeters.verify(meterCommitter).update(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderMeters.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderMeters.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantMeters() throws Exception {
+ Mockito.when(meterCommitter.remove(Matchers.<InstanceIdentifier<Meter>>any(), meterCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveMeterOutputBuilder().build()).buildFuture());
+
+ final ItemSyncBox<Meter> meterSyncBox = new ItemSyncBox<>();
+ meterSyncBox.getItemsToPush().add(DSInputFactory.createMeter(2L));
+ meterSyncBox.getItemsToPush().add(DSInputFactory.createMeter(4L));
+ meterSyncBox.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ DSInputFactory.createMeter(1L), DSInputFactory.createMeterWithBody(1L)));
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.removeRedundantMeters(
+ NODE_ID, NODE_IDENT, meterSyncBox, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Meter> metercaptorAllValues = meterCaptor.getAllValues();
+ Assert.assertEquals(2, metercaptorAllValues.size());
+ Assert.assertEquals(2L, metercaptorAllValues.get(0).getMeterId().getValue().longValue());
+ Assert.assertEquals(4L, metercaptorAllValues.get(1).getMeterId().getValue().longValue());
+
+ final InOrder inOrderMeter = Mockito.inOrder(flowCapableTxService, meterCommitter);
+ inOrderMeter.verify(meterCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Meter>>any(),
+ Matchers.<Meter>any(), Matchers.eq(NODE_IDENT));
+ //TODO: uncomment when enabled in impl
+// inOrderMeter.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ inOrderMeter.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingGroups() throws Exception {
+ Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
+
+ ItemSyncBox<Group> groupBox1 = new ItemSyncBox<>();
+ groupBox1.getItemsToPush().add(DSInputFactory.createGroup(2L));
+
+ ItemSyncBox<Group> groupBox2 = new ItemSyncBox<>();
+ groupBox2.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(3L, 2L));
+ groupBox2.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(4L, 2L));
+
+ ItemSyncBox<Group> groupBox3 = new ItemSyncBox<>();
+ groupBox3.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(5L, 3L, 4L));
+
+ final List<ItemSyncBox<Group>> groupBoxLot = Lists.newArrayList(groupBox1, groupBox2, groupBox3);
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingGroups(
+ NODE_ID, NODE_IDENT, groupBoxLot, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
+ // add 2
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 3, 4
+ inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 5
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroups.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testAddMissingGroups_withUpdate() throws Exception {
+ Mockito.when(groupCommitter.add(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new AddGroupOutputBuilder().build()).buildFuture());
+
+ Mockito.when(groupCommitter.update(Matchers.<InstanceIdentifier<Group>>any(),
+ groupUpdateCaptor.capture(), groupUpdateCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateGroupOutputBuilder().build()).buildFuture());
+
+ ItemSyncBox<Group> groupBox1 = new ItemSyncBox<>();
+ groupBox1.getItemsToPush().add(DSInputFactory.createGroup(2L));
+ groupBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ DSInputFactory.createGroup(1L), DSInputFactory.createGroupWithAction(1L)));
+
+ ItemSyncBox<Group> groupBox2 = new ItemSyncBox<>();
+ groupBox2.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(3L, 2L));
+ groupBox2.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(4L, 2L));
+
+ ItemSyncBox<Group> groupBox3 = new ItemSyncBox<>();
+ groupBox3.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(5L, 3L, 4L));
+
+ final List<ItemSyncBox<Group>> groupBoxLot = Lists.newArrayList(groupBox1, groupBox2, groupBox3);
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.addMissingGroups(
+ NODE_ID, NODE_IDENT, groupBoxLot, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final List<Group> groupUpdateCaptorAllValues = groupUpdateCaptor.getAllValues();
+ Assert.assertEquals(2, groupUpdateCaptorAllValues.size());
+ Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(1L, groupUpdateCaptorAllValues.get(1).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroups = Mockito.inOrder(flowCapableTxService, groupCommitter);
+
+ // add 2, update 1
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(groupCommitter).update(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ // add 3, 4
+ inOrderGroups.verify(groupCommitter, Mockito.times(2)).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // add 5
+ inOrderGroups.verify(groupCommitter).add(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroups.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroups.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testRemoveRedundantGroups() throws Exception {
+ Mockito.when(groupCommitter.remove(Matchers.<InstanceIdentifier<Group>>any(), groupCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new RemoveGroupOutputBuilder().build()).buildFuture());
+
+ ItemSyncBox<Group> groupBox1 = new ItemSyncBox<>();
+ groupBox1.getItemsToPush().add(DSInputFactory.createGroup(2L));
+ groupBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(
+ DSInputFactory.createGroup(1L), DSInputFactory.createGroupWithAction(1L)));
+
+ ItemSyncBox<Group> groupBox2 = new ItemSyncBox<>();
+ groupBox2.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(3L, 2L));
+ groupBox2.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(4L, 2L));
+
+ ItemSyncBox<Group> groupBox3 = new ItemSyncBox<>();
+ groupBox3.getItemsToPush().add(DSInputFactory.createGroupWithPreconditions(5L, 3L, 4L));
+
+ final List<ItemSyncBox<Group>> groupBoxLot = Lists.newArrayList(groupBox1, groupBox2, groupBox3);
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.removeRedundantGroups(
+ NODE_ID, NODE_IDENT, groupBoxLot, counters);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<Group> groupCaptorAllValues = groupCaptor.getAllValues();
+ Assert.assertEquals(4, groupCaptorAllValues.size());
+ Assert.assertEquals(5L, groupCaptorAllValues.get(0).getGroupId().getValue().longValue());
+ Assert.assertEquals(3L, groupCaptorAllValues.get(1).getGroupId().getValue().longValue());
+ Assert.assertEquals(4L, groupCaptorAllValues.get(2).getGroupId().getValue().longValue());
+ Assert.assertEquals(2L, groupCaptorAllValues.get(3).getGroupId().getValue().longValue());
+
+ final InOrder inOrderGroup = Mockito.inOrder(flowCapableTxService, groupCommitter);
+ // remove 5
+ inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // remove 3, 4
+ inOrderGroup.verify(groupCommitter, Mockito.times(2)).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ // remove 2
+ inOrderGroup.verify(groupCommitter).remove(Matchers.<InstanceIdentifier<Group>>any(),
+ Matchers.<Group>any(), Matchers.eq(NODE_IDENT));
+ inOrderGroup.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+
+ inOrderGroup.verifyNoMoreInteractions();
+ }
+
+ @Test
+ public void testUpdateTableFeatures() throws Exception {
+ Mockito.when(tableCommitter.update(Matchers.<InstanceIdentifier<TableFeatures>>any(),
+ Matchers.isNull(TableFeatures.class), tableFeaturesCaptor.capture(),
+ Matchers.same(NODE_IDENT)))
+ .thenReturn(RpcResultBuilder.success(new UpdateTableOutputBuilder().build()).buildFuture());
+
+ final FlowCapableNode operational = new FlowCapableNodeBuilder()
+ .setTable(Collections.singletonList(new TableBuilder()
+ .setId((short) 1)
+ .build()))
+ .setTableFeatures(Collections.singletonList(new TableFeaturesBuilder()
+ .setName("test table features")
+ .setTableId((short) 1)
+ .build()))
+ .build();
+
+ final ListenableFuture<RpcResult<Void>> result = syncPlanPushStrategy.updateTableFeatures(
+ NODE_IDENT, operational);
+
+ Assert.assertTrue(result.isDone());
+ Assert.assertTrue(result.get().isSuccessful());
+
+ final List<TableFeatures> groupCaptorAllValues = tableFeaturesCaptor.getAllValues();
+ //TODO: uncomment when enabled in impl
+// Assert.assertEquals(1, groupCaptorAllValues.size());
+// Assert.assertEquals("test table features", groupCaptorAllValues.get(0).getName());
+// Assert.assertEquals(1, groupCaptorAllValues.get(0).getTableId().intValue());
+
+ Mockito.verify(flowCapableTxService).sendBarrier(Matchers.<SendBarrierInput>any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.GroupActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupAction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.group.action._case.GroupActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.FlowCapableTransactionService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.transaction.rev150304.SendBarrierInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.Buckets;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.BucketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.Bucket;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.buckets.BucketBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test for {@link ReconcileUtil}.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ReconcileUtilTest {
+
+ private static final NodeId NODE_ID = new NodeId("unit-node-id");
+ private InstanceIdentifier<Node> NODE_IDENT = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(NODE_ID));
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+ @Mock
+ private FlowCapableTransactionService flowCapableService;
+ @Captor
+ private ArgumentCaptor<SendBarrierInput> barrierInputCaptor;
+
+ @Test
+ public void testChainBarrierFlush() throws Exception {
+ SettableFuture<RpcResult<Void>> testRabbit = SettableFuture.create();
+ final ListenableFuture<RpcResult<Void>> vehicle =
+ Futures.transform(testRabbit, ReconcileUtil.chainBarrierFlush(NODE_IDENT, flowCapableService));
+ Mockito.when(flowCapableService.sendBarrier(barrierInputCaptor.capture()))
+ .thenReturn(RpcResultBuilder.<Void>success().buildFuture());
+
+ Mockito.verify(flowCapableService, Mockito.never()).sendBarrier(Matchers.<SendBarrierInput>any());
+ Assert.assertFalse(vehicle.isDone());
+
+ testRabbit.set(RpcResultBuilder.<Void>success().build());
+ Mockito.verify(flowCapableService).sendBarrier(Matchers.<SendBarrierInput>any());
+ Assert.assertTrue(vehicle.isDone());
+ Assert.assertTrue(vehicle.get().isSuccessful());
+ }
+
+ @Test
+ public void testCreateRpcResultCondenser() throws Exception {
+
+ }
+
+ /**
+ * add one missing group
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroupDiffs1() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+ installedGroups.put(3L, createGroup(3L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroup(2L));
+ pendingGroups.add(createGroup(3L));
+ pendingGroups.add(createGroup(4L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(1, plan.size());
+
+ Assert.assertEquals(1, plan.get(0).getItemsToPush().size());
+ Assert.assertEquals(4L, plan.get(0).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
+ }
+
+ /**
+ * add 3 groups with dependencies - 3 steps involved
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroupDiffs2() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroup(2L));
+ pendingGroups.add(createGroupWithPreconditions(3L, 2L, 4L));
+ pendingGroups.add(createGroupWithPreconditions(4L, 2L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(3, plan.size());
+
+ Assert.assertEquals(1, plan.get(0).getItemsToPush().size());
+ Assert.assertEquals(2L, plan.get(0).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(0).getItemsToUpdate().size());
+
+ Assert.assertEquals(1, plan.get(1).getItemsToPush().size());
+ Assert.assertEquals(4L, plan.get(1).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(1).getItemsToUpdate().size());
+
+ Assert.assertEquals(1, plan.get(2).getItemsToPush().size());
+ Assert.assertEquals(3L, plan.get(2).getItemsToPush().iterator().next().getKey().getGroupId().getValue().longValue());
+ Assert.assertEquals(0, plan.get(2).getItemsToUpdate().size());
+ }
+
+ /**
+ * no actions taken - installed and pending groups are the same
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroupDiffs3() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroupWithPreconditions(2L, 1L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroup(1L));
+ pendingGroups.add(createGroupWithPreconditions(2L, 1L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(0, plan.size());
+ }
+
+ /**
+ * update 1 group
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroupDiffs4() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(1L, 2L));
+ pendingGroups.add(createGroup(2L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
+ NODE_ID, installedGroups, pendingGroups);
+
+ Assert.assertEquals(1, plan.size());
+ Assert.assertEquals(0, plan.get(0).getItemsToPush().size());
+ Assert.assertEquals(1, plan.get(0).getItemsToUpdate().size());
+ final ItemSyncBox.ItemUpdateTuple<Group> firstItemUpdateTuple = plan.get(0).getItemsToUpdate().iterator().next();
+ Assert.assertEquals(1L, firstItemUpdateTuple.getOriginal().getGroupId().getValue().longValue());
+ Assert.assertEquals(1L, firstItemUpdateTuple.getUpdated().getGroupId().getValue().longValue());
+ }
+
+ /**
+ * no action taken - update 1 group will be ignored
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroupDiffs5() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(1L, 2L));
+ pendingGroups.add(createGroup(2L));
+
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
+ NODE_ID, installedGroups, pendingGroups, false);
+
+ Assert.assertEquals(0, plan.size());
+ }
+
+ /**
+ * should add 1 group but preconditions are not met
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroupDiffs_negative1() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(3L, 4L));
+
+ thrown.expect(IllegalStateException.class);
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
+ NODE_ID, installedGroups, pendingGroups);
+ }
+
+ /**
+ * should update 1 group but preconditions are not met
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testResolveAndDivideGroupDiffs_negative2() throws Exception {
+ final Map<Long, Group> installedGroups = new HashMap<>();
+ installedGroups.put(1L, createGroup(1L));
+ installedGroups.put(2L, createGroup(2L));
+
+ final List<Group> pendingGroups = new ArrayList<>();
+ pendingGroups.add(createGroupWithPreconditions(1L, 3L));
+
+ thrown.expect(IllegalStateException.class);
+ final List<ItemSyncBox<Group>> plan = ReconcileUtil.resolveAndDivideGroupDiffs(
+ NODE_ID, installedGroups, pendingGroups);
+ }
+
+ @Test
+ public void testCheckGroupPrecondition() throws Exception {
+ final Set<Long> installedGroups = new HashSet<>(Arrays.asList(new Long[]{1L, 2L}));
+
+ final Group pendingGroup1 = createGroupWithPreconditions(3L, 2L, 4L);
+ Assert.assertFalse(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup1));
+
+ final Group pendingGroup2 = createGroupWithPreconditions(1L, 2L);
+ Assert.assertTrue(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup2));
+
+ final Group pendingGroup3 = createGroupWithPreconditions(1L);
+ Assert.assertTrue(ReconcileUtil.checkGroupPrecondition(installedGroups, pendingGroup3));
+ }
+
+ private Group createGroupWithPreconditions(final long groupIdValue, final long... requiredId) {
+ final List<Action> actionBag = new ArrayList<>();
+ for (long groupIdPrecondition : requiredId) {
+ final GroupAction groupAction = new GroupActionBuilder()
+ .setGroupId(groupIdPrecondition)
+ .build();
+ final GroupActionCase groupActionCase = new GroupActionCaseBuilder()
+ .setGroupAction(groupAction)
+ .build();
+ final Action action = new ActionBuilder()
+ .setAction(groupActionCase)
+ .build();
+ actionBag.add(action);
+ }
+
+ final Bucket bucket = new BucketBuilder()
+ .setAction(actionBag)
+ .build();
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.singletonList(bucket))
+ .build();
+
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ private Group createGroup(final long groupIdValue) {
+ final Buckets buckets = new BucketsBuilder()
+ .setBucket(Collections.<Bucket>emptyList())
+ .build();
+ return new GroupBuilder()
+ .setGroupId(new GroupId(groupIdValue))
+ .setBuckets(buckets)
+ .build();
+ }
+
+ /**
+ * covers {@link ReconcileUtil#countTotalUpdated(Iterable)} too
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testCountTotalAdds() throws Exception {
+ List<ItemSyncBox<String>> syncPlan = new ArrayList<>();
+ ItemSyncBox<String> syncBox1 = createSyncBox("a,b", "x,y,z");
+ syncPlan.add(syncBox1);
+ syncPlan.add(syncBox1);
+ Assert.assertEquals(4, ReconcileUtil.countTotalPushed(syncPlan));
+ Assert.assertEquals(6, ReconcileUtil.countTotalUpdated(syncPlan));
+ }
+
+ private ItemSyncBox<String> createSyncBox(final String pushes, final String updates) {
+ ItemSyncBox<String> syncBox1 = new ItemSyncBox<>();
+ syncBox1.getItemsToPush().addAll(Arrays.asList(pushes.split(",")));
+ for (String orig : updates.split(",")) {
+ syncBox1.getItemsToUpdate().add(new ItemSyncBox.ItemUpdateTuple<>(orig, orig + "_updated"));
+ }
+ return syncBox1;
+ }
+
+ @Test
+ public void testResolveMeterDiffs() throws Exception {
+
+ }
+
+ @Test
+ public void testResolveFlowDiffsInTable() throws Exception {
+
+ }
+
+ @Test
+ public void testResolveFlowDiffsInAllTables() throws Exception {
+
+ }
+}
\ No newline at end of file
--- /dev/null
+/**
+ * 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.openflowplugin.applications.frsync.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.openflowplugin.applications.frsync.SemaphoreKeeper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Test for {@link SemaphoreKeeperGuavaImpl}.
+ */
+public class SemaphoreKeeperTest {
+ private static final Logger LOG = LoggerFactory.getLogger(SemaphoreKeeperTest.class);
+ private SemaphoreKeeperGuavaImpl<String> semaphoreKeeper;
+ final String key = "11";
+
+ @Before
+ public void setUp() throws Exception {
+ semaphoreKeeper = new SemaphoreKeeperGuavaImpl(1, true);
+ }
+
+ @Test
+ public void testSummonGuard() throws Exception {
+ Semaphore semaphore1 = semaphoreKeeper.summonGuard(key);
+ final int g1FingerPrint = semaphore1.hashCode();
+ Semaphore semaphore2 = semaphoreKeeper.summonGuard(key);
+ final int g2FingerPrint = semaphore2.hashCode();
+
+ Assert.assertSame(semaphore1, semaphore2);
+ Assert.assertEquals(1, semaphore1.availablePermits());
+
+ semaphore1.acquire();
+ semaphore1.release();
+ Assert.assertEquals(1, semaphore1.availablePermits());
+ semaphore1 = null;
+ System.gc();
+
+ semaphore2.acquire();
+ semaphore2.release();
+ Assert.assertEquals(1, semaphore2.availablePermits());
+ semaphore2 = null;
+ Assert.assertEquals(g1FingerPrint, g2FingerPrint);
+
+ System.gc();
+ final Semaphore semaphore3 = semaphoreKeeper.summonGuard(key);
+ Assert.assertNotEquals(g1FingerPrint, semaphore3.hashCode());
+ }
+
+ @Test
+ public void testReleaseGuard() throws Exception {
+ for (int total = 1; total <= 10; total++) {
+ LOG.info("test run: {}", total);
+ final Worker task = new Worker(semaphoreKeeper, key);
+
+ final ExecutorService executorService = new ThreadPoolExecutor(5, 5,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<Runnable>()) {
+ @Override
+ protected void afterExecute(final Runnable r, final Throwable t) {
+ super.afterExecute(r, t);
+ if (t != null) {
+ LOG.error("pool thread crashed", t);
+ }
+ }
+ };
+
+ final int steps = 10;
+ for (int i = 0; i < steps; i++) {
+ executorService.submit(task);
+ }
+ Thread.sleep(50L);
+ LOG.info("STARTING new serie");
+ System.gc();
+
+ for (int i = 0; i < steps; i++) {
+ executorService.submit(task);
+ }
+ Thread.sleep(1000L);
+ System.gc();
+
+ executorService.shutdown();
+ final boolean terminated = executorService.awaitTermination(10, TimeUnit.SECONDS);
+ if (!terminated) {
+ LOG.warn("pool stuck, forcing termination");
+ executorService.shutdownNow();
+ Assert.fail("pool failed to finish gracefully");
+ }
+
+ final int counterSize = task.getCounterSize();
+ LOG.info("final counter = {}", counterSize);
+ Assert.assertEquals(20, counterSize);
+ }
+ }
+
+ private static class Worker implements Runnable {
+ private final SemaphoreKeeper<String> keeper;
+ private final String key;
+ private ConcurrentMap<Integer, Integer> counter = new ConcurrentHashMap<>();
+ private volatile int index = 0;
+
+ public Worker(SemaphoreKeeper<String> keeper, final String key) {
+ this.keeper = keeper;
+ this.key = key;
+ }
+
+ @Override
+ public void run() {
+ try {
+ final Semaphore guard = keeper.summonGuard(key);
+ Thread.sleep(2L);
+ guard.acquire();
+ counter.putIfAbsent(index, 0);
+ counter.put(index, counter.get(index) + 1);
+ LOG.debug("queue: {} [{}] - {}", guard.getQueueLength(), guard.hashCode(), counter.size());
+ index++;
+ guard.release();
+ } catch (Exception e) {
+ LOG.warn("acquiring failed.. ", e);
+ }
+ }
+
+ public int getCounterSize() {
+ return counter.size();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">\r
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">\r
+\r
+ <appender name="console" class="org.apache.log4j.ConsoleAppender">\r
+ <layout class="org.apache.log4j.PatternLayout">\r
+ <param name="ConversionPattern" value="%-6p %d{HH:mm:ss.SSS} [%10.10t] %30.30c %x - %m%n"/>\r
+ </layout>\r
+ </appender>\r
+\r
+ <logger name="org.opendaylight.openflowplugin.applications.frsync" additivity="false">\r
+ <level value="TRACE"/>\r
+ <appender-ref ref="console"/>\r
+ </logger>\r
+\r
+ <root>\r
+ <priority value="INFO"/>\r
+ <appender-ref ref="console"/>\r
+ </root>\r
+</log4j:configuration>
\ No newline at end of file
<version>0.3.0-SNAPSHOT</version>
</parent>
<groupId>org.opendaylight.openflowplugin.applications</groupId>
- <artifactId>old-notification-supplier</artifactId>
+ <artifactId>notification-supplier</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<configuration>
<artifacts>
<artifact>
- <file>${project.build.directory}/classes/initial/73-old-notification-supplier.xml</file>
+ <file>${project.build.directory}/classes/initial/73-notification-supplier.xml</file>
<type>xml</type>
<classifier>config</classifier>
</artifact>
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.openflowplugin.applications.old.notification.supplier;
+package org.opendaylight.openflowplugin.applications.notification.supplier;
/**
* Project module provider interface representation
*/
-public interface OldNotifProvider extends AutoCloseable {
+public interface NotificationProvider extends AutoCloseable {
/**
- * Method is responsible for initialization and registration all Old Notification Suppliers
+ * Method is responsible for initialization and registration all Notification Suppliers
* followed by settings from ConfigSubsystem
*/
void start();
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.NodeConnectorNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.NodeNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.FlowNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.GroupNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.MeterNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.stat.FlowStatNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.stat.FlowTableStatNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.stat.GroupStatNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.stat.MeterStatNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.stat.NodeConnectorStatNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.stat.QueueStatNotificationSupplierImpl;
+import org.opendaylight.openflowplugin.applications.notification.supplier.tools.NotificationProviderConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.meter.MeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.flow.capable.node.connector.statistics.FlowCapableNodeConnectorStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.FlowCapableNodeConnectorQueueStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.QueueStatisticsUpdate;
+
+/**
+ * Provider Implementation
+ */
+public class NotificationProviderImpl implements NotificationProvider {
+
+ private final DataBroker db;
+ private final NotificationProviderConfig config;
+ private final NotificationProviderService nps;
+
+ /* Supplier List property help for easy close method implementation and testing */
+ private List<NotificationSupplierDefinition<?>> supplierList;
+ private NotificationSupplierForItemRoot<FlowCapableNode, NodeUpdated, NodeRemoved> nodeSupp;
+ private NotificationSupplierForItemRoot<FlowCapableNodeConnector, NodeConnectorUpdated, NodeConnectorRemoved> connectorSupp;
+ private NotificationSupplierForItem<Flow, FlowAdded, FlowUpdated, FlowRemoved> flowSupp;
+ private NotificationSupplierForItem<Meter, MeterAdded, MeterUpdated, MeterRemoved> meterSupp;
+ private NotificationSupplierForItem<Group, GroupAdded, GroupUpdated, GroupRemoved> groupSupp;
+ private NotificationSupplierForItemStat<FlowCapableNodeConnectorStatistics, NodeConnectorStatisticsUpdate> connectorStatSupp;
+ private NotificationSupplierForItemStat<FlowStatistics, FlowsStatisticsUpdate> flowStatSupp;
+ private NotificationSupplierForItemStat<FlowTableStatistics, FlowTableStatisticsUpdate> flowTableStatSupp;
+ private NotificationSupplierForItemStat<MeterStatistics, MeterStatisticsUpdated> meterStatSupp;
+ private NotificationSupplierForItemStat<GroupStatistics, GroupStatisticsUpdated> groupStatSupp;
+ private NotificationSupplierForItemStat<FlowCapableNodeConnectorQueueStatisticsData, QueueStatisticsUpdate> queueStatSupp;
+
+ /**
+ * Provider constructor set all needed final parameters
+ *
+ * @param config - Configuration Object
+ * @param nps - notifProviderService
+ * @param db - dataBroker
+ */
+ public NotificationProviderImpl(final NotificationProviderConfig config,
+ final NotificationProviderService nps, final DataBroker db) {
+ this.config = Preconditions.checkNotNull(config);
+ this.db = Preconditions.checkNotNull(db);
+ this.nps = Preconditions.checkNotNull(nps);
+ }
+
+ @Override
+ public void start() {
+ nodeSupp = new NodeNotificationSupplierImpl(nps, db);
+ connectorSupp = new NodeConnectorNotificationSupplierImpl(nps, db);
+ flowSupp = config.isFlowSupport() ? new FlowNotificationSupplierImpl(nps, db) : null;
+ meterSupp = config.isMeterSupport() ? new MeterNotificationSupplierImpl(nps, db) : null;
+ groupSupp = config.isGroupSupport() ? new GroupNotificationSupplierImpl(nps, db) : null;
+ connectorStatSupp = config.isNodeConnectorStatSupport() ? new NodeConnectorStatNotificationSupplierImpl(nps, db) : null;
+ flowStatSupp = config.isFlowStatSupport() ? new FlowStatNotificationSupplierImpl(nps, db) : null;
+ flowTableStatSupp = config.isFlowTableStatSupport() ? new FlowTableStatNotificationSupplierImpl(nps, db) : null;
+ meterStatSupp = config.isMeterStatSupport() ? new MeterStatNotificationSupplierImpl(nps, db) : null;
+ groupStatSupp = config.isGroupStatSupport() ? new GroupStatNotificationSupplierImpl(nps, db) : null;
+ queueStatSupp = config.isQueueStatSupport() ? new QueueStatNotificationSupplierImpl(nps, db) : null;
+
+ supplierList = new ArrayList<>(Arrays.asList(nodeSupp, connectorSupp, flowSupp, meterSupp, groupSupp,
+ connectorStatSupp, flowStatSupp, flowTableStatSupp, meterStatSupp, groupStatSupp, queueStatSupp));
+ }
+
+ @Override
+ public void close() throws Exception {
+ for (NotificationSupplierDefinition<?> supplier : supplierList) {
+ if (supplier != null) {
+ supplier.close();
+ supplier = null;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ List<NotificationSupplierDefinition<?>> getSupplierList() {
+ return supplierList;
+ }
+}
+
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.openflowplugin.applications.old.notification.supplier;
+package org.opendaylight.openflowplugin.applications.notification.supplier;
import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
/**
- * Default definition for every Old Notification Supplier. Migration from old notification
+ * Default definition for every Notification Supplier. Migration from notification
* to {@link org.opendaylight.controller.md.sal.binding.api.DataChangeListener} has one
* keyed component - WildCarded Path which represent a changes checker in DataStoreTreeNode
*
* @param <O> - {@link DataObject} represent Data Tree Item from DataStore
*/
-public interface OldNotifSupplierDefinition<O extends DataObject> extends AutoCloseable, DataChangeListener {
+public interface NotificationSupplierDefinition<O extends DataObject> extends AutoCloseable, DataChangeListener {
/**
* Method return wildCardPath for Listener registration and for identify
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Supplier Item contracts definition for every Notification. All items are described
+ * by three notifications. Notification for Create, Update and Delete. So interface
+ * has to contain three methods for every Notification.
+ *
+ * @param <O> - data tree item Object
+ * @param <C> - Create notification
+ * @param <U> - Update notification
+ * @param <D> - Delete notification
+ */
+public interface NotificationSupplierForItem<O extends DataObject,
+ C extends Notification,
+ U extends Notification,
+ D extends Notification>
+ extends NotificationSupplierDefinition<O> {
+
+ /**
+ * Method produces relevant addItem kind of {@link Notification} from
+ * data tree item identified by {@link InstanceIdentifier} path.
+ *
+ * @param o - Data Tree Item object
+ * @param path - Identifier of Data Tree Item
+ * @return {@link Notification} - relevant API contract Notification
+ */
+ C createNotification(O o, InstanceIdentifier<O> path);
+
+ /**
+ * Method produces relevant updateItem kind of {@link Notification} from
+ * data tree item identified by {@link InstanceIdentifier} path.
+ *
+ * @param o - Data Tree Item object
+ * @param path - Identifier of Data Tree Item
+ * @return {@link Notification} - relevant API contract Notification
+ */
+ U updateNotification(O o, InstanceIdentifier<O> path);
+
+ /**
+ * Method produces relevant deleteItem kind of {@link Notification} from
+ * path {@link InstanceIdentifier} to deleted item.
+ *
+ * @param path - Identifier of Data Tree Item
+ * @return {@link Notification} - relevant API contract Notification
+ */
+ D deleteNotification(InstanceIdentifier<O> path);
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Supplier Root Item contracts definition for every Notification. All root items
+ * are described by two notifications. Notification for Create and Delete.
+ * So interface has to contain two methods for relevant Notification.
+ *
+ * @param <O> - data tree item Object
+ * @param <C> - Create notification
+ * @param <D> - Delete notification
+ */
+public interface NotificationSupplierForItemRoot<O extends DataObject,
+ C extends Notification,
+ D extends Notification>
+ extends NotificationSupplierDefinition<O> {
+
+ /**
+ * Method produces relevant addItem kind of {@link Notification} from
+ * data tree item identified by {@link InstanceIdentifier} path.
+ *
+ * @param o - Data Tree Item object
+ * @param path - Identifier of Data Tree Item
+ * @return {@link Notification} - relevant API contract Notification
+ */
+ C createNotification(O o, InstanceIdentifier<O> path);
+
+ /**
+ * Method produces relevant deleteItem kind of {@link Notification} from
+ * path {@link InstanceIdentifier} to deleted item.
+ *
+ * @param path - Identifier of Data Tree Item
+ * @return {@link Notification} - relevant API contract Notification
+ */
+ D deleteNotification(InstanceIdentifier<O> path);
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier;
+
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Notifications for Statistics have little bit different case,
+ * because it looks like they have response for create and update.
+ * But follow the statistics internal implementation processing
+ * is talks only about create event.
+ *
+ * @param <O> - data tree item Object
+ * @param <N> - Statistics Notification
+ */
+public interface NotificationSupplierForItemStat<O extends DataObject, N extends Notification>
+ extends NotificationSupplierDefinition<O> {
+
+
+ /**
+ * Method produces relevant Statistics kind of {@link Notification} from statistics
+ * data tree item identified by {@link InstanceIdentifier} path.
+ *
+ * @param o - Statistics Data Tree Item
+ * @param path - Identifier of Data Tree Item
+ * @return {@link Notification} - relevant API contract Notification
+ */
+ N createNotification(O o, InstanceIdentifier<O> path);
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.applications.notification.supplier.NotificationSupplierDefinition;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+/**
+ * Public abstract basic Supplier implementation contains code for a make Supplier instance,
+ * registration Supplier like {@link org.opendaylight.controller.md.sal.binding.api.DataChangeListener}
+ * and close method. In additional case, it contains help methods for all Supplier implementations.
+ *
+ * @param <O> - data tree item Object extends {@link DataObject}
+ */
+public abstract class AbstractNotificationSupplierBase<O extends DataObject> implements
+ NotificationSupplierDefinition<O> {
+
+ protected final Class<O> clazz;
+ private ListenerRegistration<DataChangeListener> listenerRegistration;
+
+ /**
+ * Default constructor for all Notification Supplier implementation
+ *
+ * @param db - {@link DataBroker}
+ * @param clazz - API contract class extended {@link DataObject}
+ */
+ public AbstractNotificationSupplierBase(final DataBroker db, final Class<O> clazz) {
+ Preconditions.checkArgument(db != null, "DataBroker can not be null!");
+ listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL, getWildCardPath(), this,
+ DataChangeScope.BASE);
+ this.clazz = clazz;
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (listenerRegistration != null) {
+ listenerRegistration.close();
+ listenerRegistration = null;
+ }
+ }
+
+ /**
+ * Method returns a wildCard {@link InstanceIdentifier} for {@link Node} from inventory
+ * because this path is a base for every OF paths.
+ *
+ * @return WildCarded InstanceIdentifier for Node
+ */
+ protected static InstanceIdentifier<Node> getNodeWildII() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class);
+ }
+
+ /**
+ * Method returns a keyed {@link InstanceIdentifier} for {@link Node} from inventory
+ * because this path is a base for every OF paths.
+ *
+ * @param ii - key for keyed {@link Node} {@link InstanceIdentifier}
+ * @return Keyed InstanceIdentifier for Node
+ */
+ protected static KeyedInstanceIdentifier<Node, NodeKey> getNodeII(final InstanceIdentifier<?> ii) {
+ final NodeKey key = ii.firstKeyOf(Node.class, NodeKey.class);
+ Preconditions.checkArgument(key != null);
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, key);
+ }
+
+ /**
+ * @param path pointer to element
+ * @return extracted {@link NodeKey} and wrapped in {@link NodeRef}
+ */
+ public static NodeRef createNodeRef(InstanceIdentifier<?> path) {
+ final InstanceIdentifier<Node> nodePath = Preconditions.checkNotNull(path.firstIdentifierOf(Node.class));
+ return new NodeRef(nodePath);
+ }
+
+ /**
+ * @param path pointer to element
+ * @return extracted {@link NodeId}
+ */
+ public static NodeId getNodeId(InstanceIdentifier<?> path) {
+ final NodeKey nodeKey = Preconditions.checkNotNull(path.firstKeyOf(Node.class, NodeKey.class));
+ return nodeKey.getId();
+ }
+
+}
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl;
+
+import com.google.common.base.Preconditions;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.NotificationSupplierForItemRoot;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Class is package protected abstract implementation for all Root Items
+ * Notification Suppliers
+ *
+ * @param <O> - data tree item Object
+ * @param <C> - Create notification
+ * @param <D> - Delete notification
+ */
+abstract class AbstractNotificationSupplierForItemRoot<O extends DataObject,
+ C extends Notification,
+ D extends Notification>
+ extends AbstractNotificationSupplierBase<O>
+ implements NotificationSupplierForItemRoot<O, C, D> {
+
+ private final NotificationProviderService notificationProviderService;
+
+ /**
+ * Default constructor for all Root Item Notification Supplier implementation
+ *
+ * @param notificationProviderService - notification publisher
+ * @param db - DataBroker for DataChangeEvent registration
+ * @param clazz - Statistics Notification Class
+ */
+ public AbstractNotificationSupplierForItemRoot(final NotificationProviderService notificationProviderService, final DataBroker db,
+ final Class<O> clazz) {
+ super(db, clazz);
+ this.notificationProviderService = Preconditions.checkNotNull(notificationProviderService);
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ Preconditions.checkArgument(change != null, "ChangeEvent can not be null!");
+ if (change.getCreatedData() != null && ! (change.getCreatedData().isEmpty())) {
+ for (final Entry<InstanceIdentifier<?>, DataObject> createDataObj : change.getCreatedData().entrySet()) {
+ if (clazz.isAssignableFrom(createDataObj.getKey().getTargetType())) {
+ final InstanceIdentifier<O> ii = createDataObj.getKey().firstIdentifierOf(clazz);
+ final C notif = createNotification((O) createDataObj.getValue(), ii);
+ if (notif != null) {
+ notificationProviderService.publish(notif);
+ }
+ }
+ }
+ }
+
+ if (change.getRemovedPaths() != null && !(change.getRemovedPaths().isEmpty())) {
+ for (final InstanceIdentifier<?> deleteDataPath : change.getRemovedPaths()) {
+ if (clazz.isAssignableFrom(deleteDataPath.getTargetType())) {
+ final D notif = deleteNotification(deleteDataPath.firstIdentifierOf(clazz));
+ if (notif != null) {
+ notificationProviderService.publish(notif);
+ }
+ }
+ }
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemovedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link FlowCapableNodeConnector} data object
+ * and {@link NodeConnectorUpdated} and {@link NodeConnectorRemoved} notifications.
+ */
+public class NodeConnectorNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItemRoot<FlowCapableNodeConnector, NodeConnectorUpdated, NodeConnectorRemoved> {
+
+ private static final InstanceIdentifier<FlowCapableNodeConnector> wildCardedInstanceIdent = getNodeWildII().child(NodeConnector.class).augmentation(FlowCapableNodeConnector.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public NodeConnectorNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, FlowCapableNodeConnector.class);
+ }
+
+ @Override
+ public InstanceIdentifier<FlowCapableNodeConnector> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public NodeConnectorUpdated createNotification(final FlowCapableNodeConnector o,
+ final InstanceIdentifier<FlowCapableNodeConnector> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+ final NodeConnectorUpdatedBuilder notifBuilder = new NodeConnectorUpdatedBuilder();
+ final FlowCapableNodeConnectorUpdatedBuilder connNotifBuilder = new FlowCapableNodeConnectorUpdatedBuilder(o);
+ notifBuilder.setId(path.firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId());
+ notifBuilder.setNodeConnectorRef(new NodeConnectorRef(path));
+ notifBuilder.addAugmentation(FlowCapableNodeConnectorUpdated.class, connNotifBuilder.build());
+ return notifBuilder.build();
+ }
+
+ @Override
+ public NodeConnectorRemoved deleteNotification(final InstanceIdentifier<FlowCapableNodeConnector> path) {
+ Preconditions.checkArgument(path != null);
+ final NodeConnectorRemovedBuilder notifBuilder = new NodeConnectorRemovedBuilder();
+ notifBuilder.setNodeConnectorRef(new NodeConnectorRef(path));
+ return notifBuilder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemovedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link FlowCapableNode} data object
+ * and {@link NodeUpdated} and {@link NodeRemoved} notifications.
+ */
+public class NodeNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItemRoot<FlowCapableNode, NodeUpdated, NodeRemoved> {
+
+ private static final InstanceIdentifier<FlowCapableNode> wildCardedInstanceIdent = getNodeWildII().augmentation(FlowCapableNode.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public NodeNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, FlowCapableNode.class);
+ }
+
+ @Override
+ public InstanceIdentifier<FlowCapableNode> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public NodeUpdated createNotification(final FlowCapableNode o, final InstanceIdentifier<FlowCapableNode> ii) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(ii != null);
+ final FlowCapableNodeUpdatedBuilder flowNodeNotifBuilder = new FlowCapableNodeUpdatedBuilder(o);
+ final NodeUpdatedBuilder notifBuilder = new NodeUpdatedBuilder();
+ notifBuilder.setId(ii.firstKeyOf(Node.class, NodeKey.class).getId());
+ notifBuilder.setNodeRef(new NodeRef(getNodeII(ii)));
+ notifBuilder.addAugmentation(FlowCapableNodeUpdated.class, flowNodeNotifBuilder.build());
+ return notifBuilder.build();
+ }
+
+ @Override
+ public NodeRemoved deleteNotification(final InstanceIdentifier<FlowCapableNode> path) {
+ Preconditions.checkArgument(path != null);
+ final NodeRemovedBuilder delNodeNotifBuilder = new NodeRemovedBuilder();
+ delNodeNotifBuilder.setNodeRef(new NodeRef(path));
+ return delNodeNotifBuilder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item;
+
+import com.google.common.base.Preconditions;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.NotificationSupplierForItem;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.AbstractNotificationSupplierBase;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Class is package protected abstract implementation for all Old Root Items
+ * Notification Suppliers
+ *
+ * @param <O> - data tree item Object
+ * @param <C> - Create notification
+ * @param <U> - Update notification
+ * @param <D> - Delete notification
+ */
+abstract class AbstractNotificationSupplierForItem<O extends DataObject,
+ C extends Notification,
+ U extends Notification,
+ D extends Notification>
+ extends AbstractNotificationSupplierBase<O>
+ implements NotificationSupplierForItem<O, C, U, D> {
+
+ private final NotificationProviderService notificationProviderService;
+
+ /**
+ * Default constructor for all item Notification Supplier implementation
+ *
+ * @param notifProviderService - notification publisher
+ * @param db - DataBroker for DataChangeEvent registration
+ * @param clazz - Statistics Notification Class
+ */
+ public AbstractNotificationSupplierForItem(final NotificationProviderService notifProviderService, final DataBroker db,
+ final Class<O> clazz) {
+ super(db, clazz);
+ this.notificationProviderService = Preconditions.checkNotNull(notifProviderService);
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ Preconditions.checkArgument(change != null, "ChangeEvent can not be null!");
+ if (change.getCreatedData() != null && !(change.getCreatedData().isEmpty())) {
+ for (final Entry<InstanceIdentifier<?>, DataObject> createDataObj : change.getCreatedData().entrySet()) {
+ if (clazz.isAssignableFrom(createDataObj.getKey().getTargetType())) {
+ final InstanceIdentifier<O> ii = createDataObj.getKey().firstIdentifierOf(clazz);
+ final C notif = createNotification((O) createDataObj.getValue(), ii);
+ if (notif != null) {
+ notificationProviderService.publish(notif);
+ }
+ }
+ }
+ }
+
+ if (change.getUpdatedData() != null && !(change.getUpdatedData().isEmpty())) {
+ for (final Entry<InstanceIdentifier<?>, DataObject> updateDataObj : change.getUpdatedData().entrySet()) {
+ if (clazz.isAssignableFrom(updateDataObj.getKey().getTargetType())) {
+ final InstanceIdentifier<O> ii = updateDataObj.getKey().firstIdentifierOf(clazz);
+ final U notif = updateNotification((O) updateDataObj.getValue(), ii);
+ if (notif != null) {
+ notificationProviderService.publish(notif);
+ }
+ }
+ }
+ }
+
+ if (change.getRemovedPaths() != null && !(change.getRemovedPaths().isEmpty())) {
+ for (final InstanceIdentifier<?> deleteDataPath : change.getRemovedPaths()) {
+ if (clazz.isAssignableFrom(deleteDataPath.getTargetType())) {
+ final D notif = deleteNotification(deleteDataPath.firstIdentifierOf(clazz));
+ if (notif != null) {
+ notificationProviderService.publish(notif);
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAddedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemovedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link Flow} data object
+ * and {@link FlowAdded}, {@link FlowUpdated} and {@link FlowRemoved} notifications.
+ */
+public class FlowNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItem<Flow, FlowAdded, FlowUpdated, FlowRemoved> {
+
+ private static final InstanceIdentifier<Flow> wildCardedInstanceIdent = getNodeWildII().augmentation(FlowCapableNode.class).child(Table.class).child(Flow.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public FlowNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, Flow.class);
+ }
+
+ @Override
+ public InstanceIdentifier<Flow> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public FlowAdded createNotification(final Flow o, final InstanceIdentifier<Flow> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+ final FlowAddedBuilder builder = new FlowAddedBuilder(o);
+ builder.setFlowRef(new FlowRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+
+ @Override
+ public FlowUpdated updateNotification(final Flow o, final InstanceIdentifier<Flow> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+ final FlowUpdatedBuilder builder = new FlowUpdatedBuilder(o);
+ builder.setFlowRef(new FlowRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+
+ @Override
+ public FlowRemoved deleteNotification(final InstanceIdentifier<Flow> path) {
+ Preconditions.checkArgument(path != null);
+ final FlowRemovedBuilder builder = new FlowRemovedBuilder();
+ builder.setFlowRef(new FlowRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupAddedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupRemovedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link Group} data object
+ * and {@link GroupAdded}, {@link GroupUpdated} and {@link GroupRemoved} notifications.
+ */
+public class GroupNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItem<Group, GroupAdded, GroupUpdated, GroupRemoved> {
+
+ private static final InstanceIdentifier<Group> wildCardedInstanceIdent = getNodeWildII().augmentation(FlowCapableNode.class).child(Group.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public GroupNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, Group.class);
+ }
+
+ @Override
+ public InstanceIdentifier<Group> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public GroupAdded createNotification(final Group o, final InstanceIdentifier<Group> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+ final GroupAddedBuilder builder = new GroupAddedBuilder(o);
+ builder.setGroupRef(new GroupRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+
+ @Override
+ public GroupUpdated updateNotification(final Group o, final InstanceIdentifier<Group> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+ final GroupUpdatedBuilder builder = new GroupUpdatedBuilder(o);
+ builder.setGroupRef(new GroupRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+
+ @Override
+ public GroupRemoved deleteNotification(final InstanceIdentifier<Group> path) {
+ Preconditions.checkArgument(path != null);
+ final GroupRemovedBuilder builder = new GroupRemovedBuilder();
+ builder.setGroupId(path.firstKeyOf(Group.class, GroupKey.class).getGroupId());
+ builder.setGroupRef(new GroupRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item;
+
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterAddedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterRemovedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterRef;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link Meter} data object
+ * and {@link MeterAdded}, {@link MeterUpdated} and {@link MeterRemoved} notifications.
+ */
+public class MeterNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItem<Meter, MeterAdded, MeterUpdated, MeterRemoved> {
+
+ private static final InstanceIdentifier<Meter> wildCardedInstanceIdent = getNodeWildII().augmentation(FlowCapableNode.class).child(Meter.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public MeterNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, Meter.class);
+ }
+
+ @Override
+ public InstanceIdentifier<Meter> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public MeterAdded createNotification(final Meter o, final InstanceIdentifier<Meter> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+ final MeterAddedBuilder builder = new MeterAddedBuilder(o);
+ builder.setMeterRef(new MeterRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+
+ @Override
+ public MeterUpdated updateNotification(final Meter o, final InstanceIdentifier<Meter> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+ final MeterUpdatedBuilder builder = new MeterUpdatedBuilder(o);
+ builder.setMeterRef(new MeterRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+
+ @Override
+ public MeterRemoved deleteNotification(final InstanceIdentifier<Meter> path) {
+ Preconditions.checkArgument(path != null);
+ final MeterRemovedBuilder builder = new MeterRemovedBuilder();
+ builder.setMeterId(path.firstKeyOf(Meter.class, MeterKey.class).getMeterId());
+ builder.setMeterRef(new MeterRef(path));
+ builder.setNode(createNodeRef(path));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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 contains OF Items (Flow, Group, Meter ...) notification listener/supplier implementations
+ */
+package org.opendaylight.openflowplugin.applications.notification.supplier.impl.item;
\ No newline at end of file
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import com.google.common.base.Preconditions;
+import java.util.Map.Entry;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.NotificationSupplierForItemStat;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.AbstractNotificationSupplierBase;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
+
+/**
+ * Class is package protected abstract implementation for all Old Statistics
+ * Notification Suppliers
+ *
+ * @param <O> - Statistics {@link DataObject}
+ * @param <N> - Statistics Notification
+ */
+abstract class AbstractNotificationSupplierForItemStat<O extends DataObject,
+ N extends Notification>
+ extends AbstractNotificationSupplierBase<O>
+ implements NotificationSupplierForItemStat<O, N> {
+
+ private final NotificationProviderService notifProviderService;
+
+ /**
+ * Default constructor for all Statistic Notification Supplier implementation
+ *
+ * @param notifProviderService - notification publisher
+ * @param db - DataBroker for DataChangeEvent registration
+ * @param clazz - Statistics Notification Class
+ */
+ public AbstractNotificationSupplierForItemStat(final NotificationProviderService notifProviderService,
+ final DataBroker db, final Class<O> clazz) {
+ super(db, clazz);
+ this.notifProviderService = Preconditions.checkNotNull(notifProviderService);
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ Preconditions.checkArgument(change != null, "ChangeEvent can not be null!");
+ if (change.getCreatedData() != null && !(change.getCreatedData().isEmpty())) {
+ for (final Entry<InstanceIdentifier<?>, DataObject> createDataObj : change.getCreatedData().entrySet()) {
+ if (clazz.isAssignableFrom(createDataObj.getKey().getTargetType())) {
+ final InstanceIdentifier<O> ii = createDataObj.getKey().firstIdentifierOf(clazz);
+ final N notif = createNotification((O) createDataObj.getValue(), ii);
+ if (notif != null) {
+ notifProviderService.publish(notif);
+ }
+ }
+ }
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link FlowStatistics} data object
+ * and {@link FlowsStatisticsUpdate} notification.
+ */
+public class FlowStatNotificationSupplierImpl extends AbstractNotificationSupplierForItemStat<FlowStatistics, FlowsStatisticsUpdate> {
+
+ private static final InstanceIdentifier<FlowStatistics> wildCardedInstanceIdent =
+ getNodeWildII().augmentation(FlowCapableNode.class).child(Table.class)
+ .child(Flow.class).augmentation(FlowStatisticsData.class).child(FlowStatistics.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public FlowStatNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, FlowStatistics.class);
+ }
+
+ @Override
+ public InstanceIdentifier<FlowStatistics> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public FlowsStatisticsUpdate createNotification(final FlowStatistics o, final InstanceIdentifier<FlowStatistics> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+
+ final FlowAndStatisticsMapListBuilder fsmlBuilder = new FlowAndStatisticsMapListBuilder(o);
+ fsmlBuilder.setFlowId(new FlowId(path.firstKeyOf(Flow.class, FlowKey.class).getId().getValue()));
+
+ final FlowsStatisticsUpdateBuilder builder = new FlowsStatisticsUpdateBuilder();
+ builder.setId(getNodeId(path));
+ builder.setMoreReplies(Boolean.FALSE);
+ // NOTE : fix if it needs, but we have to ask DataStore for the NodeConnector list
+ builder.setNodeConnector(Collections.<NodeConnector>emptyList());
+ builder.setFlowAndStatisticsMapList(Collections.singletonList(fsmlBuilder.build()));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.and.statistics.map.FlowTableAndStatisticsMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.and.statistics.map.FlowTableAndStatisticsMapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link FlowTableStatistics} data object
+ * and {@link FlowTableStatisticsUpdate} notification.
+ */
+public class FlowTableStatNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItemStat<FlowTableStatistics, FlowTableStatisticsUpdate> {
+
+ private static final InstanceIdentifier<FlowTableStatistics> wildCardedInstanceIdent =
+ getNodeWildII().augmentation(FlowCapableNode.class).child(Table.class)
+ .augmentation(FlowTableStatisticsData.class).child(FlowTableStatistics.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public FlowTableStatNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, FlowTableStatistics.class);
+ }
+
+ @Override
+ public InstanceIdentifier<FlowTableStatistics> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public FlowTableStatisticsUpdate createNotification(final FlowTableStatistics o,
+ final InstanceIdentifier<FlowTableStatistics> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+
+ final FlowTableAndStatisticsMapBuilder ftsmBuilder = new FlowTableAndStatisticsMapBuilder(o);
+ ftsmBuilder.setKey(new FlowTableAndStatisticsMapKey(new TableId(path.firstKeyOf(Table.class, TableKey.class).getId())));
+
+ final FlowTableStatisticsUpdateBuilder builder = new FlowTableStatisticsUpdateBuilder();
+ builder.setId(getNodeId(path));
+ builder.setMoreReplies(Boolean.FALSE);
+ // NOTE : fix if it needs, but we have to ask DataStore for the NodeConnector list
+ builder.setNodeConnector(Collections.<NodeConnector>emptyList());
+ builder.setFlowTableAndStatisticsMap(Collections.singletonList(ftsmBuilder.build()));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatisticsUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link GroupStatistics} data object
+ * and {@link GroupStatisticsUpdated} notification.
+ */
+public class GroupStatNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItemStat<GroupStatistics, GroupStatisticsUpdated> {
+
+ private static final InstanceIdentifier<GroupStatistics> wildCardedInstanceIdent =
+ getNodeWildII().augmentation(FlowCapableNode.class).child(Group.class)
+ .augmentation(NodeGroupStatistics.class).child(GroupStatistics.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public GroupStatNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, GroupStatistics.class);
+ }
+
+ @Override
+ public InstanceIdentifier<GroupStatistics> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public GroupStatisticsUpdated createNotification(final GroupStatistics o,
+ final InstanceIdentifier<GroupStatistics> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+
+ final GroupStatisticsUpdatedBuilder builder = new GroupStatisticsUpdatedBuilder();
+ builder.setId(getNodeId(path));
+ builder.setMoreReplies(Boolean.FALSE);
+ // TODO : fix if it needs, but we have to ask DataStore for the NodeConnector list
+ builder.setNodeConnector(Collections.<NodeConnector>emptyList());
+ builder.setGroupStats(Collections.singletonList(new GroupStatsBuilder(o).build()));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterStatisticsUpdatedBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.meter.MeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStatsBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link MeterStatistics} data object
+ * and {@link MeterStatisticsUpdated} notification.
+ */
+public class MeterStatNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItemStat<MeterStatistics, MeterStatisticsUpdated> {
+
+ private static final InstanceIdentifier<MeterStatistics> wildCardedInstanceIdent =
+ getNodeWildII().augmentation(FlowCapableNode.class).child(Meter.class)
+ .augmentation(NodeMeterStatistics.class).child(MeterStatistics.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public MeterStatNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, MeterStatistics.class);
+ }
+
+ @Override
+ public InstanceIdentifier<MeterStatistics> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public MeterStatisticsUpdated createNotification(final MeterStatistics o,
+ final InstanceIdentifier<MeterStatistics> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+
+ final MeterStatisticsUpdatedBuilder builder = new MeterStatisticsUpdatedBuilder();
+ builder.setId(getNodeId(path));
+ builder.setMoreReplies(Boolean.FALSE);
+ // TODO : fix if it needs, but we have to ask DataStore for the NodeConnector list
+ builder.setNodeConnector(Collections.<NodeConnector>emptyList());
+ builder.setMeterStats(Collections.singletonList(new MeterStatsBuilder(o).build()));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.flow.capable.node.connector.statistics.FlowCapableNodeConnectorStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMapBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link FlowCapableNodeConnectorStatistics} data object
+ * and {@link NodeConnectorStatisticsUpdate} notification.
+ */
+public class NodeConnectorStatNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItemStat<FlowCapableNodeConnectorStatistics, NodeConnectorStatisticsUpdate> {
+
+ private static final InstanceIdentifier<FlowCapableNodeConnectorStatistics> wildCardedInstanceIdent =
+ getNodeWildII().child(NodeConnector.class)
+ .augmentation(FlowCapableNodeConnectorStatisticsData.class)
+ .child(FlowCapableNodeConnectorStatistics.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public NodeConnectorStatNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, FlowCapableNodeConnectorStatistics.class);
+ }
+
+ @Override
+ public InstanceIdentifier<FlowCapableNodeConnectorStatistics> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public NodeConnectorStatisticsUpdate createNotification(final FlowCapableNodeConnectorStatistics o,
+ final InstanceIdentifier<FlowCapableNodeConnectorStatistics> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+
+ final NodeConnectorBuilder ncBuilder = new NodeConnectorBuilder();
+ final NodeConnectorKey ncKey = path.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+ ncBuilder.setId(ncKey.getId());
+ ncBuilder.setKey(ncKey);
+
+ final NodeConnectorStatisticsUpdateBuilder builder = new NodeConnectorStatisticsUpdateBuilder();
+ builder.setId(getNodeId(path));
+ builder.setMoreReplies(Boolean.FALSE);
+ builder.setNodeConnector(Collections.singletonList(ncBuilder.build()));
+ builder.setNodeConnectorStatisticsAndPortNumberMap(Collections
+ .singletonList(new NodeConnectorStatisticsAndPortNumberMapBuilder(o).build()));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import com.google.common.base.Preconditions;
+import java.util.Collections;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.queues.Queue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.FlowCapableNodeConnectorQueueStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.QueueStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.QueueStatisticsUpdateBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.queue.id.and.statistics.map.QueueIdAndStatisticsMapBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementation define a contract between {@link FlowCapableNodeConnectorQueueStatisticsData} data object
+ * and {@link QueueStatisticsUpdate} notification.
+ */
+public class QueueStatNotificationSupplierImpl extends
+ AbstractNotificationSupplierForItemStat<FlowCapableNodeConnectorQueueStatisticsData, QueueStatisticsUpdate> {
+
+ private static final InstanceIdentifier<FlowCapableNodeConnectorQueueStatisticsData> wildCardedInstanceIdent =
+ getNodeWildII().child(NodeConnector.class)
+ .augmentation(FlowCapableNodeConnector.class).child(Queue.class)
+ .augmentation(FlowCapableNodeConnectorQueueStatisticsData.class);
+
+ /**
+ * Constructor register supplier as DataChangeLister and create wildCarded InstanceIdentifier.
+ *
+ * @param notifProviderService - {@link NotificationProviderService}
+ * @param db - {@link DataBroker}
+ */
+ public QueueStatNotificationSupplierImpl(final NotificationProviderService notifProviderService, final DataBroker db) {
+ super(notifProviderService, db, FlowCapableNodeConnectorQueueStatisticsData.class);
+ }
+
+ @Override
+ public InstanceIdentifier<FlowCapableNodeConnectorQueueStatisticsData> getWildCardPath() {
+ return wildCardedInstanceIdent;
+ }
+
+ @Override
+ public QueueStatisticsUpdate createNotification(final FlowCapableNodeConnectorQueueStatisticsData o,
+ final InstanceIdentifier<FlowCapableNodeConnectorQueueStatisticsData> path) {
+ Preconditions.checkArgument(o != null);
+ Preconditions.checkArgument(path != null);
+
+ final NodeConnectorBuilder connBuilder = new NodeConnectorBuilder();
+ final NodeConnectorKey key = path.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+ connBuilder.setId(key.getId());
+ connBuilder.setKey(key);
+
+ final QueueIdAndStatisticsMapBuilder queueStatMapBuilder =
+ new QueueIdAndStatisticsMapBuilder(o.getFlowCapableNodeConnectorQueueStatistics());
+
+ final QueueStatisticsUpdateBuilder builder = new QueueStatisticsUpdateBuilder();
+ builder.setId(getNodeId(path));
+ builder.setMoreReplies(Boolean.FALSE);
+ builder.setQueueIdAndStatisticsMap(Collections.singletonList(queueStatMapBuilder.build()));
+ builder.setNodeConnector(Collections.singletonList(connBuilder.build()));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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 contains OF Statistics Items (Flow, Group, Meter ...) notification listener/supplier implementations
+ */
+package org.opendaylight.openflowplugin.applications.notification.supplier.impl.item.stat;
\ No newline at end of file
--- /dev/null
+/*
+ * 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 contains notification listener/supplier implementations
+ */
+package org.opendaylight.openflowplugin.applications.notification.supplier.impl;
\ No newline at end of file
/**
* Base project package for a module Provider and base notification listener/supplier interface
*/
-package org.opendaylight.openflowplugin.applications.old.notification.supplier;
\ No newline at end of file
+package org.opendaylight.openflowplugin.applications.notification.supplier;
\ No newline at end of file
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.openflowplugin.applications.old.notification.supplier.tools;
+package org.opendaylight.openflowplugin.applications.notification.supplier.tools;
/**
* Class is designed as ConfigSubsitem settings holder
*/
-public class OldNotifProviderConfig {
+public class NotificationProviderConfig {
private final boolean flowSupport;
private final boolean meterSupport;
private final boolean queueStatSupport;
private final boolean flowStatSupport;
- private OldNotifProviderConfig(final OldNotifProviderConfigBuilder builder) {
+ private NotificationProviderConfig(final NotificationProviderConfigBuilder builder) {
this.flowSupport = builder.isFlowSupport();
this.meterSupport = builder.isMeterSupport();
this.groupSupport = builder.isGroupSupport();
return flowStatSupport;
}
- public static OldNotifProviderConfigBuilder builder() {
- return new OldNotifProviderConfigBuilder();
+ public static NotificationProviderConfigBuilder builder() {
+ return new NotificationProviderConfigBuilder();
}
- public static class OldNotifProviderConfigBuilder {
+ public static class NotificationProviderConfigBuilder {
private boolean flowSupport;
private boolean meterSupport;
private boolean groupSupport;
this.flowStatSupport = flowStatSupport;
}
- public OldNotifProviderConfig build() {
- return new OldNotifProviderConfig(this);
+ public NotificationProviderConfig build() {
+ return new NotificationProviderConfig(this);
}
}
}
/**
* Package for all internal helper classes.
*/
-package org.opendaylight.openflowplugin.applications.old.notification.supplier.tools;
\ No newline at end of file
+package org.opendaylight.openflowplugin.applications.notification.supplier.tools;
\ No newline at end of file
--- /dev/null
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.notification.supplier.rev150820;
+public class NotifModule extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.notification.supplier.rev150820.AbstractNotifModule {
+ public NotifModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public NotifModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.notification.supplier.rev150820.NotifModule oldModule, java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ // TODO:implement
+ throw new java.lang.UnsupportedOperationException();
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: notification-supplier yang module local name: notification-supplier
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Sun May 22 10:45:47 IST 2016
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.notification.supplier.rev150820;
+public class NotifModuleFactory extends org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.notification.supplier.rev150820.AbstractNotifModuleFactory {
+
+}
<modules xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<module>
- <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:applications:old-notification-supplier">prefix:old-notification-supplier</type>
- <name>old-notification-supplier</name>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:openflow:applications:notification-supplier">prefix:notification-supplier</type>
+ <name>notification-supplier</name>
<data-broker>
<type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
<name>binding-data-broker</name>
<name>binding-notification-broker</name>
</notification-service>
- <old-notification-supplier-settings>
+ <notification-supplier-settings>
<!--<port-support>true</port-support>-->
<!--<flow-support>true</flow-support>-->
<meter-support>true</meter-support>
<meter-stat-support>false</meter-stat-support>
<queue-stat-support>false</queue-stat-support>
<flow-stat-support>false</flow-stat-support>
- </old-notification-supplier-settings>
+ </notification-supplier-settings>
</module>
</configuration>
<required-capabilities>
- <capability>urn:opendaylight:params:xml:ns:yang:openflow:applications:old-notification-supplier?module=old-notification-supplier&revision=2015-08-20</capability>
+ <capability>urn:opendaylight:params:xml:ns:yang:openflow:applications:notification-supplier?module=notification-supplier&revision=2015-08-20</capability>
<capability>urn:opendaylight:flow:inventory?module=flow-node-inventory&revision=2013-08-19</capability>
<capability>urn:opendaylight:inventory?module=opendaylight-inventory&revision=2013-08-19</capability>
<capability>urn:opendaylight:flow:types?module=opendaylight-flow-types&revision=2013-10-26</capability>
-module old-notification-supplier {
+module notification-supplier {
yang-version 1;
- namespace "urn:opendaylight:params:xml:ns:yang:openflow:applications:old-notification-supplier";
- prefix "old-notif";
+ namespace "urn:opendaylight:params:xml:ns:yang:openflow:applications:notification-supplier";
+ prefix "notification";
import config {prefix config; revision-date 2013-04-05;}
import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28;}
description
- "Translator tool for relevant wildcard path DataChangeEvent to Old Notifications.";
+ "Translator tool for relevant wildcard path DataChangeEvent to Notifications.";
revision "2015-08-20" {
description
"Initial revision";
}
- identity old-notification-supplier {
+ identity notification-supplier {
base "config:module-type";
- config:java-name-prefix OldNotif;
+ config:java-name-prefix Notif;
}
augment "/config:modules/config:module/config:configuration" {
- case old-notification-supplier {
- when "/config:modules/config:module/config:type = 'old-notification-supplier'";
+ case notification-supplier {
+ when "/config:modules/config:module/config:type = 'notification-supplier'";
container notification-service {
uses config:service-ref {
}
}
- container old-notification-supplier-settings {
+ container notification-supplier-settings {
leaf flow-support {
type boolean;
default true;
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.tools.NotificationProviderConfig;
+import org.opendaylight.openflowplugin.applications.notification.supplier.tools.NotificationProviderConfig.NotificationProviderConfigBuilder;
+
+public class NotificationProviderImplTest {
+
+ private NotificationProviderService notificationProviderService;
+ private NotificationProviderConfig config;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initialization() {
+ dataBroker = mock(DataBroker.class);
+ notificationProviderService = mock(NotificationProviderService.class);
+ }
+
+ @Test
+ public void testCreateAllSuppliers() {
+ final NotificationProviderConfig config = createAllConfigSupplier();
+ final NotificationProviderImpl provider = new NotificationProviderImpl(config, notificationProviderService, dataBroker);
+ provider.start();
+ final List<NotificationSupplierDefinition<?>> listSuppliers = provider.getSupplierList();
+ int nrOfSuppliers = 0;
+ for (final NotificationSupplierDefinition<?> supplier : listSuppliers) {
+ if (supplier != null) {
+ nrOfSuppliers++;
+ }
+ }
+ assertEquals(11, nrOfSuppliers);
+ }
+
+ @Test
+ public void testCreateRootSuppliersOnly() {
+ final NotificationProviderConfig config = createNonConfigSupplier();
+ final NotificationProviderImpl provider = new NotificationProviderImpl(config, notificationProviderService, dataBroker);
+ provider.start();
+ final List<NotificationSupplierDefinition<?>> listSuppliers = provider.getSupplierList();
+ int nrOfSuppliers = 0;
+ for (final NotificationSupplierDefinition<?> supplier : listSuppliers) {
+ if (supplier != null) {
+ nrOfSuppliers++;
+ }
+ }
+ assertEquals(2, nrOfSuppliers);
+ }
+
+ private NotificationProviderConfig createAllConfigSupplier() {
+ final NotificationProviderConfigBuilder builder = new NotificationProviderConfigBuilder();
+ builder.setFlowStatSupport(true);
+ builder.setFlowSupport(true);
+ builder.setFlowTableStatSupport(true);
+ builder.setGroupStatSupport(true);
+ builder.setGroupSupport(true);
+ builder.setMeterStatSupport(true);
+ builder.setMeterSupport(true);
+ builder.setNodeConnectorStatSupport(true);
+ builder.setQueueStatSupport(true);
+ return builder.build();
+ }
+
+ private NotificationProviderConfig createNonConfigSupplier() {
+ final NotificationProviderConfigBuilder builder = new NotificationProviderConfigBuilder();
+ builder.setFlowStatSupport(false);
+ builder.setFlowSupport(false);
+ builder.setFlowTableStatSupport(false);
+ builder.setGroupStatSupport(false);
+ builder.setGroupSupport(false);
+ builder.setMeterStatSupport(false);
+ builder.setMeterSupport(false);
+ builder.setNodeConnectorStatSupport(false);
+ builder.setQueueStatSupport(false);
+ return builder.build();
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class NodeConnectorNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final String FLOW_CODE_CONNECTOR_ID = "test-con-111";
+ private NodeConnectorNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new NodeConnectorNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final NodeConnectorUpdated notification = notifSupplierImpl.createNotification(createTestFlowCapableNodeConnecor(),
+ createTestFlowCapableConnectorNodePath());
+ assertNotNull(notification);
+ assertEquals(FLOW_CODE_CONNECTOR_ID, notification.getId().getValue());
+ assertEquals(FLOW_CODE_CONNECTOR_ID, notification.getNodeConnectorRef().getValue()
+ .firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNodeConnectorRef().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestFlowCapableConnectorNodePath(), createTestFlowCapableNodeConnecor());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(NodeConnectorUpdated.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestFlowCapableConnectorNodePath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlowCapableNodeConnecor(), null);
+ }
+
+ @Test
+ public void testDelete() {
+ final NodeConnectorRemoved notification = notifSupplierImpl.deleteNotification(createTestFlowCapableConnectorNodePath());
+ assertNotNull(notification);
+ assertEquals(FLOW_CODE_CONNECTOR_ID, notification.getNodeConnectorRef().getValue()
+ .firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNodeConnectorRef().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testDeleteChangeEvent() {
+ final Set<InstanceIdentifier<?>> removeData = new HashSet<>();
+ removeData.add(createTestFlowCapableConnectorNodePath());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(null, null, removeData));
+ verify(notifProviderService, times(1)).publish(Matchers.any(NodeConnectorRemoved.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDeleteFromNullPath() {
+ notifSupplierImpl.deleteNotification(null);
+ }
+
+ private static InstanceIdentifier<FlowCapableNodeConnector> createTestFlowCapableConnectorNodePath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(FLOW_CODE_CONNECTOR_ID))).augmentation(FlowCapableNodeConnector.class);
+ }
+
+ private static FlowCapableNodeConnector createTestFlowCapableNodeConnecor() {
+ final FlowCapableNodeConnectorBuilder builder = new FlowCapableNodeConnectorBuilder();
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class NodeNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private NodeNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new NodeNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final NodeUpdated notification = notifSupplierImpl.createNotification(createTestFlowCapableNode(),
+ createTestFlowCapableNodePath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNodeRef().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestFlowCapableNodePath(), createTestFlowCapableNode());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(NodeUpdated.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNode() {
+ notifSupplierImpl.createNotification(null, createTestFlowCapableNodePath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlowCapableNode(), null);
+ }
+
+ @Test
+ public void testDelete() {
+ final NodeRemoved notification = notifSupplierImpl.deleteNotification(createTestFlowCapableNodePath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getNodeRef().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testDeleteChangeEvent() {
+ final Set<InstanceIdentifier<?>> removeData = new HashSet<>();
+ removeData.add(createTestFlowCapableNodePath());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(null, null, removeData));
+ verify(notifProviderService, times(1)).publish(Matchers.any(NodeRemoved.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDeleteFromNullPath() {
+ notifSupplierImpl.deleteNotification(null);
+ }
+
+ private static InstanceIdentifier<FlowCapableNode> createTestFlowCapableNodePath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class);
+ }
+
+ private static FlowCapableNode createTestFlowCapableNode() {
+ final FlowCapableNodeBuilder builder = new FlowCapableNodeBuilder();
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.helper;
+
+import static org.mockito.Mockito.mock;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class TestChangeEventBuildHelper {
+
+ private TestChangeEventBuildHelper() {
+ throw new UnsupportedOperationException("Test utility class");
+ }
+
+ public static AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> createTestDataEvent(
+ final Map<InstanceIdentifier<?>, DataObject> createdData,
+ final Map<InstanceIdentifier<?>, DataObject> updatedData,
+ final Set<InstanceIdentifier<?>> removedData) {
+ return new AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject>() {
+
+ @Override
+ public DataObject getUpdatedSubtree() {
+ return mock(DataObject.class);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+ if (updatedData != null) {
+ return Collections.unmodifiableMap(updatedData);
+ } else {
+ return Collections.emptyMap();
+ }
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedPaths() {
+ if (removedData != null) {
+ return Collections.unmodifiableSet(removedData);
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+ @Override
+ public DataObject getOriginalSubtree() {
+ return mock(DataObject.class);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+ if (createdData != null) {
+ return Collections.unmodifiableMap(createdData);
+ } else {
+ return Collections.emptyMap();
+ }
+ }
+ };
+ }
+
+ public static AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> createEmptyTestDataEvent() {
+ return new AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject>() {
+
+ @Override
+ public DataObject getUpdatedSubtree() {
+ return mock(DataObject.class);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedPaths() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public DataObject getOriginalSubtree() {
+ return mock(DataObject.class);
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+ return Collections.emptyMap();
+ }
+ };
+ }
+
+ public static AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> createNullTestDataEvent() {
+ return new AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject>() {
+
+ @Override
+ public DataObject getUpdatedSubtree() {
+ return null;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getUpdatedData() {
+ return null;
+ }
+
+ @Override
+ public Set<InstanceIdentifier<?>> getRemovedPaths() {
+ return null;
+ }
+
+ @Override
+ public DataObject getOriginalSubtree() {
+ return null;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getOriginalData() {
+ return null;
+ }
+
+ @Override
+ public Map<InstanceIdentifier<?>, DataObject> getCreatedData() {
+ return null;
+ }
+ };
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.helper;
+
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Created by mirehak on 9/2/15.
+ */
+public class TestSupplierVerifyHelper {
+
+ private TestSupplierVerifyHelper() {
+ throw new UnsupportedOperationException("Test utility class");
+ }
+
+ /**
+ * check if wildcarded path is not null
+ *
+ * @param dataBroker
+ */
+ public static void verifyDataChangeRegistration(DataBroker dataBroker) {
+ Mockito.verify(dataBroker).registerDataChangeListener(
+ Matchers.eq(LogicalDatastoreType.OPERATIONAL),
+ Matchers.notNull(InstanceIdentifier.class),
+ Matchers.notNull(DataChangeListener.class),
+ Matchers.eq(AsyncDataBroker.DataChangeScope.BASE));
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class FlowNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final Short FLOW_TABLE_ID = 111;
+ private static final String FLOW_ID = "test-flow-111";
+ private FlowNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new FlowNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final FlowAdded notification = notifSupplierImpl.createNotification(createTestFlow(), createTestFlowPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_ID, notification.getFlowRef().getValue().firstKeyOf(Flow.class, FlowKey.class).getId().getValue());
+ assertEquals(FLOW_TABLE_ID, notification.getFlowRef().getValue().firstKeyOf(Table.class, TableKey.class).getId());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestFlowPath(), createTestFlow());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(FlowAdded.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestFlowPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlow(), null);
+ }
+
+ @Test
+ public void testUpdate() {
+ final FlowUpdated notification = notifSupplierImpl.updateNotification(createTestFlow(), createTestFlowPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_ID, notification.getFlowRef().getValue().firstKeyOf(Flow.class, FlowKey.class).getId().getValue());
+ assertEquals(FLOW_TABLE_ID, notification.getFlowRef().getValue().firstKeyOf(Table.class, TableKey.class).getId());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testUpdateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestFlowPath(), createTestFlow());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(FlowUpdated.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestFlowPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlow(), null);
+ }
+
+ @Test
+ public void testDelete() {
+ final FlowRemoved notification = notifSupplierImpl.deleteNotification(createTestFlowPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_ID, notification.getFlowRef().getValue().firstKeyOf(Flow.class, FlowKey.class).getId().getValue());
+ assertEquals(FLOW_TABLE_ID, notification.getFlowRef().getValue().firstKeyOf(Table.class, TableKey.class).getId());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testDeleteChangeEvent() {
+ final Set<InstanceIdentifier<?>> removeData = new HashSet<>();
+ removeData.add(createTestFlowPath());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(null, null, removeData));
+ verify(notifProviderService, times(1)).publish(Matchers.any(FlowRemoved.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDeleteFromNullPath() {
+ notifSupplierImpl.deleteNotification(null);
+ }
+
+ private static InstanceIdentifier<Flow> createTestFlowPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(FLOW_TABLE_ID))
+ .child(Flow.class, new FlowKey(new FlowId(FLOW_ID)));
+ }
+
+ private static Flow createTestFlow() {
+ final FlowBuilder builder = new FlowBuilder();
+ builder.setId(new FlowId(FLOW_ID));
+ builder.setTableId(FLOW_TABLE_ID);
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.GroupUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class GroupNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final Long GROUP_ID = 111L;
+ private GroupNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new GroupNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final GroupAdded notification = notifSupplierImpl.createNotification(createTestGroup(), createTestGroupPath());
+ assertNotNull(notification);
+ assertEquals(GROUP_ID, notification.getGroupId().getValue());
+ assertEquals(GROUP_ID, notification.getGroupRef().getValue().firstKeyOf(Group.class, GroupKey.class).getGroupId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestGroupPath(), createTestGroup());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(GroupAdded.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestGroupPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestGroup(), null);
+ }
+
+ @Test
+ public void testUpdate() {
+ final GroupUpdated notification = notifSupplierImpl.updateNotification(createTestGroup(), createTestGroupPath());
+ assertNotNull(notification);
+ assertEquals(GROUP_ID, notification.getGroupId().getValue());
+ assertEquals(GROUP_ID, notification.getGroupRef().getValue().firstKeyOf(Group.class, GroupKey.class).getGroupId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testUpdateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestGroupPath(), createTestGroup());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(GroupUpdated.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestGroupPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestGroup(), null);
+ }
+
+ @Test
+ public void testDelete() {
+ final GroupRemoved notification = notifSupplierImpl.deleteNotification(createTestGroupPath());
+ assertNotNull(notification);
+ assertEquals(GROUP_ID, notification.getGroupId().getValue());
+ assertEquals(GROUP_ID, notification.getGroupRef().getValue().firstKeyOf(Group.class, GroupKey.class).getGroupId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testDeleteChangeEvent() {
+ final Set<InstanceIdentifier<?>> removeData = new HashSet<>();
+ removeData.add(createTestGroupPath());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(null, null, removeData));
+ verify(notifProviderService, times(1)).publish(Matchers.any(GroupRemoved.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDeleteFromNullPath() {
+ notifSupplierImpl.deleteNotification(null);
+ }
+
+ private static InstanceIdentifier<Group> createTestGroupPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class).child(Group.class, new GroupKey(new GroupId(GROUP_ID)));
+ }
+
+ private static Group createTestGroup() {
+ final GroupBuilder builder = new GroupBuilder();
+ builder.setGroupId(new GroupId(GROUP_ID));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterAdded;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class MeterNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final Long METER_ID = 111L;
+ private MeterNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new MeterNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final MeterAdded notification = notifSupplierImpl.createNotification(createTestMeter(), createTestMeterPath());
+ assertNotNull(notification);
+ assertEquals(METER_ID, notification.getMeterId().getValue());
+ assertEquals(METER_ID, notification.getMeterRef().getValue().firstKeyOf(Meter.class, MeterKey.class).getMeterId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestMeterPath(), createTestMeter());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(MeterAdded.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestMeterPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestMeter(), null);
+ }
+
+ @Test
+ public void testUpdate() {
+ final MeterUpdated notification = notifSupplierImpl
+ .updateNotification(createTestMeter(), createTestMeterPath());
+ assertNotNull(notification);
+ assertEquals(METER_ID, notification.getMeterId().getValue());
+ assertEquals(METER_ID, notification.getMeterRef().getValue().firstKeyOf(Meter.class, MeterKey.class).getMeterId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testUdateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestMeterPath(), createTestMeter());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(MeterUpdated.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestMeterPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestMeter(), null);
+ }
+
+ @Test
+ public void testDelete() {
+ final MeterRemoved notification = notifSupplierImpl.deleteNotification(createTestMeterPath());
+ assertNotNull(notification);
+ assertEquals(METER_ID, notification.getMeterId().getValue());
+ assertEquals(METER_ID, notification.getMeterRef().getValue().firstKeyOf(Meter.class, MeterKey.class).getMeterId().getValue());
+ assertEquals(FLOW_NODE_ID, notification.getNode().getValue().firstKeyOf(Node.class, NodeKey.class).getId().getValue());
+ }
+
+ @Test
+ public void testDeleteChangeEvent() {
+ final Set<InstanceIdentifier<?>> removeData = new HashSet<>();
+ removeData.add(createTestMeterPath());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(null, null, removeData));
+ verify(notifProviderService, times(1)).publish(Matchers.any(MeterRemoved.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testDeleteFromNullPath() {
+ notifSupplierImpl.deleteNotification(null);
+ }
+
+ private static InstanceIdentifier<Meter> createTestMeterPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class).child(Meter.class, new MeterKey(new MeterId(METER_ID)));
+ }
+
+ private static Meter createTestMeter() {
+ final MeterBuilder builder = new MeterBuilder();
+ builder.setMeterId(new MeterId(METER_ID));
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class FlowStatNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final Short FLOW_TABLE_ID = 111;
+ private static final String FLOW_ID = "test-flow-111";
+ private FlowStatNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new FlowStatNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final FlowsStatisticsUpdate notification = notifSupplierImpl.createNotification(createTestFlowStat(), createTestFlowStatPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestFlowStatPath(), createTestFlowStat());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(FlowsStatisticsUpdate.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestFlowStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlowStat(), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestFlowStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlowStat(), null);
+ }
+
+ private static InstanceIdentifier<FlowStatistics> createTestFlowStatPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(FLOW_TABLE_ID))
+ .child(Flow.class, new FlowKey(new FlowId(FLOW_ID))).augmentation(FlowStatisticsData.class)
+ .child(FlowStatistics.class);
+ }
+
+ private static FlowStatistics createTestFlowStat() {
+ final FlowStatisticsBuilder builder = new FlowStatisticsBuilder();
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.flow.table.statistics.FlowTableStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class FlowTableStatNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final Short FLOW_TABLE_ID = 111;
+ private static final String FLOW_ID = "test-flow-111";
+ private FlowTableStatNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new FlowTableStatNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final FlowTableStatisticsUpdate notification = notifSupplierImpl.createNotification(createTestFlowTableStat(),
+ createTestFlowTableStatPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestFlowTableStatPath(), createTestFlowTableStat());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(FlowTableStatisticsUpdate.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestFlowTableStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlowTableStat(), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestFlowTableStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestFlowTableStat(), null);
+ }
+
+ private static InstanceIdentifier<FlowTableStatistics> createTestFlowTableStatPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(FLOW_TABLE_ID))
+ .augmentation(FlowTableStatisticsData.class).child(FlowTableStatistics.class);
+ }
+
+ private static FlowTableStatistics createTestFlowTableStat() {
+ final FlowTableStatisticsBuilder builder = new FlowTableStatisticsBuilder();
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class GroupStatNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final Long FLOW_TABLE_ID = 111L;
+ private GroupStatNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new GroupStatNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final GroupStatisticsUpdated notification = notifSupplierImpl.createNotification(createTestGroupStat(),
+ createTestGroupStatPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestGroupStatPath(), createTestGroupStat());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(GroupStatisticsUpdated.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestGroupStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestGroupStat(), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestGroupStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestGroupStat(), null);
+ }
+
+ private static InstanceIdentifier<GroupStatistics> createTestGroupStatPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class).child(Group.class, new GroupKey(new GroupId(FLOW_TABLE_ID)))
+ .augmentation(NodeGroupStatistics.class).child(GroupStatistics.class);
+ }
+
+ private static GroupStatistics createTestGroupStat() {
+ final GroupStatisticsBuilder builder = new GroupStatisticsBuilder();
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterStatisticsUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.meter.MeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.meter.MeterStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class MeterStatNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final Long FLOW_METER_ID = 111L;
+ private MeterStatNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new MeterStatNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final MeterStatisticsUpdated notification = notifSupplierImpl.createNotification(createTestMeterStat(),
+ createTestMeterStatPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestMeterStatPath(), createTestMeterStat());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(MeterStatisticsUpdated.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestMeterStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestMeterStat(), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestMeterStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestMeterStat(), null);
+ }
+
+ private static InstanceIdentifier<MeterStatistics> createTestMeterStatPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .augmentation(FlowCapableNode.class).child(Meter.class, new MeterKey(new MeterId(FLOW_METER_ID)))
+ .augmentation(NodeMeterStatistics.class).child(MeterStatistics.class);
+ }
+
+ private static MeterStatistics createTestMeterStat() {
+ final MeterStatisticsBuilder builder = new MeterStatisticsBuilder();
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.flow.capable.node.connector.statistics.FlowCapableNodeConnectorStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.flow.capable.node.connector.statistics.FlowCapableNodeConnectorStatisticsBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class NodeConnectorStatNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final String FLOW_CODE_CONNECTOR_ID = "test-con-111";
+ private NodeConnectorStatNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new NodeConnectorStatNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final NodeConnectorStatisticsUpdate notification = notifSupplierImpl.createNotification(createTestConnectorStat(),
+ createTestConnectorStatPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getId().getValue());
+ assertEquals(FLOW_CODE_CONNECTOR_ID, notification.getNodeConnector().get(0).getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestConnectorStatPath(), createTestConnectorStat());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(NodeConnectorStatisticsUpdate.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestConnectorStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestConnectorStat(), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestConnectorStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestConnectorStat(), null);
+ }
+
+ private static InstanceIdentifier<FlowCapableNodeConnectorStatistics> createTestConnectorStatPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(FLOW_CODE_CONNECTOR_ID)))
+ .augmentation(FlowCapableNodeConnectorStatisticsData.class)
+ .child(FlowCapableNodeConnectorStatistics.class);
+ }
+
+ private static FlowCapableNodeConnectorStatistics createTestConnectorStat() {
+ final FlowCapableNodeConnectorStatisticsBuilder builder = new FlowCapableNodeConnectorStatisticsBuilder();
+ return builder.build();
+ }
+}
+
--- /dev/null
+/*
+ * 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.openflowplugin.applications.notification.supplier.impl.item.stat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestChangeEventBuildHelper;
+import org.opendaylight.openflowplugin.applications.notification.supplier.impl.helper.TestSupplierVerifyHelper;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.queues.Queue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.FlowCapableNodeConnectorQueueStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.FlowCapableNodeConnectorQueueStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.QueueStatisticsUpdate;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.flow.capable.node.connector.queue.statistics.FlowCapableNodeConnectorQueueStatisticsBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ *
+ */
+public class QueueStatNotificationSupplierImplTest {
+
+ private static final String FLOW_NODE_ID = "test-111";
+ private static final String FLOW_CODE_CONNECTOR_ID = "test-con-111";
+ private QueueStatNotificationSupplierImpl notifSupplierImpl;
+ private NotificationProviderService notifProviderService;
+ private DataBroker dataBroker;
+
+ @Before
+ public void initalization() {
+ notifProviderService = mock(NotificationProviderService.class);
+ dataBroker = mock(DataBroker.class);
+ notifSupplierImpl = new QueueStatNotificationSupplierImpl(notifProviderService, dataBroker);
+ TestSupplierVerifyHelper.verifyDataChangeRegistration(dataBroker);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNullChangeEvent() {
+ notifSupplierImpl.onDataChanged(null);
+ }
+
+ @Test
+ public void testNullableChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testEmptyChangeEvent() {
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createEmptyTestDataEvent());
+ }
+
+ @Test
+ public void testCreate() {
+ final QueueStatisticsUpdate notification = notifSupplierImpl.createNotification(createTestQueueStat(),
+ createTestQueueStatPath());
+ assertNotNull(notification);
+ assertEquals(FLOW_NODE_ID, notification.getId().getValue());
+ assertEquals(FLOW_CODE_CONNECTOR_ID, notification.getNodeConnector().get(0).getId().getValue());
+ }
+
+ @Test
+ public void testCreateChangeEvent() {
+ final Map<InstanceIdentifier<?>, DataObject> createdData = new HashMap<>();
+ createdData.put(createTestQueueStatPath(), createTestQueueStat());
+ notifSupplierImpl.onDataChanged(TestChangeEventBuildHelper.createTestDataEvent(createdData, null, null));
+ verify(notifProviderService, times(1)).publish(Matchers.any(QueueStatisticsUpdate.class));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestQueueStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testCreateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestQueueStat(), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullNodeConnector() {
+ notifSupplierImpl.createNotification(null, createTestQueueStatPath());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateFromNullPath() {
+ notifSupplierImpl.createNotification(createTestQueueStat(), null);
+ }
+
+ private static InstanceIdentifier<FlowCapableNodeConnectorQueueStatisticsData> createTestQueueStatPath() {
+ return InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(new NodeId(FLOW_NODE_ID)))
+ .child(NodeConnector.class, new NodeConnectorKey(new NodeConnectorId(FLOW_CODE_CONNECTOR_ID)))
+ .augmentation(FlowCapableNodeConnector.class).child(Queue.class)
+ .augmentation(FlowCapableNodeConnectorQueueStatisticsData.class);
+ }
+
+ private static FlowCapableNodeConnectorQueueStatisticsData createTestQueueStat() {
+ final FlowCapableNodeConnectorQueueStatisticsDataBuilder builder = new FlowCapableNodeConnectorQueueStatisticsDataBuilder();
+ final FlowCapableNodeConnectorQueueStatisticsBuilder value = new FlowCapableNodeConnectorQueueStatisticsBuilder();
+ builder.setFlowCapableNodeConnectorQueueStatistics(value.build());
+ return builder.build();
+ }
+}
+
+++ /dev/null
-/*
- * 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.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.old.notification.supplier.rev150820;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.config.api.DependencyResolver;
-import org.opendaylight.controller.config.api.ModuleIdentifier;
-import org.opendaylight.openflowplugin.applications.old.notification.supplier.tools.OldNotifProviderConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Generated module introducer for OldNotificationSupplier Module class.
- */
-public class OldNotifModule extends AbstractOldNotifModule {
-
- static final Logger LOG = LoggerFactory.getLogger(OldNotifModule.class);
-
- private static final String LOAD_SETTINGS_XML_FAIL = "Load the xml ConfigSubsystem input value fail!";
- private static final boolean DEFAULT_ITEM_NOTIF_ALLOWED = true;
- private static final boolean DEFAULT_STAT_NOTIF_ALLOWED = false;
-
- /**
- * Module constructor
- *
- * @param identifier - {@link ModuleIdentifier}
- * @param dependencyResolver - {@link DependencyResolver}
- */
- public OldNotifModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver) {
- super(identifier, dependencyResolver);
- }
-
- /**
- * Module constructor
- *
- * @param identifier - {@link ModuleIdentifier}
- * @param dependencyResolver - {@link DependencyResolver}
- * @param oldModule - {@link OldNotifModule}
- * @param oldInstance - {@link AutoCloseable}
- */
- public OldNotifModule(final ModuleIdentifier identifier, final DependencyResolver dependencyResolver,
- final OldNotifModule oldModule, final AutoCloseable oldInstance) {
- super(identifier, dependencyResolver, oldModule, oldInstance);
- }
-
- @Override
- public void customValidation() {
- // add custom validation form module attributes here.
- }
-
- @Override
- public java.lang.AutoCloseable createInstance() {
- LOG.info("OldNotificationSupplier module initialization.");
- LOG.warn("OldNotificationSupplier module is marked like DEPRECATED. Modul could supplie old notification only for lithium release.");
- final OldNotifProviderConfig config = createConfig();
- return null;
- }
-
- /*
- * Help method for populating all ConfigSubsystem values to OldNotifProviderConfig immutable value holder.
- */
- private OldNotifProviderConfig createConfig() {
- final OldNotificationSupplierSettings settings = Preconditions.checkNotNull(getOldNotificationSupplierSettings());
- final OldNotifProviderConfig.OldNotifProviderConfigBuilder builder = OldNotifProviderConfig.builder();
- if (settings.getFlowSupport() != null) {
- builder.setFlowSupport(settings.getFlowSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " FlowSupport value is set to {} ", DEFAULT_ITEM_NOTIF_ALLOWED);
- builder.setFlowSupport(DEFAULT_ITEM_NOTIF_ALLOWED);
- }
-
- if (settings.getMeterSupport() != null) {
- builder.setMeterSupport(settings.getMeterSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " MeterSupport value is set to {} ", DEFAULT_ITEM_NOTIF_ALLOWED);
- builder.setMeterSupport(DEFAULT_ITEM_NOTIF_ALLOWED);
- }
- if (settings.getGroupSupport() != null) {
- builder.setGroupSupport(settings.getGroupSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " GroupSupport value is set to {} ", DEFAULT_ITEM_NOTIF_ALLOWED);
- builder.setGroupSupport(DEFAULT_ITEM_NOTIF_ALLOWED);
- }
- if (settings.getNodeConnectorStatSupport() != null) {
- builder.setNodeConnectorStatSupport(settings.getNodeConnectorStatSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " NodeConnectorStatSupport value is set to {} ", DEFAULT_STAT_NOTIF_ALLOWED);
- builder.setNodeConnectorStatSupport(DEFAULT_STAT_NOTIF_ALLOWED);
- }
- if (settings.getFlowTableStatSupport() != null) {
- builder.setFlowTableStatSupport(settings.getFlowTableStatSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " FlowTableStatSupport value is set to {} ", DEFAULT_STAT_NOTIF_ALLOWED);
- builder.setFlowTableStatSupport(DEFAULT_STAT_NOTIF_ALLOWED);
- }
- if (settings.getGroupStatSupport() != null) {
- builder.setGroupStatSupport(settings.getGroupStatSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " GroupStatSupport value is set to {} ", DEFAULT_STAT_NOTIF_ALLOWED);
- builder.setGroupStatSupport(DEFAULT_STAT_NOTIF_ALLOWED);
- }
- if (settings.getMeterStatSupport() != null) {
- builder.setMeterStatSupport(settings.getMeterStatSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " MeterStatSupport value is set to {} ", DEFAULT_STAT_NOTIF_ALLOWED);
- builder.setMeterStatSupport(DEFAULT_STAT_NOTIF_ALLOWED);
- }
- if (settings.getQueueStatSupport() != null) {
- builder.setQueueStatSupport(settings.getQueueStatSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " QueueStatSupport value is set to {} ", DEFAULT_STAT_NOTIF_ALLOWED);
- builder.setQueueStatSupport(DEFAULT_STAT_NOTIF_ALLOWED);
- }
- if (settings.getFlowStatSupport() != null) {
- builder.setFlowStatSupport(settings.getFlowStatSupport());
- } else {
- LOG.warn(LOAD_SETTINGS_XML_FAIL + " QueueStatSupport value is set to {} ", DEFAULT_STAT_NOTIF_ALLOWED);
- builder.setFlowStatSupport(DEFAULT_STAT_NOTIF_ALLOWED);
- }
- return builder.build();
- }
-}
+++ /dev/null
-/*
-* Generated file
-*
-* Generated from: yang module name: old-notification-supplier yang module local name: old-notification-supplier
-* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
-* Generated at: Fri Aug 21 00:31:49 CEST 2015
-*
-* Do not modify this file unless it is present under src/main directory
-*/
-package org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.old.notification.supplier.rev150820;
-
-public class OldNotifModuleFactory
- extends
- org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.applications.old.notification.supplier.rev150820.AbstractOldNotifModuleFactory {
-
-}
<module>of-switch-config-pusher</module>
<module>lldp-speaker</module>
<!-- deprecated apps -->
- <module>old-notification-supplier</module>
+ <module>notification-supplier</module>
<!-- nsf apps -->
<module>inventory-manager</module>
<module>statistics-manager</module>
<module>topology-manager</module>
<module>forwardingrules-manager</module>
<module>forwardingrules-manager-config</module>
+ <module>forwardingrules-sync</module>
<module>topology-lldp-discovery</module>
<!--<module>features</module>-->
<!-- experimental apps -->
private static final String DEFAULT_ARBITRARY_BIT_MASK = "255.255.255.255";
private static final String PREFIX_SEPARATOR = "/";
private static final int IPV4_ADDRESS_LENGTH = 32;
+ private static final int BYTE_SIZE = 8;
/*
* Custom EthernetMatch is required because mac address string provided by user in EthernetMatch can be in any case
if (! (statsIpMask.length > 1 && storedIpMask.length > 1 && statsIpMask[1].equals(storedIpMask[1]))){
return false;
}
- if (InetAddresses.forString(statsIpMask[0]).equals(InetAddresses.forString(storedIpMask[0]))){
+
+ final int prefix = Integer.parseInt(statsIpMask[1]);
+ final int byteIndex = prefix/BYTE_SIZE;
+ final int lastByteBits = BYTE_SIZE - (prefix % BYTE_SIZE);
+ final InetAddress statsIp = InetAddresses.forString(statsIpMask[0]);
+ final InetAddress storedIp = InetAddresses.forString(storedIpMask[0]);
+ byte[] statsIpArr = Arrays.copyOfRange(statsIp.getAddress(),0,byteIndex+1);
+ byte[] storedIpArr = Arrays.copyOfRange(storedIp.getAddress(),0,byteIndex+1);
+ statsIpArr[byteIndex] = (byte) (statsIpArr[byteIndex] & (0XFF << lastByteBits));
+ storedIpArr[byteIndex] = (byte) (storedIpArr[byteIndex] & (0XFF << lastByteBits));
+ if(Arrays.equals(statsIpArr,storedIpArr)) {
return true;
}
return false;
}
-
static Boolean checkNullValues(final Object v1, final Object v2) {
Boolean verdict = null;
if (v1 == null && v2 != null) {
import static org.junit.Assert.fail;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DottedQuad;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
import org.junit.Test;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
/**
* @author sai.marapareddy@gmail.com (arbitrary masks)
MatchComparatorHelper.layer3MatchEquals(new ArpMatchBuilder().build(), new ArpMatchBuilder().build()));
}
+ @Test
+ public void layer3MatchEqualsIpv6Test() {
+ final Ipv6MatchBuilder statsBuilder = new Ipv6MatchBuilder();
+ final Ipv6MatchBuilder storedBuilder = new Ipv6MatchBuilder();
+ assertEquals(true, MatchComparatorHelper.layer3MatchEquals(statsBuilder.build(), storedBuilder.build()));
+
+ statsBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000D:0000:0000:0000:5D99/64"));
+ storedBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000D:0000:0000:0000:4D99/64"));
+ assertEquals(true, MatchComparatorHelper.layer3MatchEquals(statsBuilder.build(), storedBuilder.build()));
+
+ statsBuilder.setIpv6Destination(new Ipv6Prefix("aabb:1234:2acf:000d:0000:0000:0000:5d99/64"));
+ storedBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000D:0000:0000:0000:4D99/64"));
+ assertEquals(true, MatchComparatorHelper.layer3MatchEquals(statsBuilder.build(), storedBuilder.build()));
+
+ statsBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000C:0000:0000:0000:5D99/64"));
+ storedBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000D:0000:0000:0000:4D99/64"));
+ assertEquals(false, MatchComparatorHelper.layer3MatchEquals(statsBuilder.build(), storedBuilder.build()));
+
+ statsBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000C:0000:0000:0000:5D99/63"));
+ storedBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000D:0000:0000:0000:4D99/63"));
+ assertEquals(true, MatchComparatorHelper.layer3MatchEquals(statsBuilder.build(), storedBuilder.build()));
+
+ statsBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000D:0000:0000:0000:5D99/63"));
+ storedBuilder.setIpv6Destination(new Ipv6Prefix("AABB:1234:2ACF:000E:0000:0000:0000:4D99/63"));
+ assertEquals(false, MatchComparatorHelper.layer3MatchEquals(statsBuilder.build(), storedBuilder.build()));
+ }
+
@Test
public void layer3MatchEqualsIpv4ArbitraryMaskTest(){
final Ipv4MatchBuilder statsBuilder = new Ipv4MatchBuilder();
<classifier>config</classifier>
<type>xml</type>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}.applications</groupId>
+ <artifactId>forwardingrules-sync</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}.applications</groupId>
+ <artifactId>forwardingrules-sync</artifactId>
+ <version>${project.version}</version>
+ <classifier>config</classifier>
+ <type>xml</type>
+ </dependency>
<dependency>
<groupId>${project.groupId}.applications</groupId>
<artifactId>inventory-manager</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}.applications</groupId>
- <artifactId>old-notification-supplier</artifactId>
+ <artifactId>notification-supplier</artifactId>
<version>${project.version}</version>
<type>xml</type>
<classifier>config</classifier>
</dependency>
<dependency>
<groupId>${project.groupId}.applications</groupId>
- <artifactId>old-notification-supplier</artifactId>
+ <artifactId>notification-supplier</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<classifier>config</classifier>
<type>xml</type>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.openflowplugin.applications</groupId>
+ <artifactId>forwardingrules-sync</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.openflowplugin.applications</groupId>
<artifactId>inventory-manager</artifactId>
</dependency>
<dependency>
<groupId>org.opendaylight.openflowplugin.applications</groupId>
- <artifactId>old-notification-supplier</artifactId>
+ <artifactId>notification-supplier</artifactId>
<type>xml</type>
<classifier>config</classifier>
</dependency>
<dependency>
<groupId>org.opendaylight.openflowplugin.applications</groupId>
- <artifactId>old-notification-supplier</artifactId>
+ <artifactId>notification-supplier</artifactId>
</dependency>
</dependencies>
</project>
<configfile finalname="etc/opendaylight/karaf/71-bulk-o-matic.xml">mvn:org.opendaylight.openflowplugin.applications/bulk-o-matic/${project.version}/xml/config</configfile>
</feature>
- <feature name='odl-openflowplugin-old-notifications-li' description="OpenDaylight :: Openflow Plugin :: app old notifications supplier" version='${project.version}'>
+ <feature name='odl-openflowplugin-notifications-li' description="OpenDaylight :: Openflow Plugin :: app notifications supplier" version='${project.version}'>
<feature version='${mdsal.version}'>odl-mdsal-broker</feature>
<feature version="${project.version}">odl-openflowplugin-nsf-model-li</feature>
- <bundle>mvn:org.opendaylight.openflowplugin.applications/old-notification-supplier/{{VERSION}}</bundle>
- <configfile finalname="etc/opendaylight/karaf/73-old-notification-supplier.xml">mvn:org.opendaylight.openflowplugin.applications/old-notification-supplier/{{VERSION}}/xml/config</configfile>
+ <bundle>mvn:org.opendaylight.openflowplugin.applications/notification-supplier/{{VERSION}}</bundle>
+ <configfile finalname="etc/opendaylight/karaf/73-notification-supplier.xml">mvn:org.opendaylight.openflowplugin.applications/notification-supplier/{{VERSION}}/xml/config</configfile>
</feature>
</features>
--- /dev/null
+module opendaylight-direct-statistics {
+ namespace "urn:opendaylight:direct:statistics";
+ prefix directstat;
+
+ import yang-ext { prefix ext; revision-date "2013-07-09"; }
+ import ietf-inet-types { prefix inet; revision-date "2010-09-24"; }
+ import opendaylight-inventory { prefix inv; revision-date "2013-08-19"; }
+ import opendaylight-statistics-types { prefix stat-types; revision-date "2013-09-25"; }
+
+ import opendaylight-flow-types { prefix flow-types; revision-date "2013-10-26"; }
+ import opendaylight-group-types { prefix group-types; revision-date "2013-10-18"; }
+ import opendaylight-meter-types { prefix meter-types; revision-date "2013-09-18"; }
+ import opendaylight-queue-types { prefix queue-types; revision-date "2013-09-25"; }
+ import opendaylight-table-types { prefix table-types; revision-date "2013-10-26"; }
+
+ import opendaylight-flow-statistics { prefix flowstat; revision-date "2013-08-19"; }
+ import opendaylight-port-statistics { prefix portstat; revision-date "2013-12-14"; }
+ import opendaylight-queue-statistics { prefix queuestat; revision-date "2013-12-16"; }
+
+ description "Openflow direct statistics polling.";
+
+ revision "2016-05-11" {
+ description "Initial revision of direct statistics service";
+ }
+
+ grouping store-stats-grouping {
+ description "Store collected statistics to DS/operational";
+
+ leaf store-stats {
+ type boolean;
+ default false;
+ }
+ }
+
+ grouping stats-input-common-grouping {
+ description "Shared input parameters for all rpc statistics (routing context and datastore flag)";
+
+ uses inv:node-context-ref;
+ uses store-stats-grouping;
+ }
+
+ rpc get-flow-statistics {
+ description "Get statistics for given flow";
+
+ input {
+ uses stats-input-common-grouping;
+ uses flow-types:flow;
+ }
+
+ output {
+ uses flowstat:flow-and-statistics-map-list;
+ }
+ }
+
+ rpc get-group-statistics {
+ description "Get statistics for given group";
+
+ input {
+ uses stats-input-common-grouping;
+
+ leaf group-id {
+ type group-types:group-id;
+ }
+ }
+
+ output {
+ uses group-types:group-statistics-reply;
+ }
+ }
+
+ rpc get-meter-statistics {
+ description "Get statistics for given meter";
+
+ input {
+ uses stats-input-common-grouping;
+
+ leaf meter-id {
+ type meter-types:meter-id;
+ }
+ }
+
+ output {
+ uses meter-types:meter-statistics-reply;
+ }
+ }
+
+ rpc get-node-connector-statistics {
+ description "Get statistics for given node connector from the node";
+
+ input {
+ uses stats-input-common-grouping;
+
+ leaf node-connector-id {
+ description "Optional, if omitted, returns statistics for all ports";
+ type inv:node-connector-id;
+ }
+ }
+
+ output {
+ uses portstat:node-connector-statistics-and-port-number-map;
+ }
+ }
+
+ rpc get-queue-statistics {
+ description "Get statistics for given queues from given port of the node";
+
+ input {
+ uses stats-input-common-grouping;
+
+ leaf node-connector-id {
+ type inv:node-connector-id;
+ }
+
+ leaf queue-id {
+ type queue-types:queue-id;
+ }
+ }
+
+ output {
+ uses queuestat:queue-id-and-statistics-map;
+ }
+ }
+}
* @return sync. future for Slave and MD-SAL completition for Master
*/
ListenableFuture<Void> shuttingDownDataStoreTransactions();
+
/**
* Method provides current devices connection context.
*
void setDeviceSynchronized(boolean deviceSynchronized);
- void setRole(OfpRole ofpRole);
-
- OfpRole getRole();
-
boolean isStatisticsPollingEnabled();
void setStatisticsPollingEnabledProp(boolean statPollEnabled);
<T extends DataObject> void writeToTransaction(final LogicalDatastoreType store, final InstanceIdentifier<T> path,
final T data) throws Exception;
+ /**
+ * Method creates put operation using provided data in underlying transaction chain and flag to create missing parents
+ * WARNING: This method is slow because of additional reading cost. Use it only if you really need to create parents.
+ */
+ <T extends DataObject> void writeToTransactionWithParentsSlow(final LogicalDatastoreType store, final InstanceIdentifier<T> path,
+ final T data) throws Exception;
+
/**
* Method creates delete operation for provided path in underlying transaction chain.
*/
import io.netty.util.TimerTask;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager;
+import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageIntelligenceAgency;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
*/
void setSafelyDeviceManager(final DeviceManager deviceManager);
+ /**
+ * Setter for statistics manager once set it cant be unset or overwritten
+ * @param statisticsManager should be set in OpenFlowPluginProviderImpl
+ */
+ void setSafelyStatisticsManager(final StatisticsManager statisticsManager);
+
/**
* Xid from outboundqueue
* @param nodeId
@Override
void close();
+
+ void setSchedulingEnabled(boolean schedulingEnabled);
+ boolean isSchedulingEnabled();
}
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceTerminationPhaseHandler;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler;
import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceLifecycleSupervisor;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
/**
* Created by Martin Bobak <mbobak@cisco.com> on 26.2.2015.
public interface StatisticsManager extends DeviceLifecycleSupervisor, DeviceInitializationPhaseHandler,
DeviceTerminationPhaseHandler, AutoCloseable {
+ void startScheduling(NodeId nodeId);
+ void stopScheduling(NodeId nodeId);
+
@Override
void close();
}
import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleConductor;
import org.opendaylight.openflowplugin.api.openflow.lifecycle.RoleChangeListener;
import org.opendaylight.openflowplugin.api.openflow.lifecycle.ServiceChangeListener;
+import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageIntelligenceAgency;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
private DeviceManager deviceManager;
private final MessageIntelligenceAgency messageIntelligenceAgency;
private ConcurrentHashMap<NodeId, ServiceChangeListener> serviceChangeListeners = new ConcurrentHashMap<>();
+ private StatisticsManager statisticsManager;
public LifecycleConductorImpl(final MessageIntelligenceAgency messageIntelligenceAgency) {
Preconditions.checkNotNull(messageIntelligenceAgency);
}
}
+ public void setSafelyStatisticsManager(final StatisticsManager statisticsManager) {
+ if (this.statisticsManager == null) {
+ this.statisticsManager = statisticsManager;
+ }
+ }
+
public void addOneTimeListenerWhenServicesChangesDone(final ServiceChangeListener manager, final NodeId nodeId){
LOG.debug("Listener {} for service change for node {} registered.", manager, nodeId);
serviceChangeListeners.put(nodeId, manager);
LOG.debug("Initialization phase skipping starting services.");
return;
}
- LOG.info("Role change to {} in role context for node {} was successful, staring/stopping services.", newRole, nodeId);
-
- //TODO: This is old way to check if statistics is running, remove after statistics changes implemented
- final DeviceState deviceState = deviceContext.getDeviceState();
- if (null != deviceState) {
- if (OfpRole.BECOMEMASTER.equals(newRole) && (getDeviceContext(nodeId) != null)) {
- deviceState.setRole(OfpRole.BECOMEMASTER);
- } else {
- deviceState.setRole(OfpRole.BECOMESLAVE);
- }
+
+ LOG.info("Role change to {} in role context for node {} was successful, starting/stopping services.", newRole, nodeId);
+
+ if (OfpRole.BECOMEMASTER.equals(newRole)) {
+ statisticsManager.startScheduling(nodeId);
+ } else {
+ statisticsManager.stopScheduling(nodeId);
}
final ListenableFuture<Void> onClusterRoleChange = deviceContext.onClusterRoleChange(null, newRole);
roleManager = new RoleManagerImpl(entityOwnershipService, dataBroker, conductor);
statisticsManager = new StatisticsManagerImpl(rpcProviderRegistry, isStatisticsPollingOff, conductor);
+ conductor.setSafelyStatisticsManager(statisticsManager);
rpcManager = new RpcManagerImpl(rpcProviderRegistry, rpcRequestsQuota, conductor);
roleManager.addRoleChangeListener((RoleChangeListener) conductor);
@Override
public <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
final InstanceIdentifier<T> path, final T data) throws Exception {
- transactionChainManager.writeToTransaction(store, path, data);
+ transactionChainManager.writeToTransaction(store, path, data, false);
+ }
+
+ @Override
+ public <T extends DataObject> void writeToTransactionWithParentsSlow(LogicalDatastoreType store, InstanceIdentifier<T> path, T data) throws Exception {
+ transactionChainManager.writeToTransaction(store, path, data, true);
}
@Override
.child(Flow.class, new FlowKey(flowDescriptor.getFlowId()));
// b) notify listener
itemLifecycleListener.onRemoved(flowPath);
+ // c) trigger off a notification
+ notificationPublishService.offerNotification(flowRemovedNotification);
} else {
LOG.debug("flow id not found: nodeId={} tableId={}, priority={}",
getDeviceState().getNodeId(), flowRegKey.getTableId(), flowRemovedNotification.getPriority());
private boolean portStatisticsAvailable;
private boolean statPollEnabled;
private boolean queueStatisticsAvailable;
- private volatile OfpRole role;
public DeviceStateImpl(@CheckForNull final FeaturesReply featuresReply, @Nonnull final NodeId nodeId) {
Preconditions.checkArgument(featuresReply != null);
version = featuresReply.getVersion();
statPollEnabled = false;
deviceSynchronized = false;
- role = OfpRole.BECOMESLAVE;
}
@Override
deviceSynchronized = _deviceSynchronized;
}
- @Override
- public OfpRole getRole() {
- return role;
- }
-
- @Override
- public void setRole(OfpRole role) {
- this.role = role;
- }
-
@Override
public boolean isStatisticsPollingEnabled() {
return statPollEnabled;
}
<T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
- final InstanceIdentifier<T> path, final T data) throws Exception {
+ final InstanceIdentifier<T> path,
+ final T data,
+ final boolean createParents) throws Exception {
final WriteTransaction writeTx = getTransactionSafely();
if (writeTx != null) {
LOG.trace("writeToTransaction called with path {} ", path);
- writeTx.put(store, path, data);
+ writeTx.put(store, path, data, createParents);
} else {
LOG.debug("WriteTx is null for node {}. Write data for {} was not realized.", nodeII, path);
throw new Exception("Cannot write into transaction.");
private Timeout pollTimeout;
private final LifecycleConductor conductor;
+ private volatile boolean schedulingEnabled;
public StatisticsContextImpl(@CheckForNull final NodeId nodeId, final boolean shuttingDownStatisticsPolling, final LifecycleConductor lifecycleConductor) {
this.conductor = lifecycleConductor;
@Override
public void close() {
+ schedulingEnabled = false;
for (final Iterator<RequestContext<?>> iterator = Iterators.consumingIterator(requestContexts.iterator());
iterator.hasNext();) {
RequestContextUtil.closeRequestContextWithRpcError(iterator.next(), CONNECTION_CLOSED);
}
if (null != pollTimeout && !pollTimeout.isExpired()) {
pollTimeout.cancel();
- }
}
+ }
+
+ @Override
+ public void setSchedulingEnabled(final boolean schedulingEnabled) {
+ this.schedulingEnabled = schedulingEnabled;
+ }
+
+ @Override
+ public boolean isSchedulingEnabled() {
+ return schedulingEnabled;
+ }
@Override
public void setPollTimeout(final Timeout pollTimeout) {
package org.opendaylight.openflowplugin.impl.statistics;
-import javax.annotation.CheckForNull;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
-import java.util.Iterator;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.Future;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.sm.control.rev150812.GetStatisticsWorkModeOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.sm.control.rev150812.StatisticsManagerControlService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.sm.control.rev150812.StatisticsWorkMode;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import java.util.Iterator;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
public class StatisticsManagerImpl implements StatisticsManager, StatisticsManagerControlService {
private static final Logger LOG = LoggerFactory.getLogger(StatisticsManagerImpl.class);
private static final long basicTimerDelay = 3000;
private static long currentTimerDelay = basicTimerDelay;
- private static long maximumTimerDelay = 900000; //wait max 15 minutes for next statistics
+ private static final long maximumTimerDelay = 900000; //wait max 15 minutes for next statistics
private StatisticsWorkMode workMode = StatisticsWorkMode.COLLECTALL;
private final Semaphore workModeGuard = new Semaphore(1, true);
final StatisticsContext statisticsContext = new StatisticsContextImpl(nodeId, shuttingDownStatisticsPolling, conductor);
Verify.verify(contexts.putIfAbsent(nodeId, statisticsContext) == null, "StatisticsCtx still not closed for Node {}", nodeId);
- if (shuttingDownStatisticsPolling) {
- LOG.info("Statistics is shutdown for node:{}", nodeId);
- } else {
- LOG.info("Schedule Statistics poll for node:{}", nodeId);
- scheduleNextPolling(deviceContext, statisticsContext, new TimeCounter());
- }
-
deviceContext.getDeviceState().setDeviceSynchronized(true);
deviceInitPhaseHandler.onDeviceContextLevelUp(nodeId);
}
void pollStatistics(final DeviceContext deviceContext,
final StatisticsContext statisticsContext,
final TimeCounter timeCounter) {
+
+ final NodeId nodeId = deviceContext.getDeviceState().getNodeId();
+
+ if (!statisticsContext.isSchedulingEnabled()) {
+ LOG.debug("Disabling statistics scheduling for device: {}", nodeId);
+ return;
+ }
if (!deviceContext.getDeviceState().isValid()) {
- LOG.debug("Session for device {} is not valid.", deviceContext.getDeviceState().getNodeId().getValue());
+ LOG.debug("Session is not valid for device: {}", nodeId);
return;
}
+
if (!deviceContext.getDeviceState().isStatisticsPollingEnabled()) {
- LOG.debug("StatisticsPolling is disabled for device: {} , try later", deviceContext.getDeviceState().getNodeId());
+ LOG.debug("Statistics polling is currently disabled for device: {}", nodeId);
scheduleNextPolling(deviceContext, statisticsContext, timeCounter);
return;
}
- if (!OfpRole.BECOMEMASTER.equals(deviceContext.getDeviceState().getRole())) {
- LOG.debug("Role is not Master so we don't want to poll any stat for device: {}", deviceContext.getDeviceState().getNodeId());
- scheduleNextPolling(deviceContext, statisticsContext, timeCounter);
- return;
- }
- LOG.debug("POLLING ALL STATS for device: {}", deviceContext.getDeviceState().getNodeId().getValue());
+ LOG.debug("POLLING ALL STATISTICS for device: {}", nodeId);
timeCounter.markStart();
final ListenableFuture<Boolean> deviceStatisticsCollectionFuture = statisticsContext.gatherDynamicData();
Futures.addCallback(deviceStatisticsCollectionFuture, new FutureCallback<Boolean>() {
@Override
public void run(final Timeout timeout) throws Exception {
if (!deviceStatisticsCollectionFuture.isDone()) {
- LOG.info("Statistics collection for node {} still in progress even after {} secs", deviceContext
- .getDeviceState().getNodeId(), STATS_TIMEOUT_SEC);
+ LOG.info("Statistics collection for node {} still in progress even after {} secs", nodeId, STATS_TIMEOUT_SEC);
deviceStatisticsCollectionFuture.cancel(true);
}
}
};
+
conductor.newTimeout(timerTask, STATS_TIMEOUT_SEC, TimeUnit.SECONDS);
}
private void scheduleNextPolling(final DeviceContext deviceContext,
final StatisticsContext statisticsContext,
final TimeCounter timeCounter) {
- LOG.debug("SCHEDULING NEXT STATS POLLING for device: {}", deviceContext.getDeviceState().getNodeId().getValue());
+ LOG.debug("SCHEDULING NEXT STATISTICS POLLING for device: {}", deviceContext.getDeviceState().getNodeId());
if (!shuttingDownStatisticsPolling) {
final Timeout pollTimeout = conductor.newTimeout(new TimerTask() {
@Override
public void onDeviceContextLevelDown(final DeviceContext deviceContext) {
final StatisticsContext statisticsContext = contexts.remove(deviceContext.getDeviceState().getNodeId());
if (null != statisticsContext) {
- LOG.trace("Removing device context from stack. No more statistics gathering for node {}", deviceContext.getDeviceState().getNodeId());
+ LOG.trace("Removing device context from stack. No more statistics gathering for device: {}", deviceContext.getDeviceState().getNodeId());
statisticsContext.close();
}
deviceTerminPhaseHandler.onDeviceContextLevelDown(deviceContext);
}
break;
default:
- LOG.warn("statistics work mode not supported: {}", targetWorkMode);
+ LOG.warn("Statistics work mode not supported: {}", targetWorkMode);
}
}
workMode = targetWorkMode;
return result;
}
+ @Override
+ public void startScheduling(final NodeId nodeId) {
+ if (shuttingDownStatisticsPolling) {
+ LOG.info("Statistics are shut down for device: {}", nodeId);
+ return;
+ }
+
+ final StatisticsContext statisticsContext = contexts.get(nodeId);
+
+ if (statisticsContext == null) {
+ LOG.warn("Statistics context not found for device: {}", nodeId);
+ return;
+ }
+
+ if (statisticsContext.isSchedulingEnabled()) {
+ LOG.debug("Statistics scheduling is already enabled for device: {}", nodeId);
+ return;
+ }
+
+ LOG.info("Scheduling statistics poll for device: {}", nodeId);
+ final DeviceContext deviceContext = conductor.getDeviceContext(nodeId);
+
+ if (deviceContext == null) {
+ LOG.warn("Device context not found for device: {}", nodeId);
+ return;
+ }
+
+ statisticsContext.setSchedulingEnabled(true);
+ scheduleNextPolling(deviceContext, statisticsContext, new TimeCounter());
+ }
+
+ @Override
+ public void stopScheduling(final NodeId nodeId) {
+ LOG.debug("Stopping statistics scheduling for device: {}", nodeId);
+ final StatisticsContext statisticsContext = contexts.get(nodeId);
+
+ if (statisticsContext == null) {
+ LOG.warn("Statistics context not found for device: {}", nodeId);
+ return;
+ }
+
+ statisticsContext.setSchedulingEnabled(false);
+ }
+
@Override
public void close() {
if (controlServiceRegistration != null) {
protected OfHeader buildRequest(final Xid xid, final GetFlowStatisticsFromFlowTableInput input) {
final MultipartRequestFlowCaseBuilder multipartRequestFlowCaseBuilder = new MultipartRequestFlowCaseBuilder();
final MultipartRequestFlowBuilder mprFlowRequestBuilder = new MultipartRequestFlowBuilder();
- mprFlowRequestBuilder.setTableId(input.getTableId());
+
+ if (input.getTableId() != null) {
+ mprFlowRequestBuilder.setTableId(input.getTableId());
+ } else {
+ mprFlowRequestBuilder.setTableId(OFConstants.OFPTT_ALL);
+ }
if (input.getOutPort() != null) {
mprFlowRequestBuilder.setOutPort(input.getOutPort().longValue());
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.device.Xid;
+import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
+import org.opendaylight.openflowplugin.impl.services.AbstractMultipartService;
+import org.opendaylight.openflowplugin.impl.services.RequestInputUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.StoreStatsGrouping;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.MultipartRequestBody;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.concurrent.Future;
+
+/**
+ * The abstract direct statistics service.
+ * This abstract service provides wrappers and tools for all other derived statistics services.
+ *
+ * @param <I> the input type parameter
+ * @param <O> the output type parameter
+ */
+public abstract class AbstractDirectStatisticsService<I extends StoreStatsGrouping, O> extends AbstractMultipartService<I> {
+
+ private final Function<RpcResult<List<MultipartReply>>, RpcResult<O>> resultTransformFunction =
+ new Function<RpcResult<List<MultipartReply>>, RpcResult<O>>() {
+ @Nullable
+ @Override
+ public RpcResult<O> apply(@Nullable RpcResult<List<MultipartReply>> input) {
+ Preconditions.checkNotNull(input);
+ final O reply = buildReply(input.getResult(), input.isSuccessful());
+ return RpcResultBuilder.success(reply).build();
+ }
+ };
+
+ private final AsyncFunction<RpcResult<O>, RpcResult<O>> resultStoreFunction =
+ new AsyncFunction<RpcResult<O>, RpcResult<O>>() {
+ @Nullable
+ @Override
+ public ListenableFuture<RpcResult<O>> apply(@Nullable RpcResult<O> input) throws Exception {
+ Preconditions.checkNotNull(input);
+
+ if (input.isSuccessful()) {
+ storeStatistics(input.getResult());
+ getDeviceContext().submitTransaction(); // TODO: If submitTransaction will ever return future, chain it
+ }
+
+ return Futures.immediateFuture(input);
+ }
+ };
+
+ private final MultipartType multipartType;
+ private final OpenflowVersion ofVersion = OpenflowVersion.get(getVersion());
+
+ /**
+ * Instantiates a new Abstract direct statistics service.
+ *
+ * @param multipartType the multipart type
+ * @param requestContextStack the request context stack
+ * @param deviceContext the device context
+ */
+ protected AbstractDirectStatisticsService(MultipartType multipartType, RequestContextStack requestContextStack, DeviceContext deviceContext) {
+ super(requestContextStack, deviceContext);
+ this.multipartType = multipartType;
+ }
+
+ /**
+ * Handle input and reply future.
+ *
+ * @param input the input
+ * @return the future
+ */
+ public Future<RpcResult<O>> handleAndReply(final I input) {
+ final ListenableFuture<RpcResult<List<MultipartReply>>> rpcReply = handleServiceCall(input);
+ ListenableFuture<RpcResult<O>> rpcResult = Futures.transform(rpcReply, resultTransformFunction);
+
+ if (Boolean.TRUE.equals(input.isStoreStats())) {
+ rpcResult = Futures.transform(rpcResult, resultStoreFunction);
+ }
+
+ return rpcResult;
+ }
+
+ @Override
+ protected OfHeader buildRequest(Xid xid, I input) throws Exception {
+ return RequestInputUtils.createMultipartHeader(multipartType, xid.getValue(), getVersion())
+ .setMultipartRequestBody(buildRequestBody(input))
+ .build();
+ }
+
+ /**
+ * Gets openflow version.
+ *
+ * @return the openflow version
+ */
+ protected OpenflowVersion getOfVersion() {
+ return ofVersion;
+ }
+
+ /**
+ * Build multipart request body.
+ *
+ * @param input the input
+ * @return the multipart request body
+ */
+ protected abstract MultipartRequestBody buildRequestBody(I input);
+
+ /**
+ * Build output from multipart reply input.
+ *
+ * @param input the input
+ * @return the output
+ */
+ protected abstract O buildReply(List<MultipartReply> input, boolean success);
+
+ /**
+ * Store statistics.
+ * TODO: Remove dependency on deviceContext from derived methods
+ * TODO: Return future, so we will be able to chain it
+ *
+ * @param output the output
+ * @throws Exception the exception
+ */
+ protected abstract void storeStatistics(O output) throws Exception;
+}
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.registry.flow.FlowRegistryKey;
+import org.opendaylight.openflowplugin.impl.registry.flow.FlowRegistryKeyFactory;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.FlowStatsResponseConvertor;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchReactor;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.flow._case.MultipartReplyFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.MultipartRequestBody;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestFlowCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.flow._case.MultipartRequestFlowBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The Flow direct statistics service.
+ */
+public class FlowDirectStatisticsService extends AbstractDirectStatisticsService<GetFlowStatisticsInput, GetFlowStatisticsOutput> {
+ private final FlowStatsResponseConvertor flowStatsConvertor = new FlowStatsResponseConvertor();
+
+ /**
+ * Instantiates a new Flow direct statistics service.
+ *
+ * @param requestContextStack the request context stack
+ * @param deviceContext the device context
+ */
+ public FlowDirectStatisticsService(RequestContextStack requestContextStack, DeviceContext deviceContext) {
+ super(MultipartType.OFPMPFLOW, requestContextStack, deviceContext);
+ }
+
+ @Override
+ protected MultipartRequestBody buildRequestBody(GetFlowStatisticsInput input) {
+ final MultipartRequestFlowBuilder mprFlowRequestBuilder = new MultipartRequestFlowBuilder();
+
+ if (input.getTableId() != null) {
+ mprFlowRequestBuilder.setTableId(input.getTableId());
+ } else {
+ mprFlowRequestBuilder.setTableId(OFConstants.OFPTT_ALL);
+ }
+
+ if (input.getOutPort() != null) {
+ mprFlowRequestBuilder.setOutPort(input.getOutPort().longValue());
+ } else {
+ mprFlowRequestBuilder.setOutPort(OFConstants.OFPP_ANY);
+ }
+
+ if (input.getOutGroup() != null) {
+ mprFlowRequestBuilder.setOutGroup(input.getOutGroup());
+ } else {
+ mprFlowRequestBuilder.setOutGroup(OFConstants.OFPG_ANY);
+ }
+
+ if (input.getCookie() != null) {
+ mprFlowRequestBuilder.setCookie(input.getCookie().getValue());
+ } else {
+ mprFlowRequestBuilder.setCookie(OFConstants.DEFAULT_COOKIE);
+ }
+
+ if (input.getCookieMask() != null) {
+ mprFlowRequestBuilder.setCookieMask(input.getCookieMask().getValue());
+ } else {
+ mprFlowRequestBuilder.setCookieMask(OFConstants.DEFAULT_COOKIE_MASK);
+ }
+
+ MatchReactor.getInstance().convert(input.getMatch(), getVersion(), mprFlowRequestBuilder, getDatapathId());
+
+ return new MultipartRequestFlowCaseBuilder()
+ .setMultipartRequestFlow(mprFlowRequestBuilder.build())
+ .build();
+ }
+
+ @Override
+ protected GetFlowStatisticsOutput buildReply(List<MultipartReply> input, boolean success) {
+ final List<FlowAndStatisticsMapList> statsList = new ArrayList<>();
+
+ if (success) {
+ for (final MultipartReply mpReply : input) {
+ final MultipartReplyFlowCase caseBody = (MultipartReplyFlowCase) mpReply.getMultipartReplyBody();
+ final MultipartReplyFlow replyBody = caseBody.getMultipartReplyFlow();
+
+ final List<FlowAndStatisticsMapList> statsListPart = flowStatsConvertor.toSALFlowStatsList(replyBody.getFlowStats(), getDatapathId(), getOfVersion());
+
+ for (final FlowAndStatisticsMapList part : statsListPart) {
+ final org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowId flowId =
+ new org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowId(generateFlowId(part).getValue());
+
+ statsList.add(new FlowAndStatisticsMapListBuilder(part)
+ .setKey(new FlowAndStatisticsMapListKey(flowId))
+ .setFlowId(flowId)
+ .build());
+ }
+ }
+ }
+
+ return new GetFlowStatisticsOutputBuilder()
+ .setFlowAndStatisticsMapList(statsList)
+ .build();
+ }
+
+ @Override
+ protected void storeStatistics(GetFlowStatisticsOutput output) throws Exception {
+ final InstanceIdentifier<FlowCapableNode> nodePath = getDeviceContext()
+ .getDeviceState().getNodeInstanceIdentifier().augmentation(FlowCapableNode.class);
+
+ for (final FlowAndStatisticsMapList flowStatistics : output.getFlowAndStatisticsMapList()) {
+ final FlowId flowId = generateFlowId(flowStatistics);
+ final FlowKey flowKey = new FlowKey(flowId);
+
+ final FlowStatisticsDataBuilder flowStatisticsDataBld = new FlowStatisticsDataBuilder()
+ .setFlowStatistics(new FlowStatisticsBuilder(flowStatistics).build());
+
+ final FlowBuilder flowBuilder = new FlowBuilder(flowStatistics)
+ .addAugmentation(FlowStatisticsData.class, flowStatisticsDataBld.build())
+ .setKey(flowKey);
+
+ final InstanceIdentifier<Flow> flowStatisticsPath = nodePath
+ .child(Table.class, new TableKey(flowStatistics.getTableId()))
+ .child(Flow.class, flowKey);
+
+ getDeviceContext().writeToTransactionWithParentsSlow(LogicalDatastoreType.OPERATIONAL, flowStatisticsPath, flowBuilder.build());
+ }
+ }
+
+ private FlowId generateFlowId(FlowAndStatisticsMapList flowStatistics) {
+ final FlowStatisticsDataBuilder flowStatisticsDataBld = new FlowStatisticsDataBuilder()
+ .setFlowStatistics(new FlowStatisticsBuilder(flowStatistics).build());
+
+ final FlowBuilder flowBuilder = new FlowBuilder(flowStatistics)
+ .addAugmentation(FlowStatisticsData.class, flowStatisticsDataBld.build());
+
+ final short tableId = flowStatistics.getTableId();
+ final FlowRegistryKey flowRegistryKey = FlowRegistryKeyFactory.create(flowBuilder.build());
+ return getDeviceContext().getDeviceFlowRegistry().storeIfNecessary(flowRegistryKey, tableId);
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.GroupStatsResponseConvertor;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.statistics.GroupStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.GroupKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.group._case.MultipartReplyGroup;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.MultipartRequestBody;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestGroupCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.group._case.MultipartRequestGroupBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The Group direct statistics service.
+ */
+public class GroupDirectStatisticsService extends AbstractDirectStatisticsService<GetGroupStatisticsInput, GetGroupStatisticsOutput> {
+ private final GroupStatsResponseConvertor groupStatsConvertor = new GroupStatsResponseConvertor();
+
+ /**
+ * Instantiates a new Group direct statistics service.
+ *
+ * @param requestContextStack the request context stack
+ * @param deviceContext the device context
+ */
+ public GroupDirectStatisticsService(RequestContextStack requestContextStack, DeviceContext deviceContext) {
+ super(MultipartType.OFPMPGROUP, requestContextStack, deviceContext);
+ }
+
+ @Override
+ protected MultipartRequestBody buildRequestBody(GetGroupStatisticsInput input) {
+ final MultipartRequestGroupBuilder mprGroupBuild = new MultipartRequestGroupBuilder();
+
+ if (input.getGroupId() != null) {
+ mprGroupBuild.setGroupId(new GroupId(input.getGroupId().getValue()));
+ } else {
+ mprGroupBuild.setGroupId(new GroupId(OFConstants.OFPG_ALL));
+ }
+
+ return new MultipartRequestGroupCaseBuilder()
+ .setMultipartRequestGroup(mprGroupBuild.build())
+ .build();
+ }
+
+ @Override
+ protected GetGroupStatisticsOutput buildReply(List<MultipartReply> input, boolean success) {
+ final List<GroupStats> groupStats = new ArrayList<>();
+
+ if (success) {
+ for (final MultipartReply mpReply : input) {
+ final MultipartReplyGroupCase caseBody = (MultipartReplyGroupCase) mpReply.getMultipartReplyBody();
+ final MultipartReplyGroup replyBody = caseBody.getMultipartReplyGroup();
+ groupStats.addAll(groupStatsConvertor.toSALGroupStatsList(replyBody.getGroupStats()));
+ }
+ }
+
+ return new GetGroupStatisticsOutputBuilder()
+ .setGroupStats(groupStats)
+ .build();
+ }
+
+ @Override
+ protected void storeStatistics(GetGroupStatisticsOutput output) throws Exception {
+ final InstanceIdentifier<FlowCapableNode> nodePath = getDeviceContext()
+ .getDeviceState().getNodeInstanceIdentifier().augmentation(FlowCapableNode.class);
+
+ for (final GroupStats groupStatistics : output.getGroupStats()) {
+ final InstanceIdentifier<GroupStatistics> groupStatisticsPath = nodePath
+ .child(Group.class, new GroupKey(groupStatistics.getGroupId()))
+ .augmentation(NodeGroupStatistics.class)
+ .child(GroupStatistics.class);
+
+ final GroupStatistics stats = new GroupStatisticsBuilder(groupStatistics).build();
+ getDeviceContext().writeToTransactionWithParentsSlow(LogicalDatastoreType.OPERATIONAL, groupStatisticsPath, stats);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.MeterStatsResponseConvertor;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.MeterKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.meter.MeterStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.meter.MeterStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MeterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.meter._case.MultipartReplyMeter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.MultipartRequestBody;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestMeterCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.meter._case.MultipartRequestMeterBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The Meter direct statistics service.
+ */
+public class MeterDirectStatisticsService extends AbstractDirectStatisticsService<GetMeterStatisticsInput, GetMeterStatisticsOutput> {
+ private final MeterStatsResponseConvertor meterStatsConvertor = new MeterStatsResponseConvertor();
+
+ /**
+ * Instantiates a new Meter direct statistics service.
+ *
+ * @param requestContextStack the request context stack
+ * @param deviceContext the device context
+ */
+ public MeterDirectStatisticsService(RequestContextStack requestContextStack, DeviceContext deviceContext) {
+ super(MultipartType.OFPMPMETER, requestContextStack, deviceContext);
+ }
+
+ @Override
+ protected MultipartRequestBody buildRequestBody(GetMeterStatisticsInput input) {
+ final MultipartRequestMeterBuilder mprMeterBuild = new MultipartRequestMeterBuilder();
+
+ if (input.getMeterId() != null) {
+ mprMeterBuild.setMeterId(new MeterId(input.getMeterId().getValue()));
+ } else {
+ mprMeterBuild.setMeterId(new MeterId(OFConstants.OFPM_ALL));
+ }
+
+ return new MultipartRequestMeterCaseBuilder()
+ .setMultipartRequestMeter(mprMeterBuild.build())
+ .build();
+ }
+
+ @Override
+ protected GetMeterStatisticsOutput buildReply(List<MultipartReply> input, boolean success) {
+ final List<MeterStats> meterStats = new ArrayList<>();
+
+ if (success) {
+ for (final MultipartReply mpReply : input) {
+ final MultipartReplyMeterCase caseBody = (MultipartReplyMeterCase) mpReply.getMultipartReplyBody();
+ final MultipartReplyMeter replyBody = caseBody.getMultipartReplyMeter();
+ meterStats.addAll(meterStatsConvertor.toSALMeterStatsList(replyBody.getMeterStats()));
+ }
+ }
+
+ return new GetMeterStatisticsOutputBuilder()
+ .setMeterStats(meterStats)
+ .build();
+ }
+
+ @Override
+ protected void storeStatistics(GetMeterStatisticsOutput output) throws Exception {
+ final InstanceIdentifier<FlowCapableNode> nodePath = getDeviceContext()
+ .getDeviceState().getNodeInstanceIdentifier().augmentation(FlowCapableNode.class);
+
+ for (final MeterStats meterStatistics : output.getMeterStats()) {
+ final InstanceIdentifier<MeterStatistics> meterPath = nodePath
+ .child(Meter.class, new MeterKey(meterStatistics.getMeterId()))
+ .augmentation(NodeMeterStatistics.class)
+ .child(MeterStatistics.class);
+
+ final MeterStatistics stats = new MeterStatisticsBuilder(meterStatistics).build();
+ getDeviceContext().writeToTransactionWithParentsSlow(LogicalDatastoreType.OPERATIONAL, meterPath, stats);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter32;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.duration.DurationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.node.connector.statistics.BytesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.node.connector.statistics.PacketsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyPortStatsCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.port.stats._case.MultipartReplyPortStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.port.stats._case.multipart.reply.port.stats.PortStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.MultipartRequestBody;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestPortStatsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.port.stats._case.MultipartRequestPortStatsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.FlowCapableNodeConnectorStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.flow.capable.node.connector.statistics.FlowCapableNodeConnectorStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.flow.capable.node.connector.statistics.FlowCapableNodeConnectorStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMapKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The Node connector direct statistics service.
+ */
+public class NodeConnectorDirectStatisticsService extends AbstractDirectStatisticsService<GetNodeConnectorStatisticsInput, GetNodeConnectorStatisticsOutput> {
+ /**
+ * Instantiates a new Node connector direct statistics service.
+ *
+ * @param requestContextStack the request context stack
+ * @param deviceContext the device context
+ */
+ public NodeConnectorDirectStatisticsService(RequestContextStack requestContextStack, DeviceContext deviceContext) {
+ super(MultipartType.OFPMPPORTSTATS, requestContextStack, deviceContext);
+ }
+
+ @Override
+ protected MultipartRequestBody buildRequestBody(GetNodeConnectorStatisticsInput input) {
+ final MultipartRequestPortStatsBuilder mprPortStatsBuilder = new MultipartRequestPortStatsBuilder();
+
+ if (input.getNodeConnectorId() != null) {
+ mprPortStatsBuilder.setPortNo(InventoryDataServiceUtil.portNumberfromNodeConnectorId(getOfVersion(), input.getNodeConnectorId()));
+ } else {
+ mprPortStatsBuilder.setPortNo(OFConstants.OFPP_ANY);
+ }
+
+ return new MultipartRequestPortStatsCaseBuilder()
+ .setMultipartRequestPortStats(mprPortStatsBuilder.build())
+ .build();
+ }
+
+ @Override
+ protected GetNodeConnectorStatisticsOutput buildReply(List<MultipartReply> input, boolean success) {
+ final List<NodeConnectorStatisticsAndPortNumberMap> nodeConnectorStatisticsAndPortNumberMap = new ArrayList<>();
+
+ if (success) {
+ for (final MultipartReply mpReply : input) {
+ final MultipartReplyPortStatsCase caseBody = (MultipartReplyPortStatsCase) mpReply.getMultipartReplyBody();
+ final MultipartReplyPortStats replyBody = caseBody.getMultipartReplyPortStats();
+
+ for (final PortStats portStats : replyBody.getPortStats()) {
+ final NodeConnectorId nodeConnectorId = InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(
+ getDatapathId(), portStats.getPortNo(), getOfVersion());
+
+ final BytesBuilder bytesBuilder = new BytesBuilder()
+ .setReceived(portStats.getRxBytes())
+ .setTransmitted(portStats.getTxBytes());
+
+ final PacketsBuilder packetsBuilder = new PacketsBuilder()
+ .setReceived(portStats.getRxPackets())
+ .setTransmitted(portStats.getTxPackets());
+
+ final DurationBuilder durationBuilder = new DurationBuilder();
+
+ if (portStats.getDurationSec() != null) {
+ durationBuilder.setSecond(new Counter32(portStats.getDurationSec()));
+ }
+
+ if (portStats.getDurationNsec() != null) {
+ durationBuilder.setNanosecond(new Counter32(portStats.getDurationNsec()));
+ }
+
+ final NodeConnectorStatisticsAndPortNumberMap stats = new NodeConnectorStatisticsAndPortNumberMapBuilder()
+ .setBytes(bytesBuilder.build())
+ .setPackets(packetsBuilder.build())
+ .setNodeConnectorId(nodeConnectorId)
+ .setDuration(durationBuilder.build())
+ .setCollisionCount(portStats.getCollisions())
+ .setKey(new NodeConnectorStatisticsAndPortNumberMapKey(nodeConnectorId))
+ .setReceiveCrcError(portStats.getRxCrcErr()).setReceiveDrops(portStats.getRxDropped())
+ .setReceiveErrors(portStats.getRxErrors())
+ .setReceiveFrameError(portStats.getRxFrameErr())
+ .setReceiveOverRunError(portStats.getRxOverErr())
+ .setTransmitDrops(portStats.getTxDropped())
+ .setTransmitErrors(portStats.getTxErrors())
+ .build();
+
+ nodeConnectorStatisticsAndPortNumberMap.add(stats);
+ }
+ }
+ }
+
+ return new GetNodeConnectorStatisticsOutputBuilder()
+ .setNodeConnectorStatisticsAndPortNumberMap(nodeConnectorStatisticsAndPortNumberMap)
+ .build();
+ }
+
+ @Override
+ protected void storeStatistics(GetNodeConnectorStatisticsOutput output) throws Exception {
+ final InstanceIdentifier<Node> nodePath = getDeviceContext().getDeviceState().getNodeInstanceIdentifier();
+
+ for (final NodeConnectorStatisticsAndPortNumberMap nodeConnectorStatistics : output.getNodeConnectorStatisticsAndPortNumberMap()) {
+ final InstanceIdentifier<FlowCapableNodeConnectorStatistics> nodeConnectorPath = nodePath
+ .child(NodeConnector.class, new NodeConnectorKey(nodeConnectorStatistics.getNodeConnectorId()))
+ .augmentation(FlowCapableNodeConnectorStatisticsData.class)
+ .child(FlowCapableNodeConnectorStatistics.class);
+
+ final FlowCapableNodeConnectorStatistics stats = new FlowCapableNodeConnectorStatisticsBuilder(nodeConnectorStatistics).build();
+ getDeviceContext().writeToTransactionWithParentsSlow(LogicalDatastoreType.OPERATIONAL, nodeConnectorPath, stats);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.OpendaylightDirectStatisticsService;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+
+import java.util.Optional;
+import java.util.concurrent.Future;
+
+/**
+ * The Opendaylight direct statistics service.
+ * This service handles RPC requests, sends them to registered handlers and returns their replies.
+ */
+public class OpendaylightDirectStatisticsServiceImpl implements OpendaylightDirectStatisticsService {
+ private final OpendaylightDirectStatisticsServiceProvider provider;
+
+ /**
+ * Instantiates a new Opendaylight direct statistics service.
+ *
+ * @param provider the openflow direct statistics service provider
+ */
+ public OpendaylightDirectStatisticsServiceImpl(final OpendaylightDirectStatisticsServiceProvider provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public Future<RpcResult<GetGroupStatisticsOutput>> getGroupStatistics(GetGroupStatisticsInput input) {
+ final Optional<GroupDirectStatisticsService> service = provider.lookup(GroupDirectStatisticsService.class);
+
+ if (!service.isPresent()) {
+ return missingImplementation(GroupDirectStatisticsService.class);
+ }
+
+ return service.get().handleAndReply(input);
+ }
+
+ @Override
+ public Future<RpcResult<GetQueueStatisticsOutput>> getQueueStatistics(GetQueueStatisticsInput input) {
+ final Optional<QueueDirectStatisticsService> service = provider.lookup(QueueDirectStatisticsService.class);
+
+ if (!service.isPresent()) {
+ return missingImplementation(QueueDirectStatisticsService.class);
+ }
+
+ return service.get().handleAndReply(input);
+ }
+
+ @Override
+ public Future<RpcResult<GetFlowStatisticsOutput>> getFlowStatistics(GetFlowStatisticsInput input) {
+ final Optional<FlowDirectStatisticsService> service = provider.lookup(FlowDirectStatisticsService.class);
+
+ if (!service.isPresent()) {
+ return missingImplementation(FlowDirectStatisticsService.class);
+ }
+
+ return service.get().handleAndReply(input);
+ }
+
+ @Override
+ public Future<RpcResult<GetMeterStatisticsOutput>> getMeterStatistics(GetMeterStatisticsInput input) {
+ final Optional<MeterDirectStatisticsService> service = provider.lookup(MeterDirectStatisticsService.class);
+
+ if (!service.isPresent()) {
+ return missingImplementation(MeterDirectStatisticsService.class);
+ }
+
+ return service.get().handleAndReply(input);
+ }
+
+ @Override
+ public Future<RpcResult<GetNodeConnectorStatisticsOutput>> getNodeConnectorStatistics(GetNodeConnectorStatisticsInput input) {
+ final Optional<NodeConnectorDirectStatisticsService> service = provider.lookup(NodeConnectorDirectStatisticsService.class);
+
+ if (!service.isPresent()) {
+ return missingImplementation(NodeConnectorDirectStatisticsService.class);
+ }
+
+ return service.get().handleAndReply(input);
+ }
+
+ private <T extends DataObject> Future<RpcResult<T>> missingImplementation(Class service) {
+ return RpcResultBuilder.<T>failed().withError(
+ RpcError.ErrorType.APPLICATION,
+ String.format("No implementation found for direct statistics service %s.", service.getCanonicalName()))
+ .buildFuture();
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * The Opendaylight direct statistics service provider.
+ */
+public class OpendaylightDirectStatisticsServiceProvider {
+ private Map<Class<? extends AbstractDirectStatisticsService>, AbstractDirectStatisticsService> services = new HashMap<>();
+
+ /**
+ * Register direct statistics service.
+ *
+ * @param type the service type
+ * @param service the service instance
+ */
+ public void register(Class<? extends AbstractDirectStatisticsService> type, AbstractDirectStatisticsService service) {
+ if (services.containsKey(type)) return;
+
+ services.put(type, service);
+ }
+
+ /**
+ * Lookup direct statistics service.
+ *
+ * @param <T> the type parameter
+ * @param type the service type
+ * @return the service instance
+ */
+ public <T extends AbstractDirectStatisticsService> Optional<T> lookup(Class<T> type) {
+ if (!services.containsKey(type)) return Optional.empty();
+
+ return Optional.of(type.cast(services.get(type)));
+ }
+}
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter32;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter64;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.queues.Queue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.queues.QueueBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.queues.QueueKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.queue.rev130925.QueueId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.duration.DurationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyQueueCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.queue._case.MultipartReplyQueue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.queue._case.multipart.reply.queue.QueueStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.MultipartRequestBody;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestQueueCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.queue._case.MultipartRequestQueueBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.FlowCapableNodeConnectorQueueStatisticsData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.FlowCapableNodeConnectorQueueStatisticsDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.flow.capable.node.connector.queue.statistics.FlowCapableNodeConnectorQueueStatistics;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.flow.capable.node.connector.queue.statistics.FlowCapableNodeConnectorQueueStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.queue.id.and.statistics.map.QueueIdAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.queue.id.and.statistics.map.QueueIdAndStatisticsMapBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The Queue direct statistics service.
+ */
+public class QueueDirectStatisticsService extends AbstractDirectStatisticsService<GetQueueStatisticsInput, GetQueueStatisticsOutput> {
+ /**
+ * Instantiates a new Queue direct statistics service.
+ *
+ * @param requestContextStack the request context stack
+ * @param deviceContext the device context
+ */
+ public QueueDirectStatisticsService(RequestContextStack requestContextStack, DeviceContext deviceContext) {
+ super(MultipartType.OFPMPQUEUE, requestContextStack, deviceContext);
+ }
+
+ @Override
+ protected MultipartRequestBody buildRequestBody(GetQueueStatisticsInput input) {
+ final MultipartRequestQueueBuilder mprQueueBuilder = new MultipartRequestQueueBuilder();
+
+ if (input.getQueueId() != null) {
+ mprQueueBuilder.setQueueId(input.getQueueId().getValue());
+ } else {
+ mprQueueBuilder.setQueueId(OFConstants.OFPQ_ALL);
+ }
+
+ if (input.getNodeConnectorId() != null) {
+ mprQueueBuilder.setPortNo(InventoryDataServiceUtil.portNumberfromNodeConnectorId(getOfVersion(), input.getNodeConnectorId()));
+ } else {
+ mprQueueBuilder.setPortNo(OFConstants.OFPP_ANY);
+ }
+
+ return new MultipartRequestQueueCaseBuilder()
+ .setMultipartRequestQueue(mprQueueBuilder.build())
+ .build();
+ }
+
+ @Override
+ protected GetQueueStatisticsOutput buildReply(List<MultipartReply> input, boolean success) {
+ final List<QueueIdAndStatisticsMap> queueIdAndStatisticsMap = new ArrayList<>();
+
+ if (success) {
+ for (final MultipartReply mpReply : input) {
+ final MultipartReplyQueueCase caseBody = (MultipartReplyQueueCase) mpReply.getMultipartReplyBody();
+ final MultipartReplyQueue replyBody = caseBody.getMultipartReplyQueue();
+
+ for (final QueueStats queueStats : replyBody.getQueueStats()) {
+ final DurationBuilder durationBuilder = new DurationBuilder()
+ .setSecond(new Counter32(queueStats.getDurationSec()))
+ .setNanosecond(new Counter32(queueStats.getDurationNsec()));
+
+ final QueueIdAndStatisticsMapBuilder statsBuilder = new QueueIdAndStatisticsMapBuilder()
+ .setNodeConnectorId(InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(
+ getDatapathId(), queueStats.getPortNo(), getOfVersion()))
+ .setTransmissionErrors(new Counter64(queueStats.getTxErrors()))
+ .setTransmittedBytes(new Counter64(queueStats.getTxBytes()))
+ .setTransmittedPackets(new Counter64(queueStats.getTxPackets()))
+ .setQueueId(new QueueId(queueStats.getQueueId()))
+ .setDuration(durationBuilder.build());
+
+ queueIdAndStatisticsMap.add(statsBuilder.build());
+ }
+ }
+ }
+
+ return new GetQueueStatisticsOutputBuilder()
+ .setQueueIdAndStatisticsMap(queueIdAndStatisticsMap)
+ .build();
+ }
+
+ @Override
+ protected void storeStatistics(GetQueueStatisticsOutput output) throws Exception {
+ final InstanceIdentifier<Node> nodePath = getDeviceContext().getDeviceState().getNodeInstanceIdentifier();
+
+ for (final QueueIdAndStatisticsMap queueStatistics : output.getQueueIdAndStatisticsMap()) {
+ if (queueStatistics.getQueueId() != null) {
+ final QueueKey qKey = new QueueKey(queueStatistics.getQueueId());
+
+ final FlowCapableNodeConnectorQueueStatistics statChild =
+ new FlowCapableNodeConnectorQueueStatisticsBuilder(queueStatistics).build();
+
+ final FlowCapableNodeConnectorQueueStatisticsDataBuilder statBuild =
+ new FlowCapableNodeConnectorQueueStatisticsDataBuilder()
+ .setFlowCapableNodeConnectorQueueStatistics(statChild);
+
+ final InstanceIdentifier<Queue> queueStatisticsPath = nodePath
+ .child(NodeConnector.class, new NodeConnectorKey(queueStatistics.getNodeConnectorId()))
+ .augmentation(FlowCapableNodeConnector.class)
+ .child(Queue.class, qKey);
+
+ final Queue stats = new QueueBuilder()
+ .setKey(qKey)
+ .setQueueId(queueStatistics.getQueueId())
+ .addAugmentation(FlowCapableNodeConnectorQueueStatisticsData.class, statBuild.build()).build();
+
+ getDeviceContext().writeToTransactionWithParentsSlow(LogicalDatastoreType.OPERATIONAL, queueStatisticsPath, stats);
+ }
+ }
+ }
+}
\ No newline at end of file
import org.opendaylight.openflowplugin.impl.statistics.services.OpendaylightPortStatisticsServiceImpl;
import org.opendaylight.openflowplugin.impl.statistics.services.OpendaylightQueueStatisticsServiceImpl;
import org.opendaylight.openflowplugin.impl.statistics.services.compatibility.OpendaylightFlowStatisticsServiceDelegateImpl;
+import org.opendaylight.openflowplugin.impl.statistics.services.direct.FlowDirectStatisticsService;
+import org.opendaylight.openflowplugin.impl.statistics.services.direct.GroupDirectStatisticsService;
+import org.opendaylight.openflowplugin.impl.statistics.services.direct.MeterDirectStatisticsService;
+import org.opendaylight.openflowplugin.impl.statistics.services.direct.NodeConnectorDirectStatisticsService;
+import org.opendaylight.openflowplugin.impl.statistics.services.direct.OpendaylightDirectStatisticsServiceImpl;
+import org.opendaylight.openflowplugin.impl.statistics.services.direct.OpendaylightDirectStatisticsServiceProvider;
+import org.opendaylight.openflowplugin.impl.statistics.services.direct.QueueDirectStatisticsService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.OpendaylightDirectStatisticsService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.echo.service.rev150305.SalEchoService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.experimenter.message.service.rev151020.SalExperimenterMessageService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flat.batch.service.rev160321.SalFlatBatchService;
rpcContext.registerRpcServiceImplementation(NodeConfigService.class, new NodeConfigServiceImpl(rpcContext, deviceContext));
rpcContext.registerRpcServiceImplementation(OpendaylightFlowStatisticsService.class, OpendaylightFlowStatisticsServiceImpl.createWithOook(rpcContext, deviceContext));
+ // Direct statistics gathering
+ final OpendaylightDirectStatisticsServiceProvider statisticsProvider = new OpendaylightDirectStatisticsServiceProvider();
+ statisticsProvider.register(FlowDirectStatisticsService.class, new FlowDirectStatisticsService(rpcContext, deviceContext));
+ statisticsProvider.register(GroupDirectStatisticsService.class, new GroupDirectStatisticsService(rpcContext, deviceContext));
+ statisticsProvider.register(MeterDirectStatisticsService.class, new MeterDirectStatisticsService(rpcContext, deviceContext));
+ statisticsProvider.register(NodeConnectorDirectStatisticsService.class, new NodeConnectorDirectStatisticsService(rpcContext, deviceContext));
+ statisticsProvider.register(QueueDirectStatisticsService.class, new QueueDirectStatisticsService(rpcContext, deviceContext));
+ rpcContext.registerRpcServiceImplementation(OpendaylightDirectStatisticsService.class, new OpendaylightDirectStatisticsServiceImpl(statisticsProvider));
+
final SalFlatBatchServiceImpl salFlatBatchService = new SalFlatBatchServiceImpl(
new SalFlowsBatchServiceImpl(salFlowService, flowCapableTransactionService),
new SalGroupsBatchServiceImpl(salGroupService, flowCapableTransactionService),
rpcContext.unregisterRpcServiceImplementation(SalFlatBatchService.class);
// TODO: experimenter symmetric and multipart message services
rpcContext.unregisterRpcServiceImplementation(SalExperimenterMessageService.class);
+ rpcContext.unregisterRpcServiceImplementation(OpendaylightDirectStatisticsService.class);
}
/**
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
import org.opendaylight.openflowplugin.api.openflow.lifecycle.ServiceChangeListener;
+import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageIntelligenceAgency;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
private HashedWheelTimer hashedWheelTimer;
@Mock
private ListenableFuture<Void> listenableFuture;
+ @Mock
+ private StatisticsManager statisticsManager;
private NodeId nodeId = new NodeId("openflow-junit:1");
private OfpRole ofpRole = OfpRole.NOCHANGE;
lifecycleConductor = new LifecycleConductorImpl(messageIntelligenceAgency);
lifecycleConductor.setSafelyDeviceManager(deviceManager);
+ lifecycleConductor.setSafelyStatisticsManager(statisticsManager);
when(connectionContext.getFeatures()).thenReturn(featuresReply);
}
when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
when(deviceContext.onClusterRoleChange(null, OfpRole.BECOMEMASTER)).thenReturn(listenableFuture);
lifecycleConductor.roleChangeOnDevice(nodeId,true,OfpRole.BECOMEMASTER,false);
- verify(deviceState,times(1)).setRole(OfpRole.BECOMEMASTER);
- verify(deviceState,times(0)).setRole(OfpRole.BECOMESLAVE);
+ verify(statisticsManager).startScheduling(nodeId);
}
/**
when(deviceManager.getDeviceContextFromNodeId(nodeId)).thenReturn(deviceContext);
when(deviceContext.onClusterRoleChange(null, OfpRole.BECOMESLAVE)).thenReturn(listenableFuture);
lifecycleConductor.roleChangeOnDevice(nodeId,true,OfpRole.BECOMESLAVE,false);
- verify(deviceState,times(1)).setRole(OfpRole.BECOMESLAVE);
- verify(deviceState,times(0)).setRole(OfpRole.BECOMEMASTER);
+ verify(statisticsManager).stopScheduling(nodeId);
}
/**
.setPriority(42)
.setCookie(new FlowCookie(BigInteger.ONE))
.setMatch(new MatchBuilder().build());
+ final NotificationPublishService mockedNotificationPublishService = mock(NotificationPublishService.class);
Mockito.when(messageTranslatorFlowRemoved.translate(any(Object.class), any(DeviceState.class), any(Object.class)))
.thenReturn(flowRemovedMdsalBld.build());
.child(Table.class, new TableKey((short) 0))
.child(Flow.class, new FlowKey(new FlowId("ut-ofp:f456")));
+ deviceContext.setNotificationPublishService(mockedNotificationPublishService);
deviceContext.processFlowRemovedMessage(flowRemovedBld.build());
Mockito.verify(itemLifecycleListener).onRemoved(flowToBeRemovedPath);
}
final DeviceManagerImpl deviceManager = prepareDeviceManager(withException);
final DeviceState mockedDeviceState = mock(DeviceState.class);
when(mockedDeviceContext.getDeviceState()).thenReturn(mockedDeviceState);
- when(mockedDeviceState.getRole()).thenReturn(OfpRole.BECOMEMASTER);
when(mockedDeviceState.getNodeId()).thenReturn(mockedNodeId);
if (withException) {
Assert.assertFalse(deviceState.isStatisticsPollingEnabled());
}
- @Test
- public void testRole_initialValue(){
- Assert.assertFalse(deviceState.getRole().equals(OfpRole.BECOMEMASTER));
- Assert.assertFalse(deviceState.getRole().equals(OfpRole.NOCHANGE));
- }
-
@Test
public void testStatistics_initialValue(){
Assert.assertFalse(deviceState.isFlowStatisticsAvailable());
@Test
public void testWriteToTransaction() throws Exception {
final Node data = new NodeBuilder().setId(nodeId).build();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(txChain).newWriteOnlyTransaction();
- Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
}
/**
public void testSubmitTransaction() throws Exception {
final Node data = new NodeBuilder().setId(nodeId).build();
txChainManager.initialSubmitWriteTransaction();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
txChainManager.submitWriteTransaction();
Mockito.verify(txChain).newWriteOnlyTransaction();
- Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(writeTx).submit();
}
@Test
public void testSubmitTransaction1() throws Exception {
final Node data = new NodeBuilder().setId(nodeId).build();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
txChainManager.submitWriteTransaction();
Mockito.verify(txChain).newWriteOnlyTransaction();
- Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(writeTx, Mockito.never()).submit();
}
Mockito.when(writeTx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")));
final Node data = new NodeBuilder().setId(nodeId).build();
txChainManager.initialSubmitWriteTransaction();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
txChainManager.submitWriteTransaction();
Mockito.verify(txChain).newWriteOnlyTransaction();
- Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(writeTx).submit();
}
@Test
public void testEnableCounter1() throws Exception {
final Node data = new NodeBuilder().setId(nodeId).build();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(txChain).newWriteOnlyTransaction();
- Mockito.verify(writeTx, Mockito.times(2)).put(LogicalDatastoreType.CONFIGURATION, path, data);
+ Mockito.verify(writeTx, Mockito.times(2)).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(writeTx, Mockito.never()).submit();
}
public void testDeactivateTransactionChainManagerFailed() throws Exception {
Mockito.when(writeTx.submit()).thenReturn(Futures.<Void, TransactionCommitFailedException>immediateFailedCheckedFuture(new TransactionCommitFailedException("mock")));
final Node data = new NodeBuilder().setId(nodeId).build();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
txChainManager.deactivateTransactionManager();
Mockito.verify(txChain).newWriteOnlyTransaction();
- Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(writeTx).submit();
Mockito.verify(txChain).close();
}
@Test
public void testShuttingDown() throws Exception{
final Node data = new NodeBuilder().setId(nodeId).build();
- txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data);
+ txChainManager.writeToTransaction(LogicalDatastoreType.CONFIGURATION, path, data, false);
txChainManager.shuttingDown();
Mockito.verify(txChain).newWriteOnlyTransaction();
- Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data);
+ Mockito.verify(writeTx).put(LogicalDatastoreType.CONFIGURATION, path, data, false);
Mockito.verify(writeTx).submit();
}
Mockito.when(connectionContext.getFeatures()).thenReturn(features);
Mockito.when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
Mockito.when(deviceContext.getDeviceState()).thenReturn(deviceState);
- Mockito.when(deviceContext.getDeviceState().getRole()).thenReturn(OfpRole.BECOMEMASTER);
Mockito.when(deviceContext.getItemLifeCycleSourceRegistry()).thenReturn(itemLifeCycleRegistry);
Mockito.when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodePath);
Mockito.when(deviceState.getFeatures()).thenReturn(featuresOutput);
@Test
public void testOnDeviceContextLevelUpMaster() throws Exception {
- Mockito.when(deviceState.getRole()).thenReturn(OfpRole.BECOMEMASTER);
rpcManager.onDeviceContextLevelUp(nodeId);
verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
}
@Test
public void testOnDeviceContextLevelUpSlave() throws Exception {
- Mockito.when(deviceState.getRole()).thenReturn(OfpRole.BECOMESLAVE);
rpcManager.onDeviceContextLevelUp(nodeId);
verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
}
@Test
public void testOnDeviceContextLevelUpOther() throws Exception {
- Mockito.when(deviceState.getRole()).thenReturn(OfpRole.NOCHANGE);
rpcManager.onDeviceContextLevelUp(nodeId);
verify(deviceINitializationPhaseHandler).onDeviceContextLevelUp(nodeId);
}
@Test
public void testOnDeviceContextLevelDown() throws Exception {
- Mockito.when(deviceState.getRole()).thenReturn(OfpRole.NOCHANGE);
rpcManager.onDeviceContextLevelDown(deviceContext);
verify(deviceTerminationPhaseHandler).onDeviceContextLevelDown(deviceContext);
}
private LifecycleConductor conductor;
@Mock
private GetFeaturesOutput featuresOutput;
+ @Mock
+ private DeviceInitializationPhaseHandler deviceInitializationPhaseHandler;
private RequestContext<List<MultipartReply>> currentRequestContext;
private StatisticsManagerImpl statisticsManager;
Matchers.<StatisticsManagerControlService>any())).thenReturn(serviceControlRegistration);
statisticsManager = new StatisticsManagerImpl(rpcProviderRegistry, false, conductor);
+ statisticsManager.setDeviceInitializationPhaseHandler(deviceInitializationPhaseHandler);
when(deviceManager.getDeviceContextFromNodeId(Mockito.<NodeId>any())).thenReturn(mockedDeviceContext);
when(conductor.getDeviceContext(Mockito.<NodeId>any())).thenReturn(mockedDeviceContext);
}
}
@Test
- public void testPollStatistics() {
- StatisticsManager statisticsManagerSpy = Mockito.spy(statisticsManager);
-
+ public void testPollStatistics() throws Exception {
final StatisticsContext statisticsContext = Mockito.mock(StatisticsContext.class);
final TimeCounter mockTimerCounter = Mockito.mock(TimeCounter.class);
statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
- verify(mockedDeviceContext, times(2)).getDeviceState();
+ verify(mockedDeviceContext).getDeviceState();
when(mockedDeviceContext.getDeviceState().isValid()).thenReturn(true);
statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
// TODO Make scheduleNextPolling visible for tests?
- when(mockedDeviceContext.getDeviceState().getRole()).thenReturn(OfpRole.BECOMEMASTER);
when(statisticsContext.gatherDynamicData()).thenReturn(Futures.immediateCheckedFuture(Boolean.TRUE));
+ when(statisticsContext.isSchedulingEnabled()).thenReturn(Boolean.TRUE);
statisticsManager.pollStatistics(mockedDeviceContext, statisticsContext, mockTimerCounter);
Mockito.verify(mockTimerCounter).markStart();
Mockito.verify(mockTimerCounter).addTimeMark();
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueue;
+import org.opendaylight.openflowplugin.api.OFConstants;
+import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
+import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
+import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack;
+import org.opendaylight.openflowplugin.api.openflow.device.TranslatorLibrary;
+import org.opendaylight.openflowplugin.api.openflow.device.handlers.MultiMsgCollector;
+import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
+import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
+import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
+import org.opendaylight.openflowplugin.openflow.md.util.OpenflowPortsUtil;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
+
+import java.math.BigInteger;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public abstract class AbstractDirectStatisticsServiceTest {
+ protected static final Long PORT_NO = 1L;
+ protected static final BigInteger DATAPATH_ID = BigInteger.TEN;
+ protected static final short OF_VERSION = OFConstants.OFP_VERSION_1_3;
+ protected static final String NODE_ID = "openflow:1";
+
+ @Mock
+ protected RequestContextStack requestContextStack;
+ @Mock
+ protected DeviceContext deviceContext;
+ @Mock
+ protected ConnectionContext connectionContext;
+ @Mock
+ protected FeaturesReply features;
+ @Mock
+ protected MessageSpy messageSpy;
+ @Mock
+ protected OutboundQueue outboundQueueProvider;
+ @Mock
+ protected MultiMsgCollector multiMsgCollector;
+ @Mock
+ protected TranslatorLibrary translatorLibrary;
+ @Mock
+ protected DeviceState deviceState;
+ @Mock
+ protected GetFeaturesOutput getFeaturesOutput;
+
+ protected NodeConnectorId nodeConnectorId;
+ protected KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier;
+
+ protected static NodeRef createNodeRef(String nodeIdValue) {
+ InstanceIdentifier<Node> nodePath = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId(nodeIdValue)));
+
+ return new NodeRef(nodePath);
+ }
+
+ @Before
+ public void init() throws Exception {
+ OpenflowPortsUtil.init();
+
+ nodeConnectorId = InventoryDataServiceUtil.nodeConnectorIdfromDatapathPortNo(
+ DATAPATH_ID, PORT_NO, OpenflowVersion.get(OF_VERSION));
+
+ nodeInstanceIdentifier = InstanceIdentifier
+ .create(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId(NODE_ID)));
+
+ when(deviceContext.getPrimaryConnectionContext()).thenReturn(connectionContext);
+ when(deviceContext.getMessageSpy()).thenReturn(messageSpy);
+ when(deviceContext.getMultiMsgCollector(any())).thenReturn(multiMsgCollector);
+ when(deviceContext.oook()).thenReturn(translatorLibrary);
+ when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ when(deviceContext.getDeviceState()).thenReturn(deviceState);
+ when(deviceState.getNodeInstanceIdentifier()).thenReturn(nodeInstanceIdentifier);
+ when(deviceState.getNodeId()).thenReturn(new NodeId(NODE_ID));
+ when(deviceState.getVersion()).thenReturn(OF_VERSION);
+ when(deviceState.getFeatures()).thenReturn(getFeaturesOutput);
+ when(getFeaturesOutput.getVersion()).thenReturn(OF_VERSION);
+ when(getFeaturesOutput.getDatapathId()).thenReturn(DATAPATH_ID);
+ when(connectionContext.getFeatures()).thenReturn(features);
+ when(connectionContext.getOutboundQueueProvider()).thenReturn(outboundQueueProvider);
+ when(features.getVersion()).thenReturn(OF_VERSION);
+ when(features.getDatapathId()).thenReturn(DATAPATH_ID);
+ setUp();
+ }
+
+ protected abstract void setUp() throws Exception;
+
+ @Test
+ public abstract void testBuildRequestBody() throws Exception;
+
+ @Test
+ public abstract void testBuildReply() throws Exception;
+
+ @Test
+ public abstract void testStoreStatistics() throws Exception;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.openflowplugin.api.openflow.registry.flow.DeviceFlowRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowAndStatisticsMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.flow._case.MultipartReplyFlow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.flow._case.multipart.reply.flow.FlowStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestFlowCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.flow._case.MultipartRequestFlow;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class FlowDirectStatisticsServiceTest extends AbstractDirectStatisticsServiceTest {
+ static final Short TABLE_NO = 1;
+ private FlowDirectStatisticsService service;
+
+ @Override
+ public void setUp() throws Exception {
+ service = new FlowDirectStatisticsService(requestContextStack, deviceContext);
+ final DeviceFlowRegistry registry = mock(DeviceFlowRegistry.class);
+ when(registry.storeIfNecessary(any(), eq(TABLE_NO))).thenReturn(new FlowId("1"));
+ when(deviceContext.getDeviceFlowRegistry()).thenReturn(registry);
+ }
+
+ @Override
+ public void testBuildRequestBody() throws Exception {
+ final GetFlowStatisticsInput input = mock(GetFlowStatisticsInput.class);
+
+ when(input.getNode()).thenReturn(createNodeRef(NODE_ID));
+ when(input.getTableId()).thenReturn(TABLE_NO);
+
+ final MultipartRequestFlowCase body = (MultipartRequestFlowCase) service.buildRequestBody(input);
+ final MultipartRequestFlow flow = body.getMultipartRequestFlow();
+
+ assertEquals(TABLE_NO, flow.getTableId());
+ }
+
+ @Override
+ public void testBuildReply() throws Exception {
+ final MultipartReply reply = mock(MultipartReply.class);
+ final MultipartReplyFlowCase flowCase = mock(MultipartReplyFlowCase.class);
+ final MultipartReplyFlow flow = mock(MultipartReplyFlow.class);
+ final FlowStats flowStat = mock(FlowStats.class);
+ final List<FlowStats> flowStats = Arrays.asList(flowStat);
+ final List<MultipartReply> input = Arrays.asList(reply);
+
+ when(flow.getFlowStats()).thenReturn(flowStats);
+ when(flowCase.getMultipartReplyFlow()).thenReturn(flow);
+ when(reply.getMultipartReplyBody()).thenReturn(flowCase);
+
+ when(flowStat.getTableId()).thenReturn(TABLE_NO);
+ when(flowStat.getByteCount()).thenReturn(BigInteger.ONE);
+ when(flowStat.getPacketCount()).thenReturn(BigInteger.ONE);
+ when(flowStat.getFlags()).thenReturn(mock(FlowModFlags.class));
+ when(flowStat.getMatch()).thenReturn(new org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.grouping.MatchBuilder()
+ .setMatchEntry(Collections.emptyList())
+ .build());
+
+ final GetFlowStatisticsOutput output = service.buildReply(input, true);
+ assertTrue(output.getFlowAndStatisticsMapList().size() > 0);
+
+ final FlowAndStatisticsMap stats = output.getFlowAndStatisticsMapList().get(0);
+
+ assertEquals(stats.getTableId(), TABLE_NO);
+ }
+
+ @Override
+ public void testStoreStatistics() throws Exception {
+ final FlowAndStatisticsMapList stat = mock(FlowAndStatisticsMapList.class);
+ when(stat.getTableId()).thenReturn(TABLE_NO);
+ when(stat.getMatch()).thenReturn(new MatchBuilder().build());
+
+ final List<FlowAndStatisticsMapList> stats = Arrays.asList(stat);
+ final GetFlowStatisticsOutput output = mock(GetFlowStatisticsOutput.class);
+ when(output.getFlowAndStatisticsMapList()).thenReturn(stats);
+
+ service.storeStatistics(output);
+ verify(deviceContext).writeToTransactionWithParentsSlow(eq(LogicalDatastoreType.OPERATIONAL), any(), any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.group._case.MultipartReplyGroup;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.group._case.multipart.reply.group.GroupStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestGroupCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.group._case.MultipartRequestGroup;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class GroupDirectStatisticsServiceTest extends AbstractDirectStatisticsServiceTest {
+ static final Long GROUP_NO = 1L;
+ private GroupDirectStatisticsService service;
+
+ @Override
+ public void setUp() throws Exception {
+ service = new GroupDirectStatisticsService(requestContextStack, deviceContext);
+ }
+
+ @Override
+ public void testBuildRequestBody() throws Exception {
+ final GetGroupStatisticsInput input = mock(GetGroupStatisticsInput.class);
+
+ when(input.getNode()).thenReturn(createNodeRef(NODE_ID));
+ when(input.getGroupId()).thenReturn(new GroupId(GROUP_NO));
+
+ final MultipartRequestGroupCase body = (MultipartRequestGroupCase) service.buildRequestBody(input);
+ final MultipartRequestGroup group = body.getMultipartRequestGroup();
+
+ assertEquals(GROUP_NO, group.getGroupId().getValue());
+ }
+
+ @Override
+ public void testBuildReply() throws Exception {
+ final MultipartReply reply = mock(MultipartReply.class);
+ final MultipartReplyGroupCase groupCase = mock(MultipartReplyGroupCase.class);
+ final MultipartReplyGroup group = mock(MultipartReplyGroup.class);
+ final GroupStats groupStat = mock(GroupStats.class);
+ final List<GroupStats> groupStats = Arrays.asList(groupStat);
+ final List<MultipartReply> input = Arrays.asList(reply);
+
+ when(group.getGroupStats()).thenReturn(groupStats);
+ when(groupCase.getMultipartReplyGroup()).thenReturn(group);
+ when(reply.getMultipartReplyBody()).thenReturn(groupCase);
+
+ when(groupStat.getGroupId()).thenReturn(new org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.GroupId(GROUP_NO));
+ when(groupStat.getByteCount()).thenReturn(BigInteger.ONE);
+ when(groupStat.getPacketCount()).thenReturn(BigInteger.ONE);
+
+ final GetGroupStatisticsOutput output = service.buildReply(input, true);
+ assertTrue(output.getGroupStats().size() > 0);
+
+ final org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats stats =
+ output.getGroupStats().get(0);
+
+ assertEquals(stats.getGroupId().getValue(), GROUP_NO);
+ }
+
+ @Override
+ public void testStoreStatistics() throws Exception {
+ final org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats stat = mock(org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats.class);
+ when(stat.getGroupId()).thenReturn(new GroupId(GROUP_NO));
+
+ final List<org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.group.statistics.reply.GroupStats> stats = Arrays.asList(stat);
+ final GetGroupStatisticsOutput output = mock(GetGroupStatisticsOutput.class);
+ when(output.getGroupStats()).thenReturn(stats);
+
+ service.storeStatistics(output);
+ verify(deviceContext).writeToTransactionWithParentsSlow(eq(LogicalDatastoreType.OPERATIONAL), any(), any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.meter._case.MultipartReplyMeter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.meter._case.multipart.reply.meter.MeterStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestMeterCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.meter._case.MultipartRequestMeter;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MeterDirectStatisticsServiceTest extends AbstractDirectStatisticsServiceTest {
+ static final Long METER_NO = 1L;
+ private MeterDirectStatisticsService service;
+
+ @Override
+ public void setUp() throws Exception {
+ service = new MeterDirectStatisticsService(requestContextStack, deviceContext);
+ }
+
+ @Override
+ public void testBuildRequestBody() throws Exception {
+ final GetMeterStatisticsInput input = mock(GetMeterStatisticsInput.class);
+
+ when(input.getNode()).thenReturn(createNodeRef(NODE_ID));
+ when(input.getMeterId()).thenReturn(new MeterId(METER_NO));
+
+ final MultipartRequestMeterCase body = (MultipartRequestMeterCase) service.buildRequestBody(input);
+ final MultipartRequestMeter meter = body.getMultipartRequestMeter();
+
+ assertEquals(METER_NO, meter.getMeterId().getValue());
+ }
+
+ @Override
+ public void testBuildReply() throws Exception {
+ final MultipartReply reply = mock(MultipartReply.class);
+ final MultipartReplyMeterCase MeterCase = mock(MultipartReplyMeterCase.class);
+ final MultipartReplyMeter meter = mock(MultipartReplyMeter.class);
+ final MeterStats meterStat = mock(MeterStats.class);
+ final List<MeterStats> meterStats = Arrays.asList(meterStat);
+ final List<MultipartReply> input = Arrays.asList(reply);
+
+ when(meter.getMeterStats()).thenReturn(meterStats);
+ when(MeterCase.getMultipartReplyMeter()).thenReturn(meter);
+ when(reply.getMultipartReplyBody()).thenReturn(MeterCase);
+
+ when(meterStat.getMeterId()).thenReturn(new org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MeterId(METER_NO));
+ when(meterStat.getByteInCount()).thenReturn(BigInteger.ONE);
+ when(meterStat.getPacketInCount()).thenReturn(BigInteger.ONE);
+
+ final GetMeterStatisticsOutput output = service.buildReply(input, true);
+ assertTrue(output.getMeterStats().size() > 0);
+
+ final org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats stats =
+ output.getMeterStats().get(0);
+
+ assertEquals(stats.getMeterId().getValue(), METER_NO);
+ }
+
+ @Override
+ public void testStoreStatistics() throws Exception {
+ final org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats stat = mock(org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats.class);
+ when(stat.getMeterId()).thenReturn(new MeterId(METER_NO));
+
+ final List<org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.meter.statistics.reply.MeterStats> stats = Arrays.asList(stat);
+ final GetMeterStatisticsOutput output = mock(GetMeterStatisticsOutput.class);
+ when(output.getMeterStats()).thenReturn(stats);
+
+ service.storeStatistics(output);
+ verify(deviceContext).writeToTransactionWithParentsSlow(eq(LogicalDatastoreType.OPERATIONAL), any(), any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyPortStatsCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.port.stats._case.MultipartReplyPortStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.port.stats._case.multipart.reply.port.stats.PortStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestPortStatsCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.port.stats._case.MultipartRequestPortStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.node.connector.statistics.and.port.number.map.NodeConnectorStatisticsAndPortNumberMap;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class NodeConnectorDirectStatisticsServiceTest extends AbstractDirectStatisticsServiceTest {
+ private NodeConnectorDirectStatisticsService service;
+
+ @Override
+ public void setUp() throws Exception {
+ service = new NodeConnectorDirectStatisticsService(requestContextStack, deviceContext);
+ }
+
+ @Override
+ public void testBuildRequestBody() throws Exception {
+ final GetNodeConnectorStatisticsInput input = mock(GetNodeConnectorStatisticsInput.class);
+
+ when(input.getNode()).thenReturn(createNodeRef(NODE_ID));
+ when(input.getNodeConnectorId()).thenReturn(nodeConnectorId);
+
+ final MultipartRequestPortStatsCase body = (MultipartRequestPortStatsCase) service.buildRequestBody(input);
+ final MultipartRequestPortStats nodeConnector = body.getMultipartRequestPortStats();
+
+ assertEquals(PORT_NO, nodeConnector.getPortNo());
+ }
+
+ @Override
+ public void testBuildReply() throws Exception {
+ final MultipartReply reply = mock(MultipartReply.class);
+ final MultipartReplyPortStatsCase nodeConnectorCase = mock(MultipartReplyPortStatsCase.class);
+ final MultipartReplyPortStats nodeConnector = mock(MultipartReplyPortStats.class);
+ final PortStats nodeConnectorStat = mock(PortStats.class);
+ final List<PortStats> nodeConnectorStats = Arrays.asList(nodeConnectorStat);
+ final List<MultipartReply> input = Arrays.asList(reply);
+
+ when(nodeConnector.getPortStats()).thenReturn(nodeConnectorStats);
+ when(nodeConnectorCase.getMultipartReplyPortStats()).thenReturn(nodeConnector);
+ when(reply.getMultipartReplyBody()).thenReturn(nodeConnectorCase);
+
+ when(nodeConnectorStat.getPortNo()).thenReturn(PORT_NO);
+ when(nodeConnectorStat.getTxBytes()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getCollisions()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getRxBytes()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getRxCrcErr()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getRxDropped()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getRxErrors()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getRxFrameErr()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getRxOverErr()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getRxPackets()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getTxDropped()).thenReturn(BigInteger.ONE);
+ when(nodeConnectorStat.getTxErrors()).thenReturn(BigInteger.ONE);
+
+ final GetNodeConnectorStatisticsOutput output = service.buildReply(input, true);
+ assertTrue(output.getNodeConnectorStatisticsAndPortNumberMap().size() > 0);
+
+ final NodeConnectorStatisticsAndPortNumberMap stats =
+ output.getNodeConnectorStatisticsAndPortNumberMap().get(0);
+
+ assertEquals(stats.getNodeConnectorId(), nodeConnectorId);
+ }
+
+ @Override
+ public void testStoreStatistics() throws Exception {
+ final NodeConnectorStatisticsAndPortNumberMap stat = mock(NodeConnectorStatisticsAndPortNumberMap.class);
+ when(stat.getNodeConnectorId()).thenReturn(nodeConnectorId);
+
+ final List<NodeConnectorStatisticsAndPortNumberMap> stats = Arrays.asList(stat);
+ final GetNodeConnectorStatisticsOutput output = mock(GetNodeConnectorStatisticsOutput.class);
+ when(output.getNodeConnectorStatisticsAndPortNumberMap()).thenReturn(stats);
+
+ service.storeStatistics(output);
+ verify(deviceContext).writeToTransactionWithParentsSlow(eq(LogicalDatastoreType.OPERATIONAL), any(), any());
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetFlowStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetGroupStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetMeterStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetNodeConnectorStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.OpendaylightDirectStatisticsService;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public class OpendaylightDirectStatisticsServiceImplTest {
+ @Mock
+ FlowDirectStatisticsService flowDirectStatisticsService;
+ @Mock
+ GroupDirectStatisticsService groupDirectStatisticsService;
+ @Mock
+ MeterDirectStatisticsService meterDirectStatisticsService;
+ @Mock
+ NodeConnectorDirectStatisticsService nodeConnectorDirectStatisticsService;
+ @Mock
+ QueueDirectStatisticsService queueDirectStatisticsService;
+
+ @Mock
+ GetGroupStatisticsInput getGroupStatisticsInput;
+ @Mock
+ GetQueueStatisticsInput getQueueStatisticsInput;
+ @Mock
+ GetFlowStatisticsInput getFlowStatisticsInput;
+ @Mock
+ GetMeterStatisticsInput getMeterStatisticsInput;
+ @Mock
+ GetNodeConnectorStatisticsInput getNodeConnectorStatisticsInput;
+
+ private OpendaylightDirectStatisticsService service;
+ private OpendaylightDirectStatisticsService emptyService;
+
+ @Before
+ public void setUp() throws Exception {
+ final OpendaylightDirectStatisticsServiceProvider provider = new OpendaylightDirectStatisticsServiceProvider();
+ provider.register(FlowDirectStatisticsService.class, flowDirectStatisticsService);
+ provider.register(GroupDirectStatisticsService.class, groupDirectStatisticsService);
+ provider.register(MeterDirectStatisticsService.class, meterDirectStatisticsService);
+ provider.register(NodeConnectorDirectStatisticsService.class, nodeConnectorDirectStatisticsService);
+ provider.register(QueueDirectStatisticsService.class, queueDirectStatisticsService);
+
+ service = new OpendaylightDirectStatisticsServiceImpl(provider);
+ emptyService = new OpendaylightDirectStatisticsServiceImpl(new OpendaylightDirectStatisticsServiceProvider());
+ }
+
+ @Test
+ public void testGetGroupStatistics() throws Exception {
+ service.getGroupStatistics(getGroupStatisticsInput);
+ verify(groupDirectStatisticsService).handleAndReply(getGroupStatisticsInput);
+ }
+
+ @Test
+ public void testGetGroupStatisticsFail() throws Exception {
+ RpcResult<GetGroupStatisticsOutput> result = emptyService
+ .getGroupStatistics(getGroupStatisticsInput)
+ .get();
+
+ assertFalse(result.isSuccessful());
+
+ for (RpcError error : result.getErrors()) {
+ assertTrue(error.getMessage().contains(GroupDirectStatisticsService.class.getSimpleName()));
+ }
+
+ verify(groupDirectStatisticsService, times(0)).handleAndReply(getGroupStatisticsInput);
+ }
+
+ @Test
+ public void testGetQueueStatistics() throws Exception {
+ service.getQueueStatistics(getQueueStatisticsInput);
+ verify(queueDirectStatisticsService).handleAndReply(getQueueStatisticsInput);
+ }
+
+ @Test
+ public void testGetQueueStatisticsFail() throws Exception {
+ RpcResult<GetQueueStatisticsOutput> result = emptyService
+ .getQueueStatistics(getQueueStatisticsInput)
+ .get();
+
+ assertFalse(result.isSuccessful());
+
+ for (RpcError error : result.getErrors()) {
+ assertTrue(error.getMessage().contains(QueueDirectStatisticsService.class.getSimpleName()));
+ }
+
+ verify(queueDirectStatisticsService, times(0)).handleAndReply(getQueueStatisticsInput);
+ }
+
+ @Test
+ public void testGetFlowStatistics() throws Exception {
+ service.getFlowStatistics(getFlowStatisticsInput);
+ verify(flowDirectStatisticsService).handleAndReply(getFlowStatisticsInput);
+ }
+
+ @Test
+ public void testGetFlowStatisticsFail() throws Exception {
+ RpcResult<GetFlowStatisticsOutput> result = emptyService
+ .getFlowStatistics(getFlowStatisticsInput)
+ .get();
+
+ assertFalse(result.isSuccessful());
+
+ for (RpcError error : result.getErrors()) {
+ assertTrue(error.getMessage().contains(FlowDirectStatisticsService.class.getSimpleName()));
+ }
+
+ verify(flowDirectStatisticsService, times(0)).handleAndReply(getFlowStatisticsInput);
+ }
+
+ @Test
+ public void testGetMeterStatistics() throws Exception {
+ service.getMeterStatistics(getMeterStatisticsInput);
+ verify(meterDirectStatisticsService).handleAndReply(getMeterStatisticsInput);
+ }
+
+ @Test
+ public void testGetMeterStatisticsFail() throws Exception {
+ RpcResult<GetMeterStatisticsOutput> result = emptyService
+ .getMeterStatistics(getMeterStatisticsInput)
+ .get();
+
+ assertFalse(result.isSuccessful());
+
+ for (RpcError error : result.getErrors()) {
+ assertTrue(error.getMessage().contains(MeterDirectStatisticsService.class.getSimpleName()));
+ }
+
+ verify(meterDirectStatisticsService, times(0)).handleAndReply(getMeterStatisticsInput);
+ }
+
+ @Test
+ public void testGetNodeConnectorStatistics() throws Exception {
+ service.getNodeConnectorStatistics(getNodeConnectorStatisticsInput);
+ verify(nodeConnectorDirectStatisticsService).handleAndReply(getNodeConnectorStatisticsInput);
+ }
+
+ @Test
+ public void testGetNodeConnectorStatisticsFail() throws Exception {
+ RpcResult<GetNodeConnectorStatisticsOutput> result = emptyService
+ .getNodeConnectorStatistics(getNodeConnectorStatisticsInput)
+ .get();
+
+ assertFalse(result.isSuccessful());
+
+ for (RpcError error : result.getErrors()) {
+ assertTrue(error.getMessage().contains(NodeConnectorDirectStatisticsService.class.getSimpleName()));
+ }
+
+ verify(nodeConnectorDirectStatisticsService, times(0)).handleAndReply(getNodeConnectorStatisticsInput);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.openflowplugin.impl.statistics.services.direct;
+
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.direct.statistics.rev160511.GetQueueStatisticsOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.queue.rev130925.QueueId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyQueueCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.queue._case.MultipartReplyQueue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.queue._case.multipart.reply.queue.QueueStats;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.MultipartRequestQueueCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.request.multipart.request.body.multipart.request.queue._case.MultipartRequestQueue;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.queue.id.and.statistics.map.QueueIdAndStatisticsMap;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class QueueDirectStatisticsServiceTest extends AbstractDirectStatisticsServiceTest {
+ static final Long QUEUE_NO = 1L;
+ private QueueDirectStatisticsService service;
+
+ @Override
+ public void setUp() throws Exception {
+ service = new QueueDirectStatisticsService(requestContextStack, deviceContext);
+ }
+
+ @Override
+ public void testBuildRequestBody() throws Exception {
+ final GetQueueStatisticsInput input = mock(GetQueueStatisticsInput.class);
+
+ when(input.getNode()).thenReturn(createNodeRef(NODE_ID));
+ when(input.getQueueId()).thenReturn(new QueueId(QUEUE_NO));
+ when(input.getNodeConnectorId()).thenReturn(new NodeConnectorId(NODE_ID + ":" + PORT_NO));
+
+ final MultipartRequestQueueCase body = (MultipartRequestQueueCase) service.buildRequestBody(input);
+ final MultipartRequestQueue queue = body.getMultipartRequestQueue();
+
+ assertEquals(PORT_NO, queue.getPortNo());
+ assertEquals(QUEUE_NO, queue.getQueueId());
+ }
+
+ @Override
+ public void testBuildReply() throws Exception {
+ final MultipartReply reply = mock(MultipartReply.class);
+ final MultipartReplyQueueCase queueCase = mock(MultipartReplyQueueCase.class);
+ final MultipartReplyQueue queue = mock(MultipartReplyQueue.class);
+ final QueueStats queueStat = mock(QueueStats.class);
+ final List<QueueStats> queueStats = Arrays.asList(queueStat);
+ final List<MultipartReply> input = Arrays.asList(reply);
+
+ when(queue.getQueueStats()).thenReturn(queueStats);
+ when(queueCase.getMultipartReplyQueue()).thenReturn(queue);
+ when(reply.getMultipartReplyBody()).thenReturn(queueCase);
+
+ when(queueStat.getPortNo()).thenReturn(PORT_NO);
+ when(queueStat.getQueueId()).thenReturn(QUEUE_NO);
+ when(queueStat.getTxBytes()).thenReturn(BigInteger.ONE);
+ when(queueStat.getTxErrors()).thenReturn(BigInteger.ONE);
+ when(queueStat.getTxPackets()).thenReturn(BigInteger.ONE);
+
+ final GetQueueStatisticsOutput output = service.buildReply(input, true);
+ assertTrue(output.getQueueIdAndStatisticsMap().size() > 0);
+
+ final QueueIdAndStatisticsMap map = output.getQueueIdAndStatisticsMap().get(0);
+ assertEquals(map.getQueueId().getValue(), QUEUE_NO);
+ assertEquals(map.getNodeConnectorId(), nodeConnectorId);
+ }
+
+ @Override
+ public void testStoreStatistics() throws Exception {
+ final QueueIdAndStatisticsMap map = mock(QueueIdAndStatisticsMap.class);
+ when(map.getQueueId()).thenReturn(new QueueId(QUEUE_NO));
+
+ final List<QueueIdAndStatisticsMap> maps = Arrays.asList(map);
+ final GetQueueStatisticsOutput output = mock(GetQueueStatisticsOutput.class);
+ when(output.getQueueIdAndStatisticsMap()).thenReturn(maps);
+
+ service.storeStatistics(output);
+ verify(deviceContext).writeToTransactionWithParentsSlow(eq(LogicalDatastoreType.OPERATIONAL), any(), any());
+ }
+}
\ No newline at end of file
public class MdSalRegistrationUtilsTest {
/**
- * Number of currently registrated services (can be changed) in {@link MdSalRegistrationUtils#registerServices
+ * Number of currently registrated services (can be changed)
* (RpcContext, DeviceContext)}
*/
- private static final int NUMBER_OF_RPC_SERVICE_REGISTRATION = 12;
+ private static final int NUMBER_OF_RPC_SERVICE_REGISTRATION = 13;
@Test
public void registerServiceTest() {